踏み台をSSM Session ManagerとAWS SSOで実現する
目次
踏み台のユーザーが増えてきたため公開鍵管理や監視と運用負荷が上がってきました。オペミスが発生しやすい上 監査的な意味で無視できない状況になってきたので重い腰を上げることにしました。

PROBLEM
- EC2インスタンスの踏み台運用がつらい
- 公開鍵管理がつらい
- 提出・設定・確認ともに運用コストがかかる
- AWSアカウント数 x ユーザー数で指数関数的に運用コストが増していくことが想定される
- インフラ管理が分散していると、提出側・設定側ともに重複コストが発生する
- 監視運用がつらい
- 定期的に踏み台がブルートフォース攻撃を受けており、脅威が低いとは言えストレスがかかる
- 踏み台アクセスへの監査ログが不十分
- 公開鍵管理がつらい
SOLUTION
というわけで、Session ManagerとSSOでアクセス管理の効率化を狙います。
踏み台サーバーの設定
まず、データフローとしては下記の図の通りで、今回はプライベートサブネット上にEC2を置いて素のSession ManagerでDBへの接続することにします。当該インスタンスは AmazonSSMManagedInstanceCore ポリシー1を含んだロールを適用。なお、ECS ExecではSession Managerでポートフォワーディングを実現でき無かったことに加え、既存の踏み台資産を流用するため今回の実装対象から外しました。
skinparam handwritten true
actor "端末\nDBクライアント" as U
box AWS
actor "STS/SSO" as S
participant "踏み台サーバ\nprivate subnet" as E
database DB as D
end box
U <-> S: AssumeRole
activate U
activate S
U <-> E: aws ssm start-session\nポートフォワード
activate E
E <-> D: 認証
activate D
U -> E
E <-> D: クエリ
deactivate D
E ---> U
deactivate E
deactivate U
deactivate S
SSOの設定
踏み台サーバーの設定が終わったら、次に当該インスタンスへ接続するためにSSOで渡すロールをアクセス権限セットに設定します。下記カスタムポリシーはEC2インスタンスにアクセスするための必要最低限のものになります。
カスタムポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"ds:CreateComputer",
"ds:DescribeDirectories",
"ec2:DescribeInstanceStatus",
"logs:*",
"ssm:*",
"ec2messages:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:StartSession"
],
"Resource": [
"arn:aws:ssm:*:*:session/<EC2インスタンスID>",
"arn:aws:ec2:*:*:instance/<EC2インスタンスID>"
]
},
{
"Effect": "Deny",
"Action": [
"ssm:Describe*",
"ssm:Get*",
"ssm:List*",
"logs:Describe*",
"logs:Get*",
"logs:List*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*",
"Condition": {
"StringLike": {
"iam:AWSServiceName": "ssm.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*",
"Condition": {
"StringLike": {
"iam:AWSServiceName": "ssm.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Action": [
"iam:DeleteServiceLinkedRole",
"iam:GetServiceLinkedRoleDeletionStatus"
],
"Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*"
},
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
]
}
セッションを張るための事前準備
セッションを張るためには下記3つの手順が必要になります。SSO経由のセッション設定が2通りありますが、クレデンシャル方式はセッションが切れる毎に変更する手間があるため、CLI方式をお薦めします。
- AWS CLI v2をインストール
- 下記いずれかの方式でSSO経由のセッション設定を行う
- Session Manager プラグインをインストール
DBクライアントの設定
最後に、DBクライアントについて3つの手順を踏んで接続を試みます2。なお、ローカル環境でポートフォワーディングを都度行うのを省略したい方は、DataGripを利用すると良いでしょう。
- ローカル環境にて
~/.ssh/configファイルを編集- Session Managerにproxyと対象RDSのエンドポイントを記載
config Host <任意のhost名> HostName <※ 指定しなければlocalhostになる> User ec2-user ProxyCommand sh -c "aws ssm start-session --target <接続する踏み台のインスタンスID> --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --region ap-northeast-1 --profile <プロフィール>" LocalForward <任意のポート> <RDSエンドポイント>:<RDSポート> IdentityFile ~/.ssh/<EC2に接続する秘密鍵> - 設定したhost名でセッションマネージャー越しにssh接続できるかを確認
sh $ ssh <設定したhost名>
- Session Managerにproxyと対象RDSのエンドポイントを記載
- 手順1で設定したsshで接続することでポートフォワーディング
- DBクライアントで下記のように接続情報を設定し接続する
- Host:
<手順1のconfigファイルにて任意指定したホスト名> - Port:
<手順4のconfigファイルにて任意指定したポート> - 他項目: DB接続情報
- Host:
WRAPUP
パブリックサブネット上の踏み台に慣れている方は馴染みのない方法に戸惑うかも知れませんが、踏み台資産を流用できるという意味で導入のコストもそれほどかかりませんし、ユーザーとしても利用の敷居は高くありませんでした。後々の管理コストを心配している方は一度検討してみてはいかがでしょうか。
-
AmazonEC2RoleforSSMは非推奨のため適用しないように注意します。 ↩ -
今回はメンテナンスコストを避けるためSSH over SSMの関連ツール
ssh-ssm.shssm-toolは使わない方針でいます。 ↩
