黄昏酒場スコアボード作成⑥～ドメインの管理

## はじめに

⑤の続きです。

## cloudflareのレジストラサービスを使ってFQDNでアクセスできるようにする

一般的にユーザはアクセスするときに `203.0.113.100` といったIPアドレスで接続するのではなく、 `hoge.example.com` といったものでアクセスする。このとき `example.com` をドメインといい、 `hoge` は `example.com` の中で管理されている `hoge` ホスト名である。さらに `hoge.example.com` を完全修飾ドメインといい、英語で `FQDN` という。
一般的にユーザは `FQDN` を使って作られたURLでアクセスすることになる。

ただしコンピュータは `FQDN` での通信はすることができず、IPアドレスで通信することしかできない。そのため、 `hoge.example.com` を指定された後に、 `hoge.example.com` に対応するIPアドレスを質問する必要がある。この作業を `名前解決` という。

名前解決には `DNS` という仕組みが取られ、DNSサーバにFQDNを聞くことでIPアドレスが得られる。
簡単に示すとこのようになる[^1]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659726/2638329f-6b9e-49a0-b8c1-516202e6baf1.png)

ここで自分は `cloudflare` という会社のドメイン登録サービスを用いて `wefma.net` というドメインを所持している。
一般的には上の画像の構成でDNSが作られるはずだがcloudflareは特殊な構成を取っていてcloudflareの所持するプロキシサーバを介して通信することになっている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659726/b6030092-8326-4d56-9e2a-dc79a2efef9c.png)

また、cloudflareはプロキシ上にセキュリティ対策として最低限のWeb Application Firewallを敷いている。[^2]これにより素人がセキュリティ対策しなくてもある程度企業に任せることができる。
しかもユーザからはサーバのIPアドレスが分からない。IPアドレスがバレると住んでる地域がざっくり特定されたりなんか嫌であるので便利である。

## HTTPSで通信できるようにする

今回実装するスコアボードサーバはパスワードを送信することになる。
パスワードは他人に覗き見られるわけにはいかないので暗号化して通信経路上で見えないようにする必要がある。この時 `HTTPS` という仕組みを使用する。
`HTTPS` を使うとそのサイトが本当にそのサイトであるということと、通信経路の暗号化が行われる。
これを理解するには公開鍵暗号と共通鍵暗号を理解しなければならない。
公開鍵暗号について、公開鍵暗号は秘密鍵と公開鍵があり、今回の場合であると秘密鍵が平文を暗号化するもの、公開鍵が暗号を平文にするものである。
共通鍵暗号は暗号化と復号化に同じ鍵を用いるものである。
公開鍵暗号の方が秘密鍵を持っている本人しか複合できないセキュアさがあるが、共通鍵暗号の方が処理が軽い。

さて、公開鍵暗号はサーバだけが知っている秘密鍵を持つことができるので信頼できる第三者に公開鍵を持たせることでそのサーバが偽物でないことを証明することができる。
この時、信頼できる第三者を認証局という。さらに第三者の持つ公開鍵を証明書という。

`HTTPS` では以下の手順で通信が行われる

1. ユーザは認証局にサーバに対応する公開鍵(証明書)を取りに行く
2. ユーザは共通鍵を生成した後サーバの公開鍵で共通鍵を暗号化
3. 暗号化された共通鍵をサーバに送信
4. 秘密鍵で共通鍵を復号
5. ユーザもサーバも共通鍵を持っているので以降の通信は共通鍵で暗号、復号する

これにより公開鍵の『秘密鍵を持っている人としか通信出来ない』という利点と共通鍵の『通信速度が軽い』というメリットが組み合わさっていい感じの通信が可能になる。
[ここ](https://www.ssl.ph/compare/ssl/datalibrary/contents06.html)のサイトがいい感じにうまくまとまっていた。

1行で要約すると、 `HTTPS` で通信を行うには認証局に頼んで証明書を取る必要がある。

## letsencryptとcloudflare

letsencryptは無料で証明書を発行してくれる最強認証局である。個人で証明書を取りたい場合は基本的にここを使う。
letsencryptとのやり取りはcertbotというツールを使用して行う。証明書の更新は `certbot renew` というコマンドで行える。
letsencryptの証明書の有効期限は3ヶ月なので最低でも3ヶ月おきに `certbot renew` を実行するようにcronに登録しておけば良い。自分は心配性なので1週間おきに更新をかけるようにした。

さて、cloudflareのプロキシ環境でcertbotを使用し証明書を取ろうとすると面倒なことが起こる。
certbotが自身で秘密鍵を作成し、 `example.wefma.net` の証明書を取りたい！とletsencryptサーバに公開鍵を渡しながら要求したとする。
この時letsencryptサーバは「本当にお前が `example.wefma.net` を所有してるんか？？」という疑問を抱く。赤の他人が自分のドメインの証明書を取れたら大変なことになる。そこら辺の犯罪者がgoogle.comの証明書を取った暁には犯罪者のサーバこそがgoogleですとなって世の中は大混乱、letsencryptの信用は地の底にまで落ちるだろう。すると各ブラウザはletsencryptの証明書を信用しなくなり、今までletsencryptを使用して証明書を取っていたサーバたちも信用されなくなってインターネットは大混乱に陥る。この世の終わりである。
そこでletsencryptサーバは「お前が本当に  `example.wefma.net` を所有しているなら`HTTP-01`チャレンジか、`DNS-01` チャレンジのどっちかは成功するよな？？」という挑戦状を叩きつけてくる。

`HTTP-01` チャレンジは example.wefma.net が指し示すサーバ上でHTTPサーバを動かしたうえで、 `http://example.wefma.net/.wellknown/acme-challenge/` というパスにletsencryptサーバから言われた合言葉(トークン)を置くというものである。
しかしcloudflareのプロキシは `.wellknown/acme-challenge/` に向かう通信をセキュリティの観点から拒絶することがある。
また、cloudflareプロキシ自体はGoogleから証明書をもらっているので「俺は証明書持ってるからHTTPSで接続しなおしてきな！」とかいう余計なことをする場合がある。letsencryptサーバ目線だと「 `example.wefma.net` の証明書欲しいって言ってきた奴がいたから `example.wefma.net` 本人に聞いてみたけどそいつ自身は証明書持ってるって言われたんやが…証明書持ってる奴が新しく証明書欲しいとかいうわけないし偽物からの要求ってコト…！？」となる。
つまりcloudflareのプロキシが余計なことをするせいでHTTP-01チャレンジは失敗に終わることが多々ある。設定次第では成功することもあるようだが一般に `DNS-01` チャレンジの方が確実に成功するのでこちらが推奨されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659726/485428c7-3358-4a6d-af75-e954042cb820.png)


`DNS-01` チャレンジは `_acme-challenge.example.wefma.net` というTXTレコードをDNSで引いた時にletsencryptサーバから与えられたトークンが書き込まれているか否かを見る、というチャレンジである。
自分の場合cloudflareからwefma.netを管轄するDNSサーバを与えられているので `_acme-challenge.example` というTXTレコードにletsencryptから与えられたトークンを書き込めばいいだけである。
また、cloudflareからはDNSの書き込み権限を持ったトークンを取得できる。このトークンをcertbotに渡してあげれば、certbotが自動でletencryptから与えられたトークンをDNSに書き込むことができ、自動でサーバの所有を自分であると証明できる。
この通信に置いてcloudflareのプロキシが登場する隙はない。よってcloudflareプロキシが悪さをせずにcertbotが動いているサーバが `example.wefma.net` を所有していると証明できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659726/15f06635-b0c6-429f-86bc-055e0416c6f2.png)


なおこの二つのチャレンジは `ACME` ( `Automatic Certificate Management Environment` )チャレンジと言われ、 RFCでまとまっているインターネット上の正式な手続きである。


## cloudflareで取れる証明書の制約

cloudflareはドメインを格安で売ってくれる代わりに基本機能だけだと通常できることが出来なくなる。
それが4LD証明書問題である。
基本機能であるとドットが3つ並んだFQDNの証明書がcloudflareのプロキシに登録できないのである。
元々自分は黄昏酒場用ページとして `alcostg.wefma.net` を取っていた。
そのため黄昏酒場スコアボードはそのドメインの支配下ということを強調するため `scoreboard.alcostg.wefma.net` とするのが適当であると考えていた。
しかし4LD証明書問題によりこのFQDNでは証明書が取れずHTTPS通信をすることができない。
金を積む、もしくはcloudflareのプロキシ機能をオフにすればこのFQDNでもHTTPS通信が可能だが、自分は貧乏なため金を払うことができないし、セキュリティ素人がプロキシ外して素っ裸でネットの世界出歩いたら何されるか分からない。
自分は泣きながら `alcostg-score.wefma.net` で証明書を取るしか無かった。
また、バックエンドのapiも `alcostg-score-api.wefma.net` にした。
IT屋さんなら気持ち悪いと思われるかもしれないがこれはしょうがないことだったので許してほしい。[^3]

## まとめ

ここまで考えたうえでようやくサービスを動かすことができた。
https://alcostg-score.wefma.net

何はともあれ自分が運用するサービスを構えることが出来た。
これは社会にとって良いことではあるが自分にとって将来への負債を抱えるということに他ならない。
サービスの改廃を行いつつ適切に運用していきたい。

## おまけ

実はcloudflareはプロキシ以外にも面白い特徴がある。それが `CNAME flatting` である。
自分は `wefma.net` というホームページを持っている。これは[github pages](https://docs.github.com/ja/pages/getting-started-with-github-pages/what-is-github-pages)というサービスを使って運営している。
github pagesは自分のサーバではなくgithub社のサーバで動いているので `wefma.net` に向かってきた通信はgithubのドメインに向け直す必要がある。つまりCNAMEレコードで『wefma.netはgithubのドメインのニックネームだよ』と指定する必要がある。
しかしここでゾーンApex問題が起きる。
自分がcloudflareから与えられているDNSサーバはwefma.netを頂点としてそこから先のサブドメインを指定できるようなものである。ここでwefma.netは頂点( `Apex` )にいるので `ゾーンApex` という。
RFCというインターネット上の規約にて「CNAMEレコードはその他の種類のレコードと共存することはできない」と書かれている。ゾーンApexは必ずSOAレコードやNSレコードなどのそのDNSサーバの情報が書かれた重要なレコードに紐ついているのでCNAMEレコードを書くことができない。これをCNAME制約という。
これを回避するのが、cloudflareが独自に提供している `CNAME flatting` である。
自分がwefma.netのCNAMEレコードを書いてgithubのドメインを紐付けようとした時、cloudflareは裏側でgithubのipアドレスを引いて裏側でwefma.netの**Aレコード**を指定する。しかしcloudflareのDNS編集ツール上ではあたかもCNAMEレコードを書いているように見えるので便利だね。というサービスである。
便利だね。みんなもcloudflareをすころう。

## 参考文献

https://ryanschiang.com/cloudflare-letsencrypt-http-01
https://tex2e.github.io/rfc-translater/html/rfc8555.html#8-3--HTTP-Challenge


[^1]:ドメインの委任やDNSキャッシュサーバといった仕組みは省略している。
[^2]:金を積めばリッチなWAFになる。
[^3]:むしろこの言い訳をするためにqiitaを書き始めたと言っても過言ではない。