リダイレクトサービス構築　AWS ALBパターン、Let’s Encryptパターン(複数の証明書付きネイキッドドメインを1台のサーバで管理)

#AWS ALBパターン

 * 下記のようにLet’s Encryptを使用して1台で運用しているが、ALB使用ではマネジドサービスのメリットがあるので切り替え検討。
 * 説明や操作は参考サイトへ（お世話になっております）
 * デメリット
  * ドメインごとALB作成が必要 ・・・ 冗長化やLet’s Encryptの依存性とトレードオフか

ロードバランサー -> リストー -> ルールの表示/編集
![2020-06-10 13.37.52 ap-northeast-1.console.aws.amazon.com b18979972105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249425/d41f18dc-2609-fc44-af08-5aea7d40230d.png)

 * IF
  * ホストヘッダーを選択: リダイレクト対象のドメインを指定
 * THEN
  * リダイレクト先
  * ホスト欄にリダイレクト対象のドメインを指定

 * できること例
  * http://domain.com -> https://domain.com or https://www.domain.com
  * https://domain.com -> https://domain.com or https://www.domain.com


###参考
https://dev.classmethod.jp/articles/alb-redirects/

---

#Let’s Encryptパターン
![nakeddomain.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249425/8087bc67-7582-507d-2f1f-7a40761810ee.png)


#運用をシンプルにしたい
ネイキッドドメインをwwwのサブドメインにリダイレクトしたいが、証明書設定がないと止まる。
https://domain.com -> ブラウザが怒る（証明書警告） -> https://www.domain.com

wwwのサブドメインは証明書費用かけず運用中。

 * AWSのRoute53(CNAME) + Elastic Beanstalk + CloudFront + Certificate Manager

SSL必須の時代になり、証明書付きのネイキッドドメインのケースが増えてきた。
ネイキッドドメインのDNSレコードはIPしか指定できないため、とりあえず下記のようなセットで対応していた。

 * S3リダイレクト用バケット + CloudFront + Certificate Manager

# EC2の一台に
サイトごと、二重の設定作業やコストが増えるので構成を変えてみた。
 * リダイレクト用EC2（IP指定のため）
 * サイトごとLet’s Encrypt設定（このくらいは我慢）


## 証明書はLet’s Encryptのwebrootオプションにする理由
  * ドメインオーナーではない(DNSレコード登録できない)
  * サーバ管理者である

##/etc/httpd/conf.d/domain.com.conf

```conf
<VirtualHost *:80>
    ServerName domain.com
    DocumentRoot /var/www/html/domain.com
</VirtualHost>
<VirtualHost *:443>
    ServerName domain.com:443
    DocumentRoot /var/www/html/domain.com
    SSLEngine on
    SSLProtocol all -SSLv2
    SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
    SSLCertificateFile /etc/letsencrypt/live/domain.com/cert.pem
    SSLCertificateKeyFile //etc/letsencrypt/live/domain.com/privkey.pem
    SSLCACertificateFile /etc/letsencrypt/live/domain.com/chain.pem
</VirtualHost>
```

##/var/www/html/domain.com/index.html

```html
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
しばらくお待ちください。
</body>
</html>
```

##/var/www/html/domain.com/.htaccess 
RewriteCond %{REQUEST_URI} !(^/.well-known/)　がないとwww.にリダイレクトされてしまうので発行に失敗する

```conf
<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{SERVER_PORT} 80
  RewriteCond %{HTTP_HOST} !^www.
  RewriteCond %{REQUEST_URI} !(^/\.well-known/)
  RewriteRule ^(.*)$ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{SERVER_PORT} 443
  RewriteCond %{HTTP_HOST} !^www.
  RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
```

##やっと証明書発行
 * テストドメイン: domain.com

```bash
certbot-auto certonly --webroot -w /var/www/html/domain.com -d domain.com -m infra@xxx.com --agree-tos
```

## crontab -l

```bash
0 3 * * * /usr/bin/certbot-auto renew && /sbin/service httpd reload
```

# メモ

 * テンプレートとする元ファイルから簡単追加

/var/www/html/default/.htaccess
/var/www/html/default/index.html

 * cmd

```bash
mkdir /var/www/html/{domain}

cp -aT /var/www/html/default /var/www/html/{domain}
```

# 監視
cronにより自動更新だが、たまに失敗するので監視は回しておく
[サーバー証明書有効期限監視](https://qiita.com/mkawanee/items/c392c1609f459962ec12)

#参考
 * [話題の無料SSL証明書！Let’s Encryptで証明書の取得に失敗したときに確認すること](https://sole-color-blog.com/blog/1345/)
 * [Let’s Encryptの証明書を簡単に削除する方法](https://blog.mrym.tv/2019/10/revoke-lets-encrypt-certificate/)

#追記　証明書定期更新をhookで使用時

## cron

```bash
10 3 * * * /etc/letsencrypt/renewal/aaa.bbb.info.sh
```
毎日更新をトライする

## /etc/letsencrypt/renewal/aaa.bbb.info.sh
```bash
#!/bin/sh

certbot certonly \
  -d aaa.bbb.info \
  --email infra@aaa.bbb.info \
  --agree-tos \
  --preferred-challenges dns \
  --keep-until-expiring \
  --text \
  --configurator certbot-external-auth:out \
  --certbot-external-auth:out-public-ip-logging-ok \
  --certbot-external-auth:out-handler /etc/letsencrypt/renewal-hooks/pre/aaa.bbb.info.sh

/sbin/service httpd restart
```
 * 期限内になると更新
 * --preferred-challenges dns にしたのでCNAME更新が必要
 * hookでRoute53レコードを更新

##  /etc/letsencrypt/renewal-hooks/pre/aaa.bbb.info.sh
```bash
#!/bin/sh

set -e
cmd="$1"
shift

ret=`aws sts assume-role --role-arn arn:aws:iam::xxxx427341:role/route53-register --role-session-name aws3-ec2`
export AWS_ACCESS_KEY_ID=`echo $ret | jq -r .Credentials.AccessKeyId`
export AWS_SECRET_ACCESS_KEY=`echo $ret | jq -r .Credentials.SecretAccessKey`
export AWS_SESSION_TOKEN=`echo $ret | jq -r .Credentials.SessionToken`

case "$cmd" in
    perform)
        HOSTED_ZONE_ID="/hostedzone/xxxx5NZ9H"
        FILENAME=`date "+%Y%m%d%H%M%S"`.json
        DIR=/var/log/letsencrypt/

        # 環境変数チェック
        if [ -z "$domain" ] || [ -z "$validation" ]; then
                echo "Undefined environment variable" 
                exit 1
        fi

        # 設定用jsonファイルを書き出し
        cat <<EOT > $DIR$FILENAME
{
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "_acme-challenge.$domain",
        "Type": "TXT",
        "TTL": 60,
        "ResourceRecords": [
          {
            "Value": "\"$validation\"" 
          }
        ]
      }
    }
  ]
}
EOT
        # jsonファイルをアップロードしてTXTレコードを追加
        aws route53 change-resource-record-sets --hosted-zone-id "$HOSTED_ZONE_ID" --change-batch file://$DIR$FILENAME

        # DNS反映待ち
        sleep 60
        ;;
    *)
        ;;
esac
```
 * STS（aws sts assume-role）で他アカウントのRoute53を参照できる