背景
GKE上でLet's Encrypt(certbot)によるhttps化をするには、いくつか課題があります。
特に自動更新は厄介です。
本記事ではkube-legoを用いて、証明書の自動更新に対応する構成を記載します。
これを用いると、サービス側のファイルや設定をほぼいじらずにhttps化できます(ついでにhttp2も)。
なお、節約のためLet's Encryptで無料の証明書を取っていますが、これが複雑さの原因です。
証明書を買っている方は設定のみ行えば良いので、この記事の内容は一部しか役立ちません。
お金の力は偉大です。
前提
上記記事の手順を踏んでいると仮定します。
なお、独自ドメイン周りは本記事の手順の後半で再度いじることになる可能性が高いです。
できること
- Let's Encryptで証明書の無料&自動更新
全体像
ingressでL7ロードバランサーを設定し、Let's Encrypt認証と通常のアクセスを振り分けます。
(nginxを挟むの手順に従っている場合は)通常のアクセスではnginxを挟み、httpであればhttpsにリダイレクトします。
Let's Encryptの認証処理や一部設定は、kube-legoが行います。
kube-legoはアプリケーションとはnamespaceから区切り、独立したサービスとして立てます。
手順
(オプション)http->httpsのリダイレクト
2016年の現状、GCEのL7ロードバランサーではリダイレクトができません。
そのため、nginxを挟んで処理してね!とドキュメントに書かれています。
http->httpsのリダイレクトを行う場合は、nginxを挟むに沿って設定を行ってください。
Service typeの変更
serviceのタイプはLoadBalancer
として設定していましたが、これをNodePort
にします。
これにより、サービスにロードバランサーを割り当てず、こちらで設定ができます。
apiVersion: v1
kind: Service
metadata:
name: yourservice
labels:
app: yourservice
spec:
type: NodePort
ports:
- name: http
port: 80
selector:
app: yourservice
上記で設定後kubectl get svc
で見てみると、EXTERNAL IP
が<nodes>
となっています。
ingressの設定
GCPなので某ゲームが真っ先に浮かびますが、一般的な名詞(入場権とか)として使われているようです。
これは、L7ロードバランサーの設定をするもので、サービスへのアクセスルールを設定することができます。
公式ドキュメントに分かりやすいアスキーアートがあります。
ingressの設定は以下のようになります。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: yourservice
namespace: default
annotations:
kubernetes.io/tls-acme: "true"
kubernetes.io/ingress.class: "gce"
spec:
tls:
- hosts:
- yourhost.jp
secretName: yourservice-tls
rules:
- host: yourhost.jp
http:
paths:
- path: /*
backend:
serviceName: yourservice
servicePort: 80
これは以下の処理を行っています。
- TLSターミネーション
-
yourservice-tls
という名前のkubernetesのsecretを取り出します - ここには.crtなどが入っています
-
- アクセスの割り振り
- ここでは/*(要はデフォルト)のみを設定しています
- Let's Encryptの/.well-known/...はどうするの?となりますが、kube-legoが作ってくれます
ingressを使うと、自動でL7ロードバランサーが割り当てられます。
詳細はGoogle HTTP(S) Load Balancer。
applyすると、kubectl get ing
でステータスが確認できます。
kube-legoを立てる
kube-legoによるGCEのサンプルが用意されているので、この手順通りに進めます。
まずはkube-lego用のnamespaceを作ります。
apiVersion: v1
kind: Namespace
metadata:
name: kube-lego
次に、configMapを設定します。
ここでLet's Encryptのサーバーを指定しますが、まずはテスト用のAPIから取得し、上手く行ってから本番のAPIに切り替えましょう。
Staging Environment
なお、本番APIにconfigMapを切り替えた際は、kube-lego周りのsecretを一旦削除しないと正しく取得をしてくれないので要注意です。
apiVersion: v1
metadata:
name: kube-lego
namespace: kube-lego
data:
lego.email: "your@mail.address"
lego.url: "https://acme-staging.api.letsencrypt.org/directory"
kind: ConfigMap
最後に、サービスのdeploymentです。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kube-lego
namespace: kube-lego
spec:
replicas: 1
template:
metadata:
labels:
app: kube-lego
spec:
containers:
- name: kube-lego
image: jetstack/kube-lego:0.1.3
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: LEGO_EMAIL
valueFrom:
configMapKeyRef:
name: kube-lego
key: lego.email
- name: LEGO_URL
valueFrom:
configMapKeyRef:
name: kube-lego
key: lego.url
- name: LEGO_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LEGO_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 1
jetstack/kube-lego
をポート8080で立て、各種値をconfigMapから拾ったり設定したりしています。
特に他サービスに依存しないため、書き換える必要はありません。
確認
kube-legoの確認
kubectl describe ing
で詳細を見てみると、
/.well-known/acme-challenge/* kube-lego-gce:8080 (<none>)
というものが追加されています。
無事アクセスが分かれてくれそうですね。
追加した時点から、kube-legoは認証の確認を行っています。
kubectl --namespace=kube-lego get pod
でポッドIDを確認し、
kubectl --namespace=kube-lego log <pod-id>
でログを見てみましょう。
上手く行っていない場合は原因がログに出ているので対処します。
成功すると、以下のようなログが出ています。
msg="creating new secret" context=secret name=yourservice-tls namespace=default
kube-legoがyourservice-tls
というsecretに設定をしてくれた模様です。
msg="cert expires in 90.0 days, no renewal needed"
というログも出ており、期限に近づくと改めて認証を行い、証明書を取得してくれます。
サービスの確認
最後に、取得したドメインにhttpsでアクセスしてみます。
ページが見つからないと言われたりする場合は、ingressで割り振られた新しいIPにドメインが正しく紐付いているか確認してください。
- ingressを一旦削除
- Cloud DNSの設定、IPを確認
- IPで正しくアクセスできるか確認
- Static IPを解放してやり直す
- などなど...
正しくアクセスできると、「この接続は信頼されていません...」というメッセージが出てくるかと思います。
これはテスト用のLet's Encrypt APIを使ったためです。
configMapを本番用のAPI URL(https://acme-v01.api.letsencrypt.org/directory
)に書き換え、applyをします。
kubectl delete secret yourservice-tls
kubectl --namespace=kube-lego delete secret --all
kubectl --namespace=kube-lego delete pod --all
を行い、secretの削除&podの再起動をしてすぐさま再取得をしてもらいます。
証明書が発行され、サービスに警告無しでhttpsアクセスできたら成功です!