legoで簡単にLet'sEncryptのSSL証明書を取得した件

Dockerで作ってGKEIngressにも紐づけてみた

tl;dr

docker run -v $PWD/lego:/.lego -it goacme/lego  --email="your@email.com" --domains="*.morifuji-is.ninja" --dns manual -k rsa2048 run

概要

自前k8sに自前のドメインをぶら下げたく調べると、GKEのマネージド証明書が使えるらしいと聞いて喜んでいたら、どうにもワイルドカード証明書は無理らしい。しかも1Ingressに1マネージド証明書なので、 ingress1つで複数の証明書をぶら下げることはできないらしい。

ということで 自前で証明書を作ることにした。

Let’sEncryptの環境を用意するのがめんどくセーなーと思ってたら、 lego というものがあるらしい。

Dockerイメージも用意されているのでさくっと使ってみた。

参考

1.起動確認

$ docker run -it goacme/lego help
NAME:
   lego - Let's Encrypt client written in Go

USAGE:
   lego [global options] command [command options] [arguments...]

VERSION:
   v3.2.0

COMMANDS:
   run      Register an account, then create and install a certificate
   revoke   Revoke a certificate
   renew    Renew a certificate
   dnshelp  Shows additional help for the '--dns' global option
   list     Display certificates and accounts information.
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --domains value, -d value    Add a domain to the process. Can be specified multiple times.
   --server value, -s value     CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v02.api.letsencrypt.org/directory")
   --accept-tos, -a             By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
   --email value, -m value      Email used for registration and recovery contact.
   --csr value, -c value        Certificate signing request filename, if an external CSR is to be used.
   --eab                        Use External Account Binding for account registration. Requires --kid and --hmac.
   --kid value                  Key identifier from External CA. Used for External Account Binding.
   --hmac value                 MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.
   --key-type value, -k value   Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "ec384")
   --filename value             (deprecated) Filename of the generated certificate.
   --path value                 Directory to use for storing the data. (default: "/.lego")
   --http                       Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.
   --http.port value            Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80")
   --http.proxy-header value    Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy. (default: "Host")
   --http.webroot value         Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge.
   --http.memcached-host value  Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.
   --tls                        Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.
   --tls.port value             Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443")
   --dns value                  Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
   --dns.disable-cp             By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.
   --dns.resolvers value        Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
   --http-timeout value         Set the HTTP timeout value to a specific value in seconds. (default: 0)
   --dns-timeout value          Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10)
   --pem                        Generate a .pem file by concatenating the .key and .crt files together.
   --cert.timeout value         Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
   --help, -h                   show help
   --version, -v                print the version

2.証明書作成

複数あるチャレンジ方法のうち今回はDNS-01を使います。 ec2とかだったらHTTP-01を選んでnginxのコンテナ立てて終わらせるんですが、k8sにいちいちあげるのが面倒だと思ったので。

DNS-01 を使う場合、ドメインの管理にGoDaddyとか Route53とかを使っていた場合は、 コマンドにSECRET_KEYを渡すと自動でチャレンジをしてくれるみたいなんですが、 GoogleDomainは未対応でしたw

なので泥臭く --dns manual で行なっています。また、 今回はワイルドカード証明書が欲しいので --domains="morifuji-is.ninja" --domains="*.morifuji-is.ninja" を指定しました。

また、チャレンジ成功後、 証明書がコンテナないの /.lego 配下に出力されるので -v $PWD/lego:/.lego -it goacme/lego を指定しています。

$ docker run -v $PWD/lego:/.lego -it goacme/lego  --email="your@email.com" --domains="morifuji-is.ninja" --domains="*.morifuji-is.ninja" --dns manual run
2019/12/12 13:26:11 No key found for account info@morifuji-is.ninja. Generating a P384 key.
2019/12/12 13:26:11 Saved key to /.lego/accounts/acme-v02.api.letsencrypt.org/info@morifuji-is.ninja/keys/info@morifuji-is.ninja.key
2019/12/12 13:26:12 Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
Do you accept the TOS? Y/n
Y
2019/12/12 13:26:16 [INFO] acme: Registering account for info@morifuji-is.ninja
!!!! HEADS UP !!!!

		Your account credentials have been saved in your Let's Encrypt
		configuration directory at "/.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.2019/12/12 13:26:16 [INFO] [morifuji-is.ninja, *.morifuji-is.ninja] acme: Obtaining bundled SAN certificate
2019/12/12 13:26:17 [INFO] [*.morifuji-is.ninja] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/1690246363
2019/12/12 13:26:17 [INFO] [morifuji-is.ninja] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/1690246364
2019/12/12 13:26:17 [INFO] [*.morifuji-is.ninja] acme: use dns-01 solver
2019/12/12 13:26:17 [INFO] [morifuji-is.ninja] acme: Could not find solver for: tls-alpn-01
2019/12/12 13:26:17 [INFO] [morifuji-is.ninja] acme: Could not find solver for: http-01
2019/12/12 13:26:17 [INFO] [morifuji-is.ninja] acme: use dns-01 solver
2019/12/12 13:26:17 [INFO] [*.morifuji-is.ninja] acme: Preparing to solve DNS-01
lego: Please create the following TXT record in your morifuji-is.ninja. zone:
_acme-challenge.morifuji-is.ninja. 120 IN TXT "zWA4q-FFTRqrzaY_SkNw0p2rJ7iD08_CR6go63Kav-c"
lego: Press 'Enter' when you are done

2019/12/12 13:27:50 [INFO] [*.morifuji-is.ninja] acme: Trying to solve DNS-01
2019/12/12 13:27:50 [INFO] [*.morifuji-is.ninja] acme: Checking DNS record propagation using [192.168.65.1:53]
2019/12/12 13:27:50 [INFO] Wait for propagation [timeout: 1m0s, interval: 2s]
2019/12/12 13:27:52 [INFO] [*.morifuji-is.ninja] The server validated our request
2019/12/12 13:27:52 [INFO] [*.morifuji-is.ninja] acme: Cleaning DNS-01 challenge
lego: You can now remove this TXT record from your morifuji-is.ninja. zone:
_acme-challenge.morifuji-is.ninja. 120 IN TXT "..."
2019/12/12 13:27:52 [INFO] sequence: wait for 1m0s
2019/12/12 13:28:52 [INFO] [morifuji-is.ninja] acme: Preparing to solve DNS-01
lego: Please create the following TXT record in your morifuji-is.ninja. zone:
_acme-challenge.morifuji-is.ninja. 120 IN TXT "uxRURNmRMQMpmK4nlerPXPA6cVrpaw3rXWDk0ctIor0"
lego: Press 'Enter' when you are done

2019/12/12 13:30:12 [INFO] [morifuji-is.ninja] acme: Trying to solve DNS-01
2019/12/12 13:30:12 [INFO] [morifuji-is.ninja] acme: Checking DNS record propagation using [192.168.65.1:53]
2019/12/12 13:30:12 [INFO] Wait for propagation [timeout: 1m0s, interval: 2s]
2019/12/12 13:30:17 [INFO] [morifuji-is.ninja] The server validated our request
2019/12/12 13:30:17 [INFO] [morifuji-is.ninja] acme: Cleaning DNS-01 challenge
lego: You can now remove this TXT record from your morifuji-is.ninja. zone:
_acme-challenge.morifuji-is.ninja. 120 IN TXT "..."
2019/12/12 13:30:17 [INFO] [morifuji-is.ninja, *.morifuji-is.ninja] acme: Validations succeeded; requesting certificates
2019/12/12 13:30:18 [INFO] [morifuji-is.ninja] Server responded with a certificate.

manualの場合、コマンド実行して表示されるvalueを、自分でTXTレコードを作成して反映されてからEnterを押して手動で検証を始める必要があります。僕の場合は ゲームしながらだらだら dig _acme-challenge.morifuji-is.ninja TXT を打ちました。

今回は2種類のドメインなので、それを2回やります。あとは./lego/certificates/lego/certificates/morifuji-is.ninja.crt./lego/certificates/lego/certificates/morifuji-is.ninja.key を好きに調理して下さい。僕はingressに紐付けます。

3.リソース作成&ingressに紐付け

$ kubectl create secret tls all-cert --cert ./morifuji-is.ninja.crt  --key ./morifuji-is.ninja.key -o yaml > all-cert.yaml

# リソース確認
$ k get secret 
NAME                  TYPE                                  DATA   AGE
all-cert              kubernetes.io/tls                     2      26m

ingressに紐付けます

apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
  name: k8slab-ingress
spec:
  tls:
    - secretName: all-cert      # この部分を追記
  rules:
  - http:
      paths:
      - path: /handler
        backend:
          serviceName: k8slab-service
          servicePort: 80
  - host: aaaaaa.morifuji-is.ninja
    http:
      paths:
      - path: "/*"
        backend:
          serviceName: nextcloud-service
          servicePort: 80
  - host: bbbbbb.morifuji-is.ninja
    http:
      paths:
      - path: "/*"
        backend:
          serviceName: blog-service
          servicePort: 80
  backend:
    serviceName: k8slab-service
    servicePort: 80

あとは待つだけ。

4.ECDSA非対応&RSA4096非対応

  Warning  Sync    6m10s (x24 over 45m)  loadbalancer-controller  Error during sync: error running load balancer syncing routine: loadbalancer default-k8slab-ingress--c82b4489f3148701 does not exist: Cert creation failures - k8s-ssl-03778049d335f6d5-7832ce86f8261bed--c82b4489f3148701 Error:googleapi: Error 400: The ECDSA curve is not supported., sslCertificateUnsupportedCurve

The ECDSA curve is not supported. 嘘やろ、、、泣く泣くECDSAからRSA4096に変更した。

$ docker run -v $PWD/lego:/.lego -it goacme/lego  --email="your@email.com" --domains="morifuji-is.ninja" --domains="*.morifuji-is.ninja" --dns manual -k rsa4096 run

今度こそ,,

  Warning  Sync    8s                    loadbalancer-controller  Error during sync: error running load balancer syncing routine: loadbalancer default-k8slab-ingress--c82b4489f3148701 does not exist: Cert creation failures - k8s-ssl-03778049d335f6d5-40b15ffdb67098e2--c82b4489f3148701 Error:googleapi: Error 400: The SSL key size is unsupported.  The loadbalancer supports RSA-2048 and ECDSA P-256 certificates., sslCertificateUnsupportedKeySize

The SSL key size is unsupported. The loadbalancer supports RSA-2048 and ECDSA P-256 certificates. クソやんけ!!!!泣く泣くRSA4096からRSA2048に変更した。

$ docker run -v $PWD/lego:/.lego -it goacme/lego  --email="your@email.com" --domains="morifuji-is.ninja" --domains="*.morifuji-is.ninja" --dns manual -k rsa2048 run
tech  k8s 

See also