ITPub博客

首页 > 数据库 > 数据库开发技术 > 使用 Open Policy Agent 实现可信镜像仓库检查

使用 Open Policy Agent 实现可信镜像仓库检查

原创 数据库开发技术 作者:云原生指北 时间:2021-07-10 07:10:00 0 删除 编辑

这今天我们尝试用“策略即代码”的实现 OPA 来实现功能。

还没开始之前可能有人会问:明明可以实现个 Admission Webhook[1] 就行,为什么还要加上 OPA?

确实可以,但是这样策略和逻辑都会耦合在一起,当策略需要调整的时候需要修改代码重新发布。而 OPA 就是用来做解耦的,其更像是一个策略的执行引擎。

什么是 OPA

Open Policy Agent(以下简称 OPA,发音 “oh-pa”)一个开源的通用策略引擎,可以统一整个堆栈的策略执行。OPA 提供了一种高级声明性语言(Rego),可让你将策略指定为代码和简单的 API,以从你的软件中卸载策略决策。你可以使用 OPA 在微服务、Kubernetes、CI/CD 管道、API 网关等中实施策略。

Rego 是一种高级的声明性语言,是专门为 OPA 建立的。更多 OPA 的介绍可以看 Open Policy Agent 官网[2],不想看英文直接看这里[3]。

现在进入正题。

启动集群

启动 minikube

minikube start

创建用于部署 OPA 的命名空间

创建并切换到命名空间 opa (命名空间的切换使用 kubens,更多工具介绍见这里)

kubectl create namespace opakubens opa

在 Kubernetes 上部署 OPA

Kubernetes 和 OPA 间的通信必须使用 TLS 进行保护。配置 TLS,使用 openssl 创建证书颁发机构(certificate authority CA)和 OPA 的证书/秘钥对。

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

为 OPA 创建 TLS 秘钥和证书:

cat >server.conf <<EOF[req]req_extensions = v3_reqdistinguished_name = req_distinguished_nameprompt = no[req_distinguished_name]CN = opa.opa.svc[ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, serverAuthsubjectAltName = @alt_names[alt_names]DNS.1 = opa.opa.svcEOF

注意 CN 和 alt_names 必须与后面创建 OPA service 的匹配。

openssl genrsa -out server.key 2048openssl req -new -key server.key -out server.csr -config server.confopenssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf

为 OPA 创建保存 TLS 凭证的 Secret:

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

将 OPA 部署为准入控制器(admission controller)。

admission-controller.yaml:

# 授权 OPA/kube-mgmt 对资源的只读权限# kube-mgmt 会同步资源信息给 OPA,以便在策略中使用kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: opa-viewerroleRef:  kind: ClusterRole  name: view  apiGroup: rbac.authorization.k8s.iosubjects:- kind: Group  name: system:serviceaccounts:opa  apiGroup: rbac.authorization.k8s.io---# 为 OPA/kube-mgmt 定义角色来在 configmaps 中更新策略状态kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata:  namespace: opa  name: configmap-modifierrules:- apiGroups: [""]  resources: ["configmaps"]  verbs: ["update", "patch"]---# 为 OPA/kube-mgmt 授予角色kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  namespace: opa  name: opa-configmap-modifierroleRef:  kind: Role  name: configmap-modifier  apiGroup: rbac.authorization.k8s.iosubjects:- kind: Group  name: system:serviceaccounts:opa  apiGroup: rbac.authorization.k8s.io---kind: ServiceapiVersion: v1metadata:  name: opa  namespace: opaspec:  selector:    app: opa  ports:  - name: https    protocol: TCP    port: 443    targetPort: 8443---apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: opa  namespace: opa  name: opaspec:  replicas: 1  selector:    matchLabels:      app: opa  template:    metadata:      labels:        app: opa      name: opa    spec:      containers:        # WARNING: OPA is NOT running with an authorization policy configured. This        # means that clients can read and write policies in OPA. If you are        # deploying OPA in an insecure environment, be sure to configure        # authentication and authorization on the daemon. See the Security page for        # details: https://www.openpolicyagent.org/docs/security.html.        - name: opa          image: openpolicyagent/opa:0.30.1-rootless          args:            - "run"            - "--server"            - "--tls-cert-file=/certs/tls.crt"            - "--tls-private-key-file=/certs/tls.key"            - "--addr=0.0.0.0:8443"            - "--addr=http://127.0.0.1:8181"            - "--log-format=json-pretty"            - "--set=decision_logs.console=true"          volumeMounts:            - readOnly: true              mountPath: /certs              name: opa-server          readinessProbe:            httpGet:              path: /health?plugins&bundle              scheme: HTTPS              port: 8443            initialDelaySeconds: 3            periodSeconds: 5          livenessProbe:            httpGet:              path: /health              scheme: HTTPS              port: 8443            initialDelaySeconds: 3            periodSeconds: 5        - name: kube-mgmt          image: openpolicyagent/kube-mgmt:0.11          args:            - "--replicate=v1/pods"      volumes:        - name: opa-server          secret:            secretName: opa-server---kind: ConfigMapapiVersion: v1metadata:  name: opa-default-system-main  namespace: opadata:  main: |    package system    import data.kubernetes.validating.images    main = {      "apiVersion": "admission.k8s.io/v1beta1",      "kind": "AdmissionReview",      "response": response,    }    default uid = ""    uid = input.request.uid    response = {        "allowed": false,        "uid": uid,        "status": {            "reason": reason,        },    } {        reason = concat(", ", images.deny)        reason != ""    }    else = {"allowed": true, "uid": uid}
kubectl apply -f admission-controller.yaml

接下来,生成将用于将 OPA 注册为准入控制器的 manifest。

cat > webhook-configuration.yaml <<EOFkind: ValidatingWebhookConfigurationapiVersion: admissionregistration.k8s.io/v1beta1metadata:  name: opa-validating-webhookwebhooks:  - name: validating-webhook.openpolicyagent.org    rules:      - operations: ["CREATE", "UPDATE"]        apiGroups: ["*"]        apiVersions: ["*"]        resources: ["pods"]    clientConfig:      caBundle: $(cat ca.crt | base64 | tr -d '\n')      service:        namespace: opa        name: opaEOF

生成的配置文件包含 CA 证书的 base64 编码,以便可以在 Kubernetes API 服务器和 OPA 之间建立 TLS 连接。

kubectl apply -f webhook-configuration.yaml

查看 OPA 日志:

kubectl logs -l app=opa -c opa -f

定义策略并通过 Kubernetes 将其加载到 OPA

这里我们定义了对容器镜像的检查:

•是否来自受信任的仓库•是否使用了 latest tag 的镜像

image-policy.rego

package kubernetes.validating.imagesdeny[msg] {    some i    input.request.kind.kind == "Pod"    image := input.request.object.spec.containers[i].image    endswith(image, ":latest")    msg := sprintf("Image '%v' used latest image", [image]) } {    some i    input.request.kind.kind == "Pod"    image := input.request.object.spec.containers[i].image    not startswith(image, "192.168.64.1:5000")    msg := sprintf("Image '%v' comes from untrusted registry", [image])}
kubectl create configmap image-policy --from-file=image-policy.rego

检查 configmap 的 annotation openpolicyagent.org/policy-status 值是否 为 '{"status":"ok"}'。否则,就要根据报错信息处理问题。

注:192.168.64.1:5000 是笔者本地容器运行的一个私有仓库。

version: '3.6'services:  registry:    image: registry:2.7.1    container_name: registry    restart: always    environment:      REGISTRY_HTTP_ADDR: 0.0.0.0:5000      REGISTRY_STORAGE: filesystem      REGISTRY_STORAGE_DELETE_ENABLED: 'true'      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry    ports:      - '5000:5000'    volumes:      - '/Users/addo/Downloads/tmp/registry:/var/lib/registry'

测试

pod-bad-repo.yaml

apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-serverspec:  containers:  - image: nginx:1.21.1    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
kubectl apply -f pod-bad-repo.yamlError from server (Image 'nginx:1.21.1' comes from untrusted registry): error when creating "pod-bad-repo.yaml": admission webhook "validating-webhook.openpolicyagent.org" denied the request: Image 'nginx:1.21.1' comes from untrusted registry

pod-bad-tag.yaml

apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-serverspec:  containers:  - image: 192.168.64.1:5000/nginx:latest    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
kubectl apply -f pod-bad-tag.yamlError from server (Image '192.168.64.1:5000/nginx:latest' used latest image): error when creating "pod-bad-tag.yaml": admission webhook "validating-webhook.openpolicyagent.org" denied the request: Image '192.168.64.1:5000/nginx:latest' used latest image

pod-ok.yaml

apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-serverspec:  containers:  - image: 192.168.64.1:5000/nginx:1.21.1    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
kubectl apply -f pod-ok.yamlpod/web-server created

总结

策略即代码,以代码的实现表达策略;在通过策略与执行引擎的解耦分离,让策略更加的灵活。

后面我们再探索 OPA 的更多场景。

引用链接

[1] Admission Webhook: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/
[2] Open Policy Agent 官网: https://www.openpolicyagent.org/
[3] 这里: https://cloudnative.to/blog/introducing-policy-as-code-the-open-policy-agent-opa/


原文作者:云原生指北;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2020-03-04

  • 博文量
    130
  • 访问量
    54637