0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Security Command Center 検出結果で(Open Port なファイアウォールルール)と(Public IP addressをもつインスタンス)があるVPC一覧をgcloudで取得する

Last updated at Posted at 2023-02-26

やりたいこと

Google Cloud の Security Command Center を有効化したところ、
複数の "Open SSH Port" や "Open RDP Port" 検出結果があるが、
コンソール上で対象リソースを調べて回るのが面倒なので、コマンドで簡単に抽出したい。

また、攻撃リスクが高い環境を優先して対応すべく、
ファイアウォールルールが Open かつ、Public IP address のあるインスタンスがある環境の一覧が欲しい。

前提条件

  • Google Cloud 以外のサードパーティ製セキュリティツールは利用しない。
  • Connectivity Test や nmap などによる実際のアクセス確認までは行わない。
  • Firewall Logging は有効になっておらず、Firewall Insights は利用できない。
    • これが使えれば、不要なファイアウォールルールの特定がより楽になりますが、今回は使用しません。

コマンド

Cloud Shell で、1 Project 毎の実行を想定しています。
パフォーマンスは考慮していません。

(あと、やたら長いコマンドになりましたが、もっと効率の良い書き方があるかも...)

1. Project ID 設定

export PROJECT_ID=プロジェクトID
gcloud config set project $PROJECT_ID

2. パブリックIP をもつインスタンスと VPC の一覧を出力

検出カテゴリ名 PUBLIC_IP_ADDRESS のインスタンスと、VPC 名をファイル出力します。
(各コマンドで何やってるかは後述)

gcloud scc findings list projects/$PROJECT_ID \
--filter="state=\"ACTIVE\" AND category=\"PUBLIC_IP_ADDRESS\"" \
--format="value[separator=\" \"](resource.displayName,resource.name)" |
sed -r 's#^([a-z0-9-]+) //compute\.googleapis\.com/projects/[a-z0-9-]+/zones/([a-z0-9-]+)/instances/[0-9]+$#\1 \2#' |
xargs -r echo |
xargs -r -n 2 bash -c 'gcloud compute instances describe ${0} --zone=${1} \
--format="value[separator=\",\"](name,networkInterfaces.network)"' |
sed -r 's#^([a-z0-9-]+),https://www\.googleapis\.com/compute/v1/projects/[a-z0-9-]+/global/networks/([a-z0-9-]+)$#\1,\2#' \
> public-ip.txt

出力されるファイルはこんな感じです。(インスタンス名,VPC名)

public-ip.txt
instance-1,vpc-1
instance-2,vpc-2
instance-3,vpc-3

3. OPEN なファイアウォールルールと VPC の一覧を出力

"OPEN" を含む検出カテゴリで検出されたファイアウォールと、VPC を抽出します。
(同じく、各コマンドで何やってるかは後述)

gcloud scc findings list projects/$PROJECT_ID \
--filter="state=\"ACTIVE\" AND category:\"OPEN\" AND resource.type=\"google.compute.Firewall\"" \
--format="value[separator=\" \"](finding.category,resource.displayName)" |
xargs -r echo |
xargs -r -n 2 bash -c 'echo "${0},$(gcloud compute firewall-rules list \
--filter="name=${1}" \
--format="value[separator=\",\"](name,network)")"' \
> open-firewall.txt

出力されるファイルはこんな感じです。(検出カテゴリ名,ファイアウォールルール名,VPC名)

open-firewall.txt
OPEN_SSH_PORT,vpc-2-allow-ssh,vpc-2
OPEN_RDP_PORT,vpc-1-allow-rdp,vpc-1
OPEN_SSH_PORT,vpc-1-allow-ssh,vpc-1

4. 2と3の結果を JOIN

join -a 1 -t, -1 2 -2 3 -o auto -e "NULL" \
<(sort -t, -k2 public-ip.txt) <(sort -t, -k3 open-firewall.txt)

出力結果はこんな感じです。

vpc-1,instance-1,OPEN_RDP_PORT,vpc-1-allow-rdp
vpc-1,instance-1,OPEN_SSH_PORT,vpc-1-allow-ssh
vpc-2,instance-2,OPEN_RDP_PORT,vpc-2-allow-ssh
vpc-3,instance-3,NULL,NULL

VPC、Public IP をもつインスタンス、同じ VPC に存在する OPEN なファイアウォールルールの組み合わせを抽出することができました。
PUBLIC IP はあるけれどもファイアウォールが OPEN なのは検出されていませんよ、という場合は NULL 表示になります。

あとは、これを元にファイアウォールやアクセス方法の見直しをすれば良さそうです。
(アクセス元IPの絞り込みや、SSH 接続だけならIAP 利用への切り替えなど。)

TODO: ファイアウォールルールは OPEN だが、Public IP をもつインスタンスは無い という情報も欲しいので、FULL JOIN に直す

備考

上記で出力されたからといって、必ずしもアクセス可能とは限らず、
他のファイアウォールルールで Deny されている可能性も、
ネットワークタグ指定によりアクセス制限されている可能性も、
インスタンス側でポートが空いていないという可能性もあります。

また、そもそも Public IP が付与される前に防止したい!というのであれば、
Organization Policy (constraints/compute.vmExternalIpAccess) を設定しておく手があります。

各コマンドの詳細

各行の詳細をメモしておきます。

参考:

2 のコマンド詳細

まずは、検出カテゴリ PUBLIC_IP_ADDRESS のインスタンスを抽出する。
--format="value" はデフォルトではタブ区切りで各項目を出力するが、xargs でdelimiter 指定が要らないようにスペース区切りに変更している。

$ gcloud scc findings list projects/$PROJECT_ID \
--filter="state=\"ACTIVE\" AND category=\"PUBLIC_IP_ADDRESS\"" \
--format="value[separator=\" \"](resource.displayName,resource.name)"
instance-1 //compute.googleapis.com/projects/PROJECT_ID/zones/us-central1-a/instances/1234567890123456789
instance-2 //compute.googleapis.com/projects/PROJECT_ID/zones/us-west1-b/instances/1234567890123456789 

続いて gcloud compute コマンドに渡して VPC 情報を取得したい...のだが、インスタンス名とゾーン名が必要なので、ゾーン名を前コマンドの resource.name からで抽出する
-r オプションで拡張正規表現使用可能にしておく

$ echo "instance-1 //compute.googleapis.com/projects/PROJECT_ID/zones/us-central1-a/instances/1234567890123456789" |
sed -r 's#^([a-z0-9-]+) //compute\.googleapis\.com/projects/[a-z0-9-]+/zones/([a-z0-9-]+)/instances/[0-9]+$#\1 \2#'
instance-1 us-central1-a

※上記のコマンドそのまま実行しても、PROJECT_ID 部分がマッチしないため抽出されません

続いて gcloud compute instance describe コマンドを各インスタンスについて実行...する前に、xargs で扱いやすいようにで改行を除去する
-r オプションで、入力が空の場合はコマンド実行しない

$ echo -e "instance-1 us-central1-a\ninstance-2 us-west1-b" |
xargs -r echo
instance-1 us-central1-a instance-2 us-west1-b

1行になったので、先頭から2つずつ取り出して gcloud compute instances describe コマンドに渡す
結果はカンマ区切りで、インスタンス名とVPC情報のみ取得する

$ echo "instance-1 us-central1-a instance-2 us-west1-b" |
xargs -r -n 2 bash -c 'gcloud compute instances describe ${0} --zone=${1} \
--format="value[separator=\",\"](name,networkInterfaces.network)"'
instance-1,https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/vpc-1
instance-2,https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/vpc-2

めんどくさいことに VPC は URL で返ってくるので、名前だけ抽出する

$ echo "instance-1,https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/vpc-1" |
sed -r 's#^([a-z0-9-]+),https://www\.googleapis\.com/compute/v1/projects/[a-z0-9-]+/global/networks/([a-z0-9-]+)$#\1,\2#'
instance-1,vpc-1

※上記のコマンドそのまま実行しても、PROJECT_ID 部分がマッチしないため抽出されません

3 のコマンド詳細

まずは、SCC から "OPEN ~" として検出されているファイアウォールルールを抽出する
category:OPEN だけだと、OPEN_GROUP_IAM_MEMBER なども引っかかる可能性があるので、resource.type も指定している
uniq は複数の検出結果に表れるファイアウォールルールがあるかもしれないので指定している

$ gcloud scc findings list projects/$PROJECT_ID \
--filter="state=\"ACTIVE\" AND category:\"OPEN\" AND resource.type=\"google.compute.Firewall\"" \
--format="value[separator=\" \"](finding.category,resource.displayName)" | uniq
OPEN_SSH_PORT,vpc-2-allow-ssh
OPEN_RDP_PORT,vpc-1-allow-rdp
OPEN_SSH_PORT,vpc-1-allow-ssh

改行除去する部分は 2 と同様なので割愛

検出カテゴリ名は そのまま echo コマンドへ、
ファイアウォールルール名は gcloud compute firewall-rules list コマンドに渡してから、その結果を echo コマンドへ

$ echo "OPEN_SSH_PORT vpc-2-allow-ssh OPEN_RDP_PORT vpc-1-allow-rdp OPEN_SSH_PORT vpc-1-allow-ssh" |
xargs -r -n 2 bash -c 'echo "${0},$(gcloud compute firewall-rules list \
--filter="name=${1}" \
--format="value[separator=\",\"](name,network)")"'
OPEN_SSH_PORT,vpc-2-allow-ssh,vpc-2
OPEN_RDP_PORT,vpc-1-allow-rdp,vpc-1
OPEN_SSH_PORT,vpc-1-allow-ssh,vpc-1

4 のコマンド詳細

join -a 1 -t, -1 2 -2 3 -o auto -e "NULL" <(sort -t, -k2 public-ip.txt) <(sort -t, -k3 open-firewall.txt)

-a 1 で LEFT JOIN
-t, で区切り文字をカンマに指定
-1 で一番目のファイルのどの列を JOIN に使うかを指定
-2 で二番目のファイルのどの列を JOIN に使うかを指定
-o auto で出力項目を自動に指定 (-e 利用時には -o が必要)
-e "NULL" で、マッチする行がなかった時に表示する文字列を指定

また、事前にソートされていないと join コマンドでエラーになるため、それぞれのファイルを sort している
-t, で区切り文字指定
-k3 でソートに使う列を指定

手順2,3 の時点でソートしておいても良かったが、コマンドがさらに長くなるのでこちらでソートをかけている

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?