😎

【 更新者も必見 】 CKS 攻略ガイド ( 2023 年 6 月版 )

2023/06/19に公開

はじめに

こんにちは。クラウドエース株式会社で SRE をしている間瀬です。

業務外にはなりますが、先日私が所持している Certified Kubernetes Security Specialist ( 以下、CKS ) の有効期限日が近くなり、更新をしてきたので同じく 2 年ほど前に受験して更新を控えている方、これから新たに受験を検討されている方向けに攻略のためのポイントを紹介させていただこうと思います。

CKS について

cks-badge

本試験はセキュリティ観点で Kubernetes を保護するために必要な知識が問われる試験になっており、受験資格として Certified Kubernetes Administrator を保持している必要があります。
2 年前の 2021 年 6 月に受験した際は言語が英語のみとなっておりましたが、日本語版がリリースされているので日本語での受験も可能となっています。
受験費用は $395 なので、最近のレートだと日本円で 55,000 円ほどになります。前回受験時よりも費用上がった気がします。。

正確な試験費用や試験範囲については改訂されることがあるので、公式のサイトを確認するようにしてください。
https://training.linuxfoundation.org/ja/certification/certified-kubernetes-security-specialist/

前回受験時と比較した体験、感想

相変わらずのRTA感

問題に対して理解だけではなく、解答するスピードが要求されるのは相変わらずでした。
試験時間2時間に対して、 15 ~ 20 問程度出るので 15 問でも 5 min / 問程のスピード感で解答する必要があります。
合格点は 67% と高くはありませんが、問題によって配点が大きく異なるので優先度や時間の管理が肝となります。

クラスタバージョン

前回受験した際のバージョンは忘れてしまいましたが、今回受験した際のバージョンは v1.27 でした。

試験範囲

試験で問われる範囲については前回受験時とほとんど変わらない印象を受けました。
強いて言えば、クラスタバージョン v.1.21 で非推奨となった Pod Security Policy の代わりに Pod Security Admission が問われるようになったのかなという点くらいでした。
https://kubernetes.io/docs/concepts/security/pod-security-admission/

試験環境 (要注意)

ここは個人差が分かれますが、前回と比較して操作し辛くなっていました。前回は自身の PC 上のブラウザから試験環境へリモートしたり、公式 doc の参照が可能でしたが、昨年あたりから仕様が変わり PSI のクライアントソフトを使ってそこからリモートして Terminal や Firefox から 公式 doc の閲覧を行う必要がありました。
私の PC は Macbook Pro 14 インチ でしたが、リモート先の Terminal や ブラウザがディスプレイに収まらず、見切れてしまっていたりコピー & ペーストのショートカットが Mac と異なるところがあり苦戦しました。

初めての操作感ということもあり、1 回目の受験では全問を解くことができず合格点 67% に対して 63% で落ちてしまいました。また、2 回目の受験では試験開始 1 時間後くらいに PSI のソフトが突如終了するという事件が起き、その後 30 分程かけて再接続できましたが受験時間は戻らずで 1 回目と同じく 63% で落ち、結果的に 3 回目の受験で無事更新することができました。

2 回目の受験での事件については、サポートの対応を待てば再試験を受験することが可能だったかもしれませんが、2 日ほど待っても回答が来なかったため、再度受験資格を購入して受験し直しました。

活用したリソース

他の方が公開している記事と同じ内容にはなりますが、受験に際して活用したリソースを紹介します。

KodeCloud の 「Certified Kubernetes Security Specialist (CKS)」 コース

前回受験した際にも利用していたので、今回も利用しました。コンテンツのアップデートも随時されているようです。これだけやれば合格に必要な知識はカバーできるかと思います。

https://kodekloud.com/courses/certified-kubernetes-security-specialist-cks/

CKS Simulator

Linux Foundation のサイトから受験費用を支払うと利用できるようになる模擬試験です。2 回まで受験ができて、1回あたりの利用可能時間は 48 時間になっています。 KodeCloud のコースに加えてこちらは試験本番で使用する環境の操作に慣れられるので必ず利用することを推奨します。 私は 1 回目の受験前に利用しなかったのですが、これを利用しておけば合格できただろうなと思います。

攻略に向けた解説

紹介したリソースで学習することは可能ですが、いくつかポイントを絞って試験範囲となっている内容について解説したいと思います。

kube-bench によるベンチマークとセキュリテイ強化

kube-benchを利用してクラスタのセキュリティ対応状況をレポートすることできます。
試験ではレポートにて脆弱と評価される問題に対して対処できることが求められます。一見、難しそうに感じますが、レポートには対処方法まで記載されているのでその通りに対処すれば問題ありません。
対処する上では、各種クラスタコンポーネントの Manifest を修正する必要があるため Manifest の場所は分かるようにしておきましょう。

kube-benchのレポート例

[INFO] 1.3 Controller Manager
[WARN] 1.3.1 Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Manual)
[FAIL] 1.3.2 Ensure that the --profiling argument is set to false (Automated)
[PASS] 1.3.3 Ensure that the --use-service-account-credentials argument is set to true (Automated)
[PASS] 1.3.4 Ensure that the --service-account-private-key-file argument is set as appropriate (Automated)
[PASS] 1.3.5 Ensure that the --root-ca-file argument is set as appropriate (Automated)
[PASS] 1.3.6 Ensure that the RotateKubeletServerCertificate argument is set to true (Automated)
[PASS] 1.3.7 Ensure that the --bind-address argument is set to 127.0.0.1 (Automated)

~~~ 省略 ~~~

1.3.2 Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube-controller-manager.yaml
on the master node and set the below parameter.
--profiling=false

例えば上記の例では kube-controller-manager の Manifest のパラメータとして --profiling=false を追加すれば対処が可能です。
kube-bench を実行する場合、出力される量が多く操作性の問題からスクロールがしにくいので、 grep などを活用することで出力量を絞ってもいいかもしれません。

kube-bench | grep 1.3.2 -B 10 -A 10

コンポーネント間のTLS接続における最小バージョン、暗号スイートの設定

Kubernetes のコンポーネント間において通信する際にはTLSによる暗号化が行われています。この暗号化の設定について最小バージョン、暗号スイートの設定をできるようにしておくと良いと思います。
公式 doc にも記載されていますが、例えば kube-apiserver では Manifest へ以下のように設定します。 kube-apiserver の設定を見直した場合は他のコンポーネントの設定も併せて見直す必要がある場合があることに気をつけてください。

kube-apiserverへの設定

  • kube-apiserver
/etc/kubernetes/manifests/kube-apiserver.yaml

--tls-min-version=VersionTLS12
--tls-cipher-suites=TLS_AES_128_GCM_SHA256

コンポーネント毎のパラメータについては、公式 doc に記載されているので目を通すようにしてください。
https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

Audit

Kubernetes に対してデプロイ等の操作を行う際は基本的に kube-apiserver を経由しますが、kube-apiserver にて設定することで監査ログを出力して保管することが可能です。
監査ログが生成されるタイミングはステージという定義によって、1つのリクエストに対して4段階あります。

  • RequestReceived : リクエストを受信して処理が行われる前の時点
  • ResponseStarted : 応答ヘッダが生成されてから応答本文が返却される前の時点。長時間実行されるリクエストに対してのみ発生
  • ResponseComplete : リクエストに対して応答が完了した時点
  • Panic : エラー発生時

Audit の設定

監査ログを出力するためにはルールを定義する必要があります。ルール別に監査ログのレベル、対象のリソース、操作、 namespace 等を定義することが可能です。また、例えば広い範囲で取得して、一部明示的にリソースを指定して監査ログを出力しないといった制御も可能です。
レベルについては以下4つから選択します。

  • None : このルールに一致する監査ログを出力しない。
  • Metadata : リクエストのメタデータ(リクエストしているユーザー、タイムスタンプ、リソース、操作など)を出力するが、リクエストやレスポンスの本文は出力しない。
  • Request : リクエストのメタデータと本文を出力するが、レスポンス内容は出力しない。これは、リソース以外に対するリクエストには適用されないとのこと。
  • RequestResponse : リクエストのメタデータ、レスポンス含めた本文を出力する。上記と同じくリソース以外に対するリクエストには適用されないとのこと。

ルールの記載例については公式 doc の通りですが、以下のように yaml ファイルにて記載します。
リソース、操作や namespace といった単位に level を設定できるようにしておきましょう。

apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
rules:
  - level: RequestResponse
    resources:
    - group: ""
      resources: ["pods"]
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: ""
  - level: Request
    resources:
    - group: ""
      resources: ["configmaps"]
    namespaces: ["kube-system"]

個人的に見落としやすいと感じたのが、 deployments や statefulsets のようなリソースを指定する際は group を apps と指定する必要がある点です。
リソースによって group の値が異なることに注意してください。以下のコマンドによって確認することが可能です。

kubectl api-resources | grep deployments

↓以下、コマンドの結果例(一部加工しています)
NAME        SHORTNAMES APIVERSION NAMESPACED KIND
deployments deploy     apps/v1    true       Deployment

バージョンを抜いたAPIVERSIONの値を group として設定します。
例えばdeploymentsの場合は apps と指定することになります。

Audit の有効化

Policy ファイルの作成後は kube-apiserver の Manifest にて以下の設定を追加して有効化する必要があります。Policy ファイル、ログ出力先の設定する他に、保管日数等のパラメータも設定できるようにしておきましょう。

/etc/kubernetes/manifests/kube-apiserver.yaml

 --audit-policy-file=/etc/kubernetes/audit-policy.yaml ※ Policyファイル
 --audit-log-path=/var/log/kubernetes/audit/audit.log  ※ ログ出力先パス

 以下も状況によっては設定する。
 --audit-log-maxage     ※ファイルを保持する最大日数
 --audit-log-maxbackup  ※ファイルの最大保持数
 --audit-log-maxsize    ※ファイルあたりの容量をMB単位で記載

公式 doc の内容も確認するようにしてください。
https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/

Falco

Kubernetes 上で動作するワークロードの脅威検知のために利用することができるものです。
例えばコンテナへの shell 接続を検知したりすることができます。検知する脅威についてはルールとしてカスタマイズが可能になっています。

検知されたメッセージから対象のコンテナを特定したり、検知した際に出力されるメッセージのフォーマットを編集できるようにしておきましょう。

検知された脅威を確認する方法

Falcoが出力するログから確認することができます。ログを見る方法は幾つかありますが、私の場合は以下のコマンドで確認していました。

journalctl -u falco | grep xxxxx

検知した際に出力されるメッセージフォーマットの編集

Falco のルールを定義することができるファイルは2つあります。

/etc/falco/falco_rules.yaml       
/etc/falco/falco_rules.local.yaml

falco_rules.yaml ではデフォルトのルールを記載して、ルールを個別に編集したい場合は falco_rules.local.yaml へ記載することでデフォルトのルールをオーバーライドすることが可能です。
オーバライドする際はルール名を合わせれば良いので、 falco_rules.yaml より対象のルールをコピーして falco_rules.local.yaml へペーストした上で編集することを推奨します。ルールの変更を適用するためにプロセスの再起動を忘れないようにしましょう。

systemctl restart falco

また、メッセージへ時刻やコンテナ名を出力するにはフィールドクラスを指定します。Falco の公式 doc にて指定方法が記載されています。試験で Falco の doc は参照することができるので覚える必要はありませんが、すぐにページを開けるようにしておくと良いです。

https://falco.org/docs/reference/rules/supported-fields/

CKS Simulator でも出てきますが、メッセージの編集については cut コマンドも使えるようにしておくと良いと思います。

runtimeClass を指定したPodのデプロイ

gvisor や Kata といった低レベルのランタイムを Kubernetes で利用する場合は runtimeClass というオブジェクトを作成した上で Pod デプロイ時の Manifest にて指定する必要があります。
もちろん事前にノードへランタイムをインストールする必要もありますが、試験ではランタイム自体のインストールまでできる必要はなく、runtimeClass を作成して Pod で指定できるようにしておきましょう。

https://kubernetes.io/docs/concepts/containers/runtime-class/

  • runtimeClassの Manifest 例
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: myclass 
handler: myconfiguration 
  • runtimeClass を指定した Pod の Manifest 例
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

Service Account でのクレデンシャルのマウント無効化

Service Account を Pod に付与する場合、デフォルトではクレデンシャルがマウントされますが、不要な場合はマウントさせない方がセキュアとされています。

https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/

マウントさせない方法は以下の通り二つありますのでいずれの方法でも設定できるようにしておきましょう。

Service Account 作成時

以下のように automountServiceAccountToken を False とすることで設定可能です。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false

Pod デプロイ時

上記と同じパラメータを spec へ指定することで設定可能です。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...

Pod の Security Context の構成

Podを実行するユーザの制限、特権アカウントの利用制限、権限昇格の制限、ホストのファイルシステムを読み取り専用にするといった設定が可能です。
以下の doc を参照してそれぞれどんなことができるのか確認しておきましょう。

https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

AppArmor によるコンテナへのリソースアクセス制限

AppArmor はプログラムのリソースへのアクセスを制限する Linux カーネルセキュリティモジュールです。プロファイルをコンテナへ適用することでコンテナからリソースへのアクセスを制限することが可能です。
プロファイルを一から作成できる必要はありませんが、プロファイルを AppArmor へ登録して Pod 上のコンテナへ適用できるようにしておきましょう。

AppArmor のプロファイル登録

プロファイルは以下のような内容となっています。

#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

上記プロファイルが保存されたファイルを次のようなコマンドでAppArmorへ登録することが可能です。

apparmor_parser -a {プロファイルのパス}

以下のコマンドでAppArmorへ登録されているプロファイルを確認することができます。

aa-status

コンテナへの適用

Pod をデプロイするための Manifest の annotations へ以下のように指定することでコンテナへ適用することが可能です。

apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
  containers:
  - name: hello
    image: busybox

https://kubernetes.io/docs/tutorials/security/apparmor/

RBAC

CKA を保持している方であればご存知かと思いますが、Role , ClusterRole によって namespace 別、クラスターワイドなロールを作成することができ、 RoleBinding, ClusterRolebinding によって Service Account やユーザに対してロールを割り当てることが可能です。提示される条件に対してロール、割り当てができるようにしておきましょう。 CKA で問われる内容と特段変わらないかと思います。

https://kubernetes.io/docs/reference/access-authn-authz/rbac/

Secret で定義される値の出力、 Pod へのマウント

こちらも CKA で問われる内容と特段変わりませんが、Secret の値の参照方法、Podへのマウントをできるようにしておきましょう。

https://kubernetes.io/docs/concepts/configuration/secret/

Network Policy による Pod のアクセス制御

Network Policy によって Pod のアクセス制御を行うことが可能です。内向き ( Ingress ) 、外向き( Egress ) に対して制御することができます。
基本的には許可する条件を Label や IP アドレス範囲で設定することになります。逆に明示的に拒否するといった設定ができないことに注意してください。 IP アドレス範囲に関しては except で指定すれば拒否したいアドレス範囲を指定することが可能です。
特定の Label を持つ Pod に対してのみまたは、特定の Namespace 上の全ての Pod に対して適用するなど適切な範囲へポリシーを設定できるようにしておきましょう。

https://kubernetes.io/docs/concepts/services-networking/network-policies/

  • Network Policy の Manifest サンプル
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default  ※適用先のnamespace
spec:
  podSelector:
    matchLabels:
      role: db  ※適用対象とするPodをLabelで指定
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from: ※from配下の条件は AND 条件となります。from間については OR 条件になります。
        - ipBlock:
            cidr: 172.17.0.0/16
            except:
              - 172.17.1.0/24
        - namespaceSelector:
            matchLabels:
              project: myproject
        - podSelector:
            matchLabels:
              role: frontend
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
  • デフォルト Deny 設定例 (全拒否)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: {namespace名}
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Network Policy の適用先 Pod を指定する際は Label にて指定することになりますが、既に存在する Pod の Label を参照する際は --show-labels が便利です。

 kubectl get pods --show-labels

ImagePolicyWebhook の構成

Kubernetes の Admission Controllers という機能を使って Kubernetes に対して操作を行う際にリクエスト内容の補完や制限を行うことできます。ImagePolicyWebhook はそれらプラグインの中の一つで Pod としてデプロイする際にイメージの検証を行うことができます。

コンフィグファイルを構成して検証ができる状態にできるようにしておきましょう。また、コンフィグファイルの defaultAllow にてデフォルトの判定を制御できることも抑えておきましょう。
デフォルトでの判定について、例えばイメージを検証する AP に接続できない等のケースにおいて、検証結果を OK とするか NG とするかというものです。

https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

構成方法

  • コンフィグファイル (以下は yaml 形式ですが、 json でも構成可能です。)
imagePolicy:
  kubeConfigFile: /path/to/kubeconfig/for/backend ※ 検証する際に利用するkubeconfigファイルを指定する。以下にてサンプルを紹介
  # time in s to cache approval
  allowTTL: 50
  # time in s to cache denial
  denyTTL: 50
  # time in ms to wait between retries
  retryBackoff: 500
  # determines behavior if the webhook backend fails
  defaultAllow: true
  • kubeconfigファイル
    clusters の server にてイメージを検証するバックエンドのURLを指定します。
# clusters refers to the remote service.
clusters:
  - name: name-of-remote-imagepolicy-service
    cluster:
      certificate-authority: /path/to/ca.pem    # CA for verifying the remote service.
      server: https://images.example.com/policy # URL of remote service to query. Must use 'https'.

contexts:
- context:
    cluster: name-of-remote-imagepolicy-service
    user: name-of-api-server
  name: sample
current-context: sample

# users refers to the API server's webhook configuration.
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # cert for the webhook admission controller to use
      client-key: /path/to/key.pem 
  • kube-apiserver での有効化
    ImagePolicyWebhook を有効化するには kube-apiserver にてパラメータを設定する必要があります。
/etc/kubernetes/manifests/kube-apiserver.yaml

--admission-control-config-file={コンフィグファイル}

Trivy によるコンテナイメージスキャン

Trivy はコンテナイメージ内の脆弱性有無をスキャンすることができます。
High や Critical といった緊急性の高い脆弱性の有無を確認できるようにしておきましょう。
trivy の doc は参照できないので、コマンドも覚えておきましょう。

  • High, Critical のみ表示するようにイメージをスキャン
trivy image --severity HIGH,CRITICAL {image:version} 
  • Pod 別のイメージの一覧化方法
    Image のスキャンを行う上では Pod 別に Image を一覧で参照できるよう以下のようなコマンドを利用していました。
 kubectl get pods -o=custom-columns=NAME:metadata.name,IMAGE:spec.containers[*].image

↓コマンドの結果
NAME             IMAGE
hello-apparmor   busybox
nginx            nginx:alpine
nginx-w          nginx:alpine,redis:alpine

CKS を攻略する上で特に重要なポイントを紹介させていただきました。これらは上記にて紹介している学習リソースを利用することでいずれも学ぶことが可能ですので活用してください。

まとめ

自身の振り返りも込めて再認定に向けて学習して良かったこと、特に重要と感じた点について紹介させていただきました。これから新規に取得される方や更新される方の役に立てば幸いです。
余談ですが、試験では浴室にて受験しています。試験開始前に周囲に人がいないことや電子機器がないようにしなければいけないため、個人的には浴室受験がベストプラクティスだと思っています。よろしければ皆さんも浴室にて受験してみてください。
記事を閲覧いただきありがとうございました。

Discussion