Kekeの日記

エンジニア、読書なんでも

Freenomの無料ドメインでcert-managerを使って、GKEのIngressをHTTPS(TLS)対応する

https://github.com/ahmetb/gke-letsencrypt/raw/master/img/gke-letsencrypt.png

本記事

本記事ではGKE上でホストしているアプリケーションサーバーのが使っているIngressに対してHTTPS対応をしたいと思います。

背景としては、実はハッカソン用で開発をしていたのですが決済プラットフォームを使うためHTTPSに対応しなければなりませんでした。

しかし、ドメインにかけるお金は学生にはないので、無料で取得できるFreenomで使ってやってみようと思います。

Freecomで無料ドメインを取得する

Freecomでは、数ヶ月程度の短い間なら無料でドメインを取得することができます。 ここで注意点ですが、FreeDomainと書いていても、Freeな期間はドメイン毎に決まっていて、1年間無料のものもあれば3ヶ月だけ無料のものもあるので、購入する前は注意してください。

無料ドメインが取得できれば、以下のようにリソースネームを設定します。

f:id:bobchan1915:20181029132832p:plain

ここには以下のようにIngressのADDRESSを入力します。

kubectl get ingress

NAME      HOSTS     ADDRESS        PORTS     AGE
frontend      *         73.231.43.45   80        5d

ここでのIngressの設定は以下のようになっています。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: frontend
  labels:
    app.kubernetes.io/name: frontend
  annotations:
    kubernetes.io/ingress.global-static-ip-name: frontend-ip
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: frontend
          servicePort: 3000

このときは簡単にファンアウト(出力を増やす)できるようにしています。例えば以下のようにすることができます。

以下のように簡単にファンアウトできて

121.31.164.232 -> / fan    service1:80
                               / out   service2:80

ドメインをとっても

example.com -> 121.31.164.232 -> / fan    service1:80
                                                            / out   service2:80

することができます。

Cert Managerを使って証明書を管理する

github.com

cert-managerとはKubernetesのアドオンでLet`s Encryptによって証明書をプロビジョニング、更新をするものです。

同じようで機能でkube-legoがありましたが、cert-manegerはこの新しいバージョンになります。

Helmを使ってCert Managerをインストール

$ helm install \
    --name https \
    --namespace kube-system \
    stable/cert-manager

すると以下のようにDeploymentができます。

$ kubectl get pods --namespace=kube-system

https-cert-manager-6ffc878445-986qf                            1/1       Running   0          7s

Deploymentだけでなく、以下のようにいくつかできています。

==> v1/ServiceAccount
NAME                AGE
https-cert-manager  1s

==> v1beta1/ClusterRole
https-cert-manager  1s

==> v1beta1/ClusterRoleBinding
https-cert-manager  1s

Let`s Encryptの認証用ファイルを作成

以下のようにcusterIssuer.yamlを作成します。

apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: XXXXXXXX@gmail.com // 自分のメールアドレス
    privateKeySecretRef:
      name: letsencrypt-prod // Certのために使うために名前をつける
    http01: {}

そしてkubectl applyします。

kubectl apply -f issuer.yaml -n kube-system

証明書のためのファイルを作成

以下のようにcert.yamlを作成します。

apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: oaiso-tk
  namespace: default
spec:
  secretName: oaiso-tk-tls
  issuerRef:
    name: letsencrypt-prod
  commonName: oaiso.tk // Domainの名前
  dnsNames:
  - oaiso.tk
  acme:
    config:
    - http01:
        ingress: frontend // Ingressの名前
      domains:
      - oaiso.tk // Domainの名前

今回はACMEプロトコルのhttp01 challengeを使います。

ACMEとは証明書発行のプロトコルで

  • http01 challenge: Let`s EncryptからTokenをつけとって特定のパスに入れて認証されてドメインを確認する方法
  • dns01 challenge: Token受け取ってTXTレコードにTokenをつけてドメインを確認する方法

の二つの種類があります。

ここまでを確認してみます。

kubectl get issuer -n kube-system

kubectl get cert

Ingressを見てみると以下のように挿入されています。

これはcertをデプロイしてから数分後に作られるのですぐに設定されていなくても安心して少し待ってみてください。

- host: oaiso.tk
    http:
      paths:
      - backend:
          serviceName: cm-acme-http-solver-jhzv7
          servicePort: 8089
        path: /.well-known/acme-challenge/lJeUWf5GOboGTFtsukxSaZpXNC_qyQkGulvTHpkly8k

lJeUWf5GOboGTFtsukxSaZpXNC_qyQkGulvTHpkly8kがTokenであり、これによってhttp01 challengeをすることができるようになるというわけです。

もちろん、バックエンドには

$ kubectl get svc
cm-acme-http-solver-jhzv7   NodePort       10.31.250.77    <none>           8089:32100/TCP   49m

$ kubectl get pods
cm-acme-http-solver-9b9nj   1/1       Running   0          49m

があるからこそ実現できます。これらはDeploymentcertを削除することで消すことができます。

Certの進捗確認はcertリソースタイプを見てみるといいです。

$ kubectl describe cert oaiso-tk

...
  Normal  CreateOrder     9m    cert-manager  Created new ACME order, attempting validation...
  Normal  DomainVerified  2m    cert-manager  Domain "oaiso.tk" verified with "http-01" validation
  Normal  IssueCert       1m    cert-manager  Issuing certificate...
  Normal  CertObtained    1m    cert-manager  Obtained certificate from ACME server
  Normal  CertIssued      1m    cert-manager  Certificate issued successfully

これで取得できていることがわかります。

cert-managerのログをみると様子がわかります。

Ingressで証明書を使用する

以下のようにspec.tlsを追加します。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: frontend
  labels:
    app.kubernetes.io/name: frontend
  annotations:
    kubernetes.io/ingress.global-static-ip-name: frontend-ip
spec:
  tls:
  - secretName: oaiso-tk-tls
    hosts:
      - oaiso.tk
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: frontend
          servicePort: 5000

そしてUPDATEします。

$ kubectl apply -f ingress.yaml

そして進捗を確認します。

$ kubectl describe ing frondend

...
  Normal  Service  1m (x141 over 1h)   loadbalancer-controller   no user specified default backend, using system default
  Normal  UPDATE   37s (x149 over 1h)  nginx-ingress-controller  Ingress default/frontend

OpenSSLコマンドで証明書を確認する

以下のコマンドをドメイン名に対して使うことによって証明書を取得できているかどうか確認することができます。

openssl s_client -connect oaiso.tk:443

CONNECTED(00000006)
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/CN=oaiso.tk
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIF+zCCBOOgAwIBAgISAw5/coR89bnHT1nLudtsywtKMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODEwMjkxMTMyMTdaFw0x
OTAxMjcxMTMyMTdaMBMxETAPBgNVBAMTCG9haXNvLnRrMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAox8hhYK/5Pg1J0f+qnSNCAtJ6s7OaNqJJ+4cMqsU
uaVCMFjx7fxaXn2O7gm9KKTIYYbFp0//EJMKHbaR7oxyWrdCF98oAWfaDFunR1MP
5CjeZgrgiHTk7QeU4hO+m9IGXdvoZtdeNnaVhnk8MP9l/h5rm7D9c5OWuB1pqqSe
rWWq3IPApd2ql/J891FMEUETy/8bJYFYRVUL2cUB/CkT5+YKcj7paqPQ16sujG0Q
/LihXCPnGRrKnyNqK3FZzExWIDgJu5z0m/9ib8MfKMgrg3nXs71wjv5UsJkvRgp5
UQbLLnmGanPAO+ceiN3Rneg3Ws3hnaEa74nyAsOu4DQGEwIDAQABo4IDEDCCAwww
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTnElbPxcgLsOg0dht0E/qyFyXdqDAfBgNV
HSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEwLgYI
KwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYI
KwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcvMBMG
A1UdEQQMMAqCCG9haXNvLnRrMIH+BgNVHSAEgfYwgfMwCAYGZ4EMAQIBMIHmBgsr
BgEEAYLfEwEBATCB1jAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlw
dC5vcmcwgasGCCsGAQUFBwICMIGeDIGbVGhpcyBDZXJ0aWZpY2F0ZSBtYXkgb25s
eSBiZSByZWxpZWQgdXBvbiBieSBSZWx5aW5nIFBhcnRpZXMgYW5kIG9ubHkgaW4g
YWNjb3JkYW5jZSB3aXRoIHRoZSBDZXJ0aWZpY2F0ZSBQb2xpY3kgZm91bmQgYXQg
aHR0cHM6Ly9sZXRzZW5jcnlwdC5vcmcvcmVwb3NpdG9yeS8wggEEBgorBgEEAdZ5
AgQCBIH1BIHyAPAAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAA
AWa/z308AAAEAwBHMEUCIGji06/WBgHj84tTKJ0DVKOmnEB29mUGjy0zuVW3n4dA
AiEAxM0HhDfeKkZbeY53+zETToF8AEPEwKqvB96C59uWdnIAdgApPFGWVMg5Zbqq
UPxYB9S3b79Yeily3KTDDPTlRUf0eAAAAWa/z30JAAAEAwBHMEUCIHynBttOOg4/
6C4qtrrptZ198CUH9TCRXTWz9YETLu8nAiEA/o67eXNHba3+NZb7GvTf1pyw/3k4
S+iyK8Wy2MQQd5IwDQYJKoZIhvcNAQELBQADggEBAJsM9AvC7o+qzrFBTw5ZAhK/
4+F9plHykrwGeICkYcmN1kG0Hjnqud8VYnH7TGoN477anAyqDUAxb57QxWn3Osbp
uK1hkKCWd1XB/g8i17YUA98iGGCVt2r8AM2qLaTRgnfjknWUDOlf9R7c2keqPCpA
ghWNVB5BaoNoqPyNOrzF80ziD/0p4i7BZjSoLsTGIpk4+H7YOFLF+khXuXlxviak
cOCIFINmt7o4Gi/k37JVGlkrl2s/tKklD6dN3aqCfBeni7YoGrLwuBFLTUgHH6PA
yekN/xcHwwZ85/cDNl+Nq7iIPdLF6cf6OSE2S1E3jTd5ULCMkSLb3fGN1GLZXiY=
-----END CERTIFICATE-----
subject=/CN=oaiso.tk
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
---
No client certificate CA names sent
---
SSL handshake has read 3417 bytes and written 444 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 9A4F4B51894315CE19C148A3B5071EB05713DF6C7A4615BD8F5EAC41B9AE6A88
    Session-ID-ctx:
    Master-Key: FDBB6D4AA680ED5219EA449E17C1A49F789D5772FAFF69FC49670DBB30D89EB24BA3AB1AB2BBBB346EA8371625B61D2C
    TLS session ticket lifetime hint: 100800 (seconds)
    TLS session ticket:
    0000 - 00 3c a8 dd 25 de 46 b4-98 61 b6 0a ec d9 5f da   .<..%.F..a...._.
    0010 - e6 d9 ac e7 30 f4 df 45-f0 48 9c eb e1 e1 ed 0f   ....0..E.H......
    0020 - 11 5c 2e e9 1f 89 39 d3-f8 d5 e4 d9 c0 5a dc 61   .\....9......Z.a
    0030 - 7f ed df f3 c1 ad 63 c3-fd 8c bd 1d 6c a0 6b d1   ......c.....l.k.

HTTPをはじく

以下のようにingressmetadata.annotation[]に追加するとhttpをはじくことができます。

...
metadata:
  name: frontend
  annotations: 
     kubernetes.io/ingress.global-static-ip-name: oaiso-frontend
     kubernetes.io/ingress.allow-http: "false"
...

ポイント

  • helmで簡単に対応することができる
  • 必ずする必要がないがspec.tlsなしで開始して、certapplyしてspec.tlsを足す方がわかりやすい。
  • kubectl apply -f cert.yamlしてからも時間がかかるし、ingressが更新されても時間がかかるので、時間を使う
  • 無料ドメインでいくらでも対応できる
  • http://IPアドレスは使うことができない
  • example.comがHTTPS対応してもhoge.example.comがHTTPS対応できるわけではない
  • Let`s Encryptの証明書が取れないときは、一度コンポーネントを削除して見るのもいい、特にsecretそのもの

Named Base Virtual Hostion

Vitual Hostingとは

バーチャルホストは、一台のサーバーで同じIPアドレスを使って仮想的に複数のドメインを運用するサーバー技術の一種です。ドメイン数を上回るサーバーを用意したくないときに使用する

ものです。IPアドレスの節約効果やサーバー機器を減らすことで運用コストの削減などのメリットがありますが、障害のリスクや負荷が集中するなどデメリットもあります。

実用的に使ってみる

今まではいかのように

以下のようにVirtual Hostionを実際に使うならば

example.com  --|                 |-> example.com            service1:80
                          | 178.91.123.132  |
example2.com --|                 |-> another.example.com       service1:80

これからは一つだけと同じなので、説明をしません。

参考文献

github.com

qiita.com

www.idcf.jp