メインコンテンツまでスキップ

Google Cloud 検査の最適化

修正例: Cloud Storage バケットのラベルで検査の挙動を変える

Shisho Cloud の初期設定には、例えば Cloud Storage バケットに関して、その公開設定を検査する検査項目が含まれています。 この検査は多くの場合有益であるものの、本来公開されていても問題ないバケットに対してもアラートを発生させてしまうため、運用上の負担となる場合が多くあります。

そこで、この検査を、バケットに付与された命名規則やラベルによって挙動を変えるように修正してみましょう。

具体的には、security-team-accepted-at ラベルが空でなければ、そのバケットは公開されていても問題ないということにして、アラートを発生させないようにします。 例えば以下の画像中の shisho-20231114-user-01-public バケットは、現在インターネットに公開されていますが、security-team-accepted-at ラベルが付与されているため公開されていても構いません:

なお Shisho Cloud 上の初期設定では、同バケットが公開バケットであるがために、アラートが発生しています:

準備

まずは以下の手順で、手元に Shisho Cloud ワークフローが含まれたリポジトリを clone してください:

git clone --recurse-submodules https://github.com/flatt-security/shisho-cloud-managed-workflows.git

clone 先のディレクトリで opa test コマンドを実行し、ワークフローのテストが全て成功することを確認しておきましょう:

$ cd shisho-cloud-managed-workflows
$ opa test -v .

...

workflows/csp/aws-fsbp/elb/logging/decide_test.rego:
data.policy.aws.elb.logging.test_lb_with_log_will_be_allowed: PASS (10.35925ms)
data.policy.aws.elb.logging.test_lb_without_log_will_be_denied: PASS (12.059792ms)

workflows/notification/slack/security-ja/triage/notify_test.rego:
data.notification.triage.test_severity_selected_successfully: PASS (506.667µs)
data.notification.triage.test_critical_severity_selected_as_failsafe_behavior: PASS (273.083µs)
--------------------------------------------------------------------------------
PASS: 186/186

挙動を変えたい対象のポリシーを特定する

まずは、挙動を変えたい検査が、どのポリシーによって定義されているかを特定する必要があります。 実際特定は簡単で、検査結果詳細のすぐそばに表示される検出元についての情報を確認するだけで完了です:

この情報からは、どのワークフローのどのジョブによる検出されたかを確認できます。 今回の場合は prebundle-googlecloud-storage という ID のワークフローの bucket-accessibility というジョブによって検出されたことがわかります。

同ワークフローは、flatt-security/shisho-cloud-managed-workflows 上では、以下のパスで定義されているものです:

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility

いま、同パスには、以下のファイルが含まれています:

  • decide.graphql: 検査対象データの定義
  • decide.rego: 検査ロジック
  • decide_test.rego: テストコード

これらのファイルは、Shisho Cloud におけるワークフローの構成要素です。 Shisho Cloud は以下の画像が説明するように、GraphQL クエリが定義する検査対象データを Shisho Cloud が収集し、それを入力として Rego コードを実行することで、セキュリティ検査を実現しています:

例えば、検査対象データを定義する decide.graphql は、以下のようにバケットの一覧と ACL、IAM ポリシーを取得するように定義されています:

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide.graphql
{
googleCloud {
projects {
cloudStorage {
buckets {
metadata {
id
}
name
acl {
entity
role
}
iamPolicy {
bindings {
role
members {
id
}
}
}
}
}
}
}
}

それを用いた検査ロジックの中身は以下の通りです。input 以下に格納された GraphQL クエリの結果をもとに、decisions という変数に検査結果を格納しています:

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide.rego
package policy.googlecloud.storage.bucket_accessibility

import data.shisho

decisions[d] {
project := input.googleCloud.projects[_]
bucket := project.cloudStorage.buckets[_]

policy_bindings := public_policy_bindings(bucket.iamPolicy.bindings)
acl_bindings := public_acl_rules(bucket.acl)

d := shisho.decision.googlecloud.storage.bucket_accessibility({
"allowed": (count(policy_bindings) + count(acl_bindings)) == 0,
"subject": bucket.metadata.id,
"payload": shisho.decision.googlecloud.storage.bucket_accessibility_payload({
"public_policy_bindings": policy_bindings,
"public_acl_rules": acl_bindings,
}),
})
}

is_public_principal(p) {
p == "allAuthenticatedUsers"
} else {
p == "allUsers"
} else = false

is_public_entity(p) := is_public_principal(p)

public_acl_rules(acl) := x {
x := [{
"role": a.role,
"entity": a.entity,
} |
a := acl[_]
is_public_entity(a.entity)
]
} else := []

public_policy_bindings(bindings) := x {
x := [{
"role": binding.role,
"principal": member.id,
} |
binding := bindings[_]
member := binding.members[_]
is_public_principal(member.id)
]
} else := []

これで変更すべき Shisho Cloud ワークフローが特定できました! 以降では、このワークフローを修正して、ラベルによって挙動を変えるようにします。

検査対象データとしてラベルを取得する

次は実際に Shisho Cloud ワークフローを修正して、Cloud Storage のラベルを検査対象データとして取得するようにします。 以下の URL の [oid] を Shisho Cloud の組織 ID に置き換えて、Shisho Cloud の GraphQL Playground にアクセスしてください:

https://cloud.shisho.dev/[oid]/playground/query

そこに workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide.graphql 内のクエリを貼り付け、buckets フィールド以下を捜索すると、labels というフィールドがあることがわかります。 これを用いて、実際にラベルを検査対象データとして取得するように、GraphQL クエリを修正すると以下のようになります:

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide.graphql
{
googleCloud {
projects {
cloudStorage {
buckets {
metadata {
id
}
name
acl {
entity
role
}
iamPolicy {
bindings {
role
members {
id
}
}
}

labels {
key
value
}
}
}
}
}
}

テストを修正する

検査対象データの定義を修正したので、次はテストを修正しましょう。 テストは、検査ロジックが正しく動作するかを確認するためのものです。

今回は security-team-accepted-at ラベルが空でなければ、そのバケットは公開されていても問題ないということにするのでした。 そこで、security-team-accepted-at ラベルが空でないバケットに対しては、検査結果が allowed となることを確認するテストを追加してみましょう:

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide_test.rego
package policy.googlecloud.storage.bucket_accessibility

import data.shisho
import future.keywords

test_whether_proper_accessibility_is_configured_for_storage_buckets if {
# ... snipped ...

# Check if the label will supress the alert
count([d |
decisions[d]
shisho.decision.is_allowed(d)
]) == 1 with input as {"googleCloud": {"projects": [{"cloudStorage": {"buckets": [{
"metadata": {"id": "google-cloud-bq-dataset|514893259785|test-1"},
"labels": [{"key": "security-team-accepted-at", "value": "2023-11-14"}],
"acl": [
{
"role": "WRITER",
"entity": "allUsers",
},
{
"role": "OWNER",
"entity": "projectWriters",
},
],
"iamPolicy": {"bindings": []},
}]}}]}}
}

この時点では、追加されたテストが、opa test コマンドを実行した際に失敗することも確認できます:

$ opa test workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility ./sdk

...

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide_test.rego:
data.policy.googlecloud.storage.bucket_accessibility.test_whether_proper_accessibility_is_configured_for_storage_buckets: FAIL (9.491125ms)
--------------------------------------------------------------------------------
PASS: 1/2
FAIL: 1/2

このテストがパスする状態が、次に目指すべき状態です。

検査ロジックでラベルを評価するよう修正する

次に実際に検査ロジックを修正して、ラベルを評価するようにします。 実装方法は様々ありますが、今回は以下のように、should_allow という関数を追加して、ラベルを評価するようにします:

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide.rego
package policy.googlecloud.storage.bucket_accessibility

import data.shisho

decisions[d] {
project := input.googleCloud.projects[_]
bucket := project.cloudStorage.buckets[_]

policy_bindings := public_policy_bindings(bucket.iamPolicy.bindings)
acl_bindings := public_acl_rules(bucket.acl)

d := shisho.decision.googlecloud.storage.bucket_accessibility({
"allowed": should_allow(bucket, policy_bindings, acl_bindings),
"subject": bucket.metadata.id,
"payload": shisho.decision.googlecloud.storage.bucket_accessibility_payload({
"public_policy_bindings": policy_bindings,
"public_acl_rules": acl_bindings,
}),
})
}

should_allow(bucket, policy_bindings, acl_bindings) {
l := bucket.labels[_]
l.key == "security-team-accepted-at"
l.value != ""
} else {
count(policy_bindings) + count(acl_bindings) == 0
} else := false

is_public_principal(p) {
p == "allAuthenticatedUsers"
} else {
p == "allUsers"
} else = false

is_public_entity(p) := is_public_principal(p)

public_acl_rules(acl) := x {
x := [{
"role": a.role,
"entity": a.entity,
} |
a := acl[_]
is_public_entity(a.entity)
]
} else := []

public_policy_bindings(bindings) := x {
x := [{
"role": binding.role,
"principal": member.id,
} |
binding := bindings[_]
member := binding.members[_]
is_public_principal(member.id)
]
} else := []

テスト結果を確認する

さて、これでテストがパスするようになるはずです。 実際に opa test コマンドを実行してみましょう:

$ opa test workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility ./sdk

...

workflows/cis-benchmark/googlecloud-v1.3.0/storage/bucket-accessibility/decide_test.rego:
data.policy.googlecloud.storage.bucket_accessibility.test_whether_proper_accessibility_is_configured_for_storage_buckets: PASS (96.768375ms)
--------------------------------------------------------------------------------
PASS: 2/2

これでテストがパスするようになりました! 残るは、この修正を Shisho Cloud にデプロイして、実際に実行してみる工程です。

デプロイする

以下のコマンドを実行して、修正したワークフローをデプロイしてみましょう:

$ shishoctl workflow apply --org $SHISHO_ORG_ID -f workflows/cis-benchmark/googlecloud-v1.3.0/storage
Applying 1 manifest files...
succeeded to update a manifest; id=prebundle-googlecloud-storage, snapshot=WS01HF4PBYFYHVRRYEG6B9F0VFB4

ワークフローを実行し、挙動を確認する

以下のコマンドを実行して、修正したワークフローを即時実行してみましょう:

$ shishoctl workflow run --org $SHISHO_ORG_ID prebundle-googlecloud-storage

ワークフロー一覧ページにアクセスすると、ワークフローの実行ステータスが表示されているはずです:

実行完了後に、検査結果一覧ページにアクセスすると、検査結果から security-team-accepted-at ラベルが付与されたバケットに対してはアラートが発生していないことが確認できます:

まとめ

以上で、Shisho Cloud ワークフローの修正例を紹介しました。 Shisho Cloud では、このようにワークフローを修正することで、検査の挙動をカスタマイズできます。

なお、このように行った修正は、GitHub Actions による継続的デプロイ も可能です。 ワークフローコードを中央集権的に管理し、コードレビューを通して変更を承認した上でデプロイすることで、より安全な運用を実現できます。 あわせてセットアップを行ってみてください。