【AWS DevSecOps】既存の複数のセキュリティグループに一括で複数のルールを追加/削除できる方法

記事タイトルとURLをコピーする

こんにちは!イーゴリです。

既存の複数のセキュリティグループに複数のルールを追加/削除する場合、手作業で行うと時間がかかるのと、オペミスなどが発生する可能性があるため、CSVファイルに必要なルールを入れてスクリプトを実行すると便利だと思い、下記のスクリプトを作ってみました。

注意点:このスクリプトについては、一切の責任を負えませんので、ご自身で十分にご検証した上で、このスクリプトを使ってもよいかご判断ください。

前提条件

  • AWS CLIがインストールされていること
  • AWS CLIが設定されていること
  • jq (任意)

CSVファイルの作成

「list.csv」のファイルを作成します。

下記の例の通り、「list.csv」に記載します。

1行目から3行目まで記入例を記載します。

4行目からスクリプトを読み込ませる情報を記載します。

4行目からは、余裕を持って「1」から「100」までの項番を作成します。項番の横に必要なSGルール内容を記載します。

ちなみに「Source」の欄にIPを記載する際、毎回「/32」のサブネットマスクを入れるのは手間がかかりますので、CSVに「/32」なしで入力し、スクリプト内に「/32」を入れました。もし別のサブネットマスクを入れる必要がある場合、CSV上で追記します(例:192.168.1.0/24)。

入力するフォーマットは上記の画像の例の通りなので、説明は不要だと思いますが、要点としてはB列に[add]を記載する場合は「ルールの追加」になり、[remove]を記載する場合は「ルールの削除」になります。

なお、この「list.csv」を手順書としても使いたいため、2-3行目を例のために残します。4行目からスクリプトが必要なパラメーターを読み始めます。

スクリプトの作成

existsg.shを作成します。作業内容(インバウンドルールの処理/アウトバウンドルールの処理)によって下記の内容をコピーします。

インバウンドルールの場合

#!/bin/bash
# CSVファイルのパス
CSV_FILE="$(pwd)/list.csv"
# 操作間の遅延(秒単位)
DELAY=3
# CSVファイルを4行目から読み込み
tail -n +4 "$CSV_FILE" | while IFS=, read -r line_number operation sg_id source_type source protocol port_from port_to description; do
    # Descriptionから使えない特殊文字を削除
    description=$(echo "$description" | tr -cd 'a-zA-Z0-9. _-:/()#,@[]+=&;{}!$*')
    # 操作が必要ない行をスキップ
    if [[ -z "$operation" ]]; then
        continue
    fi
    # 操作の定義
    if [ "$operation" == "add" ]; then
        action="authorize-security-group-ingress"
        action_text="追加"
    elif [ "$operation" == "remove" ]; then
        action="revoke-security-group-ingress"
        action_text="削除"
    else
        echo "無効な操作です: $operation"
        continue
    fi
    # Source IPにマスクが含まれているか確認し、ない場合は/32を追加
    if [[ "$source_type" == "ip" && "$source" != *"/"* ]]; then
        source="$source/32"
    fi
    # 実行中の操作に関するメッセージを表示
    echo "対象SG: $sg_id, $action_text: $source (タイプ: $source_type), $port_from から $port_to までの範囲 (プロトコル: $protocol, 説明: $description) $action_text"
    # 操作の実行
    if [ "$source_type" == "ip" ]; then
        aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",IpRanges="[{CidrIp='$source',Description='$description'}]"
    elif [ "$source_type" == "sg" ]; then
        aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",UserIdGroupPairs="[{GroupId='$source',Description='$description'}]"
    else
        echo "無効なソースタイプです: $source_type"
        continue
    fi
    # 操作の完了を確認し、3秒待つ
    if [ $? -eq 0 ]; then
        echo "対象SG: $sg_id -> $source$action_text しました"
    else
        echo "対象SG: $sg_id -> $source$action_text できませんでした"
    fi
    sleep $DELAY
done

アウトバウンドルールの場合

インバウンドルールと全く同じ内容ですが、「ingress」というキーワードを「egress」に変更しました。

#!/bin/bash
# CSVファイルのパス
CSV_FILE="$(pwd)/list.csv"
# 操作間の遅延(秒単位)
DELAY=3
# CSVファイルを4行目から読み込み
tail -n +4 "$CSV_FILE" | while IFS=, read -r line_number operation sg_id source_type source protocol port_from port_to description; do
    # Descriptionから使えない特殊文字を削除
    description=$(echo "$description" | tr -cd 'a-zA-Z0-9. _-:/()#,@[]+=&;{}!$*')
    # 操作が必要ない行をスキップ
    if [[ -z "$operation" ]]; then
        continue
    fi
    # 操作の定義
    if [ "$operation" == "add" ]; then
        action="authorize-security-group-egress"
        action_text="追加"
    elif [ "$operation" == "remove" ]; then
        action="revoke-security-group-egress"
        action_text="削除"
    else
        echo "無効な操作です: $operation"
        continue
    fi
    # Source IPにマスクが含まれているか確認し、ない場合は/32を追加
    if [[ "$source_type" == "ip" && "$source" != *"/"* ]]; then
        source="$source/32"
    fi
    # 実行中の操作に関するメッセージを表示
    echo "対象SG: $sg_id, $action_text: $source (タイプ: $source_type), $port_from から $port_to までの範囲 (プロトコル: $protocol, 説明: $description) $action_text"
    # 操作の実行
    if [ "$source_type" == "ip" ]; then
        aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",IpRanges="[{CidrIp='$source',Description='$description'}]"
    elif [ "$source_type" == "sg" ]; then
        aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",UserIdGroupPairs="[{GroupId='$source',Description='$description'}]"
    else
        echo "無効なソースタイプです: $source_type"
        continue
    fi
    # 操作の完了を確認し、3秒待つ
    if [ $? -eq 0 ]; then
        echo "対象SG: $sg_id -> $source$action_text しました"
    else
        echo "対象SG: $sg_id -> $source$action_text できませんでした"
    fi
    sleep $DELAY
done

作業手順

セキュリティグループの変更前の状態を取得

作業を行う前にセキュリティグループのDescribe情報を取得します。

BeforeのDescribe情報を取得する。
$ aws ec2 describe-security-groups --group-ids sg-XXX > before.json
  • 「sg-XXX」の代わりに適切な値を入力します。

スクリプトの実行

$ cd <スクリプト及びCSVファイルのディレクトリ>
$ sh existsg.sh

セキュリティグループの変更後の状態を取得

BeforeのDescribe情報を取得する。
$ aws ec2 describe-security-groups --group-ids sg-XXX > after.json
  • 「sg-XXX」の代わりに適切な値を入力します。

差分確認

jqがインストールされていると、下記のコマンドで差分が分かりやすく表示されます。私はjqを使っていますが、楽な方法で確認してください。

jqがインストールされている場合:

$ diff <(jq . before.json) <(jq . after.json)

after.jsonに差分があったら、下記の例の通り表示されます。

上記のdiffの例の通り、before.json->after.jsonの順番で並べて比較しますと下記の表示になります。

  • [>]はAfterにあるが、Beforeにないという意味です。→対象のルールを追加できた
  • [<]はBeforeにあるが、Afterにないという意味です。→対象のルールを削除できた

差分結果の例(ルールの追加):

>             {
>               "CidrIp": "192.168.1.1/32",
>               "Description": "from test1"
>             },
>             {
>               "CidrIp": "192.168.1.2/32",
>               "Description": "from test2"
>             },
>             {
>               "CidrIp": "192.168.1.3/32",
>               "Description": "from test3"

以上、御一読ありがとうございました。

本田 イーゴリ (記事一覧)

カスタマーサクセス部

・2024 Japan AWS Top Engineers (Security)
・AWS SAP, DOP, SCS, DBS, SAA, DVA, CLF
・Azure AZ-900
・EC-Council CCSE

趣味:日本国内旅行(47都道府県制覇)・ドライブ・音楽