AWSでセキュアなWebサーバを構築する

## はじめに
### セキュアな構成とは
1. WebサーバやDBサーバを直接インターネットに公開しない。
    - パブリックとプライベートでサブネットを分割する。
2. パブリック・プライベート共に、特定のポートのみインバウンドを受け付ける。
3. プライベートサブネットは特定のサブネットからのみインバウンドを受け付ける。
4. 踏み台サーバは必要な時のみ存在（起動）している。
![bde5f7ae-e637-5d33-1e4f-edab8e3e2555.png](https://qiita-image-store.s3.amazonaws.com/0/168463/84260b38-e168-2663-6a03-7b0173831eb8.png)


### 今回の想定
- 試しに構築するだけなので、インスタンスは最小サイズを使用する。

## おおまかな手順
0. リージョンの変更
1. VPCの構築
2. パブリックサブネットの構築
    3. サブネットの構築
    4. インターネットゲートウェイの構築
    5. ルートテーブルの構築
3. プライベートサブネットの構築
    3. サブネットの構築
    4. NATインスタンスの構築
    5. Elastic IPの割り当てと関連付け
    5. ルートテーブルの構築
4. Auto Scaling Groupのための前準備
    5. プライベートサブネットにEC2インスタンス（Webサーバ）を構築
    1. パブリックサブネットにEC2インスタンス（踏み台サーバ）を構築
    1. 踏み台サーバからWebサーバにログインし、Apacheをインストール
    1. WebサーバのAMIを作成
2. Elastic Load Balancerの構築
1. Auto Scaling Groupの構築
1. アクセスして確認
2. NATインスタンスをNATゲートウェイに変更してみる
3. HTTPS通信をサポートしてみる


## くわしい手順
### 0. リージョンの変更
リージョンはユーザに近いところを選ぶとよいと思います。  

1. 画面上部のナビゲーションバー右上の、リージョンが表示されているところをクリック
2. 東京を選択

### 1. VPCの構築
1. ナビゲーションバーの「サービス」を選択
2. メニューからネットワーキング ＆ コンテンツ...の中の「VPC」を選択（VPCダッシュボードが開く）
3. 「n VPC」と書かれているリンクをクリック
4. 「VPCの作成」をクリック
> 1. ネームタグは適当に決める（vpc-servicename-stgとかvpc-servicename-prodとか）
> 1. IPv4 CIDR blockは例示されている`10.0.0.0/16`を入力
> 1. IPv6 CIDR blockは`No IPv6 CIDR Block`を選択
> 1. テナンシーは`デフォルト`のままで

5. 「作成」ボタンをクリック

### 2. パブリックサブネットの構築
##### サブネットの作成
1. 左側のメニューから「サブネット」を選択
2. 「サブネットの作成」をクリック
> 1. ネームタグは適当に決める（subnet-servicename-stg-publicとか）
> 1. VPCは先ほど作成したものを選択
> 1. アベイラビリティゾーンは`ap-northeast-1a`を選択
> 1. IPv4 CIDR blockは`10.0.0.0/24`を入力

3. 「作成」ボタンをクリック

##### インターネットゲートウェイの構築
###### インターネットゲートウェイの作成
1. 左側のメニューから「インターネットゲートウェイ」を選択
2. 「インターネットゲートウェイの作成」をクリック
> 1. ネームタグは適当に決める（igw-servicename-stgとか）

3. 「作成」ボタンをクリック 

###### インターネットゲートウェイをVPCに適用
1. 作成したインターネットゲートウェイの左端の四角が選択されている事を確認して、「VPCにアタッチ」をクリック
2. VPCは先ほど作成したものを選択
3. 「アタッチ」をクリック 

##### ルートテーブルの構築
###### ルートテーブルの作成
1. 左側のメニューから「ルートテーブル」を選択
2. 「ルートテーブルの作成」をクリック
> 1. ネームタグは適当に決める（rtb-servicename-stg-publicとか）
> 1. VPCは先ほど作成したものを選択

3. 「作成」ボタンをクリック

###### ルートテーブルの編集
1. 作成したルートテーブルの左端の四角が選択されている事を確認して、ページ下部の「ルート」をクリック
2. 「編集」をクリック
3. 「別ルートの追加」をクリック
> 1. 送信先に`0.0.0.0/0`を入力
> 1. ターゲットに先ほど作成したインターネットゲートウェイを選択（クリックすると候補が出てくる）

4. 「保存」をクリック 

###### ルートテーブルをVPCに適用
1. 左側のメニューから「サブネット」を選択
2. 先ほど作成した`パブリック`サブネットの左端の四角が選択されている事を確認して、ページ下部の「ルートテーブル」をクリック
3. 「編集」をクリック
4. 変更先：に、先ほど作成した`パブリック`ルートテーブルを選択
5. 「保存」をクリック 

### 3. プライベートSubnetの構築
##### サブネットの作成
1. 左側のメニューから「サブネット」を選択
2. 「サブネットの作成」をクリック
> 1. ネームタグは適当に決める（subnet-servicename-stg-privateとか）
> 1. VPCは先ほど作成したものを選択
> 1. アベイラビリティゾーンは`ap-northeast-1a`を選択
> 1. IPv4 CIDR blockは`10.0.1.0/24`を入力

3. 「作成」ボタンをクリック

### 4. パブリックSubnetにNATインスタンスを構築
#### AMIからインスタンスを作成
1. ナビゲーションバーの「EC2」を選択
2. 左側のメニューから「インスタンス」を選択
3. 「インスタンスの作成」をクリック

##### AMIの選択
4. 左側のメニューから「コミュニティAMI」を選択
5. 「コミュニティAMIの検索」と書かれたボックスをクリック、「amzn nat pv」と入力してエンター
6. 日付の最新のものを探し、右にある「選択」をクリック

##### インスタンスタイプの選択
7. 「ファミリー」がマイクロインスタンスのものを探し、左端の四角が選択されている事を確認して、ページ下部の「次の手順：インスタンスの詳細の設定」をクリック

##### インスタンスの設定
1. 下記の通り設定する
> 1. インスタンス数は`1`のまま
> 1. 購入のオプションはそのままにする
> 1. ネットワークは先ほど作成したVPCを選択
> 1. サブネットは先ほど作成した**パブリック**サブネットを選択する
> 1. 自動割り当てパブリックIPはそのままにする
> 1. その下に続く設定は、そのままにする

1. 「次の手順：ストレージの追加」をクリック 

##### ストレージの追加
1. ボリュームタイプに`マグネティック`が選択されていることを確認する
（無料利用枠の場合30GBまでSSDが無料で利用できるので、SSDを選択してもよい）
1. 「次の手順：Add Tags」をクリック

##### Add Tags
1. キーに「Name」が入力されていることを確認
2. 値は適当に決める（「servicename-stg-nat」など）
3. 「次の手順：セキュリティグループの設定」をクリック

##### セキュリティグループの設定
1. セキュリティグループの割り当て：で、「新しいセキュリティグループを作成する」が選択されていることを確認
2. セキュリティグループ名は適当に決める（securitygroup-servicename-stg-natとか）
3. 説明は適当に
（ただし、日本語は入力できない。a～z、A～Z、0～9、スペース、._-:/()#,@[]+=&;{}!$* のみ使用できる）
4. タイプがSSHのものの、送信元に`カスタム`を選択、その右に`10.0.0.0/16`を入力
5. 「ルールの追加」をクリック
6. 「カスタムTCPルール」をクリック、リストから「HTTP」を選択
4. タイプがSSHのものの、送信元に`カスタム`を選択、その右に`10.0.0.0/16`を入力
5. 「確認と作成」をクリック

##### インスタンス作成の確認
1. 選択したAMI、インスタンスタイプ、セキュリティグループが正しいか確認し、「作成」をクリック

##### キーペアの作成
1. 「既存のキーペアの選択」をクリック、`新しいキーペアの作成`を選択
2. キーペア名は適当に（servicename-stg-natなど）
3. 「キーペアのダウンロード」をクリック、キーを安全な場所に保存する
4. 「インスタンスの作成」をクリック
5. 「作成ステータス」画面が表示されるので、スクロールして下にある「インスタンスの表示」をクリック

#### 送信元/送信先の変更チェック の無効化
1. 先ほど作成した（作成中の）インスタンスの左端の四角が選択されている事を確認し、「アクション」をクリック、「ネットワーキング」→「送信元/送信先の変更チェック」をクリック
2. 「無効にしてよろしいですか」と聞いてくるので、「はい、無効化する」をクリック

#### Elastic IPを設定
1. 左側のメニューから「Elastic IP」を選択
2. 「新しいアドレスの割り当て」をクリック
2. 「割り当て」をクリック
3. 「新しいアドレスのリクエストが成功しました」と表示されたら、「閉じる」をクリック
4. 今アロケートされたElastic IPの左端の四角が選択されている事を確認して、「アクション」をクリック、「アドレスの関連付け」をクリック
5. リソースタイプは「インスタンス」を選択したままにする
6. インスタンスは先ほど作成したNATインスタンスを選択する
7. 「関連付け」をクリック

#### （プライベートサブネット用の）ルートテーブルの構築
##### ルートテーブルの作成
1. ナビゲーションバーの「VPC」を選択
1. 左側のメニューから「ルートテーブル」を選択
2. 「ルートテーブルの作成」をクリック
> 1. ネームタグは適当に決める（rtb-servicename-stg-privateとか）
> 1. VPCは先ほど作成したものを選択

3. 「作成」ボタンをクリック

##### ルートテーブルの編集
1. 作成したルートテーブルの左端の四角が選択されている事を確認して、ページ下部の「ルート」をクリック
2. 「編集」をクリック
3. 「別ルートの追加」をクリック
> 1. 送信先に`0.0.0.0/0`を入力
> 1. ターゲットに先ほど作成したNATインスタンスを選択（クリックすると候補が出てくる）

4. 「保存」をクリック 

##### ルートテーブルをVPCに適用
1. 左側のメニューから「サブネット」を選択
2. 先ほど作成した**プライベート**サブネットの左端の四角が選択されている事を確認して、ページ下部の「ルートテーブル」をクリック
3. 「編集」をクリック
4. 変更先：に、先ほど作成した**プライベート**ルートテーブルを選択
5. 「保存」をクリック 

### 4. Auto Scaling Groupのための前準備
#### プライベートサブネットにEC2インスタンス（Webサーバ）を構築する
##### NATインスタンスの構築
NATインスタンスを作成したように、**プライベート**サブネットにEC2インスタンスを作成する。
AMIは、コミュニティAMI→「amzn ami pv」で検索し、日付が一番新しいものを選択。
新しいセキュリティグループを作成し、``10.0.0.0/16``からのHTTPとSSHを許可するように設定する。
キーは、Nameに「servicename-stg-web」など適当に入力する。

#### パブリックサブネットにEC2インスタンス（踏み台サーバ）を構築する
##### NATインスタンスの構築
**パブリック**サブネットにEC2インスタンスを作成する。
AMIは、コミュニティAMI→「amzn ami pv」で検索し、日付が一番新しいものを選択。
新しいセキュリティグループを作成し、タイプにSSHを、送信元に``任意の場所``を設定する。
キーは、Nameに「servicename-stg-bastion」など適当に入力する。
##### Elastic IPを設定
NATインスタンス構築時と同様に、Elstic IPを割り当て・関連付けする。

#### WebサーバにApacheをインストールする
踏み台サーバからWebサーバにSSHでログインし、yumコマンドでApacheをインストールする。
##### まず踏み台サーバに接続する
SSH接続する際のIPは、踏み台サーバに割り当てたElastic IPです。
ユーザ名は、Amazon Linux の場合 ``ec2-user`` です。
接続には、インスタンス作成時に設定したキーペアのキーファイルが必要です。
##### 踏み台サーバからWebサーバにログインする
方法はいろいろありますが、今回はポートフォワーディングを使用します。
（本当はエージェント転送など使用するとよいかと思います。）
###### ポートフォワーディング設定
リモート側のホストに、WebサーバのプライベートIPを指定、ポートはSSHなので ``22`` を指定。
ローカル側のポートは、（Well-Knownの領域を避けて）適当に指定します。今回は50022としました。
###### ローカルにもってきたポート（Webサーバ）に対してSSH接続する
localhostの50022番ポートに対してSSH接続します。
ユーザ名は、Amazon Linux の場合 ``ec2-user`` です。
接続には、インスタンス作成時に設定したキーペアのプライベートキーファイルが必要です。

##### Apacheをインストールする
yumコマンドを使いhttpdをインストールし、chkconfigコマンドで自動起動をON、serviceコマンドでhttpdのデーモンを開始します。
※CentOS 7ではsystemdが採用されたため使用するコマンドが異なります。
　また、ディストリビューションによってはパッケージ管理システムが異なるため、使用するコマンドも異なります。
インストール後、後述のELBのヘルスチェックの際ドキュメントルートにあるindex.htmlにアクセスするため、index.htmlを作成しておきます。

```bash:Apacheのインストール
sudo yum install httpd
sudo chkconfig httpd on
sudo service httpd start
cd /var/www/html
sudo sh -c "echo hello! > index.html"
```

#### AMIの作成
1. Webサーバのインスタンスの左端の四角を選択し、「アクション」をクリック、「インスタンスの状態」→「停止」をクリック
1. Webサーバのインスタンスであるか確認し、「停止する」をクリック
1. 「アクション」をクリック、「イメージ」→「イメージの作成」をクリック
2. イメージ名と説明は適当に入力、他は特に変更なし。
3. 「イメージの作成」をクリック
4. 「閉じる」をクリック

### 5. Elastic Load Balancerの構築
1. EC2ダッシュボードの左メニューから「ロードバランサー」をクリック
2. 「ロードバランサーの作成」をクリック
3. 「Classic Load Balancer」を選択し、「次へ」をクリック
> 1. ロードバランサー名は適当に決める（elb-servicename-stgとか）
> 1. ロードバランサーを作成する場所は、作成したVPCを選択
> 1. 内部向けロードバランサーの作成にはチェックをつけない
> 1. プロトコル、ポートはHTTPの80からHTTPの80に転送するのでさわらない
> 1. 利用可能なサブネットから、**パブリック**サブネットを追加する

4. 「次の手順：セキュリティグループの割り当て」をクリック
> 1. セキュリティグループの割り当てで、「新しいセキュリティグループを作成する」を選択
> 1. セキュリティグループ名は適当に決める
> 1. タイプはHTTP、送信元は``任意の場所``を選択

5. 「次の手順：セキュリティ設定の構成」をクリック
6. 「次の手順：ヘルスチェックの設定」をクリック
> 1. 上半分の設定はいじらない（pingパスで`/index.html`が指定されているので、Webサーバのドキュメントルートにindex.htmlを用意しておく）
> 1. 高度な詳細の中の、正常のしきい値を``2``に変更する（実運用時は適切な値を設定する）

6. 「次の手順：EC2 インスタンスの追加」をクリック
7. 何も変更せず、「次の手順：タグの追加」をクリック
8. キーは、Nameに「elb-servicename-stg」など適当に入力する。
8. 「確認と作成」をクリック
9. 「作成」をクリック

### 6. Auto Scaling Groupの構築
#### 起動設定の作成
1. EC2ダッシュボードの左メニューから「Auto Scaling グループ」をクリック
2. 「Auto Scaling グループの作成」をクリック
3. 「新しい起動設定を作成する」をクリック
4. 「次のステップ」をクリック
4. 左側のメニューからマイ AMIをクリック
5. 先ほど作成したAMIを「選択」
> 1. インスタンスタイプを変更する場合は変更する。今回は変更しない。

6. 「次の手順：詳細設定」をクリック
> 1. 名前は適当に決める（lc-sercicename-stgなど）
> 1. 高度な詳細を開き、IP アドレスタイプは``パブリック IP アドレスをどのインスタンスにも割り当てません。``を選択

7. 「次の手順：ストレージの追加」をクリック
> 1. ストレージ構成を変更するなら変更する。今回は変更しない。
9. 「次の手順：セキュリティグループの設定」をクリック
> 1. 「既存のセキュリティグループを選択する」にチェック
> 1. Webサーバに設定したものと同じセキュリティグループを選択

10. 「確認」をクリック
11. 「起動設定の作成」をクリック
> 1. 「既存のキーペアを選択」をクリック
> 1. Webサーバに設定したものと同じキーペアを選択

12. 「選択したプライベートキーファイル～」にチェックを入れ、「起動設定の作成」をクリック

#### Auto Scaling グループの作成
13. 「Auto Scaling グループの作成」画面が表示される
> 1. グループ名は適当に（asg-servicename-stgとか）
> 1. グループサイズは1を選択
> 1. ネットワークは作成したVPCを選択（IDしか表示されないのでダッシュボードからどのIDか確認しておく）
> 1. サブネットは**プライベート**サブネットを選択（IDしか表示されないのでダッシュボードからどのIDか確認しておく）
> 1. 高度な詳細を開き、
>     1. ロードバランシングの「ひとつまたは複数のロードバランサーからトラフィックを受信する」にチェックを入れる
>     1. Classic ロードバランサーで、作成したELB（ロードバランサー）を選択
>     1. ターゲットグループは選択しない
>     1. ヘルスチェックのタイプは`ELB`を選択
>     1. 他は特に変更しない

14. 「次の手順：スケーリングポリシーの設定」をクリック
> 1. 「このグループを初期のサイズに維持する」を選択

15. 「次の手順：通知の設定」をクリック
16. 「次の手順：タグを設定」をクリック
8. キーは、Nameに「as-servicename-stg-web」など適当に入力する。
17. 「確認」をクリック
18. 「Auto Scaling グループの作成」をクリック

### 7. アクセスして動作確認
Auto Scaling Groupの構築を行うと、AMIからEC2インスタンスが起動する。
しばらく経つとElastic Load Balancing（ロードバランサー）に組み込まれる。
ロードバランサーの「説明」タブの状態が、「1個のうち1個のインスタンスが実行中です」になってからアクセスしましょう。

1. EC2ダッシュボードの左メニューから「ロードバランサー」をクリック
2. 作成したELBをクリック
3. ページ下部の「説明」タブをクリック
4. 「DNS 名」を探し、表示されているアドレスにアクセスする
5. hello! と表示されていれば成功

また、ASGによって作成されたインスタンスを削除すると、ELBがこれを検知して切り離し、新たにインスタンスを起動・ELBに組み込まれる、という動作を確認することもできます。

### 8. NATインスタンスをNATゲートウェイに変更してみる
最近ではNATインスタンスよりNATゲートウェイを使うことが多いようです。

1. ナビゲーションバーの「VPC」を選択
2. 左側のメニューから「NAT ゲートウェイ」を選択
3. 「NAT ゲートウェイの作成」をクリック
> 1. サブネットは**パブリック**サブネットを選択（サブネットの名前を入力して検索できる）
> 1. Elastic IP 割り当て IDの横にある、「新しいEIPの作成」をクリック

3. 「NAT ゲートウェイの作成」をクリック
4. 「ルートテーブルの編集」をクリック
5. プライベートサブネット用のルートテーブルを選択
6. 画面下部の「ルート」タブをクリック
7. 「編集」をクリック
8. 送信先が0.0.0.0/0の、ターゲット欄に入力されているネットワークインタフェースを削除し、作成したNATゲートウェイを選択
9. 「保存」をクリック
10. ASGで作成されているWebサーバのインスタンスに、踏み台サーバ経由でログインする
11. pingができるかどうか確認する

```bash:pingで疎通確認
$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=55 time=1.94 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=55 time=1.78 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=55 time=1.79 ms
```

### 9. HTTPS通信をサポートしてみる
今回はApacheの80番ポートをELBに443ポートに転送することでHTTPS通信に対応させる。

1. 自己証明書の作成
    2. 適当なインスタンスにログインする  
       今回はWebサーバのインスタンスを流用しました。

    3. 秘密鍵を作る

        ```bash:秘密鍵の作成
        openssl genrsa 2048 > private-key.pem
        ```

    3. 証明書署名要求(CSR)ファイルを作る

        ```bash:CSRファイルの作成
        openssl req -new -key private-key.pem > private-key.csr
        ```
     対話形式で証明書に必要な情報を入力していくが、自己証明書かつ今回はテストなのでEnter空打ちを繰り返す。

    3. 自己署名して証明書を作る

         ```bash:証明書の作成
         openssl x509 -req -signkey private-key.pem < private-key.csr > private-key.crt 
         ```
     証明書の有効期限を指定したい場合は、
     `openssl x509 -days 3650` のように、**日数**を指定してください。

2. ELBの設定変更
    3. EC2ダッシュボードを開き、左側のメニューから「ロードバランサー」をクリック
    4. 5.で作成したELBを選択し、「アクション」→「リスナーの編集」をクリック
    5. 「追加」をクリック
    6. プロトコルに`HTTPS（セキュアHTTP）`を選択
    6. ポートはさわらない
    6. SSL証明書と書かれた列の「変更」をクリック

        > 1. 証明書タイプに`AWS Identify and Access Management(IAM)に、新規のSSL証明書をアップロードする`を選択
        > 1. 証明書の名前は、適当に入力する
        > 1. プライベートキーには、前の章で作成したpemファイルの中身を貼りつける  
             （catしてコピー、貼り付けでよいと思う）
        > 1. パブリックキー証明書には、前の章で作成したcrtファイルの中身を貼りつける  
    6. 「保存」をクリック
    7. 「保存」をクリック
       > もしうまく追加できない場合は、「5. Elastic Load Balancerの構築」の章で設定した(LB)80→(EC2)80の設定を、(LB)443→(EC2)80に変更して、上記設定をやってみてください。
その後、もともとあった(LB)80→(EC2)80の設定を、再度追加しておきましょう。

2. （ELBのセキュリティグループの設定変更
HTTPしか許可していないので、HTTPSも許可するように変更する。

3. 動作確認
`https://<ELBのDNS名>/` にアクセスする。
自己証明書なので警告が出るが、続行する。
hello! と表示されれば成功。

## 用語集
* VPC  
Virtual Private Cloudの略。AWSの中に仮想的にネットワークを持つことができ、他の仮想ネットワークとは切り離されている。
VPCの中にEC2インスタンスなどの、AWSで提供されている各リソースを作成することができる。
Virtual PCのことではない。
* サブネット  
大きなネットワークを複数の小さなネットワークに分割して使う際の、その小さなネットワークのこと。
また、ネットワークを分割することを「サブネッティング」という。
* リージョン  
AWSの各データセンターが置かれている場所。
東京やソウル、シンガポール、北カリフォルニアなど。
* アベイラビリティゾーン(AZ)  
リージョンの中にある複数の区切られた場所。同じリージョンの中にあるアベイラビリティゾーン同士は低コスト・低レイテンシでネットワーク接続できる。
* AMI  
Amazon Machine Imageの略。
ソフトウェア構成 (オペレーティングシステム、アプリケーションサーバー、アプリケーションなど) を記録したテンプレートのこと。
* インスタンス  
AMIをコピーして作成された仮想サーバ。
* 踏み台サーバ  
サービスを提供するサーバに直接SSH接続する（できる）構成は望ましくないため、一度踏み台サーバに接続し、そこからSSHで対象のサーバに接続する。  
英語だとBastion。Springboardだと踏まれて攻撃に使われる方の踏み台サーバになってしまう。

## 参考にした記事・サイト
- AWSで最低限セキュアな構成を組む  
http://qiita.com/ausuited/items/09b626fa5264f0c650fd
- AWSでSSL導入 - MyController  
http://hiromode.hatenablog.com/entry/2016/04/27/210201
- Amazon Virtual Private Cloud ユーザーガイド  
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/