NSGをドメイン名で許可したい

ネットワーキング

こちらの記事は、Oracle Cloud Infrastructure Advent Calendar 2021 の Day 17 として書いています。
※よって、記事内容は 2021/12/17 時点のものです

はじめに

OCIのネットワークで使用するセキュリティリストやNSGはIPアドレスで通信を許可します。
しかしながら、IPアドレスではなくてドメイン名で許可したい…と思う時が次のようにありました。

・外部のサービスに接続する際、外部サービスのIPアドレスを登録しても変更される場合がある
・かといって全IPアドレスを許可する 0.0.0.0/0 を登録するのは避けたい

そこで、IPアドレスではなくてドメイン名で許可できればと思い、
そんなプログラムが無いかな~とちょっとググったのですが、
見つけられなかったので自分で作成しようと思った次第です。

設計

処理フロー
・DNS名前解決にて対象のドメイン名からIPアドレスを取得
・ルールを追加するNSGのOCIDを取得 ←※NSGのOCIDをスクリプトに明記するなら不要
・NSGに既にIPアドレスが登録されているかを確認し、処理を行う
 ⇒存在しなかったら追加 (コメントにはドメイン名と日時を記載)
 ⇒存在していたら追加無し

処理概要
・手早く作成するにあたり、Linuxサーバにてシェルスクリプト+CLIを使用
・提供されるサービスのIPアドレスが頻繁に変わることは無い…と思うので、cronで定期的に実行することを想定 (実行間隔は、サービスが使えない場合の影響次第でしょうか…)
・セキュリティリストを変更するには全ての定義を再設定する必要がありそうで、設定を誤ると怖いので、セキュリティリストへの処理はあきらめてNSGのみにする。
(使用するならNSGのほうがオススメ的な話もありますので…)

NSGでは、VCNのサブネット・アーキテクチャをアプリケーションのセキュリティ要件から分離できるため、セキュリティ・リストのかわりにNSGを使用することをお薦めします。

https://docs.oracle.com/ja-jp/iaas/Content/Network/Concepts/securityrules.htm

・ルールの削除処理はせず、追記のみ行う (削除が怖いので…)
・エラー時の対応処理を入れていないです…すみません (コマンドの終了ステータスを確認すればよさそうですが)

開発

シェルスクリプトは次の通りです。

#!/bin/bash

##################################################
# 対象のドメイン名からIPアドレスを取得

# 対象のドメイン名
DOMAIN_NAME="o-labo.info"
echo "対象ドメイン名:"${DOMAIN_NAME}

# ドメイン名からIPアドレスを取得
IP_ADDRESSES=`host -t A -4 "${DOMAIN_NAME}" | grep "has address" | cut -d " " -f 4`
echo "IPアドレス:"${IP_ADDRESSES}


##################################################
# ルールを追加するNSGのOCIDを取得

# ociコマンドにて動的グループを使用する際に設定
export OCI_CLI_AUTH=instance_principal
# コンパートメントのOCID
COMPARTMENT_ID=ocid1.compartment.oc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# NSGのOCIDを取得
NSG_NAME="nsg-pub-test01-1"
echo "NSG名:"${NSG_NAME}

QUERY_STR='data[?"display-name"==`'"${NSG_NAME}"'`].id | [0]'
NSG_OCID=`oci network nsg list --compartment-id "${COMPARTMENT_ID}" --query "${QUERY_STR}" --raw-output`
echo "NSG OCID:"${NSG_OCID}


##################################################
# NSGに既存でIPアドレスが登録されているかを確認し、処理を行う

# ドメイン名にIPアドレスが複数あった場合、IPアドレスごとに処理を行う
for IP_ADDRESS in ${IP_ADDRESSES}
do
    # NSG内のルールの「対象IP かつ EGRESS かつ 対象ポート番号」の数を確認する
    PORT_NUM=443
    QUERY_STR='length(data[?"destination"==`'"${IP_ADDRESS}"/32'` && 
                            "direction"==`EGRESS` && 
                            "tcp-options"."destination-port-range"."max"==`'"${PORT_NUM}"'` && 
                            "tcp-options"."destination-port-range"."min"==`'"${PORT_NUM}"'`])'
    COUNT_NSG_RULE=`oci network nsg rules list --nsg-id "${NSG_OCID}" --query "${QUERY_STR}" 2>&1`
    echo "該当するNSGルールの数:"${COUNT_NSG_RULE}

    if [ "${COUNT_NSG_RULE}" = "Query returned empty result, no output to show." ]; then
        # クエリの戻り値が空、つまり該当するルールが無かった場合はルールを追加
        echo NSGへルールを追加
        RULE_STR='
        [{
            "description": "'`date "+%Y/%m/%d %H:%M"`' '"${DOMAIN_NAME}"' add",
            "destination": "'"${IP_ADDRESS}"/32'",
            "destination-type": "CIDR_BLOCK",
            "direction": "EGRESS",
            "icmp-options": null,
            "is-stateless": false,
            "protocol": "6",
            "source": null,
            "source-type": null,
            "tcp-options": {
                "destination-port-range": {
                    "max": '"${PORT_NUM}"',
                    "min": '"${PORT_NUM}"'
                },
                "source-port-range": null
            },
            "udp-options": null
        }]'
        oci network nsg rules add --nsg-id ${NSG_OCID} --security-rules "${RULE_STR}"
    elif [ ${COUNT_NSG_RULE} -gt 0 ]; then
        # NSGにルールが既に存在する場合は処理無し
        echo NSGにルールが既に存在する
    else 
        echo 戻り値が想定外
    fi
done

下記については、環境に合わせて設定します。
・DOMAIN_NAME(対象のドメイン名)
・COMPARTMENT_ID(コンパートメントのOCID)
・NSG_NAME(対象のNSGの名前)
・PORT_NUM(ポート番号)
※上記スクリプトはエグレス専用になっていますが、適宜イングレス・destination→source に変更してもらえればと思います。

検証

cronで1時間おきに実行してみたりしましたが、
想定通りの動作をしていると思います。

さいごに

いずれは機能が組み込まれるかもしれませんが、
それまでは 上記のプログラムで運用していようかな…と思います。
処理内容はともかくとして、コマンドラインなど何かの役に立てば幸いです。