もしあなたが1人っきりでSaaSのインフラを作ることになったら...
この記事は、クラウドワークス Advent Calendar 2023 シリーズ1の7日目の記事です。
はじめに
crowdworks.jpでSREをしていますciloholicです。
前職では、長らくバックエンド兼インフラエンジニアをしていたんですが、ある日突然SaaSを作るぞと言われて、SaaSのインフラを1人で作る羽目になったことがあります。
当時は、サービス自体の開発に追われていて、AWSのマルチアカウント管理やセキュリティサービスの導入(Config/CloudTrail/GuardDuty等)などがおざなりになっていました。
その時の戒めを込めて、再び「SaaSのインフラを作れ」と言われても大丈夫なように、TerraformでAWSのマルチアカウント管理とセキュリティサービスの導入を行なってみました。
タイトルでは SaaSのインフラ というように話題を大きめに書いてますが、本記事では AWSのマルチアカウント管理 と セキュリティサービスの導入 のみに焦点を当てています。
それ以外については、SaaSによって千差万別なので、本記事では割愛しています。
実際にTerraformで実装したコードは、GitHubに格納しています。
本記事では、Terraformのコードは載せず、実装の流れや実装時の注意点などを主に書き連ねていきます。
想定しているAWSアカウント構成
AWSでマルチアカウント管理を行なおうとした場合、AWS Organizationsを利用するのが一般的です。
AWS Organizationsでは、下記のような機能が提供されています。
AWS Organizations には、クラウド環境を一元管理および統制する機能があります。アカウントを 1 つの請求書で管理および整理したり、組織全体の中央ポリシーと設定要件を設定したり、組織内にカスタムのアクセス許可または機能を作成したり、他のアカウントに責任を委任して組織の代わりに管理できるようにしたりすることができます。
加えて、AWS Organizations は他の AWS のサービスと統合しているため、中央での設定、セキュリティメカニズム、監査要件、組織内のアカウント間でのリソース共有を定義できます。
本記事でも例に漏れず、AWS Organizationsを利用してマルチアカウント管理を行なっていきます。
便利なもので、安全なマルチアカウントAWS環境のセットアップと管理できるAWS Control Towerが提供されており、AWS Organizationsの設定やセキュリティサービスの導入を自動でセットアップしてくれます。
しかし、自主練にならないため、AWS Control Towerの構成を参考にOU構成をカスタマイズして自作していきたいと思います。
小規模なSaaSであったとしても、本番・開発・ステージングぐらいはAWSアカウントを分けたいところです。
また、ワークロード環境に加えて、セキュリティ用やログ集積用のAWSアカウントも欲しいです。
本来なら、Log-Archive用やAudit用のAWSアカウントも作成したいところですが、自主練のために何個もAWSアカウントを作成したくないので、Security用に統合しています。
- Root
- [Account]Management
- [OU]Core
- [Account]Jump
- [Account]Security( + Log-Archive + Audit)
- [OU]Workloads
- [Account]Production
- [Account]Development
- [Account]Staging
Terraform周りのバージョン
Terraform周りのバージョンについては、下記の通りです。
- Terraform: v1.6.4
- terraform-provider-aws: v5.26.0
Terraformのディレクトリ構成
Terraformで実装する際は、下記のようなディレクトリ構成にしています。
各種AWSアカウントごとにtfstateファイルを分けて管理します。
.
└── accounts
├── management
│ └── .terraform-version
├── core
│ ├── jump
│ │ └── .terraform-version
│ └── security
│ └── .terraform-version
└── workloads
├── production
│ └── .terraform-version
├── development
│ └── .terraform-version
└── staging
└── .terraform-version
AWSのマルチアカウント管理
AWS Organizations
初めにOrganizationとOU、各種AWSアカウントを作成していきます。
OrganizationとOUの作成
-
accounts/management/organization.tf
- aws_organizations_organization.org
- aws_organizations_organizational_unit.core
- aws_organizations_organizational_unit.workloads
各種AWSアカウントの作成
AWS OrganizationsでAWSアカウントを作成すると、ルートユーザーに加えて OrganizationAccountAccessRole というIAMロールが自動的に作成されます。
今後はスイッチロールを実装するまで、このIAMロールにスイッチしてAWSアカウントにアクセスすることになります。
-
accounts/management/organization.tf
- aws_organizations_account.jump
- aws_organizations_account.security
- aws_organizations_account.[production/development/staging]
[小ネタ]AWSアカウントの削除
terraform-provider-aws v4.9.0では、aws_organizations_account
にclose_on_deletion
が追加されました。
close_on_deletion
をtrue
にすると、AWSアカウントが閉鎖できるようになりました。
以前は、OraganizationからAWSアカウントが除外されるだけで、AWSアカウントを閉鎖できませんでした。
ちゃんと閉鎖したい場合は、close_on_deletion
をtrue
にしておくと良いです。
AWSアカウント閉鎖後はログインができなくなり、90日経過すると完全に削除されます。
各種AWSアカウント作成後の対応
セキュリティ強化の為、各種AWSアカウントのMFA有効化だけは必須で対応しておきましょう。
- [必須]ルートアカウントのMFA有効化
- 初期パスワードが発行されてないため、パスワードをリセットしてからMFAを有効化する必要があります
- [任意]不要なリソースの削除
- 下記URLのスクリプトを参考にデフォルトVPC、サブネット、IGWを削除します
- https://dev.classmethod.jp/articles/delete-default-vpcs-by-cloudshell
Service Control Policy(SCP)の作成
Service Control Policy(SCP)でリージョン単位のAWSへのアクセスを制限しています。
基本的に ap-northeast-1 と us-east-1 しか利用しない想定ですが、AWS Chatbotが us-east-2 で動作しているため、us-east-2 も利用できるようにしています。
AWS Chatbotのような例もあるので、リージョン単位のアクセス制限には注意が必要です。
-
accounts/management/organization.tf
- aws_organizations_policy.region_restriction
- aws_organizations_policy.core_region_restriction
- aws_organizations_policy.workloads_region_restriction
- aws_organizations_policy_attachment.core_region_restriction
- aws_organizations_policy_attachment.workloads_region_restriction
[小ネタ]AWS Chatbotのリージョン
AWS Chatbotのトラブルシューティングにリージョンの記載があります。
tfstate用S3バケットの作成
各種AWSアカウントのS3にtfstate用のS3バケットを用意します。
S3バケット名の末尾にランダム文字列を付与して重複しないようにしています。
必要であれば、バージョニングも有効にしておきます。
$ aws-vault exec *** -- aws s3api create-bucket \
--bucket tfstate-me8aelie \
--region ap-northeast-1 \
--create-bucket-configuration \
LocationConstraint=ap-northeast-1
S3バケットが作成できたら、Terraformにインポートしておきます。
この作業を各種AWSアカウントにも同様に行ないます。
-
accounts/management/s3.tf
- aws_s3_bucket.tfstate_me8aelie
- aws_s3_bucket_ownership_controls.tfstate_me8aelie
- aws_s3_bucket_server_side_encryption_configuration.tfstate_me8aelie
IAMのスイッチロールの設定
各種AWSアカウントに対して、IAMのスイッチロールを設定を行ないます。
Jumpアカウント
管理者権限付きのIAMユーザーを用意します。
必要であれば、自身のパスワード変更権限やMFA変更権限を付与した一般ユーザー用のIAMユーザーも作成してみてください。
-
accounts/core/jump/iam_user.tf
- aws_iam_user.tarou_yamada
- aws_iam_user_group_membership.tarou_yamada
Jumpアカウント以外
Jumpアカウント経由のMFA有効済みユーザーのみがスイッチロールできる、管理者権限付きのIAMロールを作成します。
必要であれば、権限を制限した一般ユーザー用のIAMロールも作成してみてください。
この作業を各種AWSアカウントにも同様に行ないます。
-
accounts/management/iam.tf
- aws_iam_role.management_admin
スイッチロールの動作確認
ここまでできていれば、各種AWSアカウントにスイッチロールできるようになっているはずです。
セキュリティサービスの導入
Audit用S3バケットの作成
SecurityアカウントにAWS ConfigとAWS CloudTrailのログを格納するS3バケットを作成します。
各種AWSアカウントのAWS ConfigとAWS CloudTrailのログは、このS3バケットに集約されます。
-
accounts/core/security/s3.tf
- aws_s3_bucket.aws_config
- aws_s3_bucket_server_side_encryption_configuration.aws_config
- aws_s3_bucket_policy.aws_config
- aws_s3_bucket.aws_cloudtrail
- aws_s3_bucket_server_side_encryption_configuration.aws_cloudtrail
- aws_s3_bucket_policy.aws_cloudtrail
AWS Config
各種AWSアカウントにAWS Configを導入します。
AWS Configは、リージョン単位に有効化する必要があるため、利用可能なすべてのリージョンで有効化するのが推奨されています。
本記事では、AWS OrganizationsのSCPで不要なリージョンのアクセス制限をしているので、利用しているリージョンのみを有効化するだけで済ませています。
また、aws_config_configuration_recorder
で作成するとグローバルリソースの記録も含まれてしまうため、東京リージョンのみでグローバルリソースを記録し、他のリージョンでは記録しないように設定しています。
この作業を各種AWSアカウントにも同様に行ないます。
-
accounts/management/config.tf
- aws_config_configuration_recorder.config_recorder
- aws_config_delivery_channel.default
- aws_config_configuration_recorder_status.default
- aws_config_configuration_recorder.config_recorder_us_east_1
- aws_config_delivery_channel.default_us_east_1
- aws_config_configuration_recorder_status.default_us_east_1
AWS Configの委任
AWS Configは、Securityアカウントに委任できます。
ただし、AWS Configの委任は、Terraform経由では行なえないため、AWS CLIで対応します。
$ aws-vault exec management -- aws organizations register-delegated-administrator --account-id 222222222222 --service-principal config.amazonaws.com
$ aws-vault exec management -- aws organizations register-delegated-administrator --account-id 222222222222 --service-principal config-multiaccountsetup.amazonaws.com
AWS Config Aggregator
上記でAWS Configの委任が完了したら、Aggregatorを導入します。
AWS Config Aggregatorという機能を利用すると、AWS Configで収集しているリソースやAWS Config Ruleのチェック結果をマルチアカウント/マルチリージョンより集約してレポーティングできます。
AggregatorをSecurityアカウントに委任することで、SecurityアカウントからAWS Configのレポートを確認できるようになります。
-
accounts/core/security/config.tf
- aws_config_configuration_aggregator.config_aggregator
AWS CloudTrail
各種AWSアカウントにAWS CloudTrailを導入します。
AWS CloudTrailは、Organizationに紐づくメンバーアカウントに対して、マルチリージョンよりログを集約できます。
is_organization_trail
をtrue
にすれば、各種AWSアカウントでAWS CloudTrailが有効化されます。
-
accounts/management/cloudtrail.tf
- aws_cloudtrail.cloudtrail
AWS CloudTrailの委任
AWS CloudTrailは、Securityアカウントに委任できます。
ただし、AWS CloudTrailの委任は、Terraform経由では行なえないため、AWS CLIで対応します。
$ aws-vault exec management -- aws organizations register-delegated-administrator --account-id 222222222222 --service-principal cloudtrail.amazonaws.com
AWS GuardDuty
各種AWSアカウントにAWS GuardDutyを導入します。
AWS GuardDutyは、リージョン単位に有効化する必要があるため、利用可能なすべてのリージョンで有効化するのが推奨されています。
この作業を各種AWSアカウントにも同様に行ないます。
-
accounts/management/guardduty.tf
- aws_guardduty_detector.guardduty_detector
- aws_guardduty_detector.guardduty_detector_us_east_1
AWS GuardDutyの委任
AWS GuardDutyは、Securityアカウントに委任できます。
-
accounts/management/guardduty.tf
- aws_guardduty_organization_admin_account.guardduty
AWS Security Hub
各種AWSアカウントにAWS Security Hubを導入します。
AWS Security Hubは、リージョン単位に有効化する必要があるため、利用可能なすべてのリージョンで有効化するのが推奨されています。
本記事では、セキュリティコントロールの適用まで行なっていないので、必要な方は別途対応をしてください。
この作業を各種AWSアカウントにも同様に行ないます。
-
accounts/management/securityhub.tf
- aws_securityhub_account.securityhub
- aws_securityhub_organization_admin_account.securityhub_us_east_1
AWS Security Hubの委任
AWS Security Hubは、Securityアカウントに委任できます。
-
accounts/management/securityhub.tf
- aws_securityhub_organization_admin_account.securityhub
AWS Security Hubの有効化
Organizationに紐づくメンバーアカウントに対して、AWS Security Hubを有効化します。
ただし、メンバーアカウントのSecurity Hub有効化は、Terraform経由では行なえないため、AWS CLIで対応します。
$ aws-vault exec security -- aws securityhub create-members --account-details '[{"AccountId":"<accountId>"}]'
AWS Security Hub Finding Aggregator
上記でAWS Security Hubの委任が完了したら、Finding Aggregatorを導入します。
AWS Security Hub Finding Aggregatorという機能を利用すると、AWS Security Hubで収集しているコントロールコンプライアンスのステータス、セキュリティスコアをマルチアカウント/マルチリージョンより集約できます。
Finding AggregatorをSecurityアカウントに委任することで、SecurityアカウントからAWS Security Hubのイベント結果を確認できるようになります。
-
accounts/core/security/securityhub.tf
- aws_securityhub_finding_aggregator.finding_aggregator
AWS Security Hubの通知
AWS Security Hubは、他のセキュリティサービスと統合することで、セキュリティインシデントの通知を一元管理できます。
本記事で言うと、AWS ConfigやAWS GuardDutyのイベントをAWS Security Hubに集約し、AWS EventBridgeで検知してAWS ChatbotでSlackに通知できます。
本来なら通知まで実装したかったんですが、AWS Security Hubの導入までで力尽きました。
さいごに
今回は、AWSのマルチアカウント管理とセキュリティサービスの導入を雑にTerraformで実装しました。
委任周りの対応がTerraformでできなかったり、セキュリティサービスの導入に四苦八苦する部分もありましたが、良い自主練になりました。
これで、突然SaaSのインフラを作れと言われても大丈夫そうです。
Discussion