ユーザーの証明書でEKSにアクセスできるのか

EKSクラスターへのアクセスは普通はIAMで行うが、それ以外のやり方で行うとしたら以下がありそう。

  • ユーザーの証明書を発行する
  • ServiceAccountを作ってそのトークンをダウンロードして使う

できるのかを確認しようとしたメモ。

コンポーネント バージョン 備考
Kubernetes 1.18
eksプラットフォームバージョン eks.3

証明書

証明書でアクセスできるかを確認する。

CertificateSigningRequestをリソースを作ってあげると署名されて返ってくる。

秘密鍵を作成する。

$ openssl genrsa -out jane.key 2048
Generating RSA private key, 2048 bit long modulus
..........+++
...............................................................................................................+++
e is 65537 (0x10001)

CSRを作成する。

$ openssl req -new -key jane.key -out jane.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:jane
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:

CSRbase64エンコードし、改行なしで出力する。

# Linux
# cat jane.csr | base64 -w 0
# CSR=$(cat jane.csr | base64 -w 0)
# Mac
cat jane.csr | base64
CSR=$(cat jane.csr | base64)

yamlを作成する。EKSの1.18のクラスターの場合はapiVersion: certificates.k8s.io/v1ではなくcertificates.k8s.io/v1beta1とする必要があった。

# 1.19以上?
cat <<EOF > csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: jane
spec:
  groups:
  - system:authenticated
  request: ${CSR}
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
EOF
# 1.18以下?
cat <<EOF > csr.yaml
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: jane
spec:
  groups:
  - system:authenticated
  request: ${CSR}
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
EOF

CSRリソースを作成する。

$ k apply -f csr.yaml
certificatesigningrequest.certificates.k8s.io/jane created

状態を確認する。

$ k get csr
NAME        AGE   SIGNERNAME                            REQUESTOR                                                       CONDITION
csr-47rlp   13m   kubernetes.io/kubelet-serving         system:node:ip-192-168-35-64.ap-northeast-1.compute.internal    Approved,Issued
csr-fvndx   13m   kubernetes.io/kubelet-serving         system:node:ip-192-168-10-143.ap-northeast-1.compute.internal   Approved,Issued
jane        7s    kubernetes.io/kube-apiserver-client   kubernetes-admin                                                Pending

Pendingになるので、approveする。

$ k certificate approve jane
certificatesigningrequest.certificates.k8s.io/jane approved

確認する。

$ k get csr
NAME        AGE   SIGNERNAME                            REQUESTOR                                                       CONDITION
csr-47rlp   15m   kubernetes.io/kubelet-serving         system:node:ip-192-168-35-64.ap-northeast-1.compute.internal    Approved,Issued
csr-fvndx   15m   kubernetes.io/kubelet-serving         system:node:ip-192-168-10-143.ap-northeast-1.compute.internal   Approved,Issued
jane        87s   kubernetes.io/kube-apiserver-client   kubernetes-admin                                                Approved,Issued

statusに証明書が入ってくるので、取り出す。

k get csr jane -o json | jq -r '.status.certificate' | base64 --decode
k get csr jane -o json | jq -r '.status.certificate' | base64 --decode > jane.crt

証明書を確認する。

$ openssl x509 -text -in jane.crt -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            09:35:1d:11:fd:1e:68:04:fd:35:a0:00:c7:c3:07:6b:6e:69:54:bd
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Dec 28 00:38:00 2020 GMT
            Not After : Dec 28 00:38:00 2021 GMT
        Subject: CN=jane
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c5:fa:83:17:03:3a:7b:e3:c0:98:14:ae:d7:c2:
                    46:3b:cb:d4:9d:8e:6e:ac:3b:49:99:9e:5a:99:9b:
                    eb:f0:5d:3b:7c:6f:97:f0:86:f5:9b:6b:bf:94:9c:
                    21:64:2f:74:56:f4:a0:58:81:52:ec:80:4c:33:74:
                    49:e4:da:6d:03:a8:3a:7b:8f:17:19:dd:32:2d:cc:
                    f2:1f:ef:e0:38:31:57:93:92:bd:fe:6b:79:99:f8:
                    ff:07:10:f3:8a:de:ec:d5:91:08:50:86:59:87:0e:
                    f4:40:be:85:48:fb:70:b1:3c:e6:b5:0e:3c:93:77:
                    8f:9f:5c:2c:08:7a:59:03:d4:8d:fa:dc:e4:69:be:
                    15:b1:d8:7c:c6:fe:86:40:f8:04:6b:1d:a7:64:33:
                    8e:70:79:9d:35:16:20:3d:08:7a:d5:86:22:4d:d9:
                    d8:4a:dc:f5:9a:62:5d:f1:83:31:1d:d5:fe:50:dc:
                    56:35:58:cb:7a:f2:8a:09:0e:a0:b3:48:8e:54:14:
                    30:b0:7c:13:54:1f:3e:b0:73:32:02:6e:9f:6a:87:
                    05:96:93:f2:7f:6c:3f:1e:df:96:8d:f5:25:72:47:
                    9f:21:43:b5:89:9d:78:08:5c:96:4b:65:ab:75:b8:
                    ca:46:96:af:7a:0e:57:8b:16:a2:06:e1:42:24:eb:
                    70:a1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                35:F4:D3:88:1C:57:AF:1D:EE:EE:F5:E0:34:2C:D2:3F:23:1E:6E:A0
    Signature Algorithm: sha256WithRSAEncryption
         b6:7b:2a:8a:d0:95:07:d7:93:6f:33:d7:dd:c0:e8:05:a5:af:
         1c:e3:38:d9:17:4a:33:c7:77:1a:f8:11:a1:f2:1e:36:cb:7c:
         4e:13:3a:a6:33:ed:60:b3:aa:b0:8d:ef:a6:45:7c:8a:c3:fe:
         23:c4:fe:8c:99:c4:93:b8:55:53:a0:4e:cc:d0:0a:70:59:cc:
         e2:3e:99:a5:30:58:b9:08:e7:d0:e4:a5:b6:9a:7f:a0:66:75:
         c4:ef:fc:77:90:84:7e:19:f4:e6:53:aa:0e:e2:ec:e7:94:9c:
         be:2d:66:94:11:c3:55:5e:78:c5:55:65:d0:de:31:cf:13:17:
         12:f0:50:d8:fe:24:41:56:38:7a:7c:70:59:2d:da:49:24:84:
         aa:fc:0c:fd:79:a1:af:d9:9a:f8:3c:60:54:7d:56:bd:6a:16:
         5c:ed:7f:38:86:0a:8b:b1:54:85:eb:a4:e5:f6:5a:3b:39:4d:
         08:74:b9:3b:bd:b8:08:1f:c7:1d:6b:de:03:82:f0:c0:1d:57:
         fa:d1:0d:4c:10:ea:ed:61:36:93:ed:81:b4:4f:7f:79:23:b1:
         d3:ad:03:05:2d:a9:8a:15:94:f0:a1:f9:7b:c2:79:a2:40:8b:
         7e:a1:b9:f8:c3:8c:33:82:46:a2:eb:46:2b:76:11:31:ba:c8:
         cb:ec:a7:46

kubeconfigにクレデンシャルをセットする。

# ファイルを参照する場合
# k config set-credentials jane --client-key=jane.key --client-certificate=jane.crt
# 秘密鍵と証明書を埋め込む場合
k config set-credentials jane --client-key=jane.key --client-certificate=jane.crt --embed-certs

クラスターを確認する。

$ k config get-clusters
NAME
mycluster.ap-northeast-1.eksctl.io
myeksdcluster.sotoiwa.dev

コンテキストを作成する。

$ k config set-context jane --cluster=mycluster.ap-northeast-1.eksctl.io --user=jane
Context "jane" created.

コンテキストを切り替える前に、ユーザーjaneviewのClusterRoleをバインドしておく。

$ k create clusterrolebinding jane --clusterrole=view --user=jane
clusterrolebinding.rbac.authorization.k8s.io/jane created

現在のコンテキストに設定する。

$ k config use-context jane
Switched to context "jane".

kubectlを実行する。

$ k get pod -n kube-system
error: You must be logged in to the server (Unauthorized)
$ k get node
error: You must be logged in to the server (Unauthorized)

ダメだった。できないのか、どこか手順を間違えたのか?

ca証明書をファイルにする。

k config view --raw -o json | jq -r '.clusters[] | select( .name == "mycluster.ap-northeast-1.eksctl.io" ) | .cluster."certificate-authority-data"' | base64 --decode > ca.crt

curlでクライアント証明書を渡さずにアクセスすると、anonymousとしてアクセスできている。

$ curl https://26BA04AD024EF5657DA104B8C1F00FEF.gr7.ap-northeast-1.eks.amazonaws.com --cacert ca.crt
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {

  },
  "code": 403
}

クライアント証明書を渡してアクセスすると、Unauthorizedになる。

$ curl https://26BA04AD024EF5657DA104B8C1F00FEF.gr7.ap-northeast-1.eks.amazonaws.com --cacert ca.crt --cert jane.crt --key jane.key
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

ServiceAccountのトーク

ServiceAccountのトークンでアクセスできるのかを確認する。

kube-system Namespaceにbatch-user ServiceAccountを作成する。

$ k -n kube-system create sa batch-user
serviceaccount/batch-user created

viewのClusterRoleをバインドする。

$ k create clusterrolebinding batch-user --clusterrole=view --serviceaccount=kube-system:batch-user
clusterrolebinding.rbac.authorization.k8s.io/batch-user created

Secretの名前を取得する。

SECRET=$(k -n kube-system get sa batch-user -o json | jq -r '.secrets[].name')

トークンを取得する。

TOKEN=$(k -n kube-system get secret $SECRET -o json | jq -r '.data.token' | base64 --decode)

kubeconfigにクレデンシャルをセットする。

kubectl config set-credentials batch-user --token=${TOKEN}

クラスターを確認する。

k config get-clusters

コンテキストを作成する。

$ k config set-context batch-user --cluster=prometheus-operator.ap-northeast-1.eksctl.io --user=batch-user
Context "batch-user" created.

現在のコンテキストに設定する。

$ k config use-context batch-user
Switched to context "batch-user".

kubectlを実行する。

$ k get pod -n kube-system
NAME                                            READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-6c4d8d8f64-m5qgs   1/1     Running   0          26d
aws-node-d9vxl                                  1/1     Running   0          25d
aws-node-skmks                                  1/1     Running   0          26d
aws-node-vzc8c                                  1/1     Running   0          26d
coredns-86f7d88d77-fdshl                        1/1     Running   0          26d
coredns-86f7d88d77-rqblg                        1/1     Running   0          26d
external-dns-7855554c94-cct6v                   1/1     Running   0          26d
kube-proxy-4wcxp                                1/1     Running   0          26d
kube-proxy-9skkb                                1/1     Running   0          25d
kube-proxy-nnhkg                                1/1     Running   0          26d
metrics-server-5f956b6d5f-plb5j                 1/1     Running   0          26d
$ k get node
Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:kube-system:batch-user" cannot list resource "nodes" in API group "" at the cluster scope

こちらはできた。

EKSベストプラクティスに、EKSではwebhook token authenticationとservice account tokensがサポートされているという記載があったので、ServiceAccountのトークンはいけるが、証明書はダメなものと思われる。

The Kubernetes project supports a variety of different strategies to authenticate requests to the kube-apiserver service, e.g. Bearer Tokens, X.509 certificates, OIDC, etc. EKS currently has native support for webhook token authentication and service account tokens.