サーバー用証明書を作る

# はじめに
httpsで接続するための証明書をきちんと作りたかったが、思わぬところでこけていたのでその備忘録

備忘録なので読みにくかったらすみません。

環境は以下の通り

Ubuntu 22.04.2 LTS
Python 3.10.6
certbot 1.21.0

# 0. DNS

DNS設定を済ませましょう。IPv4 の A と IPv6 の AAAA を設定してください。IPv6は必須です。
また、自分の環境でIPv6が有効かどうかは以下のサイトで確認することができます。

https://test-ipv6.com/index.html.ja_JP

# 1. インストール

今回はFlaskを使って証明書を作ります。Apacheも使えますが、失敗したときにファイルを作り直すのが面倒なのでこっちの方が簡単に修正できるのでこっちがいいです。必要によってユーザを切り替えてください。

```
sudo apt install python3
sudo apt install python3-pip
pip3 install Flask

sudo apt install certbot
```

# 2. ポート類

ルーターで HTTP 80番 ポートと HTTPS 443番 を開けておきましょう。また、`IPv6` は有効にしておいてください。`6to4` でも大丈夫みたいです。

# 3. Flask

任意のディレクトリに行って、以下のファイルを作ります

```python
# main.py

from flask import Flask
app = Flask(__name__)

@app.route('/.well-known/acme-challenge/AAAAAAAAAAA')
def well_known1():
    return 'BBBBBBBBBB.BBBBBBBBBBB'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80, threaded=True, debug=True)
    # app.run(host='0.0.0.0', port=443, ssl_context=('fullchain.pem', 'privkey.pem'), threaded=True, debug=True)
```

```bash
# run.sh

#!/bin/sh
python3 main.py
```

同じディレクトリ内に作成して保存します。
AとBは後で編集します。


# 4. 証明書作成

ここから生成します。

```sh
sudo certbot certonly --manual
```

を実行して、進めます。途中でURIを入力します。

以下みたいなやつが出たら、CのキーとDのキーをそれぞれ上のPythonコードに書き換えます。
`Press Enter to Continue` から終了しないでください。
以下のようにしてください
```
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

And make it available on your web server at this URL:

http://example.com/.well-known/acme-challenge/DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
```
↑コマンドで出力された文字列
↓のように変更

```python
# main.py

from flask import Flask
app = Flask(__name__)

@app.route('/.well-known/acme-challenge/DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD')
def well_known1():
    return 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80, threaded=True, debug=True)
    # app.run(host='0.0.0.0', port=443, ssl_context=('fullchain.pem', 'privkey.pem'), threaded=True, debug=True)
```

変更したら起動します。

```
sudo ./run.sh
```

起動したら一応ブラウザからアクセスできることを確認します。
`http://example.com/.well-known/acme-challenge/DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD`
のようなリンクがあるのでそれにアクセスできるかどうかをチェックします。
ブラウザで開くと`CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC`が表示されます

ここまで出来たら、さっきの`Press Enter to Continue`をEnterを押して進めます。

# 5. 移動と確認

下記のように出力された場所が出力されるので、`mv`とか`chmod 777`とか`sudo`を駆使して救出します。
```
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 0000-00-00.
These files will be updated when the certificate renews.
```

Pythonで書いたコードのところまで持ってきたら、コメントアウトを外して、もともとあった実行文と書き換えます。

```python
# main.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def well_known1():
    return 'responce test'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=443, ssl_context=('fullchain.pem', 'privkey.pem'), threaded=True, debug=True)
```

これでリンクにアクセスして、ちゃんと認証が通っていれば成功です。お疲れ様でした。


# どこで躓いたか

IPv6ですね。。。ほかのところをまんべんなくチェックしても原因が分からず、絶対にエラーで出力できない事態に陥って５時間くらい格闘してやっとReferenceのサイトを見つけて、対応するルータに変更したらあっさりできました。；；


# Reference

https://community.letsencrypt.org/t/cannot-obtain-certificate-404-error/105892/3