Google Compute EngineのUbuntuサーバにLet’s Encryptの証明書を入れる

GCEの無料枠で1台動かしているサーバを作り直したのでLets encryptを入れる。
以前はcertbotというのを使ったが、今回はlegoというGo実装のCLIでやってみた。
しかしこれをやった後、ブログ記事として整理しようとあっためてる間にGoogle Domains終了のお知らせが出るとは。
さっさと投稿しろということか…

まずlegoググるACMEクライアントのHTTP-01 challengeを使うのがよく出てくるが、設定したいのはdevドメインなのでこのやり方は上手くいきません。httpsの設定をこれからやろうというのにhttpsじゃないとアクセス出来ないドメインなんだから。

そこでDNS-01 challengeを使ってみます。
DNSでTXTレコードに特定の文字列を入れて認証するというもののようです。

letsencrypt.org

私のdevドメインGoogle Domainsで管理しています。
legoGoogle Domainsはサポートされているようです。

go-acme.github.io

さっそくやってみます。
まずは GOOGLE_DOMAINS_ACCESS_TOKEN を作ります。
Google Domainsのセキュリティ開いたらACME DNS APIという項目があるの「トークンを作成」から作れます。簡単ですね!

apt でいれたlegoでやってみる。

GOOGLE_DOMAINS_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx== lego --email foo@example.com --dns googledomains --domains www.example.dev run

2023/06/04 10:26:10 unrecognized DNS provider: googledomains

あれあれ?

# apt info lego
Package: lego
Version: 4.1.3-3ubuntu1.22.04.1

なるほど。バージョンを満たしてないのか。
Configuration for Google Domains.

  • Code: googledomains
  • Since: v4.11.0

しかたないソースから入れるか。

go-acme.github.io

要件は以下の通り。

    go1.17+
    environment variable: GO111MODULE=on
$ sudo apt remove lego
$ sudo apt install golang-go

$ go version
go version go1.18.1 linux/amd64

$ go install github.com/go-acme/lego/v4/cmd/lego@latest
(snip)
# github.com/go-acme/lego/v4/providers/dns/arvancloud/internal
go/pkg/mod/github.com/go-acme/lego/v4@v4.12.0/providers/dns/arvancloud/internal/client.go:59:24: c.baseURL.JoinPath undefined (type *url.URL has no field or method JoinPath)
go/pkg/mod/github.com/go-acme/lego/v4@v4.12.0/providers/dns/arvancloud/internal/client.go:84:24: c.baseURL.JoinPath undefined (type *url.URL has no field or method JoinPath)
go/pkg/mod/github.com/go-acme/lego/v4@v4.12.0/providers/dns/arvancloud/internal/client.go:103:24: c.baseURL.JoinPath undefined (type *url.URL has no field or method JoinPath)
note: module requires Go 1.19
# github.com/go-acme/lego/v4/providers/dns/autodns/internal
go/pkg/mod/github.com/go-acme/lego/v4@v4.12.0/providers/dns/autodns/internal/client.go:63:24: c.BaseURL.JoinPath undefined (type *url.URL has no field or method JoinPath)
note: module requires Go 1.19
(snip)

最新バージョンだと新しいGoじゃないとダメっぽい。
Goも手動で入れるか。

$ cd /usr/local/src
$ sudo wget https://go.dev/dl/go1.20.4.linux-amd64.tar.gz
$ sudo tar xvfz go1.20.4.linux-amd64.tar.gz
$ sudo mv go ..  
$ sudo vi /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin"
(/usr/local/go/binを追加)

$ go version
go version go1.20.4 linux/amd64

もう一度インストール。

$ go install github.com/go-acme/lego/v4/cmd/lego@latest

何回やってもフリーズしてしまう。
2日くらいほっといたらCPU負荷は下がったけど上手くいってなかった…
仕方が無いので、ソースから入れよう。

$ sudo apt install git make
$ mkdir ~/go/src/github.com/go-acme
$ cd ~/go/src/github.com/go-acme
$ git clone https://github.com/go-acme/lego.git
$ cd lego
$ make

これもダメ。

もーあかん。Macでクロスコンパイルすることにするよ。
Goはこの辺が簡単でいいよね。

一応環境確認。

$ go env
GOARCH="amd64"
GOOS="linux"

こっからは自分のMacで作業。
ロスコンパイルのために確認した環境変数をセットしてビルドする。

$ git clone git@github.com:go-acme/lego.git
$ cd lego
$ export GOARCH="amd64"
$ export GOOS="linux"
$ make
BIN_OUTPUT: dist/lego
rm -rf dist/ builds/ cover.out
go generate ./...
go: downloading github.com/go-jose/go-jose/v3 v3.0.0
(snip)
fork/exec /var/folders/g_/xzlp1_r95r97tw7bxs1d5vs00000gn/T/go-build1172054503/b001/exe/dnsdocs: exec format error
internal/dnsdocs/generator.go:3: running "go": exit status 1
fork/exec /var/folders/g_/xzlp1_r95r97tw7bxs1d5vs00000gn/T/go-build1101727331/b001/exe/cli_help: exec format error
internal/dnsdocs/cli_help/generator.go:3: running "go": exit status 1
make: *** [generate-dns] Error 1

ありゃ?ドキュメント作ろうとしてエラーになってる?
Makefile見たら make build でビルドだけ動くということらしい。

$ make build
BIN_OUTPUT: dist/lego
rm -rf dist/ builds/ cover.out
Version: 60d2b55dd801c7f92c51b1624f348728b014c782
$ go build -trimpath -ldflags '-X "main.version=60d2b55dd801c7f92c51b1624f348728b014c782"' -o  dist/lego ./cmd/lego/

とりあえず出来た。
サーバに送りこんで使ってみよう。

$ scp -p  dist/lego foo@gce-host:/home/foo/.
$ ssh gce-host
$ sudo mv lego /usr/local/bin/.

今度は上手くいくかなー?
(※ここからはまたGCEのサーバで作業)

# GOOGLE_DOMAINS_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx== lego --email foo@example.com --dns googledomains --domains www.example.dev run

2023/06/08 14:22:38 No key found for account foo@example.com. Generating a P256 key.
2023/06/08 14:22:38 Saved key to /home/foo/.lego/accounts/acme-v02.api.letsencrypt.org/foo@example.com/keys/foo@example.com.key
2023/06/08 14:22:39 Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf
Do you accept the TOS? Y/n
Y
2023/06/08 14:22:42 [INFO] acme: Registering account for foo@example.com
!!!! HEADS UP !!!!

Your account credentials have been saved in your Let's Encrypt
configuration directory at "/home/foo/.lego/accounts".

You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
2023/06/08 14:22:43 [INFO] [www.example.dev] acme: Obtaining bundled SAN certificate
2023/06/08 14:22:43 [INFO] [www.example.dev] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/235040568277
2023/06/08 14:22:43 [INFO] [www.example.dev] acme: Could not find solver for: tls-alpn-01
2023/06/08 14:22:43 [INFO] [www.example.dev] acme: Could not find solver for: http-01
2023/06/08 14:22:43 [INFO] [www.example.dev] acme: use dns-01 solver
2023/06/08 14:22:43 [INFO] [www.example.dev] acme: Preparing to solve DNS-01
2023/06/08 14:22:44 [INFO] [www.example.dev] acme: Trying to solve DNS-01
2023/06/08 14:22:44 [INFO] [www.example.dev] acme: Checking DNS record propagation using [127.0.0.53:53]
2023/06/08 14:22:46 [INFO] Wait for propagation [timeout: 2m0s, interval: 2s]
2023/06/08 14:22:53 [INFO] [www.example.dev] The server validated our request
2023/06/08 14:22:53 [INFO] [www.example.dev] acme: Cleaning DNS-01 challenge
2023/06/08 14:22:53 [INFO] [www.example.dev] acme: Validations succeeded; requesting certificates
2023/06/08 14:22:54 [INFO] [www.example.dev] Server responded with a certificate.

なんかそれっぽく出来てる。

nginxに証明書設定して、GCEのコンソールでファイヤーウォールを変更。443を許可。

$ cd /etc/nginx
$ sudo vi sites-available/hogehoge
        ssl on;
        ssl_certificate     /home/foo/.lego/certificates/www.example.dev.crt;
        ssl_certificate_key /home/foo/.lego/certificates/www.example.dev.key;
$ service nginx reload

みえたー!!!