232
Help us understand the problem. What are the problem?

posted at

updated at

【ベストプラクティス】Amazon VPC の構築方法を分かりやすく解説

はじめに

Amazon VPCは、AWS上で仮想ネットワークを構築できるサービスです。
VPCの概要や理論については以下の記事で詳細に解説しました。

一方で、「理論だけでなく実践も重要!
と思われる方も多いと思いますので、
本記事では実際にVPCによる仮想ネットワークを構築する方法を解説します。

構築の指針として、以下のベストプラクティスを極力満たすよう進めていきたいと思います。

特にVPCフローログVPCピアリングVPN等は設定が複雑でハマりがちですが、極力分かりやすく解説したつもりなので、効率よく構築法を習得できる記事となれば幸いです。

本記事で構築するVPCの構成

下図構成のVPC (上記記事の冒頭で紹介した構成)の構築を、実際のコンソール操作方法を交えて解説します。

構築するVPC構成図

上記構成の意図として、VPC1 (Webアプリ用のVPC)とVPC2 (社内ネットワークを模擬したVPC)を組み合わせる事で、アプリ公開用ネットワークと社内ネットワークを擬似的に再現する事を目指します。
(今回は本筋から外れるので紹介しませんが、実運用時はWAFやIDS等の防御用製品と組み合わせたり、証明書を取得してHTTPS化する等の対策を行い、セキュリティレベルを高めて下さい)

長いですが、完了する頃には簡単な業務用ネットワークに相当するVPCが構築できると思うので、ぜひお付き合い頂ければと思います。

VPCのセキュリティベストプラクティス

実際に構築に入る前に、満たすべきベストプラクティスの内容を確認します (AWS公式を参照)

・ベストプラクティス一覧

ベストプラクティス推奨事項 関連機能
複数の AZ へのサブネットの追加 (マルチ AZ 構成)による高可用性の確保 AZ
サブネット
ELB
Auto Scailing
ネットワーク ACL を使用してサブネットへのアクセスを、セキュリティグループを使用してサブネット内のインスタンスへのトラフィックを制御する セキュリティグループ
ネットワークACL
IAM の ID フェデレーション、ユーザー、ロールを使用して、AWS VPC リソースおよび API へのアクセスを管理する IAM
VPC フローログで Amazon CloudWatch を使用して IP トラフィックを監視する セキュリティグループ
ネットワークACL

それぞれ解説します

マルチ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構成図

以下の手順で構築を進めます

長いですが、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のコンソールからロググループの作成画面に入ります。

CloudWatchロググループ

ロググループ名等を指定して、ロググループを作成します

CloudWatchロググループ作成画面

フローログ記録用のIAMポリシー作成

IAMのコンソールへ移動し、「ポリシー」→「ポリシーを作成」をクリックします

IAMポリシー作成

JSON形式のエディタを開き、以下のポリシーを貼り付けます

フローログ用ポリシーJSON
フローログの記録用ポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams"
      ],
      "Resource": "*"
    }
  ]
}

必要に応じてタグを追加します(今回は追加しない)

フローログ用ポリシーのタグ追加

ポリシーに名前と説明を追記して作成を完了します

フローログ用ポリシーの名前

フローログ記録用のロール作成

IAMコンソール画面に戻り、「ロール」→「ロールを作成」をクリックします

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のコンソールから、「データベース」→「データベースの作成」をクリックします

RDSデータベース作成

以下のようにデータベースの詳細設定を指定し、「データベースの作成」をクリック

RDS作成時の設定1 RDS作成時の設定2 RDS作成時の設定3

※データベース認証に「パスワードとIAMデータベース認証」を指定すると、パスワードの代わりにIAMロールの付与でDBにアクセス可能となり、セキュリティレベルが向上します(参考)。一方で大量アクセスには向かない方式のためご注意ください



S3バケットの作成 (VPC1)

非構造データを保存するための、S3 のバケットを作成します

バケットの作成

S3のコンソールに移動し、「バケット」→「バケットを作成」とクリックします

S3バケット作成

以下のようにバケットの詳細設定を指定し、「バケットを作成」をクリック

S3バケット作成時の設定1 S3バケット作成時の設定2

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の作成は完了です。現時点では以下のようなネットワークが構築されているはずです

構築するVPC構成図

このネットワークが、想定通り設定できているか疎通確認します。

具体的には、以下の内容を確認します

①外部からWebサーバへSSH通信できる※
②外部からWebサーバへHTTP通信できる
③外部からWebサーバへELB経由でHTTP通信できる
④WebサーバからAPサーバへSSH通信できる※
⑤WebサーバからAPサーバへHTTP通信できる
⑥WebサーバからAPサーバへELB経由でHTTP通信できる
⑦外部からAPサーバへELB経由でHTTP通信できない
⑧APサーバからRDS内のデータを取得できる
⑨APサーバからS3内のデータを取得できる

VPC1疎通確認

※なお、①のSSH通信はVPC2作成後には通信不可となるよう再設定する (外部からSSH接続できるのはやや危険なため)のでご注意ください。同様に④もVPC2作成後に遮断します

Webサーバへの疎通確認

①SSHの疎通確認

以下の手順でWebサーバ用EC2インスタンス1 ("web-server-1")のグローバルIPアドレスを確認します

スクリーンショット 2022-07-10 14.45.35.png

ターミナル(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 で保存します

index.html
<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 1Web server 2どちらが表示されてもOKです)

F5 (Macならctrl + R)でブラウザを何度か更新し、Web server 1Web 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ファイルの作成を以下のように実施します。

httpdのインストールとindex.htmlファイルの作成コマンド
sudo yum update -y
sudo yum install httpd -y
cd /var/www/html
sudo nano index.html
index.html
<html><h1>AP server 1</h1></html>
httpdの開始と再起動後の有効化
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 1AP server 2どちらが表示されてもOKです)

<html><h1>AP server 1</h1></html>

同じコマンドを何度か実行し、AP server 1AP 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のエンドポイント名

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作成ウィザード

セキュリティグループの作成 (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ピアリング作成

以下のようにピアリング接続の詳細設定を入力し、「ピアリング接続を作成」をクリックします

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のプライベートサブネット用ルートテーブルをクリックします

VPC2のプライベートサブネット用ルートテーブル

以下のようにVPCピアリングへのルートが存在しない事が分かるので、「ルートを編集」をクリックします

ルート編集

送信先がVPC1のCIDR (172.16.0.0/23)のとき、VPCピアリング接続にルーティングされるよう以下のようにルートを追加します

VPC2から1へのルート修正

VPC1→VPC2へのルート

忘れがちなのがこの部分で、ルートテーブルの指定はステートレスのため、戻り方向のルートも明示的に指定する必要があります

今回はVPC1の全てのサブネット (WebサーバおよびAPサーバ)にVPC2 (開発用サーバ)からアクセスできるようにしたいので、VPC1以下の3つ (パブリック用 + プライベート用x2)のルートテーブルにVPC2への戻りルートを設定します。

VPC1のルートテーブル一覧

上記3つのルートテーブル全てに、送信先がVPC2のCIDR (172.16.2.0/24)のとき、VPCピアリング接続にルーティングされるよう以下のルートを追加します

VPC1から2へのルート修正

VPC1内のセキュリティグループ設定の修正

VPCピアリングの作成とルートテーブルの修正を実施しましたが、このままではVPC1のセキュリティグループでVPC2からのアクセスが許可されていないので、アクセスを許可する修正が必要です。
また前述のように外部からWebサーバにSSH接続できる状態はセキュリティ的にやや弱いので、Webサーバ用およびAPサーバ用のセキュリティグループを修正し、VPC2内の開発用サーバのみからアクセスできるようにします。

VPCコンソールから「セキュリティグループ」タブをクリックし、以下の2つのセキュリティグループを修正します

VPC1セキュリティグループ修正

1. Webサーバ用セキュリティグループの修正

インバウンドルールのSSHのソースを、VPC2のプライベートサブネットのCIDR (172.16.2.64/26)に限定します

Webサーバ用セキュリティグループ修正

2. APサーバ用セキュリティグループの修正

Webサーバ用と同様に、インバウンドルールのSSHのソースを、VPC2のプライベートサブネットのCIDR (172.16.2.64/26)に限定します

APサーバ用セキュリティグループ修正

疎通確認

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 にアップロードします。

ACM証明書インポート

・サーバー証明書とキーのアップロード

開いた証明書(server.crt)、キー(server.key)、証明書チェーン(ca.crt)を貼り付けます

サーバー証明書インポート

貼り付ける証明書やCAの内容の確認方法ですが、以下のようにnanoエディタ等 (Windowsではメモ帳等)で開いて下の方に記載されている-----BEGIN CERTIFICATE-----から-----END CERTIFICATE-----の部分をコピペすると便利です

cd 証明書が格納されているフォルダ
nano server.key
サーバー証明書nanoエディタ

同様にプライベートキーでは-----BEGIN PRIVATE KEY-----から-----END PRIVATE KEY-----までをコピペします。

必要に応じてタグを追加し、「次へ」を押します

サーバー証明書インポート時タグ

「インポート」をクリックしてインポートを完了させます

サーバー証明書インポート完了

・クライアント証明書とキーのダウンロード

サーバとは別に、同様の方法でクライアント証明書(server.crt)、キー(server.key)、証明書チェーン(ca.crt)の内容をアップロードします

クライアントVPNエンドポイントの作成

VPC側のVPNの出口となる、クライアントVPNエンドポイントを作成します。

VPCコンソールから「クライアントVPNエンドポイント」→「クライアントVPNエンドポイントを作成」をクリックします

クライアントVPNエンドポイント作成

以下のように設定を指定し、クライアントVPNエンドポイントの作成を完了させます
(クライアント側のCIDRブロックは/22より大きい(数字が小さい)必要がある事にご注意下さい。今回は172.16.4.0/22を指定します)

クライアントVPNエンドポイント作成画面1 クライアントVPNエンドポイント作成画面2

作成したクライアントVPNエンドポイントを選択し、以下の操作でVPC2のプライベートサブネットをターゲットネットワークとして紐付けます

クライアントVPNエンドポイント選択 ターゲットネットワーク関連付け1 ターゲットネットワーク関連付け2

認証ルールの追加

クライエントVPNでは、セキュリティグループとは別にVPNからVPCへのアクセスを明示的に許可する必要があります。これを実現するのが「認証ルール」です (「認証」と名前が付いていますが実際は認可です)

先ほどと同様にクライアントVPNエンドポイントを選択し、「承認ルール」→「認証ルールを追加」をクリックします

認証ルールの追加1

VPC2のCIDR(172.16.2.0/24)へのアクセスを有効化します。

認証ルールの追加2

セキュリティグループの修正

上記操作でVPN接続自体は確立できますが、現状のセキュリティグループはVPNからの通信を想定した設定となっていないため、一部の通信が拒否されてしまいます。

よって、VPNからの通信(開発用サーバ、プロキシサーバ両方へのSSH接続、およびプロキシサーバへのプロキシ接続)を許可するようセキュリティグループを修正します。

VPC側から見て、VPNからの通信はクライアントVPNエンドポイントがソースとみなせるので、この事を念頭に設定を修正します。

・クライアントVPNエンドポイント用セキュリティグループの作成

クライアントVPNエンドポイントをデフォルト設定で作成すると、VPCデフォルトのセキュリティグループが紐づけられます。
クライアントVPNエンドポイントはあくまで通信の経由点で通信の最終目的地となる事はないので、紐付けるセキュリティグループで明示的にインバウンド通信を許可する必要はありませんが、以下の目的で専用のセキュリティグループを作成して紐付けます

  • 他のセキュリティポイントのインバウンドルールのソース設定用
  • VPCデフォルトのセキュリティグループの設定変更時に思わぬ影響が出る事を防ぐ

以下のように、インバウンド・アウトバウンドルール共にデフォルト設定のままセキュリティグループを作成します。

クライアントVPNエンドポイント用セキュリティグループの作成

「クライアントVPNエンドポイント」タブに戻り、該当するクライアントVPNエンドポイントを選択した状態で「セキュリティグループを適用」をクリックします

クライアントVPNエンドポイント用セキュリティグループの紐付け

作成したセキュリティグループを選択して適用します

クライアントVPNエンドポイント用セキュリティグループの適用

・開発サーバ用セキュリティグループの修正

開発用サーバのセキュリティグループは、現状ではプロキシサーバ用のセキュリティグループからのSSH通信のみが許可されています。よってVPNからSSH接続を行うためには、クライアントVPNエンドポイントからのインバウンド許可設定を追加する必要があります。

以下のようにソースがクライアントVPNエンドポイント用セキュリティグループのSSH通信を許可する設定を、開発サーバ用セキュリティグループのインバウンドルールに追加します

開発サーバ用インバウンドルールにVPNを追加

・プロキシサーバ用セキュリティグループの修正

プロキシサーバに関しては、SSH通信に加えて、先ほど追加したプロキシ通信用のポート3128のインバウンドルールも許可対象に加える必要があります (VPN経由でインターネットに接続できるようにするため)

また、元々全許可となっていたSSH通信の設定はセキュリティ的に脆弱なため、開発サーバ (および上記のVPN)のみにソース範囲を絞るよう変更します。

以下のようにソースがクライアントVPNエンドポイント用セキュリティグループのSSH通信およびカスタムTCPポート3128の通信を許可する設定を、開発サーバ用セキュリティグループのインバウンドルールに追加します

プロキシサーバ用インバウンドルールにVPNを追加

上記変更により、VPC2への接続ルートがVPN経由に絞られる (インターネットから直接SSH接続できなくなる)ため、よりセキュアなネットワークを実現する事ができます。

クライアントVPNエンドポイント設定ファイルのダウンロード

AWS側のクライアントVPNエンドポイントとローカル側の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設定ファイルのTunnelBlickへのドラッグ&ドロップ

接続先を「個人用」「全ユーザ用」どちらにインストールするかを選択します。特定のユーザでしかログインしない場合は「個人用」を選択すれば良いかと思います

TunnelBlickの設定インストール先

「接続」をクリックしてVPN接続を開始します。

VPN接続開始

VPN接続が開始されると、インターネットに繋がらなくなるのでご注意下さい (プロキシサーバを経由させるようブラウザを設定変更すればインターネット接続可)
「切断」を押すと、VPN接続が終了します。

以後の疎通確認でVPN接続が正常に設定できているか確認します



各インスタンスへの疎通確認 (VPC2)

VPC2もVPC1のときと同様、想定通り設定ができているか疎通確認します。

具体的には、以下の内容を確認します

⑩VPNから開発用サーバへSSH通信できる
⑪開発用サーバからプロキシサーバへSSH通信できる
⑫VPNからプロキシサーバへSSH通信できる
⑬VPNからプロキシサーバ経由でインターネットへHTTP通信できる
⑭外部からプロキシサーバへSSH通信できない
⑮開発用サーバからWebサーバへSSH通信できる
⑯開発用サーバからAPサーバへSSH通信できる
①外部からWebサーバへSSH通信できない
④WebサーバからAPサーバへSSH通信できない

VPC2の疎通確認

※可能であれば前述の②〜③, ⑤〜⑨も再確認する事が望ましいです。

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上に構築出来たことに、感動を覚えた方も多いのではないのでしょうか? (感動の押し売りと言われそうですが…笑)

構築するVPC構成図

※CroudFrontやRoute53はVPC外のサービスのため本記事では触れませんが、今後別記事で投稿したいと思います



【参考】コストについて

参考までに、今回作成したVPC全体の無負荷時のコスト構成は、以下のようになりました。
(3日間測定しましたが、どの日もほぼ同じコスト構成でした)

VPCの実コスト
サービス (機能)名 日あたりのコスト 月換算 割合
クライアント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を構築する方法に関しても、今後記事にしたいと思います。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
232
Help us understand the problem. What are the problem?