Go 言語最新ツールチェインをビルドして、Caddy で TLS 1.3 を試す

## 概要

TLS 1.3 は、2018 年 8 月 に RFC 8446 として、正式リリースとなりました。

OpenSSL 1.1.1 で、TLS 1.3 は正式サポートとなり、各種ソフトウェアでも実装が進むものと思われます。

Golang でも、最近 TLS 1.3 サポートが、master ブランチへマージされたようです。

Golang の次のリリースは、v1.12 が 2019 年 2 月 の見込みです。Golang では、ナイトリービルドを提供していないみたいですが、処理系のビルドはそれほど難しくないので、最新の master ブランチをビルドして、TLS 1.3 を試用してみましょう。

ビルドには、docker を使用し、試用するサーバーソフトウェアとしては、Golang で実装された HTTP サーバーである Caddy を用います。

## Golang 最新版ビルド

通常であれば、Dockerfile を作成して、イメージを作成するところですが、お試しということで、volume を活用して、ビルド成果物を作成します。

docker を使用できる環境で、以下のようにコマンドを打ち込みます。

```
$ docker volume create --name v-go
$ docker run -it --rm -v v-go:/root/go -w /root/go -e GOROOT_BOOTSTRAP=/usr/local/go golang:1.11

# git clone https://go.googlesource.com/go .
# cd src
# ./all.bash
# exit
```

v-go という名前の volume に最新版の Golang がビルドされます。

## Caddy ビルド

先ほどビルドした、ツールチェインを用いて、Caddy をビルドします。

```
$ docker volume create --name v-caddy
$ docker run -it --rm -v v-go:/usr/local/go -v v-caddy:/go golang:1.11

# go get github.com/caddyserver/builds
# cd src/github.com
# mkdir mholt
# cd mholt
# git clone https://github.com/mholt/caddy.git
# cd caddy
# git checkout -b v0.11.1 v0.11.1
# cd caddy
# go run build.go
# exit
```

v-caddy 内部に、Caddy がビルドされます。

次のようなコマンドで、ビルド済ファイルを取り出すことができます。

```
$ docker create --name v-caddy -v v-caddy:/go busybox
$ docker cp v-caddy:/go/src/github.com/mholt/caddy/caddy/caddy .
```

こちらのバイナリーは、TLS 1.2 までのサポートになるバージョンになりますので、`caddy-tls1.2` 等へリネームしておきます。

## パッチあて

先ほどビルドした Caddy は、TLS 1.3 対応になっていませんので、対応させるパッチを実施します。

golang:1.11 のイメージに patch コマンドが入っていないので、buildpack-deps:stretch のイメージを使用します。

```
$ docker run -it --rm -v v-caddy:/go -w /go/src/github.com/mholt/caddy buildpack-deps:stretch

# curl -LO https://gist.github.com/noumia/ad9787f4c8094976221e0b3419ecafb2/raw/1f97a61215c0b31d829e4c102c420382b4c12149/caddy.patch
# patch -p1 < caddy.patch
# exit
```

## 再ビルド

パッチ後の再ビルドと、バイナリーファイル抽出は、以下のようになります。

```
$ docker run -it --rm -v v-go:/usr/local/go -v v-caddy:/go -w /go/src/github.com/mholt/caddy/caddy golang:1.11

# go run build.go
# exit

$ docker cp v-caddy:/go/src/github.com/mholt/caddy/caddy/caddy .
```

## 動作チェック

任意のディレクトリーへ、Caddyfile ファイル、秘密鍵、証明書ファイル等を配置します。

```text:Caddyfile
https://loop.example.com {
  tls cert/server.crt cert/server.key
  root html
}
```

`cert/server.crt` へ証明書
`cert/server.key` へ秘密鍵
`html/index.html` へ配信ファイル

を展開します。

`example.com` は例示ドメインなので、ご自身で使用するドメインに読み替えてください。

ここでは、`*.example.com` に充当するワイルドカード証明書を用意しました。

ワイルドカード証明書は、有効なドメインをお持ちであれば、Let's Encrypt で無料で取得できます。

Caddyfile のあるディレクトリーをカレントディレクトリーとして、`caddy` コマンドを起動します。

```
$ sudo ./caddy
```

## クライアントでの接続チェック

別ターミナルを立ち上げて、通信チェックを行います。ここでは、OpenSSL 1.1.1 をリンクした TLS 1.3 対応の curl を使います。

```
>curl --version
curl 7.62.0 (x86_64-pc-win32) libcurl/7.62.0 OpenSSL/1.1.1a (WinSSL) zlib/1.2.11 brotli/1.0.7 WinIDN libssh2/1.8.0 nghttp2/1.35.0
Release-Date: 2018-10-31
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile SSPI Kerberos SPNEGO NTLM SSL libz brotli TLS-SRP HTTP2 HTTPS-proxy MultiSSL
```

接続します。

```
>curl -v https://loop.example.com
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to loop.example.com (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: C:\Sirius\.bin\curl-ca-bundle.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, [no content] (0):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=example.com
*  start date: Nov 20 05:17:39 2018 GMT
*  expire date: Feb 18 05:17:39 2019 GMT
*  subjectAltName: host "loop.example.com" matched cert's "*.example.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* Using Stream ID: 1 (easy handle 0x22452c620a0)
* TLSv1.3 (OUT), TLS app data, [no content] (0):
> GET / HTTP/2
> Host: loop.example.com
> User-Agent: curl/7.62.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS app data, [no content] (0):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (IN), TLS app data, [no content] (0):
* TLSv1.3 (IN), TLS app data, [no content] (0):
* TLSv1.3 (IN), TLS app data, [no content] (0):
< HTTP/2 200
< accept-ranges: bytes
< content-type: text/html; charset=utf-8
< etag: "piucxp0"
< last-modified: Tue, 27 Nov 2018 07:32:13 GMT
< server: Caddy
< content-length: 0
< date: Tue, 27 Nov 2018 07:45:10 GMT
<
* Connection #0 to host loop.example.com left intact
```

TLSv1.3 というログが見えているので、うまくいったようです。

比較のために、TLS 1.3 対応パッチをあてる前のバイナリーを実行したときの接続ログは、次のようになります。

```
>curl -v https://loop.example.com
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to loop.example.com (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: C:\Sirius\.bin\curl-ca-bundle.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=example.com
*  start date: Nov 20 05:17:39 2018 GMT
*  expire date: Feb 18 05:17:39 2019 GMT
*  subjectAltName: host "loop.example.com" matched cert's "*.example.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x1c82c5d20a0)
> GET / HTTP/2
> Host: loop.example.com
> User-Agent: curl/7.62.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< accept-ranges: bytes
< content-type: text/html; charset=utf-8
< etag: "piucxp0"
< last-modified: Tue, 27 Nov 2018 07:32:13 GMT
< server: Caddy
< content-length: 0
< date: Tue, 27 Nov 2018 07:44:39 GMT
<
* Connection #0 to host loop.example.com left intact

```

TLS 1.2 にダウングレードして接続されています。