概要・目的
プライベートサブネット内のEC2インスタンスにSSH接続するため、踏み台サーバー(Bastion Host)を使用した多段SSH環境を構築します。
この記事で学べること
- AWS VPCを使用したネットワーク設計
- セキュリティグループを使った適切なアクセス制御
- 踏み台サーバーを経由した多段SSH接続の設定方法
- セキュリティを重視したクラウドインフラの運用方法
想定アーキテクチャ
構成概要
- リージョン: ap-northeast-1(東京)
- AZ: 1つ(ap-northeast-1a)
- VPC: 10.0.0.0/16
-
サブネット:
- Public: 10.0.1.0/24(踏み台サーバー用)
- Private: 10.0.2.0/24(アプリケーションサーバー用)
ネットワーク設計
- Public サブネット: インターネットゲートウェイ(IGW)経由でインターネットアクセス
- Private サブネット: NAT Gateway経由でアウトバウンド通信のみ
- 踏み台サーバー: Public サブネットに配置、管理者のIPからのみSSHアクセス許可
- アプリサーバー: Private サブネットに配置、踏み台サーバーからのみSSHアクセス許可
セキュリティ設計
- bastion-sg: 管理者IPからの22番ポートのみ許可
- target-sg: bastion-sgからの22番ポートのみ許可
前提条件・準備
必要な権限・ツール
- AWSコンソールへのアクセス権(EC2/VPC/EIP作成権限)
- MFA認証の設定を強く推奨
- SSH クライアント(OpenSSH)
事前確認事項
-
自分のグローバルIPアドレスの確認
# 以下のサイトで確認 https://www.whatismyip.com/ # または curl ifconfig.me
例:
203.0.113.10/32
-
SSH鍵の保管場所の決定
-
~/.ssh/
ディレクトリを推奨 - セキュアなアクセス権限設定が必要
-
手順1:VPC とサブネットを一括作成(VPC and more)
目的
ネットワーク基盤となるVPC、サブネット、ルーティング、NAT Gatewayを一括で作成します。
作成手順
-
AWSコンソール → VPC → VPCを作成 → **「VPC and more」**を選択
-
設定値:
-
名前タグの自動生成:
vpc-ssh-bastion-demo
-
IPv4 CIDR:
10.0.0.0/16
-
アベイラビリティゾーン数:
1
-
パブリックサブネット数:
1
(10.0.1.0/24) -
プライベートサブネット数:
1
(10.0.2.0/24) -
NATゲートウェイ:
AZごとに1つ
-
VPCエンドポイント:
なし
-
名前タグの自動生成:
-
VPCを作成をクリック
確認ポイント
作成後、以下が自動生成されていることを確認:
-
VPC:
vpc-ssh-bastion-demo
- Public/Private サブネット: 各1つずつ
- インターネットゲートウェイ: 1つ
- NAT Gateway: 1つ(Public サブネット内)
-
ルートテーブル: 2つ
- Public RT:
0.0.0.0/0
→ IGW - Private RT:
0.0.0.0/0
→ NAT GW
- Public RT:
重要な設定確認
- Public サブネットの「自動割り当てパブリック IPv4」が有効
-
VPCの「DNS hostnames/resolution」が有効
これにより、EC2インスタンスが自動的にDNSホスト名を取得し、VPC内でDNS名前解決が可能になります
手順2:セキュリティグループ作成
目的
踏み台サーバーとターゲットサーバー用の適切なアクセス制御を設定します。
bastion-sg(踏み台用)の作成
-
VPCコンソール → セキュリティ → セキュリティグループ → セキュリティグループを作成
-
基本設定:
-
名前:
bastion-sg
-
説明:
Bastion SSH inbound from my IP
-
VPC:
vpc-ssh-bastion-demo
を選択
-
名前:
-
インバウンドルール:
- タイプ: SSH
- プロトコル: TCP
- ポート範囲: 22
-
送信元: カスタム → あなたのグローバルIP/32(例:
203.0.113.10/32
) -
説明:
Allow SSH from my IP
-
アウトバウンドルール: デフォルト(すべて許可)のまま
target-sg(ターゲット用)の作成
-
セキュリティグループを作成
-
基本設定:
-
名前:
target-sg
-
説明:
SSH from bastion-sg
-
VPC:
vpc-ssh-bastion-demo
を選択
-
名前:
-
インバウンドルール:
- タイプ: SSH
- プロトコル: TCP
- ポート範囲: 22
- 送信元: セキュリティグループ → bastion-sgを選択
-
説明:
Allow SSH from bastion SG
🔍 確認ポイント
-
target-sg
の送信元がCIDRではなくセキュリティグループ参照になっていること - 両方のSGが同一VPC内に作成されていること
手順3:踏み台サーバー(bastion-lnx)の作成とセットアップ
目的
Public サブネットに踏み台サーバーを配置し、外部からの唯一のSSHエントリポイントとします。
EC2インスタンス起動
-
EC2コンソール → インスタンス → インスタンスを起動
-
基本設定:
-
名前:
bastion-lnx
- AMI: Amazon Linux 2023(または Ubuntu 22.04)
-
インスタンスタイプ:
t3.micro
-
名前:
-
キーペア設定:
- 新しいキーペアを作成
-
キーペア名:
kp-ssh-bastion-demo
- キーペアのタイプ: ED25519
- プライベートキーファイル形式: .pem
-
⚠️ 重要:
.pem
ファイルをダウンロードして安全な場所に保存
-
ネットワーク設定:
-
VPC:
vpc-ssh-bastion-demo
- サブネット: Public サブネット(10.0.1.0/24)
- 自動割り当てパブリックIP: 有効(一時的、後でEIPに置換)
-
セキュリティグループ:
bastion-sg
-
VPC:
-
ストレージ: 8〜16GB gp3
Elastic IP の作成と関連付け
⚠️ インスタンス起動後すぐに実行してください
-
EC2コンソール → Elastic IP → アドレスの割り当て → 割り当て
-
関連付け:
- 作成されたElastic IP → アクション → Elastic IP アドレスの関連付け
- リソースタイプ: インスタンス
-
インスタンス:
bastion-lnx
を選択 - 関連付けをクリック
ローカルでの鍵ファイル設定
# ダウンロードした.pemファイルを適切な場所に移動
mv ~/Downloads/kp-ssh-bastion-demo.pem ~/.ssh/
# パーミッション設定(必須)
chmod 600 ~/.ssh/kp-ssh-bastion-demo.pem
初回接続確認
# Amazon Linuxの場合
ssh -i ~/.ssh/kp-ssh-bastion-demo.pem ec2-user@<Elastic IP>
# 接続成功確認
whoami
hostname
手順4:Private側サーバー(app-lnx-01/02)の作成
目的
Private サブネットにアプリケーションサーバーを2台配置し、踏み台サーバー経由でのみアクセス可能にします。
1台目:app-lnx-01
-
EC2コンソール → インスタンス → インスタンスを起動
-
設定値:
-
名前:
app-lnx-01
- AMI: 踏み台と同じ(Amazon Linux 2023)
-
インスタンスタイプ:
t3.micro
-
キーペア: 既存のキーペアを使用 →
kp-ssh-bastion-demo
を選択 - サブネット: Private サブネット(10.0.2.0/24)
- 自動割り当てパブリックIP: 無効
-
セキュリティグループ:
target-sg
-
名前:
2台目:app-lnx-02
同様の設定で作成し、プライベートIPをメモしてください。
- 例:
10.0.2.10
/10.0.2.11
🔍 確認ポイント
- Private サブネットに配置されていること
- パブリックIPが割り当てられていないこと
-
target-sg
が適用されていること
手順5:bastion経由のSSH接続設定
目的
踏み台サーバーを経由してPrivateサーバーに簡単にアクセスできる環境を構築します。
~/.ssh/config での接続設定簡略化
~/.ssh/config
ファイルを作成・編集:
# 踏み台サーバー設定
Host bastion
HostName <踏み台サーバーのElastic IP>
User ec2-user
IdentityFile ~/.ssh/kp-ssh-bastion-demo.pem
# アプリサーバー1号機
Host app-01
HostName 10.0.2.10 # app-lnx-01のプライベートIP
User ec2-user
IdentityFile ~/.ssh/kp-ssh-bastion-demo.pem
ProxyJump bastion
# アプリサーバー2号機
Host app-02
HostName 10.0.2.11 # app-lnx-02のプライベートIP
User ec2-user
IdentityFile ~/.ssh/kp-ssh-bastion-demo.pem
ProxyJump bastion
接続確認
# 踏み台サーバーへ直接接続
ssh bastion
# アプリサーバーへ多段接続(自動で踏み台経由)
ssh app-01
ssh app-02
手順6:NAT Gateway経由の外部通信確認
目的
Private サブネット内のサーバーがNAT Gateway経由でインターネットにアクセスできることを確認します。
接続確認手順
-
アプリサーバーにSSH接続:
ssh app-01
-
OS更新の実行:
# Amazon Linux sudo dnf update -y
-
外向きIP確認:
curl -s ifconfig.me
表示されるIPがNAT GatewayのEIPと一致していることを確認
-
DNS解決確認:
nslookup google.com
🔍 確認ポイント
- パッケージリポジトリへの接続が成功すれば、NAT経由の外部通信が機能
-
curl ifconfig.me
で表示されるIPがNAT GatewayのEIPと一致 - 通信が成功すれば、NAT Gateway、Privateルートテーブル、SGアウトバウンドが正常
動作確認チェックリスト
基本接続確認
- 管理端末 → 踏み台サーバー(EIP)にSSH接続可能
- 踏み台サーバー → app-lnx-01/02にSSH接続可能
-
~/.ssh/config
を使用した簡略接続が動作
セキュリティ確認
- bastion-sg: 22番ポートの送信元が自分のIP/32のみ
- target-sg: 22番ポートの送信元がbastion-sgのみ(CIDR開放なし)
- Private サブネットのインスタンスにパブリックIPが割り当てられていない
ネットワーク機能確認
- app-lnx-01/02でOS更新が成功(NAT Gateway経由)
-
curl ifconfig.me
でNAT GatewayのEIPが表示される - DNS名前解決が正常に動作
クリーンアップ手順
リソース削除の順序
-
EC2インスタンス終了:
EC2コンソール → インスタンス → 3台選択 → インスタンスの状態 → 終了
必要に応じて事前にスナップショット作成
-
Elastic IP解放:
EC2コンソール → Elastic IP → 関連付けの解除 → アドレスの解放
-
VPC削除:
VPCコンソール → VPC → vpc-ssh-bastion-demo → アクション → VPCを削除
依存リソースの削除確認メッセージに従って対応
削除時の注意点
- 削除前にデータバックアップを確認
- Elastic IPの解放を忘れずに(課金継続の原因)
- NAT Gatewayは自動で削除されるが、確認推奨
削除確認
全リソースが削除されたことを以下で確認:
- EC2ダッシュボード: 実行中インスタンス0台
- VPCダッシュボード: 対象VPC削除済み
- 請求ダッシュボード: 該当リソースの課金停止