この記事は、Udemyの講座 https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests および https://kodekloud.com/ の内容を学習しながらまとめたものです。
- 141 Authentication
- 146 TLS in Kubernetes
- 147 TLS in Kubernetes - Certification Creation
- 152 Certificates API
- 155 KubeConfig
- 159 API Groups
- 160 Authorization
- 161 Role Based Access Controls
- 164 Cluster Roles and Roles Bindings
- 167 Service Account
- 170 Image Security
- 173 Security in docker
- 174 Security Contexts
- 177 Network Policy
- 178 Developing network policies
141 Authentication
Kubernetesには、User Accountを作成する機能はありませんが、serviceaccountを作成する機能はあります。

ユーザーはkubectlやAPIを使用してKubernetesクラスターにアクセスできます。

kube-apiserverの起動時に --basic-auth-file=<filename> を指定すると、パスワード認証方式のユーザーを登録できます。ただし、セキュリティが弱く、ユーザーが変更される際にAPIサーバーの再起動が必要になるため、推奨される認証方式ではありません。このように作成されたuser/passwordは、curlコマンドでの認証に使用されます。

パスワードの代わりにTokenでの認証も可能です。

146 TLS in Kubernetes
Kubernetes内部では、すべての認証がTLSで動作します。そのため、すべてのコンポーネントには .crt で終わる公開鍵と .key で終わる秘密鍵が存在します。
以下はKubernetesを動作させるサーバーコンポーネントです。

Kubernetesにアクセスするクライアントも同様にTLS認証を使用します。

そして、ここで生成されるすべてのCertificateの公開鍵が本物であることを認証するCA(Certificate Authority)が存在する必要があります。
以下のようにETCD用のCAを分離して運用することもできますが、一般的には1つのCAで運用します。この場合、クラスター全体に ca.crt、ca.key の1ペアが存在します。

147 TLS in Kubernetes - Certification Creation
Certificate作成ツールとして有名なものにEASYRSA、CFSSL、OPENSSLがありますが、ここではOPENSSLを中心に説明します。
CA(Certificate Authority)のKeyを生成するには、以下のコマンドで行えます。
openssh genrsa -out ca.key 2048 は秘密鍵の作成、openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr は署名済み公開鍵の作成、openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt は署名済み公開鍵を秘密鍵で暗号化するコマンドです。

Admin Userを作成する際、ca.crtとca.keyを使用してadmin.csrを暗号化(CAで証明書を発行)します。

以降に作成する必要があるほとんどのアカウントも同様のプロセスです。.crtはIDに相当し、.keyはパスワードに相当すると考えてください。
Certificate Signing Requestで署名する際、/CN=kube-admin/O=system:masters を追加してadminアカウントと一般アカウントを区別します。このように作成されたadmin certificateは、curlコマンドの実行時に使用するか、kube-config.yamlに登録して使用します。

ETCDクラスターはHigh Availabilityで構成されるため、Peer certificationという追加のcertificationも必要です。

APIサーバーは最も多く使用されるため、名前が多数あります(kubernetes、kubernetes.default、kubernetes.default.svc、kubernetes.default.svc.cluster.local、IPアドレス)。そのため、CAを通じて署名する際にも、すべてのalternative名を明示する必要があります。


Kubeletはすべてのノードがそれぞれのcertificateを生成し、保持する必要があります。

上記で述べたように、1つのクラスターにも数多くのCertificateが存在します。これらすべてのCertificateが正しく設定されているかチェックし管理するのは、非常に困難で退屈な作業です。kubeadm ツールを活用すると、これらの作業を簡単に行えるようになります。
openssl x509 -in <.crt file name> -text -noout コマンドを使用して、Certificateの情報を確認できます。

kubectl logs etcd-master または docker ps -a と docker logs でログを分析して、問題を特定できます。
152 Certificates API
CA(Certificate Authority)は、CAの公開鍵とCAの秘密鍵の2つと言っても過言ではありません。もしこのキーが漏洩した場合、このキーを使って多くのユーザーを作成し、Kubernetesにアクセスするハッキングが発生する可能性があります。そのため、この2つのキーは必ず安全な場所に保管する必要があります。一般的にmaster nodeで作成・管理します。kubeadmは、このCA用キーペアの保管と管理も行います(キーの有効期限管理を含む)。
署名プロセスも簡単です。以下のようにkube-apiserverにユーザーを作成し、base64でエンコードして署名を要求するだけです。

base64でエンコードする際に -w 0 オプションを使うと1行にまとめることができ、これをrequest部分に貼り付けます。
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: akshay
spec:
signerName: youngju.dev.com/youngju
usages:
- digital signature
- key encipherment
- server auth
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZV3R6YUdGNU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQXBkakxiamFRNWZMdklvNlBXTFpYaEwwa25wKzZlaUZHd0ZHUFErdmhrNWFBClpyOURYUVJINE9IZ1JESktvUGVxdTRGeDB6TkJUQndKa0ZpZEl4U01rUWNsc0lVT3VncSsyMUNaaHlkd0hFYzgKK1AzQzk2L0JVN3o1T25nWVBsa0ZyZmdBcHhacWo2WVVyTHJpS2V4Q0JHZ0cybzVBN205Q2xzRUhhalQwYlU2SwowWGxCQW9sc2cyM3NMUE1jR1RSdGZ0bGhDUDJoQkZPdHhyRW9SUHFsSUdFdWxEVitpSjZFeVY2SW9NcDhFVmxKCmJvb2MzaW93UE8zOGx4SmwrcWloVGd0OHMwNlYwTHhML0pVejdJQ3FkdFQzcGkvNHJ6VzBFeE16eVNLcllxZjYKZDVNK1RVZDdMMk1vQjF1aTgxQXFJcVpBQWQybExCR0M5dm9vbWpwZWd3SURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBQXBpdUl4bU9mcEU2NEhTd0VCc1RsbnRScjVyZUNWMStlSzFKSVEyM0UyUXRiL2FrUGZuClNmRmpaRTB0ZmNaVUFHMzVoL214ZTVleEo3ZGNMN3dNT2wxYkVsTXNoTXlydlMyajVKckJhS3ZOaXFXVXo2VTYKbGRydWFlc0dUZDVoTncwVkNUaGpmaXNMcXBWZDI1TWlPcE11SFM0eXFVWUl4dGR1RGQzb01haFVTaXpyY1kzVwpXM0tycTBjSWxaU2p5L2twSzAwdmduclQ2ZlIzaTN4bE1jSkcrVTYzalBnYUhNUHM2MS9YZGhlL0s0dmRyeDllClc2MU1GalRGa0tpY25qYnBCNGZ1Uno3SmRRNW94TS90RFFveEtEYTNGTFNHcklvSGg1YkZlcDFESlZlaHkyeTcKekJFaXZiYWFjaEpGT2xvWHYrZnlVMVpUcS9JUTRLZEdjeU09Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
$ kubectl create -f akshay-csr.yaml
certificatesigningrequest.certificates.k8s.io/akshay created
kubectl get csr コマンドでCSRの署名要求一覧を確認し、kubectl certificate approve <csr-name> で承認します。その後、kubectl get csr jane -o yaml コマンドでCertificate情報を確認できます。
$ kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
akshay 79s youngju.dev.com/youngju kubernetes-admin <none> Pending
csr-xjv6n 36m kubernetes.io/kube-apiserver-client-kubelet system:node:controlplane <none> Approved,Issued
$ kubectl certificate approve akshay
certificatesigningrequest.certificates.k8s.io/akshay approved
$ kubectl certificate deny agent-smith
certificatesigningrequest.certificates.k8s.io/agent-smith denied
$ kubectl delete csr agent-smith
certificatesigningrequest.certificates.k8s.io "agent-smith" deleted

Certificate関連の操作はController Managerで管理されます。CSR-ApprovingとCSR-Signingの機能です。 そのため、kube-controller-manager.yamlにca.keyとca.crtを登録する必要があります。

155 KubeConfig
kubectlコマンドを入力するたびにcertificateを指定するのは非常に面倒です。

ホームディレクトリ配下の .kube/config ファイルに上記の情報を登録しておけば、kubectlコマンド実行時に自動的にこの情報が使用されるため便利です。
configファイルはフォーマットに従って記述する必要があり、Cluster、Context、Userの3つを定義します。

複数のcontextの中から、.kube/configファイルの current-context にデフォルトのcontextを定義できます。

kubectl config view でconfigファイルを確認できます。kubectl config use-context <context> でcontextを切り替えます。
各Clusterには複数のnamespaceがあり、contextフィールド内で定義できます。

159 API Groups
Kubernetes APIは複数のグループに分かれています。その中でもapiとapisは重要なAPIエンドポイントグループを提供します。


kubectl proxyを使用すると、権限を持つkube proxyサーバーがAPIサーバーへのリクエストを仲介してくれるため、certificateを指定する必要がありません。

160 Authorization
Authorizationは、Authenticationが完了した後、どこまでアクセスできるかを決定する権限管理です。

Node、Attribute-Based、Role-Based、Webhookの4つの方式でAuthorizationを管理できます。
Node Authorizationは、KubeletがAPIサーバーにアクセスする際に使用する権限です。

Attribute-Based権限管理は、ユーザーが追加されるたびにattributeを変更し、APIサーバーを再起動する必要があるため、管理が困難です。
それに対してRole-Basedは管理が容易です。

Authorization modeは、kube APIサーバーの起動時に設定できます。Node,RBAC,Webhook のように複数設定することも可能です。Authorization modeが複数設定された場合、指定した順序でAuthorizationが試行されます。

161 Role Based Access Controls
authorization-mode の確認方法:
$ kubectl describe pod kube-apiserver-controlplane -n kube-system
-- Authorization-mode=Node,RBAC
現在存在するRoleの確認方法:
$ kubectl get roles --all-namespaces
Roleの内容確認方法(configmapのget権限を持つRoleが存在):
$ kubectl describe role kube-proxy -n kube-system
Name: kube-proxy
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
configmaps [] [kube-proxy] [get]
Role Bindingの確認:
$ kubectl describe rolebinding kube-proxy -n kube-system
Name: kube-proxy
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: kube-proxy
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:bootstrappers:kubeadm:default-node-token
Role-Basedを使用するにはRoleを作成する必要があり、以下のようなファイルで作成できます。


role bindingでuserとroleを紐付けます。

can-iコマンドで権限チェックが可能です。

アクセス可能なResourceに対して、より細かい権限設定も可能です。
他のユーザーとして操作を行うには --as を使用します。
$ kubectl get pods --as dev-user
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "default"
164 Cluster Roles and Roles Bindings
APIリソースの中にはnamespace内に存在するものがありますが、cluster roleはその外に存在します。これをCluster Scopeと呼びます。一種のスーパー権限を持つroleであるため、使用には注意が必要です。

このうちCluster AdminやStorage Admin Roleは以下のような権限を持ちます。Roleの作成は ClusterRole kindで行えます。

作成したRoleとUserをBindingするには、以下のようなbindingが必要です。

$ kubectl get clusterroles
$ kubectl get clusterrolebindings
167 Service Account
Kubernetesには、2種類のアカウントがあります。UserとServiceです。名前からもわかるように、User Accountは人が使用するアカウントで、Service Accountはマシンやプログラムが使用するアカウントです。
kubectl create serviceaccount <account_name>
kubectl create token <account_name>
kubectl get serviceaccount
Service Accountを作成するとtokenが一緒に生成されます。このトークンは外部アプリケーションがAPIで認証する際に使用され、この情報はKubernetes secretとして保存・管理されます。

Podを作成する際にdefault Service accountのvolumeが生成されるのも、このtokenを保存するためです。
保存場所は /var/run/secret/kubernetes.io/serviceaccount 配下です。serviceAccountの保存を行いたくない場合は、Podに automountServiceAccountToken をfalseに設定します。
Podに保存されたこのトークンにアクセスするには、kubectl exec -it <pod_name> ls /var/run/secret/kubernetes.io/serviceaccount を使用します。

default service accountは非常に限定された権限を持っています。そのため、pod-definition.ymlの spec.serviceAccountName にservice accountを指定します。
Kubernetes 1.22バージョンから、有効期限のないトークンの脆弱性改善のためにService Account Token APIが導入されました。このAPIを使用すると有効期限付きのトークンが発行されるため、より安全です。TokenRequestが使用できない場合にのみ、service account token secret Objectを作成することが推奨されています。

170 Image Security
imageを取得する際、image名の前のユーザーを省略すると 'library' というユーザーがデフォルトで付加されます。
その前にregistryを設定でき、最も有名なpublic registryは docker.io と gcr.io です。
public registryにimageをアップロードしてはいけない場合は、private registryを使用できます。
docker login private-registry.io
docker run private-registry.io/apps/internal-app
private registryの場合、usernameとpasswordが必要ですが、これをそのままpod definitionにハードコーディングするとセキュリティ上問題があります。以下のようにsecretを作成して保存し、imagePullSecret セクションにsecret名を記入すれば、Podがprivate registryからimageを取得する際に問題ありません。

173 Security in docker
Dockerはコンテナを実行しますが、このプロセスを分離(isolate)するためにnamespaceという概念が導入されています。
ホストマシンで確認したコンテナのPIDと、コンテナ内部のPIDは異なります。また、デフォルトでは、明示的に指定しない場合、コンテナ内部でプロセスを実行するユーザーは root になります。

コンテナ内部の root ユーザーはホストの root ユーザーと同じ権限を持つのでしょうか?答えは「いいえ」です。では、ホストマシンをどこまで利用する権限があるのか気になるかもしれません。実際、Dockerはコンテナを実行するユーザーに制限された権限のみを付与します。さらに多くの権限を付与したい場合は、runコマンドに --cap-add <capability> を追加します。逆にprivilegeを削除するには --cap-drop を使用します。または、すべての権限をコンテナユーザーに付与するには --privileged を付けます。

174 Security Contexts
Dockerで設定していたcapabilityをコンテナレベルで設定するには、以下のようにします。Podレベルでは設定できません。

177 Network Policy
Kubernetesでは、クラスター内部のすべてのトラフィックを許可する戦略を取っています。しかし、何らかの理由で特定のポートやIPのみを許可する必要がある場合、Network Policyを設定できます。

以下のようにDB Podで3306ポートからのIngressのみを許可するNetwork Policyを設定すると、他のポートからのトラフィックは無視されます。

Network PolicyはSelectorでLabelを利用します。

Network Policyをサポートするアドオンには、Kube-router、Calico、Romana、Weave-netがあります。Flannelの場合はNetwork Policyをサポートしないため、アドオン選択時に考慮する必要があります。
178 Developing network policies

ingress内部でruleを複数に分けた場合、ORロジックで動作し、いずれかに該当するものがあればアクセスが許可されます。逆に - を削除して1つのブロックにまとめた場合は AND で動作します。ipBlock を定義して特定のIPがPodにアクセスすることを許可することもできます。

egress用のnetwork policyもingressと同様に定義できます。

Network Policyの作成方法は以下の通りです。
Policy Name: internal-policy Policy Type: Egress Egress Allow: payroll Payroll Port: 8080 Egress Allow: mysql MySQL Port: 3306
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: internal-policy
namespace: default
spec:
podSelector:
matchLabels:
name: internal
policyTypes:
- Egress
- Ingress
ingress:
- {}
egress:
- to:
- podSelector:
matchLabels:
name: mysql
ports:
- protocol: TCP
port: 3306
- to:
- podSelector:
matchLabels:
name: payroll
ports:
- protocol: TCP
port: 8080
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
egressを複数作成する場合は以下のようにします。
kind: NetworkPolicy
metadata:
name: internal-policy
namespace: default
spec:
podSelector:
matchLabels:
name: internal
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
name: mysql
ports:
- protocol: TCP
port: 3306
- to:
- podSelector:
matchLabels:
name: payroll
ports:
- protocol: TCP
port: 8080
クイズ
Q1: 「CKA_7_Security」の主なトピックは何ですか?
CKA_7_Security
Q2: この記事の重要なポイントは何ですか?
CKA_7_Security
Q3: この記事の概念を実践にどう適用できますか?
記事全体で議論されている実践的な例やパターンを参考にしてください。