❄ RuntimeClass | gVisor

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

该集群用 containerd 作为 CRI 运行时。

containerd 的默认运行时处理程序是 runc。

containerd 已准备好支持额外的运行时处理程序 runsc(gVisor)

  1. 使用名为 runsc 的现有运行时处理程序,创建一个名为 untrusted 的 RuntimeClass

  2. 更新 namespace server 中的所有 Pod 以在 gVisor 上运行。您可以在 /cks/gVisor/rc.yaml 中找到一个模版清单。

# /cks/gVisor/rc.yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:

创建 RuntimeClass

# gvisorRuntimeClass.yml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: untrusted
handler: runsc
kubectl create -f gvisorRuntimeClass.yaml

修改 pod 的 RuntimeClass

kubectl edit pod podname -n server
...
metadata:
  namespace: server
spec:
  runtimeClassName: untrusted
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
...

验证

kubectl -n client exec podname -- dmesg | grep gVisor

❄ ServiceAccount

知识点文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-service-account

清单文件 /cks/sa/pod1.yaml 中指定的 Pod 由于 ServiceAccount 指定错误而无法调度。

  1. 在现有 namespace qa 中创建一个名为 backend-sa 的新 ServiceAccount,确保此 ServiceAccount 不自动挂载 API 凭据。

  2. 使用 /cks/sa/pod1.yaml 中的清单文件来创建一个Pod。

  3. 最后,清理 namespace qa 中任何未使用的 ServiceAccount。

# /cks/sa/pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: backend
  namespace: qa
spec:
  serviceAccountName: backend-sa
  containers:
  - image: nginx:1.9
    imagePullPolicy: IfNotPresent
    name: backend
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend-sa
  namespace: qa
automountServiceAccountToken: false   #不自动挂载API
---
apiVersion: v1
kind: Pod
metadata:
  name: backend
  namespace: qa
spec:
  serviceAccountName: backend-sa                  #给pod配置serviceaccount
  containers:
  - image: nginx:1.9
    imagePullPolicy: IfNotPresent
    name: backend
# 查看所有sa
kubectl get sa -n qa

# 查看已经被使用的sa
kubectl get pod -n qa -o yaml | grep -i serviceaccount

# 删除不用的sa
kubectl delete sa -n qa test01

❄ TLS通信配置

知识点文档:https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-apiserver/

  • 修改 apiserver

    通过 TLS 加强 kube-apiserver 安全配置,要求

    • kube-apiserver 除了 TLS 1.3 及以上的版本可以使用,其他版本都不允许使用。

    • 密码套件(Cipher suite)为 TLS_AES_128_GCM_SHA256

  • 修改 etcd

    通过 TLS 加强 ETCD 安全配置,要求

    • 密码套件(Cipher suite)为 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
# /etc/kubernetes/manifests/kube-apiserver.yaml

 - --tls-cipher-suites=TLS_AES_128_GCM_SHA256
 - --tls-min-version=VersionTLS13 # 如果题目要求 TLS 1.2,则就写 VersionTLS12
# /etc/kubernetes/manifests/etcd.yaml

- --cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
# 更改配置后将暂时get不到信息
# 等能get到信息即表示正常
kubectl get pod -n kube-system

❄ RBAC 授权

知识点文档:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/

一个名为 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

注意:请勿删除现有的 RoleBindinbg

# 查找 web-pod 所绑定的 ServiceAccount
kubectl get pod -n db -o yaml | grep -i serviceaccount

# 查找对应 role
kubectl get rolebindings.rbac.authorization.k8s.io -n db -o yaml | grep -i service-account-web -C 2

修改role对应的权限

kubectl edit role -n db web-role
...
rules:
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
...

在 namespace db 中创建 role-2,并配置对 namespaces 类型进行 update 操作

kubectl create role role-2 --verb=update --resource=namespaces -n db

创建新的 RoleBinding role-2-binding,将 role-2 绑定到 Pod 的 ServiceAccount

kubectl create rolebinding role-2-binding \
-n db \
--role=role-2 \
--serviceaccount=db:service-account-web

检查

kubectl describe rolebindings -n db role-2-binding

❄ 禁止匿名访问

重新配置 cluster 的 Kubernetes API 服务以确保只允许经过身份验证和授权的 REST 请求

使用授权模式 Node , RBAC 和准入控制器 NodeRestriction

删除用户 system:anonymous 的 ClusterRoleBinding 来进行清理

注意:所有 kubectl 配置环境/文件也被配置为使用未经身份验证和未授权的访问。您不必更改它,但请注意,一旦完成 cluster 的安全加固,kubectl 的配置将无法工作

你可以使用位于 Cluster 的 master 节点上,cluster 原本的 kubectl 配置文 /etc/kubernetes/admin.conf ,以确保经过身份验证和授权的请求任然被允许

更改授权模式和添加 NodeRestriction 准入控制器

# /etc/kubernetes/manifests/kube-apiserver.yaml
...
 - --authorization-mode=Node,RBAC # 修改这里
 - --client-ca-file=/etc/kubernetes/pki/ca.crt
 - --enable-admission-plugins=NodeRestriction # 修改这里
...

删除 clusterrolebinding

kubectl delete clusterrolebinding 

❄ Secret

知识点文档:https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/

  • 在 namespace istio-system 中获取名为 db1-test 的现有 secret 的内容

  • 将 username 字段存储在名为 /cks/sec/user.txt 的文件中,并将 password 字段存储在名为 /cks/sec/pass.txt 的文件中。

    注意:你必须创建以上两个文件,他们还不存在。

注意:不要在以下步骤中使用/修改先前创建的文件,如果需要,可以创建新的临时文件。

  • 在 namespace istio-system 中创建一个名为 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

查看 namespace istio-system 中的 db1-test 的内容

kubectl get secrets -n istio-system db1-test -o yaml

...
  password: aGVsbG8=
  username: ZGIx
...

解密用户名和密码,并放到对应的文件中

# 解密用户名
echo -n 'ZGIx' | base64 -d > /cks/sec/user.txt

# 解密密码
echo -n 'aGVsbG8=' | base64 -d > /cks/sec/pass.txt

创建新secret

kubectl create secret generic db2-test \
	--from-literal=username=production-instance \
	--from-literal=password=KvLftKgs4aVH \
    -n istio-system

创建pod,使用挂载

# secret-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
  namespace: istio-system
spec:
  containers:
  - image: nginx
    name: dev-container
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secret
  volumes:
  - name: secret-volume
    secret:
      secretName: db2-test
kubectl create -f secret-pod.yaml

❄ Dockerfile 检测

分析和编辑给定的 Dockerfile /cks/docker/Dockerfile(基于 ubuntu:16.04 镜像),并修复在文件中拥有的突出的安全/最佳实践问题的两个指令

分析和编辑给定的清单文件 /cks/docker/deployment.yaml ,并修复在文件中拥有突出的安全/最佳实践问题的两个字段

注意:

  • 请勿添加或删除配置设置;只需修改现有的配置设置让以上两个配置设置都不再有安全/最佳实践问题

  • 如果您需要非特权用户来执行任何项目,请使用用户 ID 65535 的用户 nobody

  • 只修改文件即可,不需要创建。

# /cks/docker/Dockerfile

FROM ubuntu:latest
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 $PATH:/usr/local/nginx/sbin
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
WORKDIR /usr/local/nginx
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
# /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:
        app: couchdb
        version: stable
    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: 10
          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'], 'drop': ['all']}, 'privileged': True, 'readOnlyRootFilesystem': False, 'runAsUser': 65535}
        resources:
          limits:
            cpu: 300m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
        - name: database-storage
          emptyDir: {}

Dockerfile 修改

# /cks/docker/Dockerfile

FROM ubuntu:16.04 # 镜像版本修改为16.04
...
USER nobody # 用户修改为 nobody
...
USER nobody # 用户修改为 nobody
...

deployment.yaml 修改

# /cks/docker/deployment.yaml

...
        securityContext:
          {'capabilities': {'add': ['NET_BIND_SERVICE'], 'drop': ['all']}, 'privileged': False, 'readOnlyRootFilesystem': True, 'runAsUser': 65535}
          # 修改 privileged 为 False
          # 修改 readOnlyRootFilesystem 为 True
...

❄ SecurityContext

知识点文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/security-context/

按照如下要求修改 namespace sec-ns 里的 Deployment secdep

  • 用 ID 为 30000 的用户启动容器(设置用户 ID 为: 30000)

  • 不允许进程获得超出其父进程的特权(禁止 allowPrivilegeEscalation

  • 以只读方式加载容器的根文件系统(对根文件的只读权限)

kubectl edit deployments.apps -n sec-ns secdep

spec.template.containers 加入参数

注意:可能有多个容器

...
spec:
...
  template:
  ...
    spec:
      containers:
      - image: nginx
        name: nginx
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsUser: 30000
        ...
      containers:
      - image: tomcat
        name: tomcat
        resources: {}
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsUser: 30000
        ...
...

❄ 容器安全 删除特权 Pod

查看 namespace production 下所有 pod 是否有特权 Privileged 或者挂载 volume

删除包含 Volumes 或 privileged 的 pod

kubectl get pods PODNAME -n production -o yaml | grep -i hostpath
kubectl get pods PODNAME -n production -o yaml | grep -i "privi.*: true"
kubectl delete pod PODNAME

❄ kube-bench 修复不安全项

针对 kubeadm 创建的 cluster 运行 CIS 基准测试工具时,发现了多个必须立即解决的问题

通过配置修复所有问题并重新启动受影响的组件以确保新的设置生效。

修复针对 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

修复针对 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-apiserver

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

...
    - --authorization-mode=Node,RBAC # 修改这行
...
    - --insecure-bind-address=0.0.0.0 # 注释这行 注释!!!!!!!
...
    - --insecure-port=0 # 添加
...

kubelet

# /var/lib/kubelet/config.yaml
...
authentication:
  anonymous:
    enabled: false  # 设定为 false
  webhook:
    ...
    enabled: true # 设定为 true
authorization:
  mode: Webhook # 设定为 Webhook
...

etcd

# /etc/kubernetes/manifests/etcd.yaml
    - --client-cert-auth=true # 设定为 true

重启服务

systemctl restart kubelet
systemctl status kubelet

❄ 日志审计

知识点文档:https://kubernetes.io/zh-cn/docs/tasks/debug/debug-cluster/audit/

在 cluster 中启用审计日志。为此,请启用日志后端,并确保:

  • 日志存储在 /var/log/kubernetes/audit-logs.txt
  • 日志文件能保留 10
  • 最多保留2个旧审计日志文件

/etc/kubernetes/logpolicy/sample-policy.yaml 提供了基本策略。它仅指定不记录内容

注意:基本策略位于 cluster 的 master 节点上。

编辑和扩展基本策略以记录:

  • RequestResponse 级别的 persistentvolumes 更改
  • namespace front-appsconfigmaps 更改的 请求体
  • Metadata 级别的所有 namespace 中的 ConfigMapSecret 的更改

此外,添加一个全方位的规则以在 Metadata 级别记录所有其他请求

注意:不要忘记应用修改后的策略

# /etc/kubernetes/logpolicy/sample-policy.yaml

apiVersion: audit.k8s.io/v1 
kind: Policy
omitStages:
  - "RequestReceived"
rules:
  - level: RequestResponse
    resources:
    - group: ""
      resources: ["persistentvolumes"]
  - level: Request
    resources:
    - group: ""
      resources: ["configmaps"]
    namespaces: ["front-apps"]
  - level: Metadata
    resources:
    - group: ""
      resources: ["secrets", "configmaps"]
  - level: Metadata
    omitStages:
      - "RequestReceived"
# /etc/kubernetes/manifests/kube-apiserver.yaml

# 添加以下配置
...
spec:
  containers:
  - command:
    - --audit-policy-file=/etc/kubernetes/logpolicy/sample-policy.yaml
    - --audit-log-path=/var/log/kubernetes/audit-logs.txt
    - --audit-log-maxage=10
    - --audit-log-maxbackup=2
...
# 检查一下是否挂载这两个配置文件,如果有挂载就不用管,如果未挂载则需要手动添加
  volumeMounts:
    - mountPath: /etc/kubernetes/logpolicy/sample-policy.yaml
      name: audit
      readOnly: true
    - mountPath: /var/log/kubernetes
      name: audit-log
      readOnly: false
...
  volumes:
    - hostPath:
        path: /etc/kubernetes/logpolicy/sample-policy.yaml
        type: File
      name: audit
    - hostPath:
        path: /var/log/kubernetes
        type: DirectoryOrCreate
      name: audit-log
...

检查/var/log/kubernetes/audit/audit.log日志文件是否有日志产生

tail -f /var/log/kubernetes/audit/audit.log

❄ NetworkPolicy

创建一个名为 pod-restriction 的 NetworkPolicy

限制对在 namespace dev-team 中运行的 Pod products-service 的访问

只允许以下 Pod 连接到 Pod products-service

  • namespace qa 中的 Pod
  • 位于任何 namespace,带有标签 environment: testing 的 Pod

你可以在/cks/net/po.yaml 找到一个模板清单文件

# /cks/net/po.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
# NetworkPolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: pod-restriction
  namespace: dev-team
spec:
  podSelector:
    matchLabels:
      kubernetes.io/metadata.name: dev-team # 指定 namespace
  policyTypes:
    - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: qa # 指定 namespace
  - from:
    - namespaceSelector: {} # 表示所有 namespace
    - podSelector:
        matchLabels:
          environment: testing

❄ NetworkPolicy DenyAll

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

在 namespace testing 中创建一个名为 denypolicy NetworkPolicy,拒绝所有类型为 Ingress + Egress 的流量。

  • 此新的 NetworkPolicy 必须拒绝 namespace testing 中的所有的 Ingress + Egress 流量。

  • 将新创建的 NetworkPolicy 应用在 namespace testing 中运行的所有 Pod 中。

# defaultdeny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: denypolicy
  namespace: testing
spec:
  podSelector: {} # 默认匹配本命名空间
  policyTypes:
  - Ingress # 不指定则不允许所有
  - Egress # 不指定则不允许所有

❄ trivy 检测镜像

使用 Trivy 开源容器扫描器检测 namespace kamino 中 Pod 使用的具有严重漏洞的镜像。

查找具有 HighCritical 严重性漏洞的镜像,并删除使用这些镜像的 Pod。

注意:Trivy 仅安装在 cluster 的 master 节点上

# 获取 pod image
kubectl describe pod -n kamino | grep "Image:"
# 或
kubectl get pods -n kamino -o yaml | grep "\- image:"
# 或
kubectl get pods -n kamino -o json | grep "\"image\""
# 或
kubectl get pods -n kamino -o custom-columns="NAME:.metadata.name,IMAGE:.spec.containers[*].image"

# 检测
trivy image -s HIGH,CRITICAL busybox:1.33.1
trivy image -s HIGH,CRITICAL nginx:1.14.2 #HIGH and CRITICAL
trivy image -s HIGH,CRITICAL amazonlinux:2
trivy image -s HIGH,CRITICAL amazonlinux:1
trivy image -s HIGH,CRITICAL centos:7 #HIGH and CRITICAL

删除

kubectl delete pod -n kamino PODNAME

❄ AppArmor

知识点文档:https://kubernetes.io/zh-cn/docs/tutorials/security/apparmor/

APPArmor 已在 cluster 的工作节点 node02 上被启用。一个 APPArmor 配置文件已存在,但尚未被实施。

在 cluster 的工作节点 node02 上,实施位于 /etc/apparmor.d/nginx_apparmor 的现有 APPArmor 配置文件。

编辑位于 /cks/KSSH00401/nginx-deploy.yaml 的现有清单文件以应用 AppArmor 配置文件。

最后,应用清单文件并创建其中指定的 Pod

切换到 apparmor 的目录

cat /etc/apparmor.d/nginx_apparmor
# 其中 profile nginx-profile-3 flags=(attach_disconnected) 这一行的第二个单词 nginx-profile-3 就是这个配置文件的名字

执行 apparmor 策略模块

# 加载启用这个配置文件
apparmor_parser /etc/apparmor.d/nginx_apparmor

# 检查是否成功
apparmor_status | grep nginx-profile-3

配置 Pod 使用策略

# /cks/KSSH00401/nginx-deploy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # 告知 Kubernetes 去应用 AppArmor 配置 "k8s-apparmor-example-deny-write"。
    # 请注意,如果节点上运行的 Kubernetes 不是 1.4 或更高版本,此注解将被忽略。
    # Note: annotations, container and apparmor profile to be edited
    # container.apparmor.security.beta.kubernetes.io/<<container name>>: localhost/<<profile name>>
    container.apparmor.security.beta.kubernetes.io/hello: localhost/nginx-profile-3
spec:
  containers:
  - name: hello
    image: busybox:1.28
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]

创建pod

kubectl apply -f /cks/KSSH00401/nginx-deploy.yaml

❄ sysdig & Falco

使用运行时检测工具来检测 Pod redis123 单个容器中频发生成和执行的异常进程。

有两种工具可供使用:

  • sysdig
  • falco

注: 这些工具只预装在 cluster 的工作节点 node02 上,不在 master 节点。

使用工具至少分析 30 秒 ,使用过滤器检查生成和执行的进程,将事件写到 /opt/KSR00101/incidents/summary 文件中,其中包含检测的事件, 格式如下:

timestamp,uid/username,processName
01:40:19.601363716,root,init
01:40:20.606013716,nobody,bash
01:40:21.137163716,1000,tar

注: 保持工具的原始时间戳格式不变。

注: 确保事件文件存储在集群的工作节点上。

查看容器 container id

# containerd
crictl ps

# docker
docker ps

sysdig

# user.name
sysdig -M 45 -p "%evt.time,%user.name,%proc.name" container.id=46183d7281e15 >> /opt/KSR00101/incidents/summary

# user.uid
sysdig -M 45 -p "%evt.time,%user.uid,%proc.name" container.id=46183d7281e15 >> /opt/KSR00101/incidents/summary

Falco

# /home/cloud_user/monitor_rules.yaml
- rule: rule1
  desc: rule1
  condition: container.name = "redis123"
  output: "%evt.time,%user.uid,%proc.name"
  priority: WARNING
falco -M 45 -r /home/cloud_user/monitor_rules.yaml >> /opt/KSR00101/incidents/summary

❄ ImagePolicyWebhook 容器镜像扫描

cluster 上设置了容器镜像扫描器,但尚未完全集成到 cluster 的配置中。

完成后,容器镜像扫描器应扫描并拒绝易受攻击的镜像的使用。

注意:你必须在 cluster 的 master 节点上完成整个考题,所有服务和文件都已被准备好并放置在该节点上。

给定一个目录 /etc/kubernetes/epconfig 中不完整的配置,

以及具有 HTTPS 端点 https://image-bouncer-webhook.default.svc:1323/image_policy 的功能性容器镜像扫描器:

  1. 启用必要的插件来创建镜像策略

  2. 校验控制配置并将其更改为隐式拒绝(implicit deny)

  3. 编辑配置以正确指向提供的 HTTPS 端点

最后,通过尝试部署易受攻击的资源 /cks/img/web1.yaml 来测试配置是否有效。

# /etc/kubernetes/epconfig/admission_configuration.json
{
   "imagePolicy": {
      "kubeConfigFile": "/etc/kubernetes/epconfig/kubeconfig.yaml",
      "allowTTL": 50,
      "denyTTL": 50,
      "retryBackoff": 500,
      "defaultAllow": false 
   }
}
# /etc/kubernetes/epconfig/kubeconfig.yaml
...
clusters:
- cluster:
    server: https://image-bouncer-webhook.default.svc:1323/image_policy # 添加 webhook server 地址
  name: bouncer_webhook
...
# /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
  containers:
  - command:
    - --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
    - --admission-control-config-file=/etc/kubernetes/epconfig/admission_configuration.json
...
    volumeMounts:
    - mountPath: /etc/kubernetes/epconfig
      name: epconfig
...
  volumes:
    - name: epconfig
    hostPath:
      path: /etc/kubernetes/epconfig 

等待 apiserver 自动重启,并恢复

创建易受攻击的资源 /cks/img/web1.yaml 来测试配置是否有效。因为模拟环境里的 image_policy 策略是镜像 tag 是 latest 的不允许创建,则验证成功。

kubectl apply -f /cks/img/web1.yaml

❄ PodSecurityPolicy

创建一个可以阻止创建特权 pod 的名为 prevent-psp-policy 的 PodSecurityPolicy。

创建一个使用 PodSecurityPolicy prevent-psp-policy 的 名为 restrict-access-role 的 ClusterRole。

在 namespace development 创建一个名为 pspdenial-sa 的 serviceAccount。

创建一个 ClusterRole restrict-access-role 与 serviceAccount pspdenial-sa 绑定的名为 dany-access-bind 的 clusterRoleBinding。

确认策略启用

# /etc/kubernetes/manifests/kube-apiserver.yaml
    - --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
# prevent-psp-policy.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: prevent-psp-policy
  namespace: development
spec:
  privileged: false
  runAsUser:
    rule: "RunAsAny"
  fsGroup:
    rule: "RunAsAny"
  seLinux:
    rule: "RunAsAny"
  supplementalGroups:
    rule: "RunAsAny"
# restrict-access-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: restrict-access-role
rules:
- apiGroups:
  - policy
  resources:
  - podsecuritypolicies
  verbs:
  - use
  resourceNames:
  - prevent-psp-policy
# pspdenial-sa
# kubectl create sa pspdenial-sa -n development

apiVersion: v1
kind: ServiceAccount
metadata:
  name: pspdenial-sa
  namespace: development
# dany-access-bind.yaml

# kubectl create clusterrolebinding dany-access-bind \
#     --clusterrole=restrict-access-role \
#     --serviceaccount=development:pspdenial-sa

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dany-access-bind
roleRef:
  kind: ClusterRole
  name: restrict-access-role
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: pspdenial-sa
  namespace: development