概要
Cloud ArmorのようなWAFは通常ルールベースで不正なアクセスを制御するため、悪意あるユーザがIPアドレスやパラメータをコロコロ変えたりすると都度ルールを設定する必要が出てきてイタチごっこになりがちです。
そんなケースに対応できるよう、機械学習を用いて攻撃であるかどうかを判断してくれるマネージドサービスがAdaptive Protectionです。
イメージとしては以下の図のように悪意あるユーザの攻撃と判断されたらアラートで通知してくれて、適用すべきルールまで自動的に教えてくれます。
ref: https://cloud.google.com/armor/docs/adaptive-protection-use-cases?hl=ja
なのでサービス側としてはアラートを見て「これは攻撃だろう」と判断したら提案されたルールを適用するだけで防ぐことが可能になります。
つまりAdaptive Protectionを用いることで
- 迅速な攻撃の検知ができる
- 不正リクエストを弾くためのルール(パラメータ)を人が考えなくて良い
といったメリットを享受できます。
環境
- terraform v1.2.3
- hashicorp/google-beta v4.26.0
Adaptive Protection
Managed Protection
まず前提としてAdaptive Protectionをフルで利用するためににはManaged Protection Plusを利用する必要があります。
利用していない場合
- 攻撃は検知するが攻撃シグネチャを教えてくれない
- ルールを提案してもらえない
といったことになり、検知はできても攻撃を防ぐためのルールを人が考えないといけません。
設定方法
SecurityPolicy
Adaptive Protectionはterraformで以下のように設定できます。
providerはgoogle-betaを使う必要があります。
resource "google_compute_security_policy" "adaptive" { provider = google-beta name = "adaptive-protection" description = "Adaptive Protection" adaptive_protection_config { layer_7_ddos_defense_config { enable = true rule_visibility = "STANDARD" } } rule { action = "allow" priority = "2147483647" match { versioned_expr = "SRC_IPS_V1" config { src_ip_ranges = ["*"] } } description = "default rule" } }
Kubernetes Manifest
BackendConfig
先程作成したCloudArmorを使うようBackendConfigを用意します。
apiVersion: cloud.google.com/v1 kind: BackendConfig metadata: name: prd-api-bc spec: timeoutSec: 10 securityPolicy: name: "adaptive-protection" healthCheck: checkIntervalSec: 15 timeoutSec: 10 port: 8000 type: HTTP requestPath: /
Service
BackendConfigをServiceに紐付けるようにします。
apiVersion: v1 kind: Service metadata: name: prd-api annotations: cloud.google.com/backend-config: '{"ports": {"http":"prd-api-bc"}}' cloud.google.com/neg: '{"ingress": true}' labels: name: prd-api spec: type: ClusterIP selector: name: prd-api ports: - name: http protocol: TCP port: 8000
Ingress
前述のServiceに流すようなIngressを用意します。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: prd-api annotations: kubernetes.io/ingress.allow-http: "true" kubernetes.io/ingress.class: gce kubernetes.io/ingress.global-static-ip-name: "prd-api" spec: tls: - secretName: wildcard-jun06t-com hosts: - api.jun06t.com defaultBackend: service: name: prd-api port: number: 8000 rules: - host: api.jun06t.com http: paths: - path: /* pathType: ImplementationSpecific backend: service: name: prd-api port: number: 8000
ログからslack通知させる
攻撃を検知するとドキュメントにあるように以下の様のログが生成されます。
... jsonPayload: { alertId: "11275630857957031521" backendService: "test-service" confidence: 0.71828485 headerSignatures: [ 0: { name: "RequestUri" significantValues: [ 0: { attackLikelihood: 0.88 matchType: "MATCH_TYPE_EQUALS" proportionInAttack: 0.85 proportionInBaseline: 0.01 value: "/" } ] } 1: { name: "UserAgent" significantValues: [ 0: { attackLikelihood: 0.92 matchType: "MATCH_TYPE_EQUALS" proportionInAttack: 0.85 proportionInBaseline: 0 value: "Unusual browser" } 1: { attackLikelihood: 0.87 proportionInAttack: 0.7 proportionInBaseline: 0.1 missing: true } ] } ] suggestedRule: [ 0: { action: "DENY" evaluation: { impactedAttackProportion: 0.95 impactedBaselineProportion: 0.001 impactedBaselinePolicyProportion: 0.001 } expression: "evaluateAdaptiveProtection('11275630857957031521')" } ] ruleStatus: RULE_GENERATED attackSize: 5000 } resource: { type: "network_security_policy", labels: { project_id: "your-project", policy_name: "your-security-policy-name" } }, } } ...
なのでresource.type="network_security_policy"
とalertIdありの
resource.type="network_security_policy" jsonPayload.alertId!=""
とすればイベントを絞ることができます。
最初は信頼度を 0.5 よりも高く設定することをおすすめします。アラートのしきい値が時間の経過とともに合わなくなってきた場合は、アラート ポリシーの信頼度しきい値を上げることができます。
ともあるので、信頼度の高いもののみ抽出したい場合は以下のように設定します。
resource.type="network_security_policy" jsonPayload.alertId!="" jsonPayload.confidence > 0.8
以下のボタンからログのクエリベースでアラートを設定できます。
動作確認
自前で擬似的に負荷を与えてアラートが鳴るか検証します。
import http from 'k6/http'; import { sleep } from 'k6'; export default function () { http.get('https://api.jun06t.com'); sleep(1); }
実行します。
$ k6 run --vus 1000 --duration 30s script.js
するとAdaptive Protectionのダッシュボードに以下の通知が出ます。
攻撃シグネチャと推奨ルール
日本語だとシグネチャが「署名」と翻訳されたやや意味が分かりづらいですが、実際は攻撃シグネチャのことです。
詳細を開くと以下のように表示されます。
攻撃シグネチャと推薦されたルール
今回攻撃シグネチャは
- IPアドレス
- UserAgent
の2つが出てきました。
これらのパラメータが画像の通りの値の場合、それは悪意あるユーザによる攻撃という判定になります。
ドキュメント上では他にも
- RegionCode
- RequestUri
などがあります。
そして最後には推奨ルールが表示されます。
SecurityPolicyに適用
先程の推奨ルールをSecurityPolicyにコピペすれば、このユーザからのリクエストを弾くことができます。簡単ですね。
ブロックされるか確認
では最初と同じ攻撃を実行してみます。
すると画像のように全て失敗するようになりました。ルールが適用されてブロックされていることが確認できます。
一方curlで試してみたところリクエストは通りました。これはUserAgentが異なるため今回のルールが適用されなかったと考えられます。
検証結果
検証結果をまとめると以下でした。
- ベースとなるリクエスト数に対し、特定のシグネチャ(送信元IP、UA、国コードなど)に偏ったリクエストが異常に多いと攻撃とみなされる
- 攻撃とみなされても自動的にブロックするわけではなく、推奨ルールを提示してサービス側に判断を委ねる
- 推奨ルールはコピペするだけで簡単に設定できる。設定後数分で反映される(5~10分程度かかった)
- アラートのslack通知はログベースで簡単に設定できる
まとめ
Cloud ArmorのAdaptive Protectionを利用することでイタチごっこになりがちなDDoS対策を自動的に対応できるようになりました。