Kubernetes the hard way を初学者目線で解説する ~ #3 サーバー/クライアント証明書の準備


はじめに

30代未経験からエンジニアを目指して勉強中のYNと申します。
インフラ初学者の私ですが、Kubernetes the hard wayを進めるにあたって、インフラに関する基本的な知識を体系的に学ぶことができました。
そこで、初学者目線での学びなどを本記事にまとめておきたいと思います。

目次

こちらをご覧ください

サーバー/クライアント証明書の準備

概要

Kubernetesクラスター内1のコンポーネント同士で通信を行うとき、お互いが意図した相手であるかを確認するため認証(Authentication)を行います。認証の方法はいくつかありますが、今回は、TLS認証を行います。このとき、リクエストする側はクライアント証明書で自分を証明し、リクエストを受ける側はサーバー証明書で自分を証明する必要があります。
このためには、RSA公開鍵/秘密鍵のペアを作成し、公開鍵を認証局によりサインしてもらうことで証明書を生成します。これはopenSSLというアプリで行うことができます。生成された秘密鍵は厳重に保管する必要があります。

(※ちなみに、「認証が完了したあとの通信の暗号化」については、別の共通鍵を別の方法で共有して暗号化を行います。Kubernetesでこの暗号化方式について設定するにはcipher-suiteオプションを指定する必要があります。)

各ノードには既にopenSSLがインストールされているので、証明書の生成はどのノードでも行うことが出来ますが、今回はmaster-1ノードにて作成します。

全体図

今回作成するコンポーネントの連携を下図にまとめます。
(矢印の方向はリクエストを表します。)

上記の全体図に、これから生成するサーバー/クライアント証明書を表現するとこんな感じです。

認証局の証明書を生成する

これから作成するKubernetesクラスタ内のみで通用する、認証局の証明書を作成します。
これ以降、openSSLでは次の流れを踏襲します。

(1) RSA秘密鍵を生成
(2) CSR(認証局への証明書発行リクエスト)を生成
(3) RSA秘密鍵とCSRを使って証明書を生成

ちなみに、今回は認証局自身の証明書であるため、いわゆる「オレオレ証明書」となります。

master-1.node
# Create private key for CA
# RSA秘密鍵を生成
openssl genrsa -out ca.key 2048 
 
# Comment line starting with RANDFILE in /etc/ssl/openssl.cnf definition to avoid permission issues
sudo sed -i '0,/RANDFILE/{s/RANDFILE/\#&/}' /etc/ssl/openssl.cnf

# Create CSR using the private key
# RSA秘密鍵を用いて、CSR(認証局への証明書発行リクエスト)を生成
openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr 

# Self sign the csr using its own private key
# CSRに基づきオレオレ証明書を生成
openssl x509 -req -in ca.csr -signkey ca.key -CAcreateserial  -out ca.crt -days 1000

kubectlのクライアント証明書を生成する

kubectlコマンドによるユーザー認証のための証明書を生成します。

master-1.node
# Generate private key for admin user
openssl genrsa -out admin.key 2048

# Generate CSR for admin user. Note the OU.
openssl req -new -key admin.key -subj "/CN=admin/O=system:masters" -out admin.csr

# Sign certificate for admin user using CA servers private key
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out admin.crt -days 1000

Controller Managerのクライアント証明書を生成する

master-1.node
openssl genrsa -out kube-controller-manager.key 2048
openssl req -new -key kube-controller-manager.key -subj "/CN=system:kube-controller-manager" -out kube-controller-manager.csr
openssl x509 -req -in kube-controller-manager.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kube-controller-manager.crt -days 1000

Kube-proxyのクライアント証明書を生成する

master-1.node
openssl genrsa -out kube-proxy.key 2048
openssl req -new -key kube-proxy.key -subj "/CN=system:kube-proxy" -out kube-proxy.csr
openssl x509 -req -in kube-proxy.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out kube-proxy.crt -days 1000

Schedulerのクライアント証明書を生成する

master-1.node
openssl genrsa -out kube-scheduler.key 2048
openssl req -new -key kube-scheduler.key -subj "/CN=system:kube-scheduler" -out kube-scheduler.csr
openssl x509 -req -in kube-scheduler.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out kube-scheduler.crt -days 1000

API-serverのサーバー/クライアント証明書を生成する

マルチドメインを1枚の証明書で作成したい場合、コマンド一発でCSRをつくることができず、事前にopenssl.confを設定する必要があります。

master-1.node
cat > openssl.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE #(1)
keyUsage = nonRepudiation, digitalSignature, keyEncipherment #(2)
subjectAltName = @alt_names #(3)
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
IP.1 = 10.96.0.1
IP.2 = 192.168.5.11
IP.3 = 192.168.5.12
IP.4 = 192.168.5.30
IP.5 = 127.0.0.1
EOF

opensl.confの簡単な解説です。openssl.confについてはこちらの記事が分かりやすかったです。

(1) 証明書がCA証明書かどうか
(2) 鍵の使い道について明示
(3) マルチドメインを1枚の証明書で作成したい場合に指定する

master-1.node
openssl genrsa -out kube-apiserver.key 2048
openssl req -new -key kube-apiserver.key -subj "/CN=kube-apiserver" -out kube-apiserver.csr -config openssl.cnf
openssl x509 -req -in kube-apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out kube-apiserver.crt -extensions v3_req -extfile openssl.cnf -days 1000

etcdのサーバー証明書を生成する

API-server同様、マルチドメインを1枚の証明書で作成したい場合、コマンド一発でCSRをつくることができず、事前にopenssl.confを設定する必要があります。

master-1.node
cat > openssl-etcd.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.5.11
IP.2 = 192.168.5.12
IP.3 = 127.0.0.1
EOF
master-1.node
openssl genrsa -out etcd-server.key 2048
openssl req -new -key etcd-server.key -subj "/CN=etcd-server" -out etcd-server.csr -config openssl-etcd.cnf
openssl x509 -req -in etcd-server.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out etcd-server.crt -extensions v3_req -extfile openssl-etcd.cnf -days 1000

Service Account Tokenに署名するためのKey Pairをつくる

ここまで触れてきたように、Kubernetesクラスターにおいて、コンポーネントやユーザーの認証はTLS証明書をもちいたクライエント認証を使うことが推奨されています。
一方で、Podなどクラスターオブジェクトの認証はService Account(SA)を用います。
Kubernetesで生成されるPodはデフォルトのSAをもっており、このSAには自動的にsecretが付与されます。このsecretにはBearer tokenが記載されており、このtokenを使ってPodはAPI-serverに認証されます。
このtokenはcontroller managerのtoken controllerでつくられ、ここで作成するSA秘密鍵により署名(暗号化)されます。そしてPodがこのtokenを持ってAPI-serverにアクセスすると、API-serverではSA公開鍵をつかってtokenを復号し、Podの認証を行います。

You must pass a service account private key file to the token controller in the kube-controller-manager using the --service-account-private-key-file flag. The private key is used to sign generated service account tokens. Similarly, you must pass the corresponding public key to the kube-apiserver using the --service-account-key-file flag. The public key will be used to verify the tokens during authentication.

ここでは、上記で説明した、

  • controller-managerに登録する秘密鍵
  • API-serverに登録する公開鍵

を生成します。
詳細はこちらこちらを参照ください。

master-1.node
openssl genrsa -out service-account.key 2048
openssl req -new -key service-account.key -subj "/CN=service-accounts" -out service-account.csr
openssl x509 -req -in service-account.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out service-account.crt -days 1000

作成した証明書を他のmasterノードにも配布する

master-1ノードで作った証明書をmaster-2ノードにも配布します。
(kuber-proxyについては後ほどworkerノードに配布します。)

master-1.node
for instance in master-1 master-2; do
  scp ca.crt ca.key kube-apiserver.key kube-apiserver.crt \
    service-account.key service-account.crt \
    etcd-server.key etcd-server.crt \
    ${instance}:~/
done