モチベーション
Bastionサーバーは、会社のサーバーの入り口であり、アクセス管理は厳重に行う必要があります。Bastion(要塞)という名前の通り、各社強力なセキュリティの対策を行っていました。最も一般的なものだと、個人ごとにアカウントとSSHアクセス用のKey Pairを発行したり、LDAPを利用した全社共通のBastion認証基盤の用意などがあります。しかし、その設定コストはそれなりにあって、インフラエンジニアの負担となることがありました。 これらの問題に対処するため、AWSは 2018年にSSM(Session Manager) という機能をリリースしました。本記事では、SSMを用いた Bastion サーバーの構築について説明をします。
前提知識
従来のBastionサーバー管理方法

従来、Bastionサーバーは上の図に示す形で運用されてきました。Public SubnetにEC2 instanceを配置し、そこから Private Subnetの中にあるEC2 Instanceや、RDS、Redshiftにアクセスする方式です。2016年時点のAWS公式ブログにおいてユーザーごとのkey pairを発行する方法が説明されているため、2016年時点においてはこの方法が主流だったと言って良いでしょう。
しかし、この方法は冒頭にて説明したように、安全な運用を継続するには膨大なコストが掛かるという問題がありました。
Session Manager

2018年9月11日、Session Managerという機能が公式よりアナウンスされました。この機能は、SSHではなく aws-cli を用いてBastionへアクセスする方法を提供します。これにより、Bastionサーバの管理は大幅にセキュアになります。
- aws-cli は AWSのIAMを用いてアクセス制御をすることができます。
- Terraform等を用いて、アクセス管理のIaC化が可能
- IAMのPolicyを用いた柔軟なアクセスコントロールや、MFAの強制が可能
- SSHの鍵管理のコストが不要になる
- IDaaSによる ID Federation を利用すれば、リスクベース認証などIDaaSの保有している、よりセキュアな方法で認証できる
- EC2 instanceを Private Subnetに配置することが可能
- 従来のBastionサーバーはPublic Subnetにあったため、攻撃されうる可能性があった
- IP制限を掛けることで一定の対処は可能だが、同時に利便性も損なわれていた
- 操作履歴はCloud Watch Logsに自動的に残される
という訳で、2021年現在において、新たにBastionサーバーを建てる必要があれば SSMの利用を想定した構築をすべしと言えるでしょう。
SSMベースのBastionサーバーの作り方
SSM ベースでBastionサーバーを建てる場合、必要なリソースは前の図に示したとおりです。
ざっくり書き出すと、下記のリソースになります。
- VPC
- Public Subnet
- NAT Gateway ( Public Subnet 内に配置 )
- Private Subnet
- EC2 Instance (SSM Agentをinstall済みのもの)
踏み台を立てようとしているので、VPCやPublic/Private Subnetはすでにある前提で説明します。
Terraform を利用している場合、terraform-aws-ec2-bastionというモジュールを利用するのが一番楽でしょう。
下記のような設定をすればすぐに動きます。
module "bastion" {
source = "hazelops/ec2-bastion/aws"
version = "~> 2.0"
aws_profile = "xxx"
env = local.env
ec2_key_pair_name = local.ec2_key_pair_name
vpc_id = module.vpc.id
private_subnets = module.bastion_private_subnet.ids
}
aws-cdk を使っている場合、公式が提供しているモジュールがあるためそちらを利用すると良いでしょう。
動作確認とPort Forwardingを利用したRDSへのアクセス
まず、踏み台が正常に動作していることを、下記のコマンドで確認します。
$ aws ssm start-session --target ${EC2のID} --region ap-northeast-1 --profile ${任意のprofile設定}
# MFAの確認
Enter MFA code for arn:aws:iam::xxxx:mfa/${ユーザーアカウント名}:
Starting session with SessionId: botocore-session-xxxx
# EC2 instanceにアクセスしていることを確認
$ pwd
/var/snap/amazon-ssm-agent/2996
$ hostname
ip-xxx
次に、よく利用するBastion経由でのRDS接続方法について記載します。まず、.ssh/config
に下記のような設定を記載します。ProxyCommandの中では StartSSHSessionというドキュメントを読み込んでいます。これは、ssh tonnelを構築する際に必要な設定です。
Host xxx-bastion
HostName ${EC2 internal IP}.ap-northeast-1.compute.internal
User ubuntu
Port 22
ProxyCommand aws ssm start-session --target ${EC2 ID} --document-name AWS-StartSSHSession --region ap-northeast-1 --parameters "portNumber=22" --profile ${aws profile}
IdentityFile ~/.ssh/${bastionのkey pair}
その後、1つめのconsoleで下記コマンドを実施します。これによって、Bastionを経由してRDSに接続するためのトンネルが構築されます。
ssh -N -L 15432:${RDSのURL}:5432 xxx-bastion
先程のconsoleを閉じないまま、別のconsoleで下記のようなコマンドを実行してください。すると、RDSに接続することができるようになります。
$ psql -h localhost -p 15432 -U {username} -d ${database_name}
Password for user {username}:
psql (13.0, server 11.9)
SSL connection (protocol: TLSv1.2, cipher: xxx, bits: 256, compression: off)
Type "help" for help.
database_name=>
将来の話
CloudShell
2020年12月18日、AWS CloudShellという機能がリリースされました。この機能はAWSのマネジメントコンソールから、Shellを実行できる機能です。そのリリース記事の中に下記の文面が存在しました。
ネットワークアクセス – セッションはインターネットへのアウトバウンド接続を確率できますが、インバウンド接続はどの種類でも許可しません。現在、セッションはプライベート VPC サブネット内のリソースに接続できませんが、近日中に接続できるようになる予定です。
つまり、CloudShellで実質 Bastionと同等の作業ができるようになります。そのため、この記事を読んでいる方がBastionサーバーを建てることになったら、まずはCloudShellの最新のアップデートを確認することをオススメします。 ただし、CloudShellでどの程度証跡が残すことができるかをよく検討して、導入の可否判断をする必要があります。
EC2 instance Connect
現在、手元のPCからBastionを経由してRDSにアクセスするには、どうしても鍵の設定が必要になります。そのため、どうしてもkey pairの共有が必要となってしまいます。認証自体にはAWS IAMを利用しているので形骸化された手順ではあるものの、手間といえば手間でしょう。EC2 instance connectを利用すれば、必要に応じて一時的に公開鍵をuploadすることができるため、この手間を削減することができそうです。
現在、踏み台に触る人数が少ないときはさほど問題はないですが、関係者が増えたタイミングではEC2 instance connectの導入を検討すると良いでしょう。