5、审计日志
Task
在 cluster 中启用审计日志。为此,请启用日志后端,并确保:
- 日志存储在 /var/log/kubernetes/audit-logs.txt
- 日志文件能保留 10 天
- 最多保留 2 个旧审计日志文件
/etc/kubernetes/logpolicy/sample-policy.yaml 提供了基本策略。它仅指定不记录的内容
注意:基本策略位于cluster 的master 节点上。
编辑和扩展基本策略以记录:
- RequestResponse 级别的 persistentvolumes 更改
- namespace front-apps 中 configmaps 更改的请求体
- Metadata 级别的所有 namespace 中的 ConigMap 和 Secret 的更改
此外。添加一个全方位的规则以在 Metadata 级别记录所有其他请求。
注意:不要忘记应用修改后的策略。
/etc/kubernetes/logpolicy/sample-policy.yaml 内容如下:
kind: Policy
omitStages:
- "RequestReceived"
rules:
# Don't log watch requests by the "system:kube-proxy" on endpoints or services
- level: None
users: ["system:kube-proxy"]
verbs: ["watch"]
resources:
- group: # core API group
resources: ["endpoints","services"]
# Don't log authenticated requests to certain non-resource URL paths
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*" # Wildcesd matching.
- "/version"
#Please do not delete the above rule content, you can continue to add it below
这题比较复杂,建议考试时打开文档来做,在官方文档搜审计 或 audit log找到审计日志的相关介绍
5.1 启用审计日志
启用审计日志,并设置相应的日志保存参数。点击右边的"Log后端"找到在kube-apiserver中配置log审计后端的方法。
# vi /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --audit-policy-file=/etc/kubernetes/logpolicy/sample-policy.yaml ##定义了日志策略配置文件
- --audit-log-path=/var/log/kubernetes/audit-logs.txt ##定义了日志存储文件
- --audit-log-maxage=10 ## 最多保留10天
- --audit-log-maxbackup=2 ## 最多保留2个旧审计日志文件
接着挂载数据卷,把日志写到本地的存储和把本地的日志策略配置放进去。
# vi /etc/kubernetes/manifests/kube-apiserver.yaml
...
volumeMounts:
- mountPath: /var/log/kubernetes/
name: audit-log
readOnly: false
- mountPath: /etc/kubernetes/logpolicy/sample-policy.yaml
name: audit
readOnly: true
...
volumes:
- name: audit
hostPath:
path: /etc/kubernetes/logpolicy/sample-policy.yaml
type: File
- name: audit-log
hostPath:
path: /var/log/kubernetes/
type: DirectoryOrCreate
上面的yaml配置文件和日志文件路径保持一致。
5.2、修改日志策略配置
点击“审计策略”,官方给出了一个比较详细的示例,基本在里面就能找到题目要求的策略配置。注意,考试时不要更改或者删除策略配置文件中原有的配置项。只需增加题目要求的配置项即可。
#vi /etc/kubernetes/audit/policy.yaml
...
# RequestResponse 级别的 persistentvolumes 更改
- level: RequestResponse
resources:
- group: ""
resources: ["persistentvolumes"]
# namespace front-apps 中 configmaps 更改的请求体
- level: Request
resources:
- group: ""
resources: ["configmaps"]
namespaces: ["front-apps"]
# Metadata 级别的所有 namespace 中的 ConigMap 和 Secret 的更改
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps"]
# Metadata 级别记录所有其他请求
- level: Metadata
omitStages:
- "RequestReceived"
基本格式官网都给出了,主要是修改下资源类型和命名空间。
5.3、应用日志策略
这里要注意,如果之前没启用过日志策略,那么就是正常的重启kubelet就能加载日志策略生效
# systemctl daemon-reload
# systemctl restart kubelet
但是如果你之前已经启用了日志策略,例如你做了第一部分然后就重启kubelet了,这个时候你再去修改日志策略文件,想通过重启kubelet来让新的策略生效是不行的!这个时候要用下面的方法
#cd /etc/kubernetes/manifests
#mv kube-apiserver.yaml ..
#docker ps|grep apiserver ## 观察直到没有输出
#mv ../ kube-apiserver.yaml .
这样重启完apiserver就会重新加载策略配置文件。
如果apiserver的配置修改有问题或者日志策略配置文件有问题,都会导致其起不来,这时候记得再检查下,看是不是有空格或者单词有没写错。也可以看看默认日志提示什么
# ls -rlt /var/log/containers/*apiserver*
6、创建 Secret
在namespace istio-System 中获取名为 db1-test 的现有 secret 的内容
将 username 字段存储在名为 /cks/sec/user.txt 的文件中,并将 password 字段存储在名为 /cks/sec/pass.txt 的文件中。
注意:你必须创建以上两个文件,他们还不存在。
注意:不要在以下步骤中使用/修改先前创建的文件,如果需要,可以创建新的临时文件。
在 istio-system namespace 中创建一个名为 db2-test 的新 secret,内容如下:
usemname : production-instance
password : KvLftKgs4aVH
最后,创建一个新的 Pod,它可以通过卷访问 secret db2-test
Pod 名称 secret-pod
Namespace istio-system
容器名 dev-container
镜像 nginx
卷名 secret-volume
挂载路径 /etc/secret
6.1 查看 Secret 的值
一般通过get来获取,输出格式使用jsonpath,记得获取出来后要使用 base64 -d 解编码。
#kubectl get secrets db1-test -n istio-System -o jsonpath='{.data.username}'|base64 -d > /cks/sec/user.txt
#kubectl get secrets db1-test -n istio-System -o jsonpath='{.data.password}'|base64 -d > /cks/sec/pass.txt
如果不清楚jsonpath路径,可以先用json格式输出看看
#kubectl get secrets db1-test -n istio-System -o json
6.2 创建新 Secret
直接使用命令行创建,注意secret类型是generic,如果忘了,直接用-h参数查看
# kubectl create secret generic -n istio-system db2-test --from-literal=username=production-instance --from-literal=password=KvLftKgs4aVH
6.3 创建pod挂载Secret
先通过命令生成符合要求的pod的yaml,再修改yaml添加sercet内容。最后再应用该yaml文件。
#kubectl run secret-pod --image=nginx -n istio-system -o yaml --dry-run=client > 6_pod.yaml
然后修改这个yaml文件.建议官方文档搜 secret 第一篇,点击右边“使用场景: 在Secret卷中带句点的文件"找到例子。
containers:
- image: nginx
name: secret-pod
resources: {}
imagePullPolicy: IfNotPresent
volumeMounts: # add
- name: secret # add
mountPath: /etc/secret # add
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes: # add
- name: secret # add
secret: # add
secretName: db2-test # add
最后apply生效,可以查看一下是否已挂载
#kubectl apply -f 6_pod.yaml
#kubectl -n istio-system describe pod secret-pod
...
Volumes:
secret:
Type: Secret (a volume populated by a Secret)
SecretName: db2-test
Optional: false
7、Dockerfile检测
Task
分析和编辑给定的 Dockerfile /cks/docker/Dockerfile (基于 ubuntu:16.04 镜像)
并修复在文件中拥有的突出的安全/最佳实践问题的两个指令。
分析和编辑给定的清单文件 /cks/docker/deployment.yaml
并修复在文件中拥有突出的安全/最佳实践问题的两个字段
注意: 请勿添加或删除配置设置;只需修改现有的配置设置让以上两个配置设置都不再有安全/最佳实践问题。
注意: 如果您需要非特权用户来执行任何项目,请使用用户D 65535 的用户 nobody 。
只修改即可,不需要创建
/cks/docker/Dockerfile 文件内容如下
FROM ubuntu:last
RUN apt-get install -y wget curl gcc gcc-c++ make openssl-devel pcre-devel gd-devel \
iproute net-tools telnet && \
yum clean all && \
rm -rf /var/cache/apt/*
USER root
COPY sunnydale.sh .
ADD nginx-1.15.5.tar.gz /
RUN cd nginx-1.15.5 && \
./configure --prefix=/usr/local/nginx \
--with-http_ssl module \
--with-http_stub_status module && \
make -j 4 && make install && \
mkdir /usr/local/nginx/conf/vhost && \
cd / && rm -rf nginx* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
USER root
CMD ["./sunnydale . sh"]
ENV PATH SPATH:/usr/local/nginx/sbin
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
WORKDIR /usr/local/nginx
/cks/docker/deployment.yaml 文件内容如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: couchdb
namespace: default
labels:
app: couchdb
version: stable
spec:
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app: couchdb
version: stable
template:
metadata:
labels:
run: couchdb
spec:
containers:
- name: couchdb
image: demo:v1
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /healthCheck
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 30
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /healthCheck
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 1
successThreshold:1
failureThreshold: 5
ports:
- name: http
containerPort: 8080
protocol: TCP
volumeMounts:
- name: database-storage
mountPath: /var/lib/database
securityContext:
{'Capabilities': {'add': ['NET BIND SERVICE','SYS_ADMIN'],'drop': ['all']},'privileged': False,'readonlyRootFilesystem': False,'runAsUser': 65535}
resources :
limits:
cpu: 300m
memory: 500Mi
requests:
cpu: 100m
memory: 100Mi
volumes:
- name: database-storage
emptyDir: {}
本题第一部分主要考察对 Dockerfile 文件的一些安全知识。Dockerfile是创建镜像的脚本,它的一些创建命令直接影响到创建的镜像是否足够安全。第二部分考察pod部署时候的运行方式怎样才安全(最小权限运行)
7.1、 Dockerfile修改
这里主要是修改两个地方:
- 修改基础镜像版本 last 为题目要求的 16.04
- 修改运行脚本时使用的用户,也即是修改 CMD上面的 USER root,把 root 修改为 nobody
7.2、 deployment 文件修改
这里主要是考察"Pod 安全性标准"。官方文档搜索 “Pod 安全性标准” 打开第一个结果,点击右侧”Restricted",然后找到 “权能(v1.22+)” 一项的介绍。
可以看到,capabilities 限制了 add 字段,只允许是 未定义、nil、NET_BIND_SERVICE 这三种取值。
而 deployment 文件里的 securityContext 字段的值里,有 ‘Capabilities’: {‘add’: [‘NET BIND SERVICE’,‘SYS_ADMIN’],这里的 SYS_ADMIN 是不被允许的,所以要删除。
同时也限制了 drop字段,允许值是 “包括 ALL 在内的任意权能列表” ,securityContext 里 ‘drop’: [‘all’] 是符合要求的,不需要更改。
privileged 这个必须是 False, runAsUser 的取值不能为 0 。
注意: 正式考试时可能是不一样的,但是基本就三面几个知识点,理解好才能正确修改。如果正式考试时SYS_ADMIN没有,privileged也是False,则直接把这两行注释掉(为何?不懂,哈)
#securityContext:
#{'Capabilities': {'add': ['NET BIND SERVICE','SYS_ADMIN'],'drop': ['all']},'privileged': False,'readonlyRootFilesystem': False,'runAsUser': 65535}
另外,还存在一个问题就是标签没有保持一致:
template:
metadata:
labels:
run: couchdb ## 这里和前面的没有保持一致,应该修改为 app: couchdb
只修改即可,不需要创建,所以不需要应用这个yaml文件。
8、沙箱运行容器gVisor
Context
该 cluster 使用 containerd 作为 CRI 运行时。containerd 的默认运行时处理程序是 runc。
containerd 已准备好支持额外的运行时处理程序 runsc(gVisor)。
Task
使用名为 runsc 的现有运行时处理程序,创建一个名为 untrusted 的 RuntimeClass。
更新 namespace server 中的所有 Pod 以在 gVisor 上运行。
您可以在 /cks/gVisor/rc.yaml 中找到一个模版清单
/cks/gVisor/rc.yaml 基本没写啥,内容就不贴了。
gVisor(runsc) 和 runc 都是低级别的容器运行时 ,而containerd是高级别的容器运行时。gVisor有自己独立的内核,可以使各容器之间不会互相影响。
但是gVisor的兼容性要差很多,有很多应用可能并不能在其上运行,需要充分测试。
8.1 创建 runtimeclass
官网搜runtimeclass找到创建模板,然后按要求修改如下
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: untrusted
handler: runsc
然后应用该yaml
#kubectl apply -f 8_runtime.yaml
8.2 更新pod以应用新的runtimeclass
官网中有使用说明(就上面的同一页),要使用新的runtimeclass,只需要增加参数 runtimeClassName: untrusted
正在运行的pod是不能直接修改runtimeClassName的,考试时的pod是deployment管理的,所以我们修改deployment,在第二个spec后面,containers同级的位置增加这参数
#kubectl -n server get deployment
#kubectl -n server edit deployment xxxxxx
...
template:
metadata:
creationTimestamp: null
labels:
app: nginx-01
spec:
runtimeClassName: untrusted
containers:
注意,该命名空间下的所有deployment都要修改。
...