前言:
cka和cks认证真的比较恶心,他们的那个PSI Bridge Secure Browser真的非常卡。
吐槽完毕,不废话,直接上真题解析。
CKS总共是16道题,题目顺序是打乱的,由于认证系统非常卡,因此,不建议使用官方文档,本文采用尽量避开官方文档的方式解析。
注:考试使用的是kubernetes1.25,整个系统有N个kubernetes集群。
一,
Context
针对 kubeadm 创建的 cluster 运行 CIS 基准测试工具时,发现了多个必须立即解决的问题。
Task
通过配置修复所有问题并重新启动受影响的组件以确保新的设置生效。
修复针对 API 服务器发现的所有以下违规行为:
1.2.7 Ensure that the --authorization-mode argument is not set to AlwaysAllow FAIL
1.2.8 Ensure that the --authorization-mode argument includes Node FAIL
1.2.9 Ensure that the --authorization-mode argument includes RBAC FAIL
1.2.18 Ensure that the --insecure-bind-address argument is not set FAIL (1.25 中这项题目没给出,但最好也检查一下,模拟环境里需要
改)
1.2.19 Ensure that the --insecure-port argument is set to 0 FAIL (1.25 中这项题目没给出,不需要再修改了)
修复针对 kubelet 发现的所有以下违规行为:
Fix all of the following violations that were found against the kubelet:
4.2.1 Ensure that the anonymous-auth argument is set to false FAIL
4.2.2 Ensure that the --authorization-mode argument is not set to AlwaysAllow FAIL
注意:尽可能使用 Webhook 身份验证/授权。
修复针对 etcd 发现的所有以下违规行为:
Fix all of the following violations that were found against etcd:
2.2 Ensure that the --client-cert-auth argument is set to true FAIL
解析:
kube-bench是干什么的工具我在这就不班门弄斧了,很明显,题目是指定了一个集群,而kube-bench工具主要是检测的集群配置,因此,我们需要准确的登陆到集群的master节点上,然后,根据题目要求修改配置文件。
题目要求的修复对象包括kube-apiserver,kubelet,etcd这三个组件的配置文件,kube-apiserver和etcd都是静态pod的形式部署的。因此,我们修改文件后等待一段时间,这两个组件就会自动重启,而kubelet服务是yum安装的,因此,需要systemctl daemon-reload&&systemctl restart kubelet让改动生效即可。
可以用kube-bench确认一下题目里的要求是否正确:
kube-bench run --targets=master
kube-bench run --targets=node
kube-bench run --targets=etcd
例如,第一个命令输出如下:
输出中有如何修改的方法,这个不需要去官方文档查看,自己就可以搞定。
[FAIL] 1.2.6 Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)
[FAIL] 1.2.7 Ensure that the --authorization-mode argument includes Node (Automated)
[FAIL] 1.2.8 Ensure that the --authorization-mode argument includes RBAC (Automated)
[FAIL] 1.2.18 Ensure that the --insecure-bind-address argument is not set (Automated)
[FAIL] 1.2.19 Ensure that the --insecure-port argument is set to 0 (Automated)
。。。
1.2.7 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the master node and set the --authorization-mode parameter to values other than AlwaysAllow.
One such example could be as below.
--authorization-mode=RBAC
1.2.8 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the master node and set the --authorization-mode parameter to a value that includes Node.
--authorization-mode=Node,RBAC
1.2.9 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the master node and set the --authorization-mode parameter to a value that includes RBAC,
for example:
--authorization-mode=Node,RBAC
1.2.18 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the master node and remove the --insecure-bind-address parameter
kubectl config use-context KSCS00201
切换集群后,查看该集群有几个节点,并ssh到该集群的主节点:
假设主节点名称是master01
kubectl get no -owide
ssh master01
1,kube-apiserver配置文件的修改
修改之前,备份一下配置文件。
mkdir bak1
cp /etc/kubernetes/manifests/kube-apiserver.yaml bak1/
vim /etc/kubernetes/manifests/kube-apiserver.yaml
#修改、添加、删除相关内容
#修改 authorization-mode,注意 Node 和 RBAC 之间的符号是英文状态的逗号,而不是点。
- --authorization-mode=Node,RBAC
#删除 insecure-bind-address,考试中,有可能本来就没写这行。
- --insecure-bind-address=0.0.0.0
2,kubelet配置文件的修改
修改之前,备份一下配置文件。
mkdir bak1
cp /var/lib/kubelet/config.yaml bak1/
vim /var/lib/kubelet/config.yaml
修改
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous: #修改 anonymous 下的,将 true 改为 false
enabled: false #改为 false
webhook:
cacheTTL: 0s
enabled: true #这个 webhook 下的 true 不要改
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization: #修改 authorization 下的
mode: Webhook #改为 Webhook
webhook:
……
#编辑完后重新加载配置文件,并重启 kubelet
systemctl daemon-reload
systemctl restart kubelet.service
修改之前,备份一下配置文件。
mkdir bak1
cp /etc/kubernetes/manifests/etcd.yaml bak1/
vim /etc/kubernetes/manifests/etcd.yaml
修改
- --client-cert-auth=true #修改为 true
Context
您组织的安全策略包括:
⚫ ServiceAccount 不得自动挂载 API 凭据
⚫ ServiceAccount 名称必须以“-sa”结尾
清单文件 /cks/sa/pod1.yaml 中指定的 Pod 由于 ServiceAccount 指定错误而无法调度。
请完成一下项目:
Task
1. 在现有 namespace qa 中创建一个名为 backend-sa 的新 ServiceAccount,
确保此 ServiceAccount 不自动挂载 API 凭据。
2. 使用 /cks/sa/pod1.yaml 中的清单文件来创建一个 Pod。
3. 最后,清理 namespace qa 中任何未使用的 ServiceAccount
解析:
三个任务,第一个任务是创建一个名称为backend-sa的sa,第二个任务是部署pod,当然是使用新的sa,第三个任务是清理多余的sa
第一个任务:新创建的sa backend-sa 要求不自动挂载api凭据,serviceAccount创建使用命令生成模板文件,然后修改模板文件,在metadata下添加auotmountServiceAccountToken: false即可(auotmountServiceAccountToken不支持命令行方式,所以先生成模板文件,然后在修改,最后应用创建)
第二个任务:修改/cks/sa/pod1.yaml文件,该文件内挂载的serviceAccount指定上一步新创建的sa backend-sa即可。主要是serviceAccountName:backend-sa ,这一段是在pod文件的spec下面。
第三个任务:kubectl get sa -n qa 先查询有哪些sa在这个namespace下,然后kubectl get po -n qa -oyaml|grep serviceAccountName查询在使用的serviceAccount有哪些,删除没有使用的serviceAccount即可
答题:
按题目要求,先做第一个任务:
生成模板文件
kubectl create sa backend-sa -n qa --dry-run=client -oyaml >2-sa.yaml
编辑模板文件2-sa.yaml .在metadata下添加auotmountServiceAccountToken: false
apiVersion: v1
kind: ServiceAccount
metadata:
name: backend-sa #修改 name
namespace: qa #注意添加 namespace
automountServiceAccountToken: false #修改为 false,表示不自动挂载 secret
然后应用此文件,如果模板文件修改有问题,将不会应用成功,继续修改文件即可
kubectl apply -f 2-sa.yaml
第二个任务:
创建 Pod 使用该 ServiceAccount
vi /cks/sa/pod1.yaml
修改如下内容
……
metadata:
name: backend
namespace: qa #注意命名空间是否对
spec:
serviceAccountName: backend-sa # 没有则添加一行,有则修改这一行为刚才创建的 ServiceAccount(考试时,默认已有这一行,需要修改。)
containers:
……
第三个任务:
查看所有 sa
kubectl get sa -n qa
查看已经在用的 sa
kubectl get pod -n qa -o yaml | grep -i serviceAccountName
删除不用的 sa
kubectl delete sa test01 -n qa
Context
绑定到 Pod 的 ServiceAccount 的 Role 授予过度宽松的权限。完成以下项目以减少权限集。
Task
一个名为 web-pod 的现有 Pod 已在 namespace db 中运行。
编辑绑定到 Pod 的 ServiceAccount service-account-web 的现有 Role,仅允许只对 services 类型的资源执行 get 操作。
在 namespace db 中创建一个名为 role-2 ,并仅允许只对 namespaces 类型的资源执行 delete 操作的新 Role。
创建一个名为 role-2-binding 的新 RoleBinding,将新创建的 Role 绑定到 Pod 的 ServiceAccount。
注意:请勿删除现有的 RoleBinding
解析:
此题是有一定的迷惑性的, 名称为service-account-web的sa是通过rolebinding绑定role和serviceAccount的,而题目只告诉了sa的名称是service-account-web,因此,我们需要先查询出namespace db 下的rolebinding的名称(rolebinding是有namespace限定的),然后通过此rolebinding查询出role,然后修改此role。
第二步是创建一个名称为role-2的role,并对这个新的role赋予指定权限,然后创建名为role-2-binding的新绑定,绑定新角色role2和service-account-web
这里有一个点,一个serviceaccount可以通过rolebinding绑定多个role(比较拗口,但请仔细阅读理解)
root@k8s-master:~# kubectl get rolebindings.rbac.authorization.k8s.io -n db -owide
NAME ROLE AGE USERS GROUPS SERVICEACCOUNTS
role1-db Role/role1 31m db/service-account-web
第二个命令:
kubectl edit role -n db role1
输出如下:
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: "2023-01-15T09:04:27Z"
name: role1
namespace: db
resourceVersion: "692739"
uid: 12c9ff1a-4e30-4f09-99ab-01fb42956186
rules:
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list #删除这一行
- watch#删除这一行
- create#删除这一行
第三个命令:
按题目要求创建新的role-2
这一步不需要看官方文档,命令补全就可以十分轻松的创建好了
root@k8s-master:~# kubectl create role role-2 -n db --verb=delete --resource=namespace
role.rbac.authorization.k8s.io/role-2 created
第四个命令:
root@k8s-master:~# kubectl create rolebinding role-2-binding -n db --role=role-2 --serviceaccount=db:service-account-web
rolebinding.rbac.authorization.k8s.io/role-2-binding created
四,默认网络策略
题目:
Context
一个默认拒绝(default-deny)的 NetworkPolicy 可避免在未定义任何其他 NetworkPolicy 的 namespace 中意外公开 Pod。
Task
为所有类型为 Ingress+Egress 的流量在 namespace testing 中创建一个名为 denypolicy 的新默认拒绝 NetworkPolicy。
此新的 NetworkPolicy 必须拒绝 namespace testing 中的所有的 Ingress + Egress 流量。
将新创建的默认拒绝 NetworkPolicy 应用与在 namespace testing 中运行的所有 Pod。
你可以在 /cks/net/p1.yaml 找到一个模板清单文件。
解析:
这道题目还是需要官方文档的,不过有模板文件,只是参考一下而已,基本算是送分题
网络策略 | Kubernetes
上面是官网的示例,按题目要求修改微调一下就可以了
解答:
vim /cks/net/p1.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: denypolicy
namespace: testing
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
应用以上文件:
kubectl apply -f /cks/net/p1.yaml
五,
Task
创建一个名为 pod-restriction 的 NetworkPolicy 来限制对在 namespace dev-team 中运行的 Pod products-service 的访问。
只允许以下 Pod 连接到 Pod products-service
⚫ namespace qaqa 中的 Pod
⚫ 位于任何 namespace,带有标签 environment: testing 的 Pod
注意:确保应用 NetworkPolicy。
解析:
很明显,这个networkPolicy创建的时候需要指定namespace是dev-team,名称是pod-restriction
由于是允许其它pod连接到Pod products-service,因此,策略应该是ingress,也就是入站策略
入站策略指定了两个范围一个是namespace为qaqa(由于网络策略是基于标签的,因此,本题需要查看namespace qaqa的标签),一个是带有指定标签envirnment:testing的pod,因此,该策略需要两个from
解答:
查询namespace qaqa的标签:
kubectl get ns qaqa --show-labels
查询Pod products-service的标签:
kubectl get po products-service -n dev-team --show-labels
vi /cks/net/po.yaml
根据官网,修改为如下内容:
……
metadata:
name: pod-restriction #修改
namespace: dev-team #修改
spec:
podSelector:
matchLabels:
environment: testing #根据题目要求的标签修改,这个写的是 Pod products-service 的标签,也就是使用 kubectl get pod -n dev-team --show-labels
查出来的 pod 的标签,这个标签不要和题目里要求的“位于任何 namespace,带有标签 environment: testing 的 Pod”这句话里的标签混淆了,两个没有关系,所以
可不一样。比如你考试时查出来的 POD products-service 的标签是 name: products,那这里的 environment: testing 就要换成 name: products。
policyTypes:
- Ingress #注意,这里只写 - Ingress,不要将 - Egress 也复制进来!
ingress:
- from: #第一个 from
- namespaceSelector:
matchLabels:
name: qaqa #命名空间有 name: qaqa 标签的
- from: #第二个 from
- namespaceSelector: {} #修改为这样,所有命名空间
podSelector: #注意,这个 podSelector 前面的“-” 要删除,换成空格,空格对齐要对。
matchLabels:
environment: testing #有 environment: testing 标签的 Pod,这个地方是根据题目要求“Pods with label environment: testing , in any namespace”,这
句话里的 pod 标签写的。不要和上面 spec 里的混淆
六,创建 Secret
题目:
Task
在 namespace istio-system 中获取名为 db1-test 的现有 secret 的内容
将 username 字段存储在名为 /cks/sec/user.txt 的文件中,并将 password 字段存储在名为 /cks/sec/pass.txt 的文件中。
注意:你必须创建以上两个文件,他们还不存在。
注意:不要在以下步骤中使用/修改先前创建的文件,如果需要,可以创建新的临时文件。
在 istio-system namespace 中创建一个名为 db2-test 的新 secret,内容如下:
username : production-instance
password : KvLftKgs4aVH
最后,创建一个新的 Pod,它可以通过卷访问 secret db2-test :
Pod 名称 secret-pod
Namespace istio-system
容器名 dev-container
镜像 nginx
卷名 secret-volume
挂载路径 /etc/secret
解析:
此题有三个任务,
第一个任务是:要求查看已存在的名为db1-test的位于namespace istio-system的secrets,然后将编码存储的username和password解密为明文后存放到 /cks/sec/user.txt和/cks/sec/pass.txt 这个任务基本是送分的
第二个任务是按照题目要求,创建新的secrets
第三个任务是创建规定名称的pod,并引用第二个任务创建的secrets
这三个任务都可以不使用官方文档就可以完成,只需要使用命令即可。
那么,第一个任务先做一个简单的演示(随意创建一个secrets,简单的加密形式,username是zsk,password是123456):
注意,secrets后面紧跟 generic,test是secrets的名称
kubectl create secret generic -n kube-system test --from-literal=username=zsk --from-literal=password=123456
查看这个secrets:
kubectl get secrets test -n kube-system -o jsonpath={.data}
{"password":"MTIzNDU2","username":"enNr"}
解密后对比解密结果是否符合我们前面创建的值:
root@k8s-master:~# echo "MTIzNDU2" |base64 -d
123456root
root@k8s-master:~# echo "enNr" |base64 -d
zsk
可以看到解密后的明文是符合我们创建的时候的值的,因此,第一个和第二个任务还是用命令就可以比较简单的完成。
第三个任务可以复制kube-apiserver.yaml里的关于volume挂载,这个也是不需要官方文档就可以搞定的。
解答:
1,
kubectl get secrets db1-test -n istio-system -o jsonpath={.data}
会反馈结果为:{"password":"aGVsbG8=","username":"ZGIx"}
解密,并将结果写入指定文件
echo 'ZGIx'|base64 -d > /cks/sec/user.txt
echo 'aGVsbG8='|base64 -d > /cks/sec/pass.txt
2,
创建名为 db2-test 的 secret 使用题目要求的用户名和密码作为键值。注意要加命名空间。
注意,如果密码中有特殊字符(例如:$,\,*,= 和 !),需要加单引号来转义--from-literal=password='G!Y\*d$zDsb'这样。
kubectl create secret generic db2-test -n istio-system --from-literal=username=production-instance --from-literal=password=KvLftKgs4aVH
3,
根据题目要求,创建 Pod 使用该 secret
vim k8s-secret.yaml
添加如下内容
apiVersion: v1
kind: Pod
metadata:
name: secret-pod #pod 名字
namespace: istio-system #命名空间
spec:
containers:
- name: dev-container #容器名字
image: nginx #镜像名字
volumeMounts: #挂载路径
- name: secret-volume #卷名
mountPath: /etc/secret
volumes:
- name: secret-volume #卷名
secret:
secretName: db2-test #名为 db2-test 的 secret
创建
kubectl apply -f k8s-secret.yaml
七,Dockerfile 检测
题目:
Task
分析和编辑给定的 Dockerfile /cks/docker/Dockerfile(基于 ubuntu:16.04 镜像),
并修复在文件中拥有的突出的安全/最佳实践问题的两个指令。
分析和编辑给定的清单文件 /cks/docker/deployment.yaml ,
并修复在文件中拥有突出的安全/最佳实践问题的两个字段。
注意:请勿添加或删除配置设置;只需修改现有的配置设置让以上两个配置设置都不再有安全/最佳实践问题。
注意:如果您需要非特权用户来执行任何项目,请使用用户 ID 65535 的用户 nobody 。
只修改即可,不需要创建
解析:
这一题是两个任务,第一个任务是dockerfile的安全排查,dockerfile里是镜像版本不正确,需要修改为ubuntu:16.04 还一个是USER 指定的是root,修改为nobody就可以了,十分简单的两个地方。
第二个任务是/cks/docker/deployment.yaml 文件的安全排查,一个是pod的安全上下文权限问题,一个是标签问题
解答:
编辑/cks/docker/Dockerfile
<1> 修改 Dockerfile
vi /cks/docker/Dockerfile
1、仅将 CMD 上面的 USER root 修改为 USER nobody,不要改其他的
USER nobody
2、修改基础镜像为题目要求的 ubuntu:16.04
FROM ubuntu:16.04
编辑 /cks/docker/deployment.yaml
三个标签一致即可;
下图是正确的
此题也是送分题,没什么好说的。
八,沙箱运行容器 gVisor
题目:
该 cluster 使用 containerd 作为 CRI 运行时。containerd 的默认运行时处理程序是 runc。
containerd 已准备好支持额外的运行时处理程序 runsc (gVisor)。
Task
使用名为 runsc 的现有运行时处理程序,创建一个名为 untrusted 的 RuntimeClass。
更新 namespace server 中的所有 Pod 以在 gVisor 上运行。
您可以在 /cks/gVisor/rc.yaml 中找到一个模版清单。
解析:
这道题是两个任务,第一个任务创建一个容器运行时类,第二个任务是应用第一个任务创建的容器运行时类到名为server的namespace下的所有pod,也就是重新编辑pod
runrc是已经创建好的,直接使用即可,这里就不啰嗦如何创建runrc了
解答:
官方文档:容器运行时类(Runtime Class) | Kubernetes
根据官方文档修改即可:
1 创建 RuntimeClass
vi /cks/gVisor/rc.yaml
修改或添加如下内容
apiVersion: node.k8s.io/v1 ##将 apiVersion: node.k8s.io/v1beta1 修改为 apiVersion: node.k8s.io/v1。这个在 1.25 正式考试的时候,是对的,不需要修改的。
kind: RuntimeClass
metadata:
name: untrusted # 用来引用 RuntimeClass 的名字,RuntimeClass 是一个集群层面的资源
handler: runsc # 对应的 CRI 配置的名称
创建
kubectl create -f /cks/gVisor/rc.yaml
2 将命名空间为 server 下的 Pod 引用 RuntimeClass。
考试时,3 个 Deployment 下有 3 个 Pod,修改 3 个 deployment 即可。
kubectl -n server get deployment
编辑 deployment
kubectl -n server edit deployments busybox-run
kubectl -n server edit deployments nginx-host
kubectl -n server edit deployments run-test
修改如下内容
spec: #找到这个 spec,注意在 deployment 里是有两个单独行的 spec 的,要找第二个,也就是下面有 containers 这个字段的 spec。
runtimeClassName: untrusted #添加这一行,注意空格对齐,保存会报错,忽略即可。
containers:
- image: nginx:1.9
imagePullPolicy: IfNotPresent
name: run-test
未完待续!!!!!!!!!!!!!!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~