使用Kube-Mgmt将OPA集成到Kubernetes集群中

使用 Kube-Mgmt 将 OPA 集成到 Kubernetes 集群中

作者:阳明 2022-03-24 07:44:41

云计算

云原生 OPA 将策略决策与策略执行分离,当应用需要做出策略决策时,它会查询 OPA 并提供结构化数据(例如 JSON)作为输入,OPA 接受任意结构化数据作为输入。

成都创新互联公司服务项目包括威远网站建设、威远网站制作、威远网页制作以及威远网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,威远网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到威远省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

Open Policy Agent 简称 OPA,是一种开源的通用策略代理引擎,是 CNCF 毕业的项目。OPA 提供了一种高级声明式语言 Rego,简化了策略规则的定义,以减轻程序中策略的决策负担。在微服务、Kubernetes、CI/CD、API 网关等场景中均可以使用 OPA 来定义策略。

我们这里主要讲解在 Kubernetes 中如何集成 OPA,在 Kubernetes 中 OPA 是通过 Admission Controllers 来实现安全策略的。事实上使用 Pod 安全策略(要废弃了)来执行我们的安全策略并没有什么问题,然而,根据定义,PSP 只能应用于 pods。它们不能处理其他 Kubernetes 资源,如 Ingresses、Deployments、Services 等,OPA 的强大之处在于它可以应用于任何 Kubernetes 资源。OPA 作为一个准入控制器部署到 Kubernetes,它拦截发送到 APIServer 的 API 调用,并验证和/或修改它们。你可以有一个统一的 OPA 策略,适用于系统的不同组件,而不仅仅是 pods,例如,有一种策略,强制用户在其服务中使用公司的域,并确保用户只从公司的镜像仓库中拉取镜像。

概述

OPA 将策略决策与策略执行分离,当应用需要做出策略决策时,它会查询 OPA 并提供结构化数据(例如 JSON)作为输入,OPA 接受任意结构化数据作为输入。

OPA 通过评估查询输入策略和数据来生成策略决策,你可以在你的策略中描述几乎任何的不变因素,例如:

  • 哪些用户可以访问哪些资源。
  • 哪些子网的出口流量被允许。
  • 工作负载必须部署到哪些集群。
  • 二进制文件可以从哪里下载。
  • 容器可以用哪些操作系统的能力来执行。
  • 系统在一天中的哪些时间可以被访问。

策略决定不限于简单的是/否或允许/拒绝,与查询输入一样,你的策略可以生成任意结构化数据作为输出。让我们看一个例子。OPA 的策略是用一种叫做 Rego 的高级声明性语言来声明的,Rego 是专门为表达复杂的分层数据结构的策略而设计的。

在 Kubernetes 中,准入控制器在创建、更新和删除操作期间对对象实施策略。准入控制是 Kubernetes 中策略执行的基础。通过将 OPA 部署为准入控制器,可以:

  • 要求在所有资源上使用特定标签。
  • 要求容器镜像来自企业镜像仓库。
  • 要求所有 Pod 指定资源请求和限制。
  • 防止创建冲突的 Ingress 对象。
  • ......

Kubernetes APIServer 配置为在创建、更新或删除对象时查询 OPA 以获取准入控制策略。APIServer 将 webhook 请求中的整个对象发送给 OPA,OPA 使用准入审查作为输入来评估它已加载的策略。这个其实和我们自己去实现一个准入控制器是类似的,只是不需要我们去编写代码,只需要编写策略规则,OPA 就可以根据我们的规则去对输入的对象进行验证。

部署

接下来我们介绍下如何在 Kubernetes 集群中集成 OPA,由于 Kubernetes 中是通过准入控制器来集成 OPA 的,所以我们必须在集群中启用 ValidatingAdmissionWebhook 这个准入控制器。

首先创建一个名为 opa 的命名空间,可以让 OPA 从该命名空间中的 ConfigMap 去加载策略:

 kubectl create namespace opa

并将上下文更改为 opa 命名空间:

 kubectl config current-context
kubernetes-admin@kubernetes
content kubectl config set-context kubernetes-admin@kubernetes --namespace=opa
Context "kubernetes-admin@kubernetes" modified.
kubectl get pods
No resources found in opa namespace.

为了保护 APIServer 和 OPA 之间的通信,我们需要配置 TLS 证书。

创建证书颁发机构和密钥:

 openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"

为 OPA 生成密钥和证书:

cat >server.conf <[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = opa.opa.svc
EOF
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=opa.opa.svc" -config server.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf

创建一个 Kubernetes TLS Secret 来存储我们的 OPA 凭证:

 kubectl create secret tls opa-server --cert=server.crt --key=server.key

证书准备好后就可以部署准入控制器了,对应的资源清单文件如下所示:

# opa-admission-controller.yaml
# Grant OPA/kube-mgmt read-only access to resources. This lets kube-mgmt
# replicate resources into OPA so they can be used in policies.
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: opa-viewer
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: system:serviceaccounts:opa
apiGroup: rbac.authorization.k8s.io
---
# Define role for OPA/kube-mgmt to update configmaps with policy status.
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: opa
name: configmap-modifier
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["update", "patch"]
---
# Grant OPA/kube-mgmt role defined above.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: opa
name: opa-configmap-modifier
roleRef:
kind: Role
name: configmap-modifier
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: system:serviceaccounts:opa
apiGroup: rbac.authorization.k8s.io
---
kind: Service
apiVersion: v1
metadata:
name: opa
namespace: opa
spec:
selector:
app: opa
ports:
- name: https
port: 443
targetPort: 443
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: opa-viewer
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: system:serviceaccounts:opa
apiGroup: rbac.authorization.k8s.io
---
# Define role for OPA/kube-mgmt to update configmaps with policy status.
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: opa
name: configmap-modifier
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["update", "patch"]
---
# Grant OPA/kube-mgmt role defined above.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: opa
name: opa-configmap-modifier
roleRef:
kind: Role
name: configmap-modifier
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: system:serviceaccounts:opa
apiGroup: rbac.authorization.k8s.io
---
kind: Service
apiVersion: v1
metadata:
name: opa
namespace: opa
spec:
selector:
app: opa
ports:
- name: https
protocol: TCP
port: 443
targetPort: 443
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: opa
namespace: opa
spec:
selector:
matchLabels:
app: opa
template:
metadata:
labels:
app: opa
spec:
containers:
- name: opa
image: openpolicyagent/opa:latest
args:
- "run"
- "--server"
- "--tls-cert-file=/certs/tls.crt"
- "--tls-private-key-file=/certs/tls.key"
- "--addr=0.0.0.0:443"
- "--addr=http://127.0.0.1:8181"
- "--log-level=debug"
- "--log-format=json-pretty"
volumeMounts:
- readOnly: true
mountPath: /certs
name: opa-server
readinessProbe:
httpGet:
path: /health
scheme: HTTPS
port: 443
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
scheme: HTTPS
port: 443
initialDelaySeconds: 10
periodSeconds: 15
- name: kube-mgmt
image: openpolicyagent/kube-mgmt:4.0.0
args:
- --replicate-cluster=v1/namespaces
- --replicate=networking.k8s.io/v1/ingresses
- --opa-url=http://127.0.0.1:8181/v1
- --enable-data=true
- --enable-policies=true
- --policies=opa
- --require-policy-label=true
volumes:
- name: opa-server
secret:
secretName: opa-server
---
kind: ConfigMap
apiVersion: v1
metadata:
name: opa-default-system-main
namespace: opa
labels:
openpolicyagent.org/policy: rego
data:
main: |
package system
import data.kubernetes.admission
main = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": response,
}
default uid = ""
uid = input.request.uid
response = {
"allowed": false,
"uid": uid,
"status": {
"message": reason,
},
} {
reason = concat(", ", admission.deny)
reason != ""
}
else = {"allowed": true, "uid": uid}

上面的资源清单中我们添加了一个 kube-mgmt 的 Sidecar 容器,该容器可以将 ConfigMap 对象中的策略动态加载到 OPA 中,kube-mgmt 容器还可以将任何其他 Kubernetes 对象作为 JSON 数据加载到 OPA 中。

另外需要注意的是 Service 的名称(opa)必须与我们证书配置的 CN 匹配,否则 TLS 通信会失败。在 kube-mgmt 容器中还指定了以下命令行参数:

  • --replicate-cluster=v1/namespaces。
  • --replicate=networking.k8s.io/v1/ingresses。
  • --enable-policies=true。
  • --policies=opa。
  • --require-policy-label=true。

前两个参数允许 sidecar 容器复制命名空间、Ingress 对象,并将它们加载到 OPA 引擎中,enable-policies=true 表示会通过 Configmap 加载 OPA 策略,下面的 --policies=opa 表示从 opa 命名空间中的 Configmap 来加载策略,如果还配置了 --require-policy-label=true 参数,则需要 Configmap 中带有 openpolicyagent.org/policy=rego 这个标签才会被自动加载。

现在直接应用上面的资源清单即可:

 kubectl apply -f opa-admission-controller.yaml
kubectl get pods
NAME READY STATUS RESTARTS AGE
opa-6cd68f74f-s9zcv 2/2 Running 0 5m28s

为了让准入控制器工作,我们还需要一个准入 webhook 来接收准入 HTTP 回调并执行它们,创建如下所示的 webhook 配置文件:

 cat > webhook-configuration.yaml <kind: ValidatingWebhookConfiguration
apiVersion: admissionregistration.k8s.io/v1
metadata:
name: opa-validating-webhook
webhooks:
- name: validating-webhook.openpolicyagent.org
admissionReviewVersions: ["v1", "v1beta1"]
namespaceSelector:
matchExpressions:
- key: openpolicyagent.org/webhook
operator: NotIn
values:
- ignore
failurePolicy: Ignore
rules:
- apiGroups:
- '*'
apiVersions:
- '*'
operations:
- '*'
resources:
- '*'
sideEffects: None
clientConfig:
caBundle: $(cat ca.crt | base64 | tr -d '\n')
service:
namespace: opa
name: opa
EOF

上面的 webhook 中配置了以下属性:

  • 不会监听来自带有 openpolicyagent.org/webhook=ignore 标签的命名空间的操作。
  • 会监听所有资源的操作。
  • 它使用我们之前创建的 CA 证书,以便能够与 OPA 通信。

现在,在使用配置之前,我们标记 kube-system 和 opa 命名空间,使它们不在 webhook 范围内:

 kubectl label ns kube-system openpolicyagent.org/webhook=ignore
kubectl label ns opa openpolicyagent.org/webhook=ignore

然后应用上面的配置对象将 OPA 注册为准入控制器:

 kubectl apply -f webhook-configuration.yaml
kubectl get pods
NAME READY STATUS RESTARTS AGE
opa-6cd68f74f-s9zcv 2/2 Running 0 72m
kubectl get validatingwebhookconfiguration
NAME WEBHOOKS AGE
opa-validating-webhook 1 2m14s

策略示例

OPA 使用 Rego 语言来描述策略,这里我们使用官方文档中提到的示例来进行说明,创建一个限制 Ingress 可以使用的主机名策略,只允许匹配指定正则表达式的主机名。

创建如下所示名为 ingress-allowlist.rego 的策略文件:

package kubernetes.admission
operations = {"CREATE", "UPDATE"}
deny[msg] {
input.request.kind.kind == "Ingress"
operations[input.request.operation]
host := input.request.object.spec.rules[_].host
not fqdn_matches_any(host, valid_ingress_hosts)
msg := sprintf("invalid ingress host %q", [host])
}
valid_ingress_hosts = {host |
allowlist := namespaces[input.request.namespace].metadata.annotations["ingress-allowlist"]
hosts := split(allowlist, ",")
host := hosts[_]
}
fqdn_matches_any(str, patterns) {
fqdn_matches(str, patterns[_])
}
fqdn_matches(str, pattern) {
pattern_parts := split(pattern, ".")
pattern_parts[0] == "*"
str_parts := split(str, ".")
n_pattern_parts := count(pattern_parts)
n_str_parts := count(str_parts)
n_pattern_parts == n_str_parts
suffix := trim(pattern, "*.")
endswith(str, suffix)
}
fqdn_matches(str, pattern) {
not contains(pattern, "*")
str == pattern
}

如果你是 Rego 新手,上面的代码看上去可能有点陌生,但 Rego 让定义策略变得非常容易,我们来分析下这个策略是如何使用白名单中的 Ingress 命名空

文章名称:使用Kube-Mgmt将OPA集成到Kubernetes集群中
分享链接:http://www.36103.cn/qtweb/news29/36579.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联