k8s | OPA
任务
任务:特权容器,系统目录,不调度到 master,不同命名空间生效—>形成 调研文档,OPA的功能和具体实现,怎么使用,规则如何写,可以运用到哪些场景,具体的测试
根据不同的命名空间(名字) 来限制挂载的 白名单。比如说,特权用户 放开所有的挂载目录,普通的Namespace 只能挂载某些目录(白名单), 而不能 挂载到一些 系统的目录(黑名单)
/sys
/proc
/data 只能读 readonly
/ tmp 可读可写
其他所有的目录都不能挂载
什么是 webhook
当请求进入Kubernetes API时,它会通过一系列步骤进行处理,然后才会执行。
-
请求进行身份验证和授权。
-
请求由一系列特殊的Kubernetes Webhook集合(称为"准入控制器")处理,这些Webhook可以对请求中的对象进行突变、修改和验证。
-
请求被持久化到etcd中以便执行。
Kubernetes准入控制器是集群的中间件。它们控制可以进入集群的内容。准入控制器管理请求过多资源的部署,强制执行Pod安全策略,甚至阻止部署存在漏洞的镜像。
什么是 OPA?
Open Policy Agent (OPA)是一个开源的通用策略引擎,可在整个堆栈上实现统一的、上下文感知的策略执行。
OPA使用一种叫做“策略”的东西来工作。这些策略是规则的集合,告诉Kubernetes如何处理不同情况。就像在组织中有规定和政策来指导员工行为一样,Kubernetes集群中也需要一些规则来确保运行的容器和应用程序是安全的、合规的。
OPA 解决了哪些问题
OPA通过评估查询输入以及针对策略和数据来生成策略决策
- 哪些用户可以访问哪些资源;
- 允许哪些子网出口流量;
- 必须将工作负载部署到哪个群集;
- 可以从哪些注册表二进制文件下载;
- 容器可以执行哪些OS功能;
- 可以在一天的哪个时间访问系统;
- 需要策略控制用户是否可登陆服务器或者做一些操作;
- 需要策略控制哪些项目/哪些组件可进行部署;
- 需要策略控制如何访问数据库;
- 需要策略控制哪些资源可部署到 Kubernetes 中;
什么是 OPA Gatekeeper
github: https://github.com/open-policy-agent/gatekeeper
官方文档:https://open-policy-agent.github.io/gatekeeper/website/docs/howto
OPA Gatekeeper是Open Policy Agent的一个子项目,专门设计用于将OPA实现到Kubernetes集群中。Gatekeeper是一个验证和突变Webhook,通过由Open Policy Agent执行的基于CRD的策略强制执行。
为了确保这些应用程序在kubernetes集群中的安全性、合规性和一致性,你可能希望强制实施一些规则。这就是 OPA Gatekeeper 出现的背景。
OPA Gatekeeper 允许定义各种策略,这些策略可以规定哪些类型的资源是允许的,哪些是禁止的。这些策略可以涵盖诸如容器镜像来源、资源配额、网络策略等方面。一旦策略被定义,OPA Gatekeeper 就会在集群中监控资源的创建和更新,并根据策略进行验证。
OPA Gatekeeper 工作原理
Kubernetes 通过 准入控制 Webhook 将策略决策与 API Server 的内部工作解耦,这些 Webhook 在创建、更新或删除资源时执行。Gatekeeper 是一个验证和变异 Webhook,它执行由 Open Policy Agent 执行的基于 CRD 的策略。
Gatekeeper 充当 Kubernetes API Server 和 OPA 之间的桥梁。在实践中,这意味着 Gatekeeper 检查进入集群的每个请求,以查看它是否违反任何预定义的策略。如果是,则 apiserver 将拒绝它。
OPA Gatekeeper Library 库
github:https://github.com/open-policy-agent/gatekeeper-library
该库由两个主要部分组成:验证和突变。
-
验证:网守可以根据网守验证策略(如库中定义的策略)验证集群中的资源。这些策略被定义为约束模板和约束—ConstraintTemplates
-
变异:网守可以根据网守变异策略(如库中定义的策略)变异集群中的资源。突变策略只是示例,在应用之前,应该对它们进行定制以满足您的需求。
当涉及到 Kubernetes 集群的安全性和策略时,我们希望确保部署的应用程序和资源都遵循一些预定的规则和条件,以确保系统的健康和安全。OPA(Open Policy Agent)是一个用于定义和强制执行这些规则的工具,而 OPA Gatekeeper Library 则是 OPA 的一个插件,用于在 Kubernetes 集群中实施这些规则。
可以把 OPA Gatekeeper Library 想象成一个安全守门员,它会对您的 Kubernetes 集群中的资源进行检查,确保它们符合预设的安全策略和要求。这个 "守门员" 在部署过程中会查看每个资源(如部署、服务、配置等),并根据事先设定的规则来判断是否符合规范。
这个库的工作方式类似于一个 "检查清单",您可以将您希望在集群中执行的各种规则列出来。例如,您可以定义规则,要求每个 Deployment 必须使用特定标签,或者每个 Service 必须暴露的端口必须在特定范围内。当您部署新的资源时,这个库会自动检查资源是否符合您的规则。如果资源不符合规则,部署就会被拒绝,从而帮助确保集群的安全性和一致性。
总之,OPA Gatekeeper Library 是一个帮助您管理 Kubernetes 集群中资源的工具,通过强制执行预定义的规则,确保您的集群中的资源都遵循了您的安全和策略要求,从而提高了集群的可靠性和安全性。
安装 helm
wget https://get.helm.sh/helm-v3.5.4-linux-amd64.tar.gz
tar xf helm-v3.5.4-linux-amd64.tar.gz
cp linux-amd64/helm /usr/local/bin/helm
chmod +x /usr/local/bin/helm
安装 OPA gatekeeper
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace
安装之后,会自动创建一个名字为gatekeeper-system的命名空间,在此命名空间里会创建一系列的pod,确认这些pod的状态都是运行的。
kubectl get pods -n gatekeeper-system
gatekeeper会创建一系列的资源类型。
kubectl get crd | grep gatekeep
Rego 语法
OPA策略以称为Rego的高级声明性语言表示
- 资源基本属性:
input.review.object.metadata.name
:获取资源的名称。input.review.object.metadata.namespace
:获取资源所在的命名空间。input.review.object.kind
:获取资源的类型(例如,Pod、Deployment等)。
- 容器相关属性:
input.review.object.spec.containers[_].name
:获取容器的名称。input.review.object.spec.containers[_].image
:获取容器的镜像。input.review.object.spec.containers[_].ports[_].containerPort
:获取容器端口。
- 标签和注释:
input.review.object.metadata.labels["label_key"]
:获取特定标签的值。input.review.object.metadata.annotations["annotation_key"]
:获取特定注释的值。
- 其他资源引用:
data.<namespace>.<resource>[<index>]
:获取其他资源的属性,比如data.k8s.pods["my-pod"].spec.containers[_].image
。
- 数组和循环:
count(array)
:计算数组中元素的数量。array[_]
:遍历数组中的每个元素。array[index]
:获取数组中特定索引的元素。
- 字符串操作:
startswith(str, prefix)
:检查字符串是否以指定前缀开始。endswith(str, suffix)
:检查字符串是否以指定后缀结束。contains(str, substr)
:检查字符串是否包含指定子串。
- 逻辑和条件:
==
,!=
,<
,>
,<=
,>=
:比较运算符。not
,and
,or
:逻辑运算符。
- 自定义函数:
- 使用
func
关键字可以定义自定义函数,可以在策略中调用这些函数。
- 使用
实例
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
名为 violation
的规则
- 首先,它创建了两个集合,
provided
包含已有的标签,required
包含所需的标签。 - 然后,通过计算
required
集合与provided
集合的差集,得到了缺失的标签集合missing
。 - 接着,它检查是否存在缺失的标签,如果存在,则返回一个包含提示消息和详细信息的 JSON 对象,其中详细信息包括缺失的标签。
- 如果没有缺失的标签,那么就没有违反规则,不会返回违反信息。
1、创建资源对象必须指定标签
创建模板
vi k8srequiredlabels-template.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
kubectl apply -f k8srequiredlabels-template.yaml
根据模板创建 contraints
vi k8srequiredlabels-contraints.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-gk
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels: ["gatekeeper"]
kubectl apply -f k8srequiredlabels-contraints.yaml
创建 Namespace 进行测试
kubectl create ns test
出现错误
[root@hz-gd-k8s-ipstest-master-199-154-119 test]# kubectl create ns test
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-gk] you must provide labels: {"gatekeeper"}
加上标签 gatekeeper
vi test-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test
labels:
gatekeeper: enabled
kubectl apply -f test-namespace.yaml
创建成功!
2、禁止挂载到系统目录
定义策略 disallow-mounting-system.rego
白名单,限定
用于定义策略规则以确保挂载路径不包含系统敏感目录。例如,阻止挂载到 /etc
和 /var
目录下。
package k8sdenyvolumes
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
contains(volumeMount.mountPath, "/etc") # 阻止挂载到 /etc
msg := "Mounting to system directory /etc is disallowed"
}
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
contains(volumeMount.mountPath, "/var") # 阻止挂载到 /var
msg := "Mounting to system directory /var is disallowed"
}
contains(volumeMount.mountPath, "/etc")
:这行代码使用 contains
函数检查 volumeMount
的 mountPath
是否包含 “/etc” 字符串,从而判断是否尝试挂载到 /etc 目录。
创建 ConstraintTemplate 创建一个 ConstraintTemplate,以在策略中引用。创建一个文件 disallow-mounting-system-template.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sdenyvolumes
spec:
crd:
spec:
names:
kind: K8sDenyVolumes
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenyvolumes
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
contains(volumeMount.mountPath, "/etc") # 阻止挂载到 /etc
msg := "Mounting to system directory /etc is disallowed"
}
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
contains(volumeMount.mountPath, "/var") # 阻止挂载到 /var
msg := "Mounting to system directory /var is disallowed"
}
创建 ConstraintTemplate
kubectl apply -f disallow-mounting-system-template.yaml
创建 Constraint disallow-mounting-system-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: k8sdenyvolumes
metadata:
name: disallow-mounting-system
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
allowedPrefixes:
- "/data" # 允许挂载到 /data 目录
创建 Constraint
kubectl apply -f disallow-mounting-system-constraint.yaml
部署 Constraint
kubectl apply -f test-deployment.yaml
创建一个 deployment 来测试,让其挂载到 /etc 目录
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
spec:
replicas: 1
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
- name: etc-volume
mountPath: /etc
volumes:
- name: etc-volume
emptyDir: {}
创建出现问题,符合预期
[root@hz-bd-k8s-release-master-safetest-199-159-12 system]# kubectl apply -f test.yaml
Error from server (Forbidden): error when creating "test.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [disallow-mounting-system] Mounting to system directory /etc is disallowed
监控和管理:
# 检查 Constraint 的状态
kubectl get k8sdenyvolumes disallow-mounting-system -o yaml
# 检查 Deployment 的状态
kubectl get deployment test-deployment -o yaml
3、禁止使用特定的镜像
自定义一个类型为blacklistimages
的CRD
资源类型,用于禁止以hub.c.163.com
开头的镜像。
创建Constraint Templates
vi gatekeeper-blk-type.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: blacklistimages
spec:
crd:
spec:
names:
kind: BlacklistImages
targets:
- rego: |
package k8strustedimages
images {
image := input.review.object.spec.containers[_].image
not startswith(image, "hub.c.163.com/")
}
violation[{"msg": msg}] {
not images
msg := "不可用的镜像!"
}
target: admission.k8s.gatekeeper.sh
创建
kubectl apply -f gatekeeper-blk-type.yaml
查看资源
kubectl get blacklistimages
这里提示"No resources found"
,说明已经存在资源类型blacklistimages
了,只是这个类型下面还没创建任何资源。
下面创建一个名字为pod-blk-img
的BlacklistImages
。
vi gatekeeper-blacklist.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: BlacklistImages
metadata:
generation: 1
managedFields:
name: pod-blk-img
resourceVersion: "14449"
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- Pod
创建
kubectl apply -f gatekeeper-blacklist.yaml
创建一个 pod 来测试
test.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
containers:
- image: hub.c.163.com/library/nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
kubectl apply -f pod1.yaml
提示错误
Error from server (Forbidden): error when creating "test.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [pod-blk-img] 不可用的镜像!
4、禁止创建LB类型的svc
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: lbtypesvcnotallowed
spec:
crd:
spec:
names:
kind: LBTypeSvcNotAllowed
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package kubernetes.admission
violation[{"msg": msg}] {
input.review.kind.kind = "Service"
input.review.operation = "CREATE"
input.review.object.spec.type = "LoadBalancer"
msg := "不允许创建LB类型的service!"
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: LBTypeSvcNotAllowed
metadata:
name: deny-create-lb-type-svc
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Service"]
namespaces:
- "default"
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
containers:
- image: hub.c.163.com/library/nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
kubectl expose --name=svc1 pod pod1 --port=80 --type=LoadBalancer
Task
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: namespacemountpolicy
spec:
crd:
spec:
names:
kind: NamespaceMountPolicy
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package namespace_mount_policy
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
namespace := input.review.object.metadata.namespace
# 在 kube-system 命名空间中允许挂载到 /etc
namespace == "kube-system"
volumeMount.mountPath == "/etc"
msg := "Container in kube-system namespace is allowed to mount /etc"
}
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
namespace := input.review.object.metadata.namespace
# 在 default 命名空间中不允许挂载到 /etc
namespace == "default"
# volumeMount.mountPath == "/etc"
contains(volumeMount.mountPath, "/etc") # 阻止挂载到 /etc
msg := "Container in default namespace cannot mount /etc"
}
violation[{"msg": msg}] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
namespace := input.review.object.metadata.namespace
# 在 default 命名空间中只允许挂载到 /data
namespace == "default"
volumeMount.mountPath != "/data"
msg := "Container in default namespace is allowed to mount only /data"
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NamespaceMountPolicy
metadata:
name: disallow-master-mount
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
allowedPrefixes:
- "/data" # 允许挂载到 /data 目录
# 这里可以添加你的参数,如果有的话
# 例如 labels: ["gatekeeper"]
# 例如 allowedPaths: ["/data"]
不调度到 master 节点
创建 constraint template
vi disallow-master-scheduling-template.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: disallowmasterscheduling
spec:
crd:
spec:
names:
kind: DisallowMasterScheduling
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenydeployment
violation[{"msg": msg}] {
node := input.review.object.spec.template.spec.nodeSelector
contains(disallowed_labels, node)
err := "master erro"
msg := sprintf("Deployment scheduling on master nodes is disallowed,%v", err)
}
kubectl apply -f disallow-master-scheduling-template.yaml
创建模板的时候报错,应该是语法问题,但是没有找到具体的解决方法
创建一个具体的策略实例
vi disallow-master-scheduling-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: DisallowMasterScheduling
metadata:
name: disallow-master-scheduling-constraint
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
enforcementAction: deny
kubectl apply -f disallow-master-scheduling-constraint.yaml
创建一个 deployment 来测试
vi test-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
spec:
replicas: 2
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
annotations:
prometheus.io/scrape: "true" # An example annotation
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
kubectl apply -f test-deployment.yaml
package deployment-mount-policy
import data.k8s
import data.mount_policy
violation[msg] {
container := input.review.object.spec.template.spec.containers[_]
volumeMount := container.volumeMounts[_]
not allowed_mounts
allowed_mounts[_].mountPath == volumeMount.mountPath
not allowed_mounts[_].readonly
allowed_mounts[_].mountPath == "/tmp"
not volumeMount.readOnly
msg := sprintf("Mounting at path %v with disallowed options", [volumeMount.mountPath])
}
allowed_mounts[mount] {
namespace := input.review.object.metadata.namespace
mount := mount_policy.allowed_mounts[namespace][_]
}
not allowed_mounts {
namespace := input.review.object.metadata.namespace
mount := mount_policy.disallowed_mounts[namespace][_]
mount_path := mount.mountPath
not volumeMount.mountPath == mount_path
}
常用指令
查看所有的 constrainttemplate
kubectl get constrainttemplate
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: namespacemountpolicy
spec:
crd:
spec:
names:
kind: NamespaceMountPolicy
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package namespace_mount_policy
special_namespaces = {
"kube-system",
}
allowed_mounts = {
"data",
}
blocked_mounts = {
"etc",
"var",
}
violation[{"msg": msg}] {
input.review.object.kind == "Pod"
container := input.review.object.spec.containers[_]
volume := container.volumeMounts[_]
namespace := input.review.object.metadata.namespace
not allow_mount[namespace]
allow_mount[namespace]
volume.mountPath == allowed_mount
not volume.mountPath == blocked_mounts
msg := sprintf("Namespace %v has an invalid mount policy", [namespace])
}
allow_mount[namespace] {
namespace == special_namespaces[_]
}
allow_mount[namespace] {
namespace != special_namespaces[_]
volume.mountPath == allowed_mounts[_]
not volume.mountPath == blocked_mounts[_]
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NamespaceMountPolicy
metadata:
name: enforce-namespace-mount-policy
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
enforcementAction: deny
评论区