はじめに
Amazon VPCは、AWS上で仮想ネットワークを構築できるサービスです。
VPCの概要や理論については以下の記事で詳細に解説しました。
一方で、「理論だけでなく実践も重要!」
と思われる方も多いと思いますので、
本記事では実際にVPCによる仮想ネットワークを構築する方法を解説します。
構築の指針として、以下のベストプラクティスを極力満たすよう進めていきたいと思います。
特にVPCフローログやVPCピアリング、VPN等は設定が複雑でハマりがちですが、極力分かりやすく解説したつもりなので、効率よく構築法を習得できる記事となれば幸いです。
本記事で構築するVPCの構成
下図構成のVPC (上記記事の冒頭で紹介した構成)の構築を、実際のコンソール操作方法を交えて解説します。
上記構成の意図として、VPC1 (Webアプリ用のVPC)とVPC2 (社内ネットワークを模擬したVPC)を組み合わせる事で、アプリ公開用ネットワークと社内ネットワークを擬似的に再現する事を目指します。
(今回は本筋から外れるので紹介しませんが、実運用時はWAFやIDS等の防御用製品と組み合わせたり、証明書を取得してHTTPS化する等の対策を行い、セキュリティレベルを高めて下さい)
長いですが、完了する頃には簡単な業務用ネットワークに相当するVPCが構築できると思うので、ぜひお付き合い頂ければと思います。
VPCのセキュリティベストプラクティス
実際に構築に入る前に、満たすべきベストプラクティスの内容を確認します (AWS公式を参照)
・ベストプラクティス一覧
それぞれ解説します
マルチAZ構成による高可用性の確保
VPC内の複数のAZに並列にサブネットおよびインスタンスを設置するマルチAZ構成を構築することで、片方のAZで障害が起きたとしても、高可用性、耐障害性、拡張性を確保できます。
特に商用のWebアプリケーションにおいては必須とも言える構成であり、ELB (ALB, NLB)やAuto Scalingと組み合わせることで、平常時もアクセス数に応じて適切にAZ間で負荷分散を実現する事ができます (ELB, Auto Scalingについては別記事で詳細に紹介します)
下記記事のように、過去のAWS障害においてもマルチAZ構成の運用法選択が耐障害性に大きく寄与した事が見て取れます
本記事においては、冒頭の構成の「VPC1」のように、2個のAZに並列にサブネットを作成し、それぞれWebサーバ・APサーバ・DBサーバを設置することで、マルチAZ構成を実現しています
ネットワークACLを使用してサブネットへのアクセスを、セキュリティグループを使用してサブネット内のインスタンスへのトラフィックを制御する
前述の記事で触れたように、VPCではファイアウォールの役割を果たすネットワークACLとセキュリティグループを利用して、VPC内部のアクセス制御を実施します。
これらのアクセス制御機能はVPCのセキュリティの要と言える存在のため、ユースケースに合わせて必ず設定してください。
両者の主な違いは以下の表を参照下さい。
名称 | 外部側フィルタ対象 | フィルタ方式 | 戻り通信の扱い | 内部側フィルタ対象 |
---|---|---|---|---|
ネットワークACL | 外部側IPアドレス + ポート番号 + プロトコル | ホワイトリスト + ブラックリスト | ステートレス | サブネットごと |
セキュリティグループ | 外部側IPアドレス (セキュリティグループID, プレフィックスリストも指定可) + ポート番号 + プロトコル | ホワイトリスト | ステートフル | インスタンスごと (セキュリティグループIDでグルーピング) |
なお、AWS公式では触れられていませんが、以下のような多くのサードパーティーの記事では、ネットワークACLよりもセキュリティグループを優先して使用する事が推奨されています。
主な理由として、ステートレスかつホワイトリストとブラックリスト両方を指定可能なネットワークACLは設定が煩雑で、ステートフルかつホワイトリストのみで指定するセキュリティグループは設定がシンプルであり、ミス等を防ぎやすい事が挙げられます (他にも、サブネット単位でしかルール設定できないネットワークACLと比べ、セキュリティグループはインスタンス単位でより細かいアクセス制御ができることも挙げられます)
本記事でも、セキュリティグループによるアクセス制御を実施します (ネットワークACLはデフォルト設定のまま使用します)
IAM の ID フェデレーション、ユーザー、ロールを使用して、AWS VPC リソースおよび API へのアクセスを管理する
こちらはVPCというよりIAMに関する内容 (公式の解説もIAMの定義そのもの)となりますが、VPC内外のリソース (インスタンスやS3のバケット)にはIAMによるアクセス権限を適切に付与する必要があります。
前述のネットワークACLやセキュリティグループが「ネットワーク側でのアクセス制御」であれば、このIAMは「ホスト側でのアクセス制御」に相当します。
詳細は以下のIAMの記事を参照ください
本記事では、VPCフローログやS3へのアクセスにおいてIAMの設定が登場します。
このようにCloudWatchやS3等、VPC外のサービスとのアクセス制御において、特にIAMは重要な役割を果たします。
VPC フローログで Amazon CloudWatch を使用して IP トラフィックを監視する
セキュリティ対策として、ログを保存して後から攻撃や不具合の原因を追跡できるようにする事は重要です。
VPCにおいては、VPCフローログがこの役割を果たします。
VPCフローログの保存場所にはCloudWatchとS3を選択する事ができます。
こちらのサイトをみる限り、2つの保存場所は以下のように使い分けるとよさそうです。
フローログの保存場所 | 用途 |
---|---|
CloudWatch Logs | ネットワークトラフィックの特定のアクセス傾向を検知してアラームを発する場合 |
S3 | フローログを蓄積してAthenaで分析する場合 or コストを低減したい場合 |
ベストプラクティスにおいては、CloudWatch Logsへの保存が推奨されています。
本記事においても、CloudWatchへのフローログ保存の設定を実施します
具体的な構築方法
ここからは具体的な構築方法を解説します。
繰り返しとなりますが、以下のVPC構成をベストプラクティスを満たすよう構築していきます。
以下の手順で構築を進めます
- VPCとサブネットの作成 (VPC1)
- セキュリティグループの作成 (VPC1)
- ルートテーブルの設定 (VPC1)
- EC2インスタンスの作成 (VPC1)
- フローログの作成 (VPC1)
- ELBの作成 (VPC1)
- RDSの作成 (VPC1)
- S3バケットの作成 (VPC1)
- 各インスタンスへの疎通確認 (VPC1)
- VPCとサブネットの作成 (VPC2)
- セキュリティグループの作成 (VPC2)
- EC2インスタンスの作成 (VPC2)
- VPCピアリングの作成 (VPC1&2)
- ルートテーブルの設定 (VPC2)
- プロキシサーバの設定 (VPC2)
- VPNの作成 (VPC2)
- 各インスタンスへの疎通確認 (VPC2)
長いですが、Webアプリを前提としたVPC構築ではほぼ同様のフローが生じる (Lambdaのようなサーバレス主体の構成を除く)ため、VPCをこれから使おうという方は、ぜひご一読頂ければと思います。
VPCとサブネットの作成 (VPC1)
最初にVPCを作成する際には、VPCと一緒にサブネットやエンドポイント等を作成できるVPCウィザードを使用すると便利です。
VPCウィザードは、以下のように「VPCウィザード」→「VPCを作成」の順で操作することで作成できます
※以下のように「お使いのVPC」タブから作成することもできます
VPCウィザードで指定できる設定
VPCウィザードでは、下図のような設定を指定することができます。
選択した設定のVPC構成が「プレビュー」に表示されるため、GUIで逐一設定を確認できて便利です。
それぞれ解説します
・作成するリソース
「VPCのみ」と「VPCなど」どちらかを選択でき、「VPCなど」を選択した場合は一緒にサブネットやエンドポイントを作成する事ができます。
「VPCなど」を選択した方が設定が楽になるため、最初はこちらを選択すると良いでしょう
・名前タグの自動生成
こちらで設定した名称がVPC名や内部のリソース名 (サブネット、ルートテーブル、IGW、NATゲートウェイ、エンドポイント)の冒頭に付加されます(厳密にはName
タグに登録されます)
VPCの機能を表す名称を付けておくと、あとで名前順でソートするだけで整理できて便利です。
今回はfor-web
という名前を付ける事とします
・IPv4 CIDRブロック
VPC全体のネットワークアドレスをCIDR記法で指定します。
今回は上記構成図に合わせ、172.16.0.0/23
を指定します。
・IPv6 CIDRブロック
IPv6におけるVPC全体のネットワークアドレスを指定します
通常はIPv4のみを使用する事が多いので、「IPv6 CIDRブロックなし」を選択すると良いでしょう
・テナンシー
「デフォルト」「専有」どちらかを選択します。
「専有」を選択した場合、VPC内のインスタンスにはハードウェア専有インスタンスが適用されます (詳細はEC2の記事 (作成中)で解説します)
「デフォルト」を選択した場合、ハードウェア専有インスタンスは適用されません。その分コストが安くなるので、通常はこの「デフォルト」を選択すると良いでしょう。
・AZの数
VPC内のAZの数を選択する事ができます。
冗長性を確保するためには、2以上を設定する事がおすすめです (3を選択すると可用性がさらに向上することもあるようです)
また、「AZのカスタマイズ」を展開すると、具体的なAZの場所を選択できます。
今回は上記構成図に合わせ、「2」を選択します
・パブリックサブネットの数
VPC内のパブリックサブネットの数を選択できます。デフォルトでは各AZに均等にサブネットが配置されます (冗長性確保のため)
今回は上記構成図に合わせ、「2」を選択します
・プライベートサブネットの数
VPC内のプライベートサブネットの数を選択できます。デフォルトでは各AZに均等にサブネットが配置されます (冗長性確保のため)
今回は上記構成図に合わせ、「2」を選択します
・サブネットCIDRブロックをカスタマイズ
作成する各サブネットのネットワークアドレスを、CIDR記法で指定できます。
今回は上記構成図に合わせて、4個のサブネットそれぞれのアドレスを指定します
・NATゲートウェイ
NATゲートウェイの作成数を選択します。
「なし」を選択すると、NATゲートウェイを作成しません
「1AZ内」を選択すると、VPC内に1個のみNATゲートウェイを作成します
「AZごとに1」を選択すると、各VPCに1つずつNATゲートウェイを作成します
今回は上記構成図に合わせ、「なし」を選択します。
(S3用ゲートウェイエンドポイント経由でプライベートサブネット内のインスタンスもyumコマンドを実行できるため、最低限のアップデート適用が実現できます)
NATゲートウェイはそれなりの維持費が掛かるため、プライベートサブネット内のインスタンスからyum以外のインターネットアクセスが必要な時のみ、都度作成することとします。
・VPCエンドポイント
S3用のゲートウェイエンドポイントの作成有無を選択します。
「なし」を選択すると、ゲートウェイエンドポイントを作成しません
「S3ゲートウェイ」を選択すると、S3用のゲートウェイエンドポイントを作成します
今回は上記構成図に合わせ、「S3ゲートウェイ」を選択します。
・DNSオプション
詳細は以下を参照頂きたいですが、VPC内のパブリックIPアドレスを持つインスタンスにドメイン名でアクセス (Route53で名前解決)したい場合は、「DNSホストを有効化」「DNS解決を有効化」両方をチェックしてください
今回はWebアプリ用という用途を考慮し、「DNSホストを有効化」「DNS解決を有効化」両方をチェックします
セキュリティグループの作成 (VPC1)
ベストプラクティスに従い、ファイアウォールの役割を果たすセキュリティグループを作成することで、VPC内のネットワークセキュリティを高めます。
(後でEC2インスタンスや起動テンプレートと紐づける必要があります)
今回は、「1. Webサーバ用のセキュリティグループ」、「2. APサーバ用のセキュリティグループ」、「3. RDS用のセキュリティグループ」を別個に作成する事とします。
(VPC作成時にデフォルトのセキュリティグループも作成されていますが、こちらは利用しません)
1. Webサーバ用のセキュリティグループ
セキュリティグループを作成するには、以下のように「セキュリティグループ」→「セキュリティグループの作成」を押します
セキュリティグループは、以下のような画面で作成する事ができます。
基本的には画面の指示に従い作成できますが、以下に注意が必要です
- インバウンドルールの「プロトコル」は、対象インスタンスにアクセスする必要のあるプロトコルのみを指定する(不要なプロトコルは指定しない)
- インバウンドルールの「ソース」は、送信元のAnywhere-IPv4(全送信元)、CIDRブロック、セキュリティグループ、プレフィックスリスト等を指定可能。セキュリティ的にはAnywhere-IPv4よりも範囲を絞った方が良い
- アウトバンドルールはデフォルトでは全許可。通常はそのままでも良いが、高度なセキュリティ(C&Cサーバとの通信防止等)を実現したい場合は制限を加える
- セキュリティグループは見分け辛いので、"Name"タグを指定した方がよい (例えば、VPC作成時の名前タグ"for-web"と類似した名称を指定)
今回はWebサーバーとして使用するので、Web通信用のHTTP, HTTPSおよび開発用のSSH通信を許可する事とします。
最初は全てソースとしてAnywhere-IPv4を許可しますが、SSHの全許可はセキュリティ的にやや危険なため、後ほどVPC2の範囲内のみにアクセスを絞ります。
2. APサーバ用のセキュリティグループ
APサーバ用に以下のようなセキュリティグループを作成します
Webサーバ用と同様に、インバウンドルールのSSH、HTTP、HTTPSを許可しますが、HTTP, HTTPS通信はWebサーバとしか行わないため、ソースをWebサーバ用のセキュリティグループに変更します
上記作成が完了後、以下のようにHTTP, HTTPS通信の許可対象として自身(APサーバ用のセキュリティグループ)を追加します
本セキュリティグループには後ほどAPサーバ用インスタンスとAPサーバ用ELBを所属させますが、前述のように自身を許可対象に追加しないと内部のインスタンスとELB間の通信が許可されないため、上記操作を実施します。
(セキュリティ的にはインスタンスとELBのセキュリティグループを分けた方がより強固となりますが、Webサーバから直接APサーバにアクセスできなくなったりと開発や管理が煩雑となるため、今回は同一のセキュリティグループに所属させます)
また、1と同様に後ほどSSH通信をVPC2の範囲内のみにアクセスを絞ります。
3. RDS用のセキュリティグループ
RDS用に以下のようなセキュリティグループを作成します。
今回はRDSのDBエンジンとしてMySQLを使用する前提で、インバウンドルールのタイプとしてMYSQL/Auroraを許可し、ソースをAPサーバ用のセキュリティグループに変更します。
PostgreSQL等他のDBエンジンを使用する場合は、適宜タイプを変更してください。
ルートテーブルの設定 (VPC1)
本来であればルートテーブルを作成して各サブネットや外部とのルーティングを設定する必要がありますが、VPCウィザードを使用してVPCを作成した場合、デフォルトで以下のルートテーブルが設定されており、基本的には設定変更は必要ありません。
対象サブネット | 作成されるルートテーブルの数 | ルーティング先 |
---|---|---|
パブリックサブネット用のルートテーブル | 全AZで1個 | インターネットゲートウェイ、プライベートサブネット |
プライベートサブネット用のルートテーブル | 各AZあたり1個ずつ | パブリックサブネット、S3用エンドポイント、(NATゲートウェイ※) |
※今回、NATゲートウェイは作成していないためルートも設定されていない
なお、後からNATゲートウェイやサブネットを追加した場合、別途ルートテーブルを作成・修正する必要があります。NATゲートウェイでの設定例は後述します
EC2インスタンスの作成 (VPC1)
Webサーバ用およびAPサーバ用のインスタンスを作成します。
詳細な作成方法は割愛しますが (別途EC2の記事を作成中)、以下の内容で図中の4個のインスタンス(Webサーバ用×2 + APサーバ用×2)を作成します
- 名前: "web-server-1", "web-server-2", "ap-server-1", "ap-server-2"
- AMI: Amazon Linux2 AMI (HVM)
- インスタンスタイプ: デフォルトのt2.microを選択
- キーペア: あり (作成していなければ新たに作成)
- VPC: 先ほど作成したVPCを指定
- サブネット: 作成したサブネットのうち冒頭の構成図でインスタンスに対応するものを選択
- パブリックIPの自動割り当て: Webサーバ用は「有効化」、APサーバ用は「無効化」
- ファイアウォール: 「既存のセキュリティグループを選択する」をチェックし、先ほど作成したWebサーバ用またはAPサーバ用から対応するセキュリティグループを選択する
- ストレージ: デフォルトの8GBのgp2を選択 (EC2ベストプラクティス的には複数EBSをアタッチする事が望ましいですが、今回はデフォルト設定とします)
なお、キーペアを新たに作成した場合は、後でSSH接続に必要となりますので忘れずにローカルにダウンロードしておいてください
フローログの作成 (VPC1)
ベストプラクティスに従い、CloudWatchにフローログを保存するよう設定します。
IAMのポリシー設定が少し複雑なので、IAMの記事も併せて参照ください。
フローログ用のCloudWatchロググループ作成
まず、CloudWatchのコンソールからロググループの作成画面に入ります。
ロググループ名等を指定して、ロググループを作成します
フローログ記録用のIAMポリシー作成
IAMのコンソールへ移動し、「ポリシー」→「ポリシーを作成」をクリックします
JSON形式のエディタを開き、以下のポリシーを貼り付けます
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Resource": "*"
}
]
}
必要に応じてタグを追加します(今回は追加しない)
ポリシーに名前と説明を追記して作成を完了します
フローログ記録用のロール作成
IAMコンソール画面に戻り、「ロール」→「ロールを作成」をクリックします
信頼されたエンティティタイプ「AWSのサービス」、ユースケース「EC2」を選択し、次へを押します
先ほど作成したポリシーをアタッチします
ロールの名称と説明を記載し、「ロールを作成」をクリックします
IAMコンソール画面に戻り、「ロール」→作成したロールを選択します
「信頼関係」タブ→「信頼ポリシーを編集」とクリックします
元々このロールはEC2のユースケースを元に作成されているので、信頼ポリシーの権限移譲先 ("Principal"フィールド)もEC2が適用されていますが、これを以下のようにVPCフローログに書き換えて保存します
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "vpc-flow-logs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
フローログの作成
VPCのコンソールに戻り、該当するVPCを選択して「アクション」→「フローログを作成」をクリックします
以下のようにフローログの各種設定を選択し、作成を完了させます
以上でフローログの設定は完了です
※S3にフローログを保存したい場合
CloudWatchではなくS3にフローログを保存し、Athenaで分析したい場合は以下を参照下さい
S3に保存した方がCloudWatchよりも料金が安めなので、コスト重視の方もS3を選択肢に入れて良いかと思います。
ELBの作成 (VPC1)
EC2インスタンスへの通信をロードバランシングするELBを作成します。
ELBの詳細は以下の記事を参照ください
今回はELBの中で最も一般的なALBを作成します。
また、本来であれば暗号化されたHTTPS通信をロードバランシングする事が望ましいですが、証明書の登録等、本記事の本筋とは外れた操作が多くなるため、今回はHTTP通信によるロードバランシングを採用します
Webサーバ用ELBの作成
Webサーバ用インスタンスに紐づけるELBを作成します
・ターゲットグループの作成
インスタンスを紐づけるためのターゲットグループを作成します。
EC2コンソールから「ターゲットグループ」→「ターゲットグループの作成」をクリックし、
以下のようにターゲットグループの設定を指定します
以下のようにWebサーバ用のインスタンスをターゲットグループに登録します
・ロードバランサー(ALB)の作成
EC2コンソールから「ロードバランサー」→「ロードバランサーの作成」をクリックし、
以下のようにApplication Load Balancer (ALB)を選択します
以下のようにALBの詳細設定を選択します
※セキュリティグループはWeb用のEC2インスタンスに設定したものと同じでOKです
※「AWS Global Accelerator」をチェックするとGlobal Acceleratorによる高速化を有料で設定できます
APサーバ用ELBの作成
・ターゲットグループの作成
Webサーバ用ELBと同じ方法で、APサーバ用のインスタンスをターゲットグループに登録します
(Nameタグは例えば"for-web-tg-ap"とします)
・ロードバランサー(ALB)の作成
Webサーバ用ELBと同じ方法で、ロードバランサーを作成します。Webサーバ用との差として以下にご注意ください
- ロードバランサー名、NameタグはWeb用と別の名称にしてください (例: ロードバランサー名"alb-ap", Nameタグ"for-web-alb-ap")
- APサーバ用は内部のWebサーバからのリクエストのみを受け付けるため、「スキーム」に「内部」を選択してください
- サブネットはプライベートサブネットを指定してください
- セキュリティグループはAPサーバ用を指定してください
- ターゲットグループはAPサーバ用を指定してください
RDSの作成 (VPC1)
WebアプリのDBとして使用する、RDSのインスタンスを作成します。
RDSのエンジンにはAurora、MySQL、PostgreSQL等が使用できますが、今回はMySQLを使用する事とします。
サブネットグループの作成
まずはRDSを設置するサブネットを指定するため、サブネットグループ (RDSの機能のため詳細解説は割愛します)を作成します。
RDSのコンソールに入り、「サブネットグループ」→「DBサブネットグループを作成」とクリックします
以下のようにサブネットグループの設定を入力し、「作成」をクリックします
RDSデータベースの作成
RDSのデータベースを作成します
RDSのコンソールから、「データベース」→「データベースの作成」をクリックします
以下のようにデータベースの詳細設定を指定し、「データベースの作成」をクリック
※データベース認証に「パスワードとIAMデータベース認証」を指定すると、パスワードの代わりにIAMロールの付与でDBにアクセス可能となり、セキュリティレベルが向上します(参考)。一方で大量アクセスには向かない方式のためご注意ください
S3バケットの作成 (VPC1)
非構造データを保存するための、S3 のバケットを作成します
バケットの作成
S3のコンソールに移動し、「バケット」→「バケットを作成」とクリックします
以下のようにバケットの詳細設定を指定し、「バケットを作成」をクリック
S3アクセス用IAMロールの付与
後述の疎通確認④と同様の方法で、Webサーバを踏み台としてAPサーバ1にSSH接続し、以下コマンドでS3のバケット一覧を表示させます
aws s3 ls
恐らく以下のようなメッセージが表示され、バケット一覧の取得に失敗するかと思います (いきなり指示に従って"aws configure"コマンドでアクセスキーを登録してはいけません)
Unable to locate credentials. You can configure credentials by running "aws configure".
EC2からS3にアクセスするためには、EC2インスタンスにS3へのアクセス権 (S3FullAccess
ポリシー)を付与したロールをアタッチする必要があります。
※なお、今回はEC2とS3を同アカウントで作成しているので、バケットポリシーでの明示的な許可は不要です
各インスタンスへの疎通確認 (VPC1)
以上でVPC1の作成は完了です。現時点では以下のようなネットワークが構築されているはずです
このネットワークが、想定通り設定できているか疎通確認します。
具体的には、以下の内容を確認します
①外部からWebサーバへSSH通信できる※
②外部からWebサーバへHTTP通信できる
③外部からWebサーバへELB経由でHTTP通信できる
④WebサーバからAPサーバへSSH通信できる※
⑤WebサーバからAPサーバへHTTP通信できる
⑥WebサーバからAPサーバへELB経由でHTTP通信できる
⑦外部からAPサーバへELB経由でHTTP通信できない
⑧APサーバからRDS内のデータを取得できる
⑨APサーバからS3内のデータを取得できる
※なお、①のSSH通信はVPC2作成後には通信不可となるよう再設定する (外部からSSH接続できるのはやや危険なため)のでご注意ください。同様に④もVPC2作成後に遮断します
Webサーバへの疎通確認
①SSHの疎通確認
以下の手順でWebサーバ用EC2インスタンス1 ("web-server-1")のグローバルIPアドレスを確認します
ターミナル(Mac)やコマンドプロンプト(Windows)から、以下のようなSSH接続コマンドを打ちます
ssh ec2-user@[インスタンスのIPアドレス] -i [ダウンロードしたキーペア秘密鍵のパス]
以下のようなメッセージが出てきたら「yes」と入力してEnterを押してください
Are you sure you want to continue connecting (yes/no/[fingerprint])?
以下のようにAmazon Linuxの起動マークが表示されたら成功です
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
同様の操作をWebサーバ用インスタンス2 ("web-server-2")にも実行し、疎通確認します
②HTTPの疎通確認 (直接通信)
HTTPの疎通確認にはcurlコマンドを使用する等の方法もありますが、今回はWebアプリの実際の動作を想定し、ブラウザでHTMLファイルを表示できるかで疎通確認をします。
まず、Webサーバ用インスタンス1にSSHでインスタンスに接続した状態で以下コマンドでApache HTTP Server (httpd)をインストールします (nginxを使用したい場合はこちらでもOKです)
sudo yum update -y
sudo yum install httpd -y
以下コマンドで、Webページ表示用のindex.htmlを作成します
cd /var/www/html
sudo nano index.html
以下の内容を記述し、ctrl + x で保存します
<html><h1>Web server 1</h1></html>
以下のコマンドを打ち、Apache HTTP Serverによるホスティングをスタートさせます
sudo systemctl start httpd
以下のコマンドで再起動後もホスティングを自動実行するようにします
sudo systemctl enable httpd
Chrome等のブラウザを起動して、http://Webサーバ用インスタンス1のIPアドレス
とアドレス欄に打ってEnterを押し、以下のように表示されれば成功です
同様の操作をWebサーバ用インスタンス2 ("web-server-2")にも実行し、疎通確認します (index.htmlの中身を"Web server 2"に変えておいてください)
以下のようにブラウザに表示されれば成功です
③HTTPの疎通確認 (ELB経由)
②ではIPアドレスを指定して各Webサーバ用インスタンスに直接アクセスしましたが、実運用時と同様にELB経由でのアクセスも確認します。
②が完了している状態 (httpdのインストールindex.htmlファイルの設置済)で、
EC2コンソールから「ロードバランサー」→ Webサーバ用のELBを選択 → DNS名をコピペします
ブラウザのアドレス欄に貼り付けてEnterを押し (あるいは先頭にhttpをつけてhttp://コピペしたDNS名
とする)、②の時と同様に以下のように表示されれば成功です
(Web server 1
、Web server 2
どちらが表示されてもOKです)
F5 (Macならctrl + R)でブラウザを何度か更新し、Web server 1
とWeb server 2
でランダムに切り替わっていればELBが正しく機能しています
APサーバへの疎通確認
④SSHの疎通確認 (Webサーバを踏み台にした場合)
APサーバ用インスタンス"ap-server-1"はプライベートサブネット上に存在するため、外部から直接アクセスする事ができません。よってAPサーバにアクセスするためには、パブリックサブネット上のWebサーバ用インスタンスを踏み台とする必要があります
まずはWebサーバ1用インスタンス"web-server-1"にキーペア秘密鍵を送ります (秘密鍵なので流出には気を付けてください)。これがないと公開鍵認証ができず、APサーバにアクセスできません
scp -i [ダウンロードしたキーペア秘密鍵のパス] [ダウンロードしたキーペア秘密鍵のパス] ec2-user@[Webサーバ1のIPアドレス]:~/.ssh
①と同様にWebサーバ1用インスタンスにSSH接続します
ssh ec2-user@[Webサーバ1のIPアドレス] -i [ダウンロードしたキーペア秘密鍵のパス]
以下コマンドで、アップロードした秘密鍵が格納されている事を確認します
ls .ssh
以下コマンドで、APサーバ1にSSH接続します
ssh ec2-user@[APサーバ1のプライベートIPアドレス] -i [アップロードしたキーペア秘密鍵のパス]
※APサーバ1のプライベートIPアドレスは、EC2コンソールの「インスタンス」→APサーバ1用インスタンスIDをクリック→「プライベート IPv4 アドレス」に記載されています
以下のようにAmazon Linuxの起動マークが表示されたら成功です
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
⑤HTTPの疎通確認 (直接通信)
WebサーバからAPサーバへのHTTP通信を確認します。
②ではブラウザを使用しましたが、今回はcurlコマンドで確認します。
まずは④でAPサーバにSSH接続した状態で、②と同様にhttpdのインストールindex.htmlファイルの作成を以下のように実施します。
sudo yum update -y
sudo yum install httpd -y
cd /var/www/html
sudo nano index.html
<html><h1>AP server 1</h1></html>
sudo systemctl start httpd
sudo systemctl enable httpd
※NATゲートウェイがないのにyum経由でインストールできるのが少し不思議ですが、前述のようにS3用ゲートウェイエンドポイント経由でyumによるネットワーク接続が可能です)
以下コマンドでAPサーバへのSSH接続を切断し、Webサーバに戻ります
exit
以下curlコマンドで、WebサーバからAPサーバにHTTPリクエストを送ります
curl http://[APサーバのIPアドレス]
以下のように先ほど作成したindex.htmlファイルの内容が表示されれば成功です
<html><h1>AP server 1</h1></html>
同様の操作をAPサーバ2"ap-server-2"にも実行(index.htmlのヘッダは"AP server 2"と記載しておく)し、以下のようにindex.htmlファイルの内容が表示されれば成功です
<html><h1>AP server 2</h1></html>
⑥HTTPの疎通確認 (ELB経由)
Webサーバ用インスタンスからAPサーバ用インスタンスへのAPサーバ用ELB経由でのHTTPアクセスも確認します。
まずはEC2コンソールから「ロードバランサー」→ APサーバ用のELBを選択 → DNS名をコピペします
WebサーバにSSH接続した状態で、以下コマンドでELBにHTTPリクエストを送信します
curl http://コピペしたDNS名
以下のようにAPサーバ内のindex.htmlファイルの内容が表示されれば成功です
(AP server 1
、AP server 2
どちらが表示されてもOKです)
<html><h1>AP server 1</h1></html>
同じコマンドを何度か実行し、AP server 1
、AP server 2
でランダムに切り替わっていればELBが正しく機能しています
⑦外部からのHTTPの非疎通確認 (ELB経由)
⑥ではWebサーバからAPサーバへのELB経由へのアクセスを確認しました。
一方で、外部からAPサーバへアクセスできてしまうとセキュリティ的にまずいので、外部からAPサーバ用ELB経由でインスタンスにHTTPアクセスできないことを確認します
外部から(WebサーバにSSH接続していない状態で)、以下コマンドを打ちます
curl http://コピペしたDNS名
APサーバ内のindex.htmlファイルの内容が帰ってこなければ成功です。
RDSへの疎通確認
⑧APサーバからRDSへの疎通確認
④と同様に、Webサーバを踏み台としてAPサーバ1にSSH接続します。
APサーバ1に入ったら、以下コマンドでmysqlをインストールします (疎通確認のみに使用するので、確認が完了したらアンインストールしても構いません)
sudo yum install mysql -y
以下コマンドでRDSのMySQLにアクセスできるか確認します
mysql -h [RDSのエンドポイント名] -u [RDSのユーザ名] -p
なお、RDSのエンドポイント名はRDSコンソール→「データベース」→該当するデータベースのDB識別子をクリックし、以下から確認できます
RDSのパスワードが求められるので入力し、以下のようにMySQLコマンドが打てる状態となれば成功です
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 8.0.28 Source distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
APサーバ2でも同様の操作を実行します
S3への疎通確認
⑨APサーバからS3への疎通確認
④と同様に、Webサーバを踏み台としてAPサーバ1にSSH接続し、以下コマンドでAWS CLIの初期設定を行います (アクセスキーは入力しないでください)
aws configure
AWS Access Key ID [None]: 空欄のままEnter
AWS Secret Access Key [None]: 空欄のままEnter
Default region name [None]: ap-northeast-1
Default output format [None]: 空欄のままEnter
以下コマンドでバケット一覧を表示され、作成したS3バケットが表示されればOKです
aws s3 ls
2022-08-08 00:24:28 for-web-bucket
以上でVPC1 (Webアプリ用のVPC)の作成は完了です
VPCとサブネットの作成 (VPC2)
ここからはVPC2 (社内ネットワークを模擬したVPC)の作成に移ります。
まず、VPCとサブネットの作成 (VPC1)と同様に、VPCウィザードを使用してVPCを作成します。
以下の設定でVPCを作成して下さい
- 作成するリソース: VPCなど
- 名前タグの自動生成: "for-internal"と名前を付ける
- IPv4 CIDRブロック: 172.16.2.0/24
- IPv6 CIDRブロック: IPv6 CIDRブロックなし
- テナンシー: デフォルト
- AZの数: 1 (社内ネットでも冗長性を確保したい方は2を選択して下さい)
- AZのカスタマイズ: ap-northeast-1a
- パブリックサブネットの数: 1
- プライベートサブネットの数: 1
- サブネットCIDRブロックをカスタマイズ: パブリックは172.16.2.0/26、プライベートは172.16.2.64/26を指定
- NATゲートウェイ: なし
- VPCエンドポイント: なし (S3アクセスの利便性を上げたい場合は「S3ゲートウェイ」を指定してもOK)
- DNSオプション: 「DNSホスト名を有効化」「DNS解決を有効化」両方チェックする
セキュリティグループの作成 (VPC2)
1. プロキシサーバ用のセキュリティグループ
セキュリティグループを作成するには、VPC1のときと同様VPCコンソールから「セキュリティグループ」→「セキュリティグループの作成」を押します
プロキシサーバ用に以下のようなセキュリティグループを作成します
インバウンドは初期設定用にSSHのみ全許可し、アウトバウンドはデフォルトのまま全許可とします。
SSHの全許可はセキュリティ的にやや危険なため、VPN接続の確立後にインバウンドの許可を内部ネットワーク (プライベートサブネット + VPN)に限定します。
これにより、社内ネットワークからしかアクセスできないセキュアなネットワークを構築する事ができます。
2. 開発用サーバ用のセキュリティグループ
開発用サーバ用に以下のようなセキュリティグループを作成します
インバウンドは初期設定用にプロキシサーバ用セキュリティグループからのSSHのみ許可 (プロキシサーバが踏み台の役割を果たす)し、アウトバウンドはデフォルトのまま全許可とします。
こちらもプロキシサーバ用セキュリティグループと同様、VPN接続の確立後にインバウンドのSSH通信許可をVPNに限定します。
EC2インスタンスの作成 (VPC2)
プロキシサーバ用および開発用サーバ用のインスタンスを作成します。
VPC1のときと同様に、以下の内容で図中の2個のインスタンス(プロキシサーバ用×1 + 開発用サーバ用×1)を作成します
名前: "proxy-server-1", "development-server-1"
AMI: Amazon Linux2 AMI (HVM)
インスタンスタイプ: デフォルトのt2.microを選択
キーペア: あり (VPC1と同じものを指定。セキュリティを高めたいなら新たに作成してもOK)
VPC: 先ほど作成したVPC2を指定
サブネット: 作成したサブネットのうち冒頭の構成図でインスタンスに対応するものを選択
パブリックIPの自動割り当て: プロキシサーバ用は「有効化」、開発用サーバ用は「無効化」
ファイアウォール: 「既存のセキュリティグループを選択する」をチェックし、先ほど作成したプロキシサーバ用または開発用サーバ用から対応するセキュリティグループを選択する
ストレージ: デフォルトの8GBのgp2を選択 (EC2ベストプラクティス的には複数EBSをアタッチする事が望ましいですが、今回はデフォルト設定とします)
VPCピアリングの作成 (VPC2)
VPC2からVPC1への接続路を確保するため、VPCピアリングを作成します
VPCコンソールから「ピアリング接続」→「ピアリング接続を作成」とクリックします
以下のようにピアリング接続の詳細設定を入力し、「ピアリング接続を作成」をクリックします
VPCコンソールに戻り、「ピアリング接続」タブで作成したピアリング接続を選択肢、「アクション」→「リクエストを承諾」をクリックします
ルートテーブルの設定 (VPC2)
VPC1のときと同様、VPC作成時にデフォルトで以下のルートテーブルが設定されており、同一VPC内での通信に関してはルートテーブルの変更は必要ありません。
対象サブネット | 作成されるルートテーブルの数 | ルーティング先 |
---|---|---|
パブリックサブネット用のルートテーブル | 全AZで1個 | インターネットゲートウェイ、プライベートサブネット |
プライベートサブネット用のルートテーブル | 各AZあたり1個ずつ | パブリックサブネット、(NATゲートウェイ、S3用エンドポイント※) |
※今回はNATゲートウェイ、エンドポイントは作成していないためルートも設定されていない
一方で、VPC2→VPC1へのルートはデフォルトでは設定されていないため。ルートテーブルでVPCピアリングへのルートを明示的に追加する必要があります。
今回はVPC2上の開発用サーバからVPC1のWebサーバおよびAPサーバにアクセスできるよう、ルートテーブルを修正します。
VPC2→VPC1へのルート
開発用サーバが所属するVPC2のプライベートサブネットから、VPCピアリングへのルートを追加します。
まず、VPCコンソールの「ルートテーブル」→VPC2のプライベートサブネット用ルートテーブルをクリックします
以下のようにVPCピアリングへのルートが存在しない事が分かるので、「ルートを編集」をクリックします
送信先がVPC1のCIDR (172.16.0.0/23)のとき、VPCピアリング接続にルーティングされるよう以下のようにルートを追加します
VPC1→VPC2へのルート
忘れがちなのがこの部分で、ルートテーブルの指定はステートレスのため、戻り方向のルートも明示的に指定する必要があります
今回はVPC1の全てのサブネット (WebサーバおよびAPサーバ)にVPC2 (開発用サーバ)からアクセスできるようにしたいので、VPC1以下の3つ (パブリック用 + プライベート用x2)のルートテーブルにVPC2への戻りルートを設定します。
上記3つのルートテーブル全てに、送信先がVPC2のCIDR (172.16.2.0/24)のとき、VPCピアリング接続にルーティングされるよう以下のルートを追加します
VPC1内のセキュリティグループ設定の修正
VPCピアリングの作成とルートテーブルの修正を実施しましたが、このままではVPC1のセキュリティグループでVPC2からのアクセスが許可されていないので、アクセスを許可する修正が必要です。
また前述のように外部からWebサーバにSSH接続できる状態はセキュリティ的にやや弱いので、Webサーバ用およびAPサーバ用のセキュリティグループを修正し、VPC2内の開発用サーバのみからアクセスできるようにします。
VPCコンソールから「セキュリティグループ」タブをクリックし、以下の2つのセキュリティグループを修正します
1. Webサーバ用セキュリティグループの修正
インバウンドルールのSSHのソースを、VPC2のプライベートサブネットのCIDR (172.16.2.64/26)に限定します
2. APサーバ用セキュリティグループの修正
Webサーバ用と同様に、インバウンドルールのSSHのソースを、VPC2のプライベートサブネットのCIDR (172.16.2.64/26)に限定します
疎通確認
VPCピアリング・ルーティング・セキュリティグループが正しく設定できているか確認するため、作成したEC2インスタンスおよびVPC1にアクセスできるか疎通確認します。
・VPC2内のインスタンスへの疎通確認
まず、ローカルから以下のコマンドでプロキシサーバ用インスタンスにSSH接続します
ssh ec2-user@[プロキシサーバのパブリックIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
接続できたら、いったんexit
コマンドで接続を切断し、以下のコマンドでプロキシサーバ用インスタンスにキーペア秘密鍵を送ります
scp -i [ローカルのキーペア秘密鍵のパス] [ローカルのキーペア秘密鍵のパス] ec2-user@[プロキシサーバのパブリックIPアドレス]:~/.ssh
キーペア秘密鍵を送信できたら、再び以下コマンドでプロキシサーバ用インスタンスにSSH接続します
ssh ec2-user@[プロキシサーバのパブリックIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
ここから以下のコマンドで開発用サーバ用インスタンスにSSH接続します
ssh ec2-user@[開発用サーバのプライベートIPアドレス] -i [プロキシサーバ内のキーペア秘密鍵のパス]
※開発用サーバのプライベートIPアドレスは、EC2コンソールの「インスタンス」→開発用サーバ用インスタンスIDをクリック→「プライベート IPv4 アドレス」に記載されています
Amazon Linuxのロゴが表示されたら成功です
・VPC1への疎通確認
開発用サーバからVPCピアリング経由でVPC1(WebサーバおよびAPサーバ)にSSH接続できるか確認します。
まず開発用サーバにキーペア秘密鍵を送る必要があるため、プロキシサーバに接続した状態で以下コマンドを実行します
scp -i [プロキシサーバ内のキーペア秘密鍵のパス] [プロキシサーバ内のキーペア秘密鍵のパス] ec2-user@[開発用サーバのプライベートIPアドレス]:~/.ssh
以下コマンドで再度開発用サーバ用インスタンスにSSH接続します
ssh ec2-user@[開発用サーバのプライベートIPアドレス] -i [プロキシサーバ内のキーペア秘密鍵のパス]
以下コマンドで、VPC1のAPサーバ1にSSH接続します
ssh ec2-user@[APサーバ1のプライベートIPアドレス] -i [開発用サーバ内のキーペア秘密鍵のパス]
Amazon Linuxのロゴが表示されれば接続成功です!
開発用サーバに戻り以下コマンドで、VPC1のWebサーバ1にSSH接続します
ssh ec2-user@[Webサーバ1のプライベートIPアドレス] -i [開発用サーバ内のキーペア秘密鍵のパス]
Amazon Linuxのロゴが表示されれば接続成功です!
同様にWebサーバ2、APサーバ2についても疎通確認して下さい。
プロキシサーバの設定 (VPC2)
VPC2のプロキシサーバにプロキシとしての機能を持たせるため、プロキシサーバーソフトSquidをインストールします
プロキシサーバにSSH接続した状態で、以下コマンドでSquidをインストールします(参考)
sudo yum update -y
sudo yum install squid -y
以下のコマンドで再起動後も自動実行するようにします
sudo systemctl enable squid
sudo systemctl restart squid
Squidがポート3128を使用していることを確認します。
sudo lsof -i:3128
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
squid 3598 squid 11u IPv6 25329 0t0 TCP *:squid (LISTEN)
プロキシサーバ用セキュリティグループの修正
先ほど設定したプロキシサーバ用セキュリティグループではSSH通信のみの通信が許可されており、それ以外の通信は許可されていません。
本プロキシサーバーは開発用サーバからのHTTP, HTTPS通信を中継する役割を果たすため、これらの通信をセキュリティグループで許可する必要があります。
ここで注意すべきが、Squidでのプロキシ使用時のポート(3128)は通常のポート(HTTP: 80, HTTPS:443)と異なる事です。
このような特殊なポートを使用する場合、セキュリティグループの「タイプ」は通常の「HTTP」「HTTPS」ではなく、「カスタムTCP」を選択して直接ポートを指定する必要があります
以下のようにプロキシサーバ用セキュリティグループのインバウンドルールを修正します
プロキシサーバの動作確認
プロキシサーバ経由で開発用サーバ (プライベートサブネット)からインターネットにアクセスできる事を確認します。
先ほどと同様に、プロキシサーバを踏み台として開発用サーバにSSH接続し、以下のコマンドを打ちます
(-x
オプションでプロキシを指定します)
curl http://www.example.com -x http://[プロキシサーバのプライベートIPアドレス]:3128
※プロキシサーバはパブリックでなくプライベートのIPアドレスを指定する事にご注意下さい
http://www.example.com
から以下のようなHTMLが返って来れば成功です
<!doctype html>
<html>
<head>
<title>Example Domain</title>
:
以下略
VPNの作成 (VPC2)
VPC2はオンプレの社内ネットワークと一体化して運用することを想定しているため、オンプレとのセキュアな通信路を確保するためにVPNを使用します。
AWSでVPNを使用するためには主にSite-to-Site VPNとクライアントVPNが存在しますが、今回はオンプレルータ側の設定が不要で導入が簡易な(ノートPC等にもインストールできる)クライアントVPNを使用します。
以下の公式チュートリアルに従い、クライアントVPNの構築を進めます
証明書とキーの発行
VPNはセキュアな通信路を確保するという特性上、認証を実施する必要があります。
Client VPNでは認証方法としてActive Directory (AD) または相互認証 (公開鍵証明書によるクライアント認証とサーバ認証)を利用する事ができますが、今回は個人でも簡単に実行可能なOpenVPN easy-rsaによる相互認証を採用します (公式チュートリアルに準じた方法)
・OpenVPN easy-rsaとは?
セキュリティ的に強固なシステムを構築するためには、認証に使用する証明書(SSL証明書)は信頼できる認証局(CA)から購入する必要があります。
一方で個人や小規模組織では、個人でCAを構築して発行された証明書、いわゆる「オレオレ証明書」が使用されることも多いです (当然ながらセキュリティレベルは落ちるので、業務用であればオレオレ証明書でなく信頼できる証明書を取得してください)
この個人で認証局を立ててオレオレ証明書を発行できるツールの一つが、OpenVPN easy-rsaです。
・OpenVPN easy-rsaのインストール
ローカル内の適当なフォルダに入り、以下コマンドでOpenVPN easy-rsaをダウンロードします
git clone https://github.com/OpenVPN/easy-rsa.git
・証明書とキーの発行
OpenVPN easy-rsaをダウンロードしたフォルダに入り、以下コマンドでPKI基盤 (公開鍵証明書の運用に必要な認証局、証明書等のセット)を初期化します
cd easy-rsa/easyrsa3
./easyrsa init-pki
以下コマンドで認証局 (CA)を構築します (途中進めるか聞かれたら"yes"と入力します)
./easyrsa build-ca nopass
以下コマンドでサーバー証明書とキーを生成します
./easyrsa build-server-full server nopass
以下コマンドでクライアント証明書とキーを生成します
./easyrsa build-client-full client1.domain.tld nopass
生成したファイルを以下のコマンドで1つのフォルダに集めます
mkdir 送信先フォルダのパス
cp pki/ca.crt 送信先フォルダのパス
cp pki/issued/server.crt 送信先フォルダのパス
cp pki/private/server.key 送信先フォルダのパス
cp pki/issued/client1.domain.tld.crt 送信先フォルダのパス
cp pki/private/client1.domain.tld.key 送信先フォルダのパス
cd 送信先フォルダのパス
※各ファイルは以下の意味を持ちます
- ca.crt: ルートCAの証明書
- server.crt: サーバー証明書
- server.key: サーバーの秘密鍵(キー)
- client1.domain.tld.crt: クライアント証明書
- client1.domain.tld.key: クライアントの秘密鍵(キー)
証明書とキーのACMへのアップロード
サーバー証明書とキー、およびクライアント証明書とキーを ACM にアップロードします。
・サーバー証明書とキーのアップロード
開いた証明書(server.crt)、キー(server.key)、証明書チェーン(ca.crt)を貼り付けます
貼り付ける証明書やCAの内容の確認方法ですが、以下のようにnanoエディタ等 (Windowsではメモ帳等)で開いて下の方に記載されている-----BEGIN CERTIFICATE-----
から-----END CERTIFICATE-----
の部分をコピペすると便利です
cd 証明書が格納されているフォルダ
nano server.key
同様にプライベートキーでは-----BEGIN PRIVATE KEY-----
から-----END PRIVATE KEY-----
までをコピペします。
必要に応じてタグを追加し、「次へ」を押します
「インポート」をクリックしてインポートを完了させます
・クライアント証明書とキーのダウンロード
サーバとは別に、同様の方法でクライアント証明書(server.crt)、キー(server.key)、証明書チェーン(ca.crt)の内容をアップロードします
クライアントVPNエンドポイントの作成
VPC側のVPNの出口となる、クライアントVPNエンドポイントを作成します。
VPCコンソールから「クライアントVPNエンドポイント」→「クライアントVPNエンドポイントを作成」をクリックします
以下のように設定を指定し、クライアントVPNエンドポイントの作成を完了させます
(クライアント側のCIDRブロックは/22より大きい(数字が小さい)必要がある事にご注意下さい。今回は172.16.4.0/22を指定します)
作成したクライアントVPNエンドポイントを選択し、以下の操作でVPC2のプライベートサブネットをターゲットネットワークとして紐付けます
認証ルールの追加
クライエントVPNでは、セキュリティグループとは別にVPNからVPCへのアクセスを明示的に許可する必要があります。これを実現するのが「認証ルール」です (「認証」と名前が付いていますが実際は認可です)
先ほどと同様にクライアントVPNエンドポイントを選択し、「承認ルール」→「認証ルールを追加」をクリックします
VPC2のCIDR(172.16.2.0/24)へのアクセスを有効化します。
セキュリティグループの修正
上記操作でVPN接続自体は確立できますが、現状のセキュリティグループはVPNからの通信を想定した設定となっていないため、一部の通信が拒否されてしまいます。
よって、VPNからの通信(開発用サーバ、プロキシサーバ両方へのSSH接続、およびプロキシサーバへのプロキシ接続)を許可するようセキュリティグループを修正します。
VPC側から見て、VPNからの通信はクライアントVPNエンドポイントがソースとみなせるので、この事を念頭に設定を修正します。
・クライアントVPNエンドポイント用セキュリティグループの作成
クライアントVPNエンドポイントをデフォルト設定で作成すると、VPCデフォルトのセキュリティグループが紐づけられます。
クライアントVPNエンドポイントはあくまで通信の経由点で通信の最終目的地となる事はないので、紐付けるセキュリティグループで明示的にインバウンド通信を許可する必要はありませんが、以下の目的で専用のセキュリティグループを作成して紐付けます
- 他のセキュリティポイントのインバウンドルールのソース設定用
- VPCデフォルトのセキュリティグループの設定変更時に思わぬ影響が出る事を防ぐ
以下のように、インバウンド・アウトバウンドルール共にデフォルト設定のままセキュリティグループを作成します。
「クライアントVPNエンドポイント」タブに戻り、該当するクライアントVPNエンドポイントを選択した状態で「セキュリティグループを適用」をクリックします
作成したセキュリティグループを選択して適用します
・開発サーバ用セキュリティグループの修正
開発用サーバのセキュリティグループは、現状ではプロキシサーバ用のセキュリティグループからのSSH通信のみが許可されています。よってVPNからSSH接続を行うためには、クライアントVPNエンドポイントからのインバウンド許可設定を追加する必要があります。
以下のようにソースがクライアントVPNエンドポイント用セキュリティグループのSSH通信を許可する設定を、開発サーバ用セキュリティグループのインバウンドルールに追加します
・プロキシサーバ用セキュリティグループの修正
プロキシサーバに関しては、SSH通信に加えて、先ほど追加したプロキシ通信用のポート3128のインバウンドルールも許可対象に加える必要があります (VPN経由でインターネットに接続できるようにするため)
また、元々全許可となっていたSSH通信の設定はセキュリティ的に脆弱なため、開発サーバ (および上記のVPN)のみにソース範囲を絞るよう変更します。
以下のようにソースがクライアントVPNエンドポイント用セキュリティグループのSSH通信およびカスタムTCPポート3128の通信を許可する設定を、開発サーバ用セキュリティグループのインバウンドルールに追加します
上記変更により、VPC2への接続ルートがVPN経由に絞られる (インターネットから直接SSH接続できなくなる)ため、よりセキュアなネットワークを実現する事ができます。
クライアントVPNエンドポイント設定ファイルのダウンロード
AWS側のクライアントVPNエンドポイントとローカル側のVPNクライアントソフトを連携させるため、クライアントVPNエンドポイント設定ファイルをダウンロードします。
「クライアントVPNエンドポイント」タブに戻り、該当のクライアントVPNエンドポイントを選択して「クライアント設定をダウンロード」をクリックします
ダウンロードしたdownloaded-client-config.ovpnファイルを、nanoやメモ帳等のエディタで開きます
nano ダウンロードしたdownloaded-client-config.ovpn
以下のように記述されているので、
前略
:
-----END CERTIFICATE-----
</ca>
reneg-sec 0
以下のようにクライアント証明書(client1.domain.tld.crt)とクライアントキー(client1.domain.tld.key)の内容をタグとタグ内に記述します
(証明書は-----BEGIN CERTIFICATE-----
から-----END CERTIFICATE-----
の部分を、キーは-----BEGIN PRIVATE KEY-----
から-----END PRIVATE KEY-----
までをコピペします)
前略
:
-----END CERTIFICATE-----
</ca>
<cert>
クライアント証明書の内容
</cert>
<key>
クライアントキーの内容
</key>
reneg-sec 0
OpenVPNのクライアントをインストール
クライアントとなるオンプレPCに、OpenVPNのクライアントソフトをインストールします。
私のPCはMac (M1)なので、クライアントソフトとしてTunnelBlickを使用します (Windowsの場合はOpenVPN GUI等を使用して下さい)
以下コマンドでHomeBrewからTunnelBlickをインストールします
brew install --cask tunnelblick
VPN接続の実行
※TunnelBlickの実行例です。Mac以外ではこちらの公式ドキュメントを参照ください
TunnelBlickを開いて出てきた画面の「接続先」欄に、先ほどダウンロードしたクライアントVPNエンドポイント設定ファイル (downloaded-client-config.ovpn)をドラッグ&ドロップします
接続先を「個人用」「全ユーザ用」どちらにインストールするかを選択します。特定のユーザでしかログインしない場合は「個人用」を選択すれば良いかと思います
「接続」をクリックしてVPN接続を開始します。
VPN接続が開始されると、インターネットに繋がらなくなるのでご注意下さい (プロキシサーバを経由させるようブラウザを設定変更すればインターネット接続可)
「切断」を押すと、VPN接続が終了します。
以後の疎通確認でVPN接続が正常に設定できているか確認します
各インスタンスへの疎通確認 (VPC2)
VPC2もVPC1のときと同様、想定通り設定ができているか疎通確認します。
具体的には、以下の内容を確認します
⑩VPNから開発用サーバへSSH通信できる
⑪開発用サーバからプロキシサーバへSSH通信できる
⑫VPNからプロキシサーバへSSH通信できる
⑬VPNからプロキシサーバ経由でインターネットへHTTP通信できる
⑭外部からプロキシサーバへSSH通信できない
⑮開発用サーバからWebサーバへSSH通信できる
⑯開発用サーバからAPサーバへSSH通信できる
①外部からWebサーバへSSH通信できない
④WebサーバからAPサーバへSSH通信できない
※可能であれば前述の②〜③, ⑤〜⑨も再確認する事が望ましいです。
VPN動作確認
⑩VPNから開発用サーバへのSSH疎通確認
VPN経由で開発用サーバにSSH接続できる事を確認します
先ほどのVPN接続で「接続」ボタンを押した状態 (VPN接続が有効となった状態)で、VPN接続されたローカルから以下コマンドで開発用サーバにSSH接続します
ssh ec2-user@[開発用サーバのプライベートIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
Amazon Linuxの起動マークが表示されたら成功です。
本来であればローカルからクラウドにプライーベートIPでアクセスする事はできませんが、VPNを経由すればアクセスを実現できる事が分かります。VPNすごい!
⑪開発用サーバからプロキシサーバへのSSH疎通確認
VPN経由でSSH接続した開発用サーバを踏み台に、プロキシサーバにSSH接続できる事を確認します。
⑩で開発用サーバにSSH接続した状態で、以下コマンドでプロキシサーバにSSH接続します
ssh ec2-user@[プロキシサーバのプライベートIPアドレス] -i [開発用サーバ内のキーペア秘密鍵のパス]
Amazon Linuxの起動マークが表示されたら成功です。
⑫VPNからプロキシサーバへのSSH疎通確認
現在のプロキシサーバ用セキュリティグループの設定ではVPNからのSSH接続を許可しているので、開発用サーバを踏み台にしなくともVPNから直接SSH接続できるはずです。
先ほどのVPN接続で「接続」ボタンを押した状態で、VPN接続されたローカルから以下コマンドで開発用サーバにSSH接続します
ssh ec2-user@[プロキシサーバのプライベートIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
なお、VPNからプロキシサーバへのSSH接続を拒否 (開発用サーバを踏み台にする事を強制する)した方がセキュリティ的には強いので、満たすべきセキュリティレベルに合わせて適宜セキュリティグループの設定を変更してください。
⑬VPNからプロキシサーバ経由でのインターネットへのHTTP疎通確認
プロキシサーバ用セキュリティグループの設定ではVPNからのプロキシ通信 (カスタムTCPポート3128)を許可しているので、VPNからプロキシサーバ経由でのインターネットへとHTTPできるはずです。
先ほどのVPN接続で「接続」ボタンを押した状態で、VPN接続されたローカルから以下curlコマンドでプロキシサーバ経由でのHTTP通信を実行します
curl http://www.example.com -x http://[プロキシサーバのプライベートIPアドレス]:3128
http://www.example.com
から以下のようなHTMLが返って来れば成功です
<!doctype html>
<html>
<head>
<title>Example Domain</title>
:
以下略
先ほど「VPN接続が開始されると、インターネットに繋がらなくなる」と言いましたが、このプロキシサーバを経由するようブラウザに設定する事で、VPNからインターネットに接続する事ができます。
ただし、デフォルト設定では速度が遅かったりHTTPSが上手く繋がらなかったりと実用的なレベルではないので、Squidをチューニングする等してプロキシの動作速度を上げてください (本記事の本筋からずれるので詳細は割愛します)
⑭外部からプロキシサーバへのSSH「非疎通」確認
先ほどのプロキシサーバ用セキュリティグループの設定変更で、プロキシサーバへのSSH接続は開発用サーバまたはVPN経由のみ許可するように設定しました。
よってVPNを経由せずに外部からプロキシサーバにSSH接続できない事を確認します。
ローカルからVPNを切断した状態で以下コマンドを打ち、インターネット経由でプロキシサーバにSSH接続します
ssh ec2-user@[プロキシサーバのパブリックIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
セキュリティグループ設定変更前のVPC2内のインスタンスへの疎通確認と全く同じコマンドですが、前とは異なり接続できない事がわかります。
インターネット経由でSSH接続できなくなったので、よりセキュリティレベルが高まったと言えるでしょう
VPN経由でのVPC1への疎通確認
VPNからVPC2の開発サーバを踏み台として、VPC1の各インスタンスにSSH接続できる事を確認します
⑮開発用サーバからWebサーバへのSSH疎通確認
先ほどの「VPC1への疎通確認」とほぼ同内容ですが、VPN経由でも接続できる事を確認します。
先ほどのVPN接続で「接続」ボタンを押した状態で、VPN接続されたローカルから以下コマンドで開発用サーバにSSH接続します
ssh ec2-user@[開発用サーバのプライベートIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
以下コマンドで、VPC1内のWebサーバ1にSSH接続します
ssh ec2-user@[Webサーバ1のプライベートIPアドレス] -i [開発用サーバ内のキーペア秘密鍵のパス]
Amazon Linuxのロゴが表示されれば接続成功です!
同様にWebサーバ2についても疎通確認して下さい。
⑯開発用サーバからAPサーバへのSSH疎通確認
引き続き開発用サーバにSSH接続した状態をスタートとし、以下コマンドで、VPC1内のAPサーバ1にSSH接続します
ssh ec2-user@[APサーバ1のプライベートIPアドレス] -i [開発用サーバ内のキーペア秘密鍵のパス]
Amazon Linuxのロゴが表示されれば接続成功です!
同様にAPサーバ2についても疎通確認して下さい。
VPC1への直接接続の非疎通確認
VPC1構築直後の疎通確認ではインターネット経由でVPC1にSSH接続できましたが、セキュリティグループ設定の変更でこれが出来なくなった (セキュリティレベルが高まった)事を確認します。
①外部からWebサーバへのSSH「非疎通」確認
①外部からWebサーバへのSSH疎通確認では疎通していたインターネット→WebサーバのSSH接続の非疎通を確認します。
VPNを切断した状態で、以下コマンドでWebサーバにSSH接続します
ssh ec2-user@[WebサーバのパブリックIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
応答が返ってこなければ成功です。
④WebサーバからAPサーバへのSSH「非疎通」確認
④WebサーバからAPサーバへのSSH疎通確認では疎通していたインターネット→WebサーバのSSH接続の非疎通を確認します。
先ほどのVPN接続で「接続」ボタンを押した状態で、VPN接続されたローカルから以下コマンドで開発用サーバにSSH接続します
ssh ec2-user@[開発用サーバのプライベートIPアドレス] -i [ローカルのキーペア秘密鍵のパス]
以下コマンドで、VPC1内のWebサーバ1にSSH接続します
ssh ec2-user@[Webサーバ1のプライベートIPアドレス] -i [開発用サーバ内のキーペア秘密鍵のパス]
以下コマンドで、APサーバ1にSSH接続します
ssh ec2-user@[APサーバ1のプライベートIPアドレス] -i [Webサーバ1内のキーペア秘密鍵のパス]
応答が返ってこなければ成功です。
APサーバ2についても同様に非疎通を確認しましょう。
Webサーバは外部に公開されているという特性上、攻撃により乗っ取られるリスクが大きいサーバーと言えます。よって内部の他のサーバーへと被害が広がらないよう、今回のようにWebサーバから他のサーバーへのSSH接続をセキュリティグループで拒否すると、よりセキュアなシステムを実現できます。
なお、現状では各サーバーに同じ秘密鍵がばら撒かれている状態となっているため、いずれかのサーバーが乗っ取られた際に危険な状態となってしまいます。よって、開発用サーバ以外の秘密鍵は削除してしまいましょう。
以上で、疎通確認および本記事の全作業が完了しました。
長かったですが、以下の複雑なネットワークをAWS上に構築出来たことに、感動を覚えた方も多いのではないのでしょうか? (感動の押し売りと言われそうですが…笑)
※CroudFrontやRoute53はVPC外のサービスのため本記事では触れませんが、今後別記事で投稿したいと思います
【参考】コストについて
参考までに、今回作成したVPC全体の無負荷時のコスト構成は、以下のようになりました。
(3日間測定しましたが、どの日もほぼ同じコスト構成でした)
サービス (機能)名 | 日あたりのコスト | 月換算 | 割合 |
---|---|---|---|
クライアントVPN | $3.6 | $108 | 41.9% |
EC2 | $2.2 | $65.7 | 25.5% |
RDS | $1.2 | $37.5 | 14.6% |
ELB | $1.2 | $35.1 | 13.6% |
EBS | $0.2 | $5.7 | 2.2% |
その他(RDSのストレージ等) | $0.2 | $5.5 | 2.1% |
計 | $8.6 | $257.5 | 100% |
クライアントVPNが多くを占めており、VPNは無負荷時でもそれなりのコストが掛かる事が分かります。
一方でアクセス負荷が増えた場合はWebサーバやRDSのインスタンス数を増やす (またはインスタンスタイプを上げる)必要が生じるため、これらのコストが増大する事が想定されます。
【参考】NATゲートウェイの作成
上記構成にはNATゲートウェイが存在しないため、一見プライベートサブネットからインターネットにアクセスできないように見えますが、以下サイトのようにyumコマンドはS3用ゲートウェイエンドポイント経由で実行可能です。
よって最低限のパッケージアップデートはNATゲートウェイなしで実行できますが、yumコマンド以外でプライベートサブネットからインターネットにアクセスしたい場合は、以下のようにパブリックサブネットにNATゲートウェイの作成が必要です。
NATゲートウェイは他のAZのプライベートサブネットからもアクセス可能なため、冗長性を考慮しないのであれば1個のみ作成すればOKです。
また、そのままでは作成したNATゲートウェイへのルーティングが設定されておらず、インターネットへのアクセスができないので、以下のようにプライベートサブネットのルートテーブル(インターネット接続用の0.0.0.0/0)のターゲットにNATゲートウェイを指定する必要があります。
感想
長くて疲れました…
ただ、オンプレで動的ルーティングやFWを設定してVRRPやスパニングツリーで冗長化…なんて事になると、この比ではない大変さだと思うので、気軽に冗長構成やセキュリティ強化を実現できるクラウドの有り難みを感じる事も出来ました。
継続的に開発するのであれば、再現性があり、かつミスを減らせるようIaCで自動化したいですね。
IaC (Terraform)でVPCを構築する方法に関しても、今後記事にしたいと思います。