Kubernetes の TLS 証明書について調べてみた

Posted on

先日、ステージング環境の K8s クラスタに一切接続できなくなる障害が起きました。調査してみると K8s クラスタの TLS 証明書が失効していたことが原因でした。

ドキュメントにもある通り、kubelet は Kubernetes API の認証に TLS 証明書を使っており、その有効期限はデフォルトで 1 年です。有効期限が 1 年というのは、今回の障害ではじめて知りました。

The kubelet uses certificates for authenticating to the Kubernetes API. By default, these certificates are issued with one year expiration so that they do not need to be renewed too frequently.

要するに、K8s クラスタを起動してから 365 日経つと証明書が失効して TLS のハンドシェイクに失敗し kubelet が通信できなくなります。 kubelet は Node で動く Pod を管理するエージェントなので、kubelet が通信できなくなると K8s クラスタ全体の障害に発展します。

ちなみに、GKE (Google Kubernetes Engine) の TLS 証明書は有効期限が 5 年でした。

TLS 証明書の有効期限を監視する

Kubernetes は 3 か月おきに新しいバージョンがリリースされるので同じクラスタが 1 年以上稼働することはあまりなさそうですが、再び障害を起こさないために証明書の有効期限を監視するようにしました。

kubeconfig の clusters.cluster.server に API のエンドポイントが載っています。証明書の内容を確認するには openssl コマンドを使います。

$ openssl s_client -connect k8s.example.com:443 -servername k8s.example.com < /dev/null 2> /dev/null | openssl x509 -text | grep 'Not After'
            Not After : Oct 24 10:37:16 2019 GMT

後述しますが、この TLS 証明書の認証局はクラスタ自身なので、curl コマンドなどは TLS のハンドシェイクに失敗します(CA 証明書を指定してあげる必要がある)。

あとは適当に自動化すればいいので、Lambda と CloudWatch Events を使って日次で監視するようにしました。詳細は 2 年前に書いたこの記事を参考にしてください。

Kubernetes 1.8 から TLS 証明書を自動的にローテーションする機能がベータ版で提供されていますが今回は試していません。

Kubernetes の TLS 証明書の CA

証明書の中身を見て気づいたのですが、この TLS 証明書の認証局 (CA) は K8s クラスタ自身です。たとえば kube-aws で構築した K8s クラスタだと Issuer はこんな感じでした。

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 3085772283849125021 (0x2ad2dd8b0492ac9d)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: O=kube-aws, CN=kube-ca
        Validity
            Not Before: Mar 16 04:32:17 2018 GMT
            Not After : Mar 16 04:32:19 2019 GMT
        Subject: O=kube-aws, CN=kube-apiserver
        (snip)

これについてもドキュメントに記載がありました。

Every Kubernetes cluster has a cluster root Certificate Authority (CA). The CA is generally used by cluster components to validate the API server’s certificate, by the API server to validate kubelet client certificates, etc.

コードを読んでみる

K8s クラスタ自体が認証局になっているということは、CA 証明書の発行とその CA から署名された TLS 証明書を生成しているコードがあるはずです。気になったので Kubernetes 1.12.2 のコードを読んでみました。

まず CA 証明書を発行しているのは NewSelfSignedCACert という関数です。ここで 10 年間の CA 証明書を生成しています。

tmpl := x509.Certificate{
    SerialNumber: new(big.Int).SetInt64(0),
    Subject: pkix.Name{
        CommonName:   cfg.CommonName,
        Organization: cfg.Organization,
    },
    NotBefore:             now.UTC(),
    NotAfter:              now.Add(duration365d * 10).UTC(),
    KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    BasicConstraintsValid: true,
    IsCA: true,
}

この CA から証明書を生成しているのは NewSignedCert という関数です。こっちは NotAfter が 365 日になっています。

certTmpl := x509.Certificate{
    Subject: pkix.Name{
        CommonName:   cfg.CommonName,
        Organization: cfg.Organization,
    },
    DNSNames:     cfg.AltNames.DNSNames,
    IPAddresses:  cfg.AltNames.IPs,
    SerialNumber: serial,
    NotBefore:    caCert.NotBefore,
    NotAfter:     time.Now().Add(duration365d).UTC(),
    KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    ExtKeyUsage:  cfg.Usages,
}

おわりに

自前で構築した K8s クラスタの運用はまだこなれていないので、引き続き勉強してノウハウを溜めていこうと思います。

Popular Entries

Recent Entries