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都要修改。