Let's Encryptの更新はHTTPで受けなければならない


初めに

備忘録です。
諸事情でCloudFront無しの状態でletsencryptの更新を受けていたときは
そもそもしょっぱなHTTPSへの強制転送がなかったので成功していましたが、

改めて構成が変わり更新の時期になった際にletsencrypt側からのアクセスが403になっていたので、
解決までの簡単なフローをメモしておきます。
多分似たような記事はあると思います。

環境

  • CloudFrontを経由してALBを利用せずにEC2に転送する構成
    • 費用抑えるためなのでこんな構成になってます
      • 冗長性無し!本番と構成が違う!(本番はALBがあります)
    • HTTP->HTTPSのリダイレクト側はCloudFront側で対応
  • Nginxを利用している
  • IP制限をNginx側でかけている
  • 一発目の証明書の更新は以下。{{}}の中はそれぞれドメインとメールアドレスが入ります
certbot certonly --nginx --non-interactive -d {{item.server_name}} -m {{item.email}} --agree-tos -n

原因

どうやって認証をとっているかの仕組みを把握できていなかったことが原因です。

certbot renew --dry-run --verbose をしてみた結果、
以下のようなログが存在しました。

Writing nginx conf tree to /etc/nginx/conf.d/xxxxxx.conf:
server {rewrite ^(/.well-known/acme-challenge/.*) $1 break; # managed by Certbot


    listen       80;
    listen       [::]:80;
    (中略)

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
location = /.well-known/acme-challenge/xxxxxxxxxxxxxxx{default_type text/plain;return 200 xxxxxxxxxxxxxxxxxx;} # managed by Certbot

}

letsencryptは相手側からこちらのサーバにアクセスすることは知っていました。

ただそれはどこかに一時的なファイルを作っていると思ってたですが、
どうやらアクセスの返却値として固定で文字列を返すような変更をconfファイルにしているみたいです。
なのでHTTPSが使える状態であっても必ずHTTPで受けないといけないみたいです。

ログをもう少し見てると最初の方でCreating backup of xxxxxとあり、
戻すために?ファイルのバックアップをとってるの思ったより原始的で可愛い。

解消

リダイレクトはCloudFront側で行なっているため、
CloudFrontの設定として.well-known配下のファイルはHTTPSに強制転送しないようにします。
CloudFormationでCacheBehaviorsの最上部に.well-known/*の設定を追加します。

        CacheBehaviors:
          - TargetOriginId: !Ref WebEc2
            AllowedMethods:
              - GET
              - HEAD
            Compress: True
            DefaultTTL: 0
            ForwardedValues:
              Headers:
                - Host
                - Referer
                - User-agent
               QueryString: False
            PathPattern: ".well-known/*"
            ViewerProtocolPolicy: allow-all

また、nginx側でIP制限をかけるために以下のような設定をserverディレクティブ直下でやっているのですが、
これはそのままでも繋がりました(記載の順番?何かのキャッシュ?)


set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
allow xxx.xxx.xxx/32;
deny all;

無事に更新できました。

$ sudo certbot renew --dry-run
(略)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/xxxxxxxxxxxx/fullchain.pem (success)
  /etc/letsencrypt/live/yyyyyyyyyyyy/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)

$ sudo certbot renew
(省略)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/xxxxxxxxxxxx/fullchain.pem (success)
  /etc/letsencrypt/live/yyyyyyyyyyyy/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-