【ほぼ無料】FreenomとLet's Encryptを使って0円でドメインとSSL証明書を手に入れて、AWS EC2にdockerを入れてnginxコンテナを起動してドメインで繋がるHTTPS WEBサービスを立ち上げる方法

# 経緯
何か業務で使う技術や新しい技術を試したいときにAWS上にWebサーバーを立ててAPIとかテストページとか用意して、ローカルのChromeやAndroid端末からhttpで繋いでごにょごにょやるってのを結構やる。
RestAPIの疎通確認ぐらいならそれでいいんだけど、WebRTCやWebSocketの検証で使いたい場合、SSL化されたサイトでないとエラーになって接続できないため動作確認が出来ない問題があった。
どうにかこうにか安価でオレオレ証明書じゃないWebサーバーを立てられないかなーと調べたところ、FreenomとLet's Encryptを使えばほぼ無料で独自ドメインのhttpsサーバーを立てられそうだと判明。

Freenomでのドメイン取得方法やLet's EncryptでのSSL証明書取得方法、それらをAWSで設定するやり方等は探せば断片的にはあるけど、全部まとまった記事は無かったので、備忘録もかねてここに残しておく。

# やること
1. 【無料】Freenomで無料ドメインを取得する
1. 【条件付無料】AWS EC2インスタンスを作成してdockerをインストールする
1. 【条件付無料】AWS Elastic IPで静的IPアドレスを発行してEC2インスタンスに関連付ける
1. 【ほぼ無料】AWS Route53でDNS設定を行いドメイン名でEC2インスタンスに繋がるようにする
1. 【無料】dockerでEC2インスタンスにnginxコンテナを立ち上げる
1. 【無料】Let's Encryptで無料SSL証明書を取得してhttpsで繋がるようにする

# 無料の条件とほぼ無料の詳細
- AWS EC2インスタンスは無料利用枠を使用する
    - 無料利用枠と記載のあるAmazonマシンイメージ（AMI）は月に750時間無料で使える。今回はそれを使うことで無料とする
- ElasticIPで割り当てたEC2インスタンスは起動したままにしておく
    - ElasticIPで発行したIPアドレスがEC2インスタンスに関連付けされていない場合は課金が発生する。いくら課金されるかは調べていないがわずかであるらしい。こういう仕組みになっているのは無駄にIPアドレスを確保されるのを防ぐためだろう。
- AWS Route53では月に0.5$課金される
    - 下記は公式（2020年11月現在）から抜粋してきたもの。まあこのくらいなら缶コーヒー半分くらいだし、いいよね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/6a534006-897f-2e2d-c3c2-f40fbf252b26.png)

# 1. 【無料】Freenomで無料ドメインを取得する
早速やっていきます。ドメインはFreenomというサービスを使って取得します。Freenomとは、ドメインを無料で取得出来る海外のサービスです。ドメイン名は、XXXX.tkやXXXX.ml、XXXX.ga等が無料で取得できます。
https://www.freenom.com/ja/index.html

### ドメインが使えることを確認し、チェックアウトする

好きなドメインを取得することが出来ますので、取得したいドメインを入力しましょう。利用可能か調べてくれて利用可能であれば以下のような画面になりますので「今すぐ入手!」を押下した後、「チェックアウト」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/96e9bf9a-5207-a06b-a235-f0098cb4a496.png)

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/f76c7b14-cc76-3306-5c9a-eed458187931.png)

### 使用期間を選択する

12ヵ月まで無料で使えるようですので、最大限使用させていただきましょう。Periodで「12 Months @ FREE」を選択して「Continue」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/50e8a50a-b5db-10ff-85eb-659cee707be2.png)

### ユーザー登録 or ログイン
**金額が$0.00USD(無料)なのを確認**します。問題なければ購入のためログインします。
私の場合は既に登録済みなので、普通にGoogleでログインします。
初回登録してないと、いろいろ登録しないといけないかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/7d8969d4-bccc-34e8-fc2c-1b5613790b7e.png)

### 規約に同意して購入

再度、**金額が$0.00USD(無料)なのを確認**してから、規約に同意のチェックを入れ、「Complete Order」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/d8dfa703-a748-5082-dd92-8c2f13a0957b.png)

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/0dd79598-70b6-51e6-906b-f8737e908481.png)

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/6500cfda-d975-8920-8279-629fe796966c.png)

これにてドメインの取得は完了しました。

# 2. 【条件付無料】AWS EC2インスタンスを作成してdockerをインストールする
こちらは私が書いた別の記事がそのまま使用できますので、こちら↓を参照ください。
>[AWS EC2インスタンスにdockerとdocker-composeをインストールして簡単なWEBサービスを立ち上げる方法](https://qiita.com/y-do/items/e127211b32296d65803a)

# 3. AWS Elastic IPで静的IPアドレスを発行してEC2インスタンスに関連付ける
静的IPアドレス（固定IPアドレス）を取得し、上記で起動したEC2インスタンスにそのIPアドレスを割り当てます。こうすることで、EC2を再起動したりしてもIPアドレスが変わることは無くなります。

### 静的IPアドレスを取得する
まずは画面操作して静的IPアドレスを取得します。
「Elastic IP」を押下し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/e8b48921-4150-c2a8-b408-5e8606c903d0.png)
次の画面で「Elastic IP アドレスの割り当て」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/c9698ae9-e103-25f2-4d6f-11daaa37ef74.png)
次の画面で「割り当て」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/79fdd06c-6224-271b-81f2-b01fa91347bf.png)
静的IPアドレスが取得できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/a7ef3085-b0a4-6b2a-efab-3627c431ece6.png)

### EC2インスタンスに静的IPアドレスを関連付ける
割り当てられたIPアドレスを選択状態にして「アクション」内の「Elastic IPアドレスの関連付け」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/dfbb2d19-ca04-3996-21da-29750ccd0df1.png)
インスタンスには、先ほど作成したEC2のインスタンスIDを入力します。
入力したら「関連付ける」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/10b59011-8e7d-c04f-9a09-686f4d76a2b3.png)

EC2インスタンスと静的IPアドレスの関連付けが完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/868cfbc2-4b6d-c08a-9d28-eee50120e867.png)

# 4. 【ほぼ無料】AWS Route53でDNS設定を行いドメイン名でEC2インスタンスに繋がるようにする
ドメイン名で繋がるようにDNS設定を行います。
ホストゾーン作成→Aレコード登録→FreenomでNameserver設定という手順になります。

### ホストゾーンの作成
Route53の画面に行き、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/f0cae3ed-e331-4965-8ed4-d8a990ce5090.png)
「ホストゾーン」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/95f50736-0f55-fd49-a9a7-d270934af5cc.png)
「ホストゾーンの作成」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/16b831ce-0239-112a-2859-db3a26e0cb12.png)
Freenomで取得したドメインを入力して「ホストゾーンの作成」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/62220274-41ce-73b4-8b66-52ee7c1cc959.png)
ホストゾーンの作成が完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/ebbf9c73-1d0e-206e-eb93-9b22c407220f.png)

### Aレコードの作成
「レコードの作成」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/ce8c7012-67bd-24ae-bd37-cec28d5446da.png)
シンプルルーティングがデフォルト選択されているのを確認して「次へ」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/c8dadf1d-6cb0-92d2-2e10-b81a41853ce6.png)
「シンプルなレコードを定義」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/15bc1cdd-dc6d-7512-8789-a2f82299505d.png)
値/トラフィックのルーティング先で「レコードタイプに応じたIPアドレスまたは別の値」を選択した後、先ほど取得した静的IPアドレスを入力して、「シンプルなレコードを定義」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/12ccb29e-32cb-281e-69d2-b93167359df7.png)
レコードの作成を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/1798915c-6f5b-3f8d-474b-bae876bd68d9.png)
レコードの作成が完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/098c0bea-ada3-0e7b-0960-7edff13ad45d.png)

### Nameserverの設定

今度はFreenomでNameserverの設定を行います。

「My Domains」を押下し、次の画面で「Manage Domain」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/6a3cf151-7d9d-6298-fff2-a3647712f973.png)

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/62403e63-e6ab-3156-605b-197d9ffb3d79.png)
「Management Tools」の「Nameservers」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/90b2eb03-c118-caf9-61d4-9ba7d6969f54.png)
「Use custom nameservers(enter below)」を選択、Nameserver欄にはRoute53 ホストゾーンのNSレコードの値をコピペします。
入力したら「Change Nameservers」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/21f897de-ac98-3f2e-b97a-2957e56ed1f8.png)

この時点で数分経過すると、DNSが浸透して行って「http://ドメイン名」でサイトが表示されるようになっているかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/3736f837-80a3-25ab-c3b6-bdb19766621d.png)

# 5. 【無料】dockerでEC2インスタンスにnginxコンテナを立ち上げる
# 6. 【無料】Let's Encryptで無料SSL証明書を取得してhttpsで繋がるようにする

5と6は一気にやっちゃいます。
いろいろやり方はあるかと思いますが、以下構成でdockerを使用してやってみます。

- reverse-proxyコンテナをインターネットに公開し、https通信を受け付けます。
- reverse-proxyコンテナからwebコンテナへ通信を転送、webコンテナではreverse-proxyからhttp通信を受け付けます。

![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/f11ff835-3b28-ecb4-f494-6273edf6e6d3.png)

### 全てのdockerを停止しておく
誤動作防止のため、作業前に一度全てのdockerコンテナを止めて、dockerオブジェクトも全部削除しておくといいでしょう。

```bash
docker stop $(docker ps -q)
docker system prune -a
```

### webコンテナの作成と起動

homeディレクトリ配下にwebディレクトリを作成し、必要な構成ファイルを作成します。

```bash
cd
mkdir web
```

ディレクトリ配下とファイルの内容は以下のような構成にしました。

```:ディレクトリ構成
web
  - html
      - index.html # hogeとでも書いておく
  - docker-compose.yml
```

```yml:docker-compose.yml
version: '3'

services:
  web:
    image: nginx:latest
    container_name: web
    volumes:
      - ./html:/usr/share/nginx/html
```

作成が終わったらdockerコンテナを起動します。

```bash
docker-compose up -d --build
```

起動したことを確認します。

```bash
docker-compose ps
Name              Command               State   Ports
------------------------------------------------------
web    /docker-entrypoint.sh ngin ...   Up      80/tcp
```

### reverse-proxyコンテナの作成と起動

homeディレクトリ配下にreverse-proxyディレクトリを作成し、必要な構成ファイルを作成します。

```bash
cd
mkdir reverse-proxy
```

ディレクトリ配下とファイルの内容は、悪戦苦闘の結果、以下のような構成にしました。

```:ディレクトリ構成
reverse-proxy
  - reverse-proxy
    - default.conf
    - Dockerfile
    - entrypoint.sh
  - docker-compose.yml
```

```conf:default.conf
server{

    server_name y-do.tk;

    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
      proxy_pass     http://web/;
    }

}
```

```dockerfile:Dockerfile
FROM nginx

COPY default.conf /etc/nginx/conf.d/default.conf

RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

RUN apt-get update && apt-get install -y \
  wget cron && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/*

ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /usr/local/bin/wait-for-it.sh
RUN chmod +x /usr/local/bin/wait-for-it.sh

ADD https://dl.eff.org/certbot-auto /usr/local/bin/certbot-auto
RUN chmod a+x /usr/local/bin/certbot-auto
RUN certbot-auto --os-packages-only -n

COPY ./entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

ENTRYPOINT ["entrypoint.sh"]
```

```bash:entrypoint.sh
#!/bin/bash

# hogehoge@example.comはメールアドレス
certbot-auto --nginx -d y-do.tk -m hogehoge@example.com --agree-tos -n
certbot-auto renew

# cron job settings
# Let's Encrypt automatic Renew
echo '0 8 * * * certbot-auto renew --post-hook "nginx -s reload"' >> /cron-tmpfile
crontab /cron-tmpfile
rm /cron-tmpfile

# cron start
/etc/init.d/cron start

/bin/bash
```

```yml:docker-compose.yml
version: '3'

services:
  reverse-proxy:
    build: ./reverse-proxy
    tty: true
    container_name: reverse-proxy
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - '/srv/letsencrypt:/etc/letsencrypt'
    command: ["wait-for-it.sh", "web:80"]
    networks:
      - default
      - web_default

networks:
  web_default:
    external: true
```

作成が終わったらdockerコンテナを起動します。

```bash
docker-compose up -d --build
```

起動したことを確認します。

```bash
docker-compose ps
    Name                   Command               State                    Ports
-------------------------------------------------------------------------------------------------
reverse-proxy   entrypoint.sh wait-for-it. ...   Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
```

### インターネットからhttpsで繋がるようにAWSでセキュリティグループを更新する

FWで弾かれているため、まだインターネットからhttps通信を受け付けることが出来ません。
AWSのセキュリティグループのインバウンドルールを修正します。

ルールを追加して、タイプ「HTTPS」、ソースは「0.0.0.0/0」を入力します。
入力したら「ルールを保存」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/708df803-e03c-751a-e7cf-adaa94bf4873.png)

# 動作確認

ここまでやってやっとhttpsで通信が出来るようになっているはずです。アクセスしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/b7051bfb-c736-ddc6-78a1-c5be2ef62b31.png)

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273110/c68e7d1f-a317-57f8-96e4-a28b99f3e903.png)

アクセス出来た！！！

# 結果
月額0.5USDで独自ドメインのSSLサイトが運用できる。
ただし、ドメインは12ヵ月、SSL証明書は3か月で有効期限が切れる（無料だから文句は言えない）ので、今後は自動更新の仕組みを導入したい