[APIGateway.1] API ゲートウェイRESTと WebSocket API実行のログ記録を有効にする必要があります を CloudFormation Guard でチェックする
こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。
みなさん CloudFormation テンプレートをデプロイする前に、 Security Hub の検知項目に引っかかっていないか、チェックしたいなぁと思ったことはありますでしょうか。
そういった時に、役に立つのが CloudFormation Guard や cfn-nag、Trivy、Snyk IaC などです。
AWS Foundational Security Best Practices v1.0.0 (FSBP) 標準に準拠したいケースもあるのではないでしょうか。(私です。)
そこで、今回は [APIGateway.1] API ゲートウェイRESTと WebSocket API実行のログ記録を有効にする必要があります
を CloudFormation Guard で表現してみます。
前置き
「すでにこういった取り組みがあるのでは?」と思われた方もいるのではないかと思います。実際、ありました。aws-guard-rules-registry
というものが、
ただし、今回私が求める AWS Foundational Security Best Practices v1.0.0 (FSBP) 標準に最適化されたものは少し違ったり、
API Gateway v2 の方は反映されてなかったりするため、コードを参考にさせていただきつつ、自前で実装してみたいと思います。
作成したコード
作成したコードは以下になります。
#################################################
# API Gateway.01
##################################################
# [APIGateway.1] API Gateway REST and WebSocket API execution logging should be enabled
# Control URL: https://docs.aws.amazon.com/securityhub/latest/userguide/apigateway-controls.html#apigateway-1
#
# Category: Identify > Logging
# Severity: Medium
# Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
# AWS Config rule: api-gw-execution-logging-enabled
# Config Rule URL:https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/api-gw-execution-logging-enabled.html
let valid_logging_levels = ['ERROR', 'INFO']
let api_gw_execution_logging_enabled = Resources.*[ Type == 'AWS::ApiGateway::Stage'
Metadata.guard.SuppressedRules not exists or
Metadata.guard.SuppressedRules.* != "API_GW_EXECUTION_LOGGING_ENABLED"
]
let api_gw_v2_execution_logging_enabled = Resources.*[ Type == 'AWS::ApiGatewayV2::Stage'
Metadata.guard.SuppressedRules not exists or
Metadata.guard.SuppressedRules.* != "API_GW_EXECUTION_LOGGING_ENABLED"
]
# AWS::ApiGateway::Stage
rule api_gw_execution_logging_check when %api_gw_execution_logging_enabled !empty {
%api_gw_execution_logging_enabled.Properties {
# MethodSettings が存在するかチェック
MethodSettings exists
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
# MethodSettings が存在した場合、全ての LoggingLevel が %valid_logging_levels 内のどれかに当てはまるかチェック
when MethodSettings exists {
MethodSettings[*] {
LoggingLevel in %valid_logging_levels
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
}
}
}
}
# AWS::ApiGatewayV2::Stage
rule api_gw_v2_execution_logging_check when %api_gw_v2_execution_logging_enabled !empty {
%api_gw_v2_execution_logging_enabled.Properties {
# DefaultRouteSettings が存在するかチェック
DefaultRouteSettings exists
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
# DefaultRouteSettings が存在した場合、LoggingLevel が %valid_logging_levels 内のどれかに当てはまるかチェック
when DefaultRouteSettings exists {
DefaultRouteSettings {
LoggingLevel in %valid_logging_levels
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
}
}
# RouteSettings が存在した場合、全ての LoggingLevel が %valid_logging_levels 内のどれかに当てはまるかチェック
when RouteSettings exists {
RouteSettings.* {
LoggingLevel in %valid_logging_levels
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if:
- DefaultRouteSettings is missing or its LoggingLevel isn't ERROR or INFO
- Any RouteSettings entry is missing LoggingLevel or its LoggingLevel isn't ERROR or INFO
>>
}
}
}
}
Metadata
まず、この手のチェックツールは Suppress できないと、オオカミ少年になりがちです。そのため今回は本家を参考に CloudFormation テンプレートの Metadata で、次のように SuppressedRules に API_GW_EXECUTION_LOGGING_ENABLED
が含まれない場合はチェックしないよう設定しました。
Resources:
ApiGatewayStage:
+ Metadata:
+ guard:
+ SuppressedRules:
+ - API_GW_EXECUTION_LOGGING_ENABLED
Type: "AWS::ApiGateway::Stage"
Properties:
StageName: "hoge"
DeploymentId: "ad6uyy"
RestApiId: "yfrjlllnh3"
CacheClusterEnabled: false
CacheClusterSize: "0.5"
MethodSettings:
チェックロジック部分
# SuppressedRules が存在しない or API_GW_EXECUTION_LOGGING_ENABLED が含まれない場合、
let api_gw_execution_logging_enabled = Resources.*[ Type == 'AWS::ApiGateway::Stage'
Metadata.guard.SuppressedRules not exists or
Metadata.guard.SuppressedRules.* != "API_GW_EXECUTION_LOGGING_ENABLED"
]
let api_gw_v2_execution_logging_enabled = Resources.*[ Type == 'AWS::ApiGatewayV2::Stage'
Metadata.guard.SuppressedRules not exists or
Metadata.guard.SuppressedRules.* != "API_GW_EXECUTION_LOGGING_ENABLED"
]
MethodSettings
AWS::ApiGateway::Stage では MethodSettings で LoggingLevel
を指定します。
CacheDataEncrypted: Boolean
CacheTtlInSeconds: Integer
CachingEnabled: Boolean
DataTraceEnabled: Boolean
HttpMethod: String
LoggingLevel: String
MetricsEnabled: Boolean
ResourcePath: String
ThrottlingBurstLimit: Integer
ThrottlingRateLimit: Number
逆を言えば MethodSettings を設定しなければ、LoggingLevel
を設定できないため、MethodSettings が存在しているかどうかを判定ロジックに利用しました。
また、LoggingLevel in valid_logging_levels
とすることで、LoggingLevel
が 'ERROR' でも 'INFO' でもない場合にエラーの流量を減らすようにしてみました。今回、Custom Error Message を利用してみましたが、調べてみた限りではひとつにまとめられなかったため、冗長的な書き方をしています。
# AWS::ApiGateway::Stage
rule api_gw_execution_logging_check when %api_gw_execution_logging_enabled !empty {
%api_gw_execution_logging_enabled.Properties {
# MethodSettings が存在するかチェック
MethodSettings exists
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
# MethodSettings が存在した場合、全ての LoggingLevel が %valid_logging_levels 内のどれかに当てはまるかチェック
when MethodSettings exists {
MethodSettings[*] {
LoggingLevel in %valid_logging_levels
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
}
}
}
}
DefaultRouteSettings
AWS::ApiGatewayV2::Stage では、 DefaultRouteSettings と RouteSettings でログレベルの設定を行います。
DataTraceEnabled: Boolean
DetailedMetricsEnabled: Boolean
LoggingLevel: String
ThrottlingBurstLimit: Integer
ThrottlingRateLimit: Number
RouteSettings では次のように、特定ルート(hoge
)でのログレベルの上書きを行えます。
Resources:
Stage:
Type: AWS::ApiGatewayV2::Stage
Properties:
StageName: stg
StageVariables: {}
ApiId: XXXXXXXXX
RouteSettings:
$hoge:
DataTraceEnabled: false
DetailedMetricsEnabled: false
LoggingLevel: ERROR
DefaultRouteSettings:
DataTraceEnabled: false
DetailedMetricsEnabled: false
LoggingLevel: INFO
そのため、どちらのパターンも拾えるよう、次のように記載しました。
# AWS::ApiGatewayV2::Stage
rule api_gw_v2_execution_logging_check when %api_gw_v2_execution_logging_enabled !empty {
%api_gw_v2_execution_logging_enabled.Properties {
# DefaultRouteSettings が存在するかチェック
DefaultRouteSettings exists
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
# DefaultRouteSettings が存在した場合、LoggingLevel が %valid_logging_levels 内のどれかに当てはまるかチェック
when DefaultRouteSettings exists {
DefaultRouteSettings {
LoggingLevel in %valid_logging_levels
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGateway::Stage, AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if the loggingLevel isn't ERROR or INFO for all stages of the API. Unless you provide custom parameter values to indicate that a specific log type should be enabled, Security Hub produces a passed finding if the logging level is either ERROR or INFO.
>>
}
}
# RouteSettings が存在した場合、全ての LoggingLevel が %valid_logging_levels 内のどれかに当てはまるかチェック
when RouteSettings exists {
RouteSettings.* {
LoggingLevel in %valid_logging_levels
<<
RuleID: APIGateway.1
Category: Identify > Logging
Severity: Medium
Resource type: AWS::ApiGatewayV2::Stage
Description: This control checks whether all stages of an Amazon API Gateway REST or WebSocket API have logging enabled. The control fails if:
- DefaultRouteSettings is missing or its LoggingLevel isn't ERROR or INFO
- Any RouteSettings entry is missing LoggingLevel or its LoggingLevel isn't ERROR or INFO
>>
}
}
}
}
テストしてみる
次のテストコードを用意しました。
# api-gw-execution-logging-enabled.yaml
---
# テストケース1: 空のテンプレート
# 期待値: SKIP - リソースが存在しないため、ルールはスキップされる
- name: Empty
input: {}
expectations:
rules:
api_gw_execution_logging_check: SKIP
api_gw_v2_execution_logging_check: SKIP
# テストケース2: 空のリソースセクション
# 期待値: SKIP - 評価対象のリソースが存在しないため、ルールはスキップされる
- name: No resources
input:
Resources: {}
expectations:
rules:
api_gw_execution_logging_check: SKIP
api_gw_v2_execution_logging_check: SKIP
# テストケース3: MethodSettings未設定のAPI Gateway Stage
# 期待値: FAIL - MethodSettingsが必須だが設定されていないため、チェックは失敗する
- name: No Method set in API GW Stage
input:
Resources:
MyApiStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
Variables:
Stack: Prod
expectations:
rules:
api_gw_execution_logging_check: FAIL
# テストケース4: LoggingLevelがINFOに設定されたケース
# 期待値: PASS - INFOは有効なログレベルの一つ
- name: LoggingLevel set to INFO
input:
Resources:
myApiGWStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
MethodSettings:
- ResourcePath: /
HttpMethod: GET
MetricsEnabled: true
LoggingLevel: "INFO"
expectations:
rules:
api_gw_execution_logging_check: PASS
# テストケース5: LoggingLevelがERRORに設定されたケース
# 期待値: PASS - ERRORは有効なログレベルの一つ
- name: LoggingLevel set to ERROR
input:
Resources:
myApiGWStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
MethodSettings:
- ResourcePath: /
HttpMethod: GET
MetricsEnabled: true
LoggingLevel: "ERROR"
expectations:
rules:
api_gw_execution_logging_check: PASS
# テストケース6: 無効なLoggingLevel値
# 期待値: FAIL - "SUPER"は有効なログレベルではない
- name: LoggingLevel set to invalid value
input:
Resources:
myApiGWStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
MethodSettings:
- ResourcePath: /
HttpMethod: GET
MetricsEnabled: true
LoggingLevel: "SUPER"
expectations:
rules:
api_gw_execution_logging_check: FAIL
# テストケース7: LoggingLevel設定なし
# 期待値: FAIL - LoggingLevelは必須パラメータ
- name: LoggingLevel missing
input:
Resources:
myApiGWStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
MethodSettings:
- ResourcePath: /
HttpMethod: GET
MetricsEnabled: true
expectations:
rules:
api_gw_execution_logging_check: FAIL
# テストケース8: ルール抑制が設定されたケース
# 期待値: SKIP - Metadataでルールが抑制されているため
- name: Rule suppressed
input:
Resources:
myApiGWStage:
Type: AWS::ApiGateway::Stage
Metadata:
guard:
SuppressedRules:
- "API_GW_EXECUTION_LOGGING_ENABLED"
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
MethodSettings:
- ResourcePath: /
HttpMethod: GET
MetricsEnabled: true
expectations:
rules:
api_gw_execution_logging_check: SKIP
# テストケース9: WebSocket APIでINFOログレベル
# 期待値: PASS - INFOは有効なログレベル
- name: WebSocket API with INFO logging
input:
Resources:
MyWebSocketStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
DefaultRouteSettings:
LoggingLevel: "INFO"
expectations:
rules:
api_gw_v2_execution_logging_check: PASS
# テストケース10: WebSocket APIでERRORログレベル
# 期待値: PASS - ERRORは有効なログレベル
- name: WebSocket API with ERROR logging
input:
Resources:
MyWebSocketStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
DefaultRouteSettings:
LoggingLevel: "ERROR"
expectations:
rules:
api_gw_v2_execution_logging_check: PASS
# テストケース11: WebSocket APIで無効なログレベル
# 期待値: FAIL - DEBUGは許可されていないログレベル
- name: WebSocket API with invalid logging level
input:
Resources:
MyWebSocketStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
DefaultRouteSettings:
LoggingLevel: "DEBUG"
expectations:
rules:
api_gw_v2_execution_logging_check: FAIL
うまくテストケースが通っていますね。
takakuni@ api_gateway % cfn-guard test -r api-gw-execution-logging-enabled.guard -t api-gw-execution-logging-enabled_test.yaml
Test Case #1
Name: Empty
PASS Rules:
api_gw_execution_logging_check: Expected = SKIP
api_gw_v2_execution_logging_check: Expected = SKIP
Test Case #2
Name: No resources
PASS Rules:
api_gw_v2_execution_logging_check: Expected = SKIP
api_gw_execution_logging_check: Expected = SKIP
Test Case #3
Name: No Method set in API GW Stage
No Test expectation was set for Rule api_gw_v2_execution_logging_check
PASS Rules:
api_gw_execution_logging_check: Expected = FAIL
Test Case #4
Name: LoggingLevel set to INFO
No Test expectation was set for Rule api_gw_v2_execution_logging_check
PASS Rules:
api_gw_execution_logging_check: Expected = PASS
Test Case #5
Name: LoggingLevel set to ERROR
No Test expectation was set for Rule api_gw_v2_execution_logging_check
PASS Rules:
api_gw_execution_logging_check: Expected = PASS
Test Case #6
Name: LoggingLevel set to invalid value
No Test expectation was set for Rule api_gw_v2_execution_logging_check
PASS Rules:
api_gw_execution_logging_check: Expected = FAIL
Test Case #7
Name: LoggingLevel missing
No Test expectation was set for Rule api_gw_v2_execution_logging_check
PASS Rules:
api_gw_execution_logging_check: Expected = FAIL
Test Case #8
Name: Rule suppressed
No Test expectation was set for Rule api_gw_v2_execution_logging_check
PASS Rules:
api_gw_execution_logging_check: Expected = SKIP
Test Case #9
Name: WebSocket API with INFO logging
No Test expectation was set for Rule api_gw_execution_logging_check
PASS Rules:
api_gw_v2_execution_logging_check: Expected = PASS
Test Case #10
Name: WebSocket API with ERROR logging
No Test expectation was set for Rule api_gw_execution_logging_check
PASS Rules:
api_gw_v2_execution_logging_check: Expected = PASS
Test Case #11
Name: WebSocket API with invalid logging level
No Test expectation was set for Rule api_gw_execution_logging_check
PASS Rules:
api_gw_v2_execution_logging_check: Expected = FAIL
まとめ
以上、「[APIGateway.1] API ゲートウェイRESTと WebSocket API実行のログ記録を有効にする必要があります
を CloudFormation Guard でチェックする」でした。
ブログネタに困ったら、このシリーズを擦ろうと思いました。つもりに積もって、完成したら嬉しいですね。
クラウド事業本部コンサルティング部のたかくに(@takakuni_)でした!