この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
AWS Configはリソース構成を記録・管理するサービスで、Config ルールでは管理しているリソースの評価ができます。その条件に違反しているリソースは非準拠としてフラグをつけられます。
Config ルールにはこの違反した非準拠のリソースに対して「修復」のアクションを定義でき、想定したリソースの状態へと自動修復する機能があります。
今回は パブリックアクセス可能なRDSインスタンスを自動で修復する(パブリックアクセスを無効にする) Configルールを作成してみました。
CloudFormationテンプレート
StackSetsでマルチリージョン・マルチアカウントに展開できるよう、CloudFormationテンプレートで作成しました。
作成するリソース
- IAMロール
- 自動修復実行用
- Configルール
- 修復アクション
- SSMドキュメント
- AWS提供のドキュメントではなく新規に作成(理由は後ほど)
AWSTemplateFormatVersion: "2010-09-09"
# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
IamRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ssm-automation-role-for-rds-disable-public-access-${AWS::Region}
Description: "role for ssm automation: s3 encryption"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "ssm.amazonaws.com"
Action: "sts:AssumeRole"
Policies:
- PolicyName: !Sub Ssm-Automation-Policy-DisablePublicAccess-${AWS::Region}
PolicyDocument:
Version: "2012-10-17"
Statement:
Effect: "Allow"
Action:
- ssm:StartAutomationExecution
- ssm:GetAutomationExecution
- rds:DescribeDBInstances
- rds:ModifyDBInstance
Resource: "*"
ConfigRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: rds-instance-public-access-check
Description: "Checks whether the Amazon Relational Database Service (RDS) instances are not publicly accessible. The rule is non-compliant if the publiclyAccessible field is true in the instance configuration item."
Source:
Owner: AWS
SourceIdentifier: RDS_INSTANCE_PUBLIC_ACCESS_CHECK
Scope:
ComplianceResourceTypes:
- "AWS::RDS::DBInstance"
RemediationConfiguration:
Type: AWS::Config::RemediationConfiguration
Properties:
Automatic: true
ConfigRuleName: !Ref ConfigRule
MaximumAutomaticAttempts: 5
RetryAttemptSeconds: 1200
TargetId: !Ref ConfigRemediationDisablePublicAccessToRDSInstance
TargetType: "SSM_DOCUMENT"
TargetVersion: "1"
Parameters:
DbiResourceId:
ResourceValue:
Value: RESOURCE_ID
AutomationAssumeRole:
StaticValue:
Values:
- !GetAtt [IamRole, "Arn"]
############################################################################################################
# RDSインスタンスのパブリックアクセス修正用SMSドキュメント
# 当初以下AWS管理のドキュメントを使用予定だったが、新規作成のRDSインスタンスへの実行がエラーとなったため一部AWS管理のドキュメントを修正して利用
# https://docs.aws.amazon.com/systems-manager-automation-runbooks/latest/userguide/automation-aws-disable-rds-instance-public-access.html
############################################################################################################
ConfigRemediationDisablePublicAccessToRDSInstance:
Type: AWS::SSM::Document
Properties:
DocumentType: Automation
Name: ConfigRemediation-DisablePublicAccessToRDSInstance
Content:
schemaVersion: "0.3"
description: |
### Document name - ConfigRemediation-DisablePublicAccessToRDSInstance
## What does this document do?
The runbook disables public accessibility for the Amazon RDS database instance you specify using
the [ModifyDBInstance](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_ModifyDBInstance.html) API.
## Input Parameters
* AutomationAssumeRole: (Required) The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that allows Systems Manager Automation to perform the actions on your behalf.
* DbiResourceId: (Required) The resource identifier for the DB instance you want to disable public accessibility.
## Output Parameters
* DisablePubliclyAccessibleOnRDS.Response: The standard HTTP response from the ModifyDBInstance API.
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
AutomationAssumeRole:
type: String
description: (Required) The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that allows Systems Manager Automation to perform the actions on your behalf.
allowedPattern: ^arn:(aws[a-zA-Z-]*)?:iam::\d{12}:role/[\w+=,.@-]+
DbiResourceId:
type: String
description: (Required) The resource identifier for the DB instance you want to disable public accessibility.
allowedPattern: "db-[A-Z0-9]{26}"
outputs:
- DisablePubliclyAccessibleOnRDS.Response
mainSteps:
- name: GetRDSInstanceIdentifier
action: "aws:executeAwsApi"
description: |
## GetRDSInstanceIdentifier
Gathers the DB instance identifier from the DB instance resource identifier.
## Outputs
* DbInstanceIdentifier: The Amazon RDS DB instance identifier.
timeoutSeconds: 600
isEnd: false
inputs:
Service: rds
Api: DescribeDBInstances
Filters:
- Name: "dbi-resource-id"
Values:
- "{{ DbiResourceId }}"
outputs:
- Name: DbInstanceIdentifier
Selector: $.DBInstances[0].DBInstanceIdentifier
Type: String
- name: WaitForDBInstanceStatusToAvailable
action: "aws:waitForAwsResourceProperty"
timeoutSeconds: 1200
isEnd: false
description: |
## WaitForDBInstanceStatusToAvailable
Waits for the DB instance to change to a AVAILABLE state.
inputs:
Service: rds
Api: DescribeDBInstances
DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}"
PropertySelector: "$.DBInstances[0].DBInstanceStatus"
DesiredValues:
- "available"
- name: VerifyDBInstanceStatus
action: "aws:assertAwsResourceProperty"
timeoutSeconds: 600
isEnd: false
description: |
## VerifyDBInstanceStatus
Verifies the DB instances is in an AVAILABLE state.
inputs:
Service: rds
Api: DescribeDBInstances
DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}"
PropertySelector: "$.DBInstances[0].DBInstanceStatus"
DesiredValues:
- "available"
- name: DisablePubliclyAccessibleOnRDS
action: "aws:executeAwsApi"
description: |
## DisablePubliclyAccessibleOnRDS
Disables public accessibility on your DB instance.
## Outputs
* Response: The standard HTTP response from the ModifyDBInstance API.
timeoutSeconds: 600
isEnd: false
inputs:
Service: rds
Api: ModifyDBInstance
DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}"
PubliclyAccessible: false
outputs:
- Name: Response
Selector: $
Type: StringMap
- name: WaitForDBInstanceStatusToModify
action: "aws:waitForAwsResourceProperty"
timeoutSeconds: 600
isEnd: false
description: |
## WaitForDBInstanceStatusToModify
Waits for the DB instance to change to a MODIFYING state.
inputs:
Service: rds
Api: DescribeDBInstances
DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}"
PropertySelector: "$.DBInstances[0].DBInstanceStatus"
DesiredValues:
- "modifying"
- name: WaitForDBInstanceStatusToAvailableAfterModify
action: "aws:waitForAwsResourceProperty"
timeoutSeconds: 600
isEnd: false
description: |
## WaitForDBInstanceStatusToAvailableAfterModify
Waits for the DB instance to change to an AVAILABLE state
inputs:
Service: rds
Api: DescribeDBInstances
DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}"
PropertySelector: "$.DBInstances[0].DBInstanceStatus"
DesiredValues:
- "available"
- name: VerifyDBInstancePubliclyAccess
action: "aws:assertAwsResourceProperty"
timeoutSeconds: 600
isEnd: true
description: |
## VerifyDBInstancePubliclyAccess
Confirms public accessibility is disabled on the DB instance.
inputs:
Service: rds
Api: DescribeDBInstances
DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}"
PropertySelector: "$.DBInstances[0].PubliclyAccessible"
DesiredValues:
- "False"
リソースの確認
展開されるリソースの確認をしていきます。Configが有効化されているリージョンで先ほどのCloudFormationテンプレートを使ってスタックを作成してください。
Configルール
rds-instance-public-access-check
というConfigルールが作成されています。
対象のリソース変更時にチェックが実行され、非準拠となった場合には修復アクションで定義したSSMドキュメント(ConfigRemediation-DisablePublicAccessToRDSInstance)が実行されます。
ここの修復アクションで使われる権限は、新規で作成しているIAMロールを指定しています。
このConfigルールではRDSのDBインスタンスを対象にチェックしており、パブリックアクセスが有効になっているものがあると非準拠となります。
SSMドキュメント
先ほど修復アクションで指定されていたSSMドキュメントを確認します。
AWS Systems Managerのコンソールから ドキュメント>自己所有 を確認すると、ConfigRemediation-DisablePublicAccessToRDSInstance
というSSMドキュメントが作成されています。
このドキュメントで実行されるステップを確認すると、以下の7つが定義されています。
ざっと簡単に説明すると、以下のようなことを各ステップで行なっています。
- DBインスタンスの情報を取得
- ステータスが
available
になるまで待機 - ステータスが
available
になったことを確認 - DBインスタンスの
public accessibility
を無効化 - ステータスが
modifying
になるまで待機 - ステータスが
available
になるまで待機 - DBインスタンスの
public accessibility
が無効化されていることを確認
上記を非準拠となったDBインスタンスに対して実行することで、パブリックアクセスが可能なものを自動修復する仕組みとなっています。
動作確認
それではパブリックアクセスが有効になっているDBインスタンスを作成して、どのように動作するのか確認してみましょう。
パブリックアクセスを「あり」にチェックを入れてDBインスタンスを作成してみます。
Configルールを確認すると、非準拠のリソースとして作成したDBインスタンスが表示されました。
そこから少し待つと、ステータスに「アクションが正常に実行されました」と表示されます。
改めて作成したDBインスタンスを確認してみると、パブリックアクセスが自動で修復されていることが確認できました。
なぜAWS管理のSSMドキュメントを使わないのか
AWSから提供されているSSMドキュメントにAWSConfigRemediation-DisablePublicAccessToRDSInstance
という今回作成したドキュメントに似たものがあります。(というよりこのドキュメントをもとに作成しました。)
このドキュメントを使わなかった理由は、DBインスタンスを作成時の修復を想定されたステップになっていなかったからです。
このSSMドキュメントを使用して修復アクションを実行してみた所、既存のRDSに対しては問題なく実行できるのですが、新規作成のDBインスタンスを実行した際にはエラーとなります。
コンソールからエラー詳細が確認できないので、CLIで確認すると以下のエラーメッセージが出力されました。
"Step fails when it is Execute/Cancelling action. Property value 'creating' from the API output is not in the desired values. Desired values: ['available'].. Please refer to Automation Service Troubleshooting Guide for more diagnosis details."
これは、DBインスタンスが作成中(creating)のステータス時にSSMドキュメントが ステータスがavailable
になったことを確認する のステップが実行されてしまったため、想定とステータスが違うことでエラーとなったようです。
そのため、今回はAWS管理のSSMドキュメントをもとに ステータスがavailable
になるまで待機するステップを追加したものを新規で作成しています。
おわりに
Configルールの自動修復を使ってRDSのパブリックアクセスを無効にしてみました。RDSのインスタンスをパブリックに公開するケースはほとんどないかと思いますので、統制をかけたい際にぜひ利用してみてください。