You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多租户K8s集群域名归属管控及Gatekeeper规则配置排障

问题背景
  • 现有多租户Kubernetes集群,内部部署搭载负载均衡的Nginx反向代理,泛域名*.example.com已解析指向该负载均衡IP地址
  • 集群内命名空间按所属用户划分为项目A、项目B两个分组,已通过NetworkPolicy实现跨项目通信隔离,仅开放所有命名空间到Nginx所在命名空间、反向代理的访问权限
  • 核心访问管控需求:带有project=a标签的命名空间内的服务,仅允许使用my-service.project-a.example.com格式的域名访问,禁止使用my-service.project-b.example.com、my-service.example.com等不符合项目归属的域名
  • 已在GKE集群通过Helm部署Gatekeeper,尝试实现Ingress Host格式校验:要求对应命名空间内创建的Ingress域名必须匹配*.{namespace-project-label}.example.com格式,编写的资源配置如下:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredingress
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredIngress
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredingress

        operations := {"CREATE", "UPDATE"}
        ns := input.review.object.metadata.namespace


        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          input.request.kind.kind == "Ingress"
          not data.kubernetes.namespaces[ns].labels.project

          msg := sprintf("Ingress denied as namespace '%v' is missing 'project' label", [ns])
        }


        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          input.request.kind.kind == "Ingress"
          operations[input.request.operation]

          host := input.request.object.spec.rules[_].host
          project := data.kubernetes.namespaces[ns].labels.project
          not fqdn_matches(host, project)
          msg := sprintf("invalid ingress host %v, has to be of the form *.%v.example.com", [host, project])

        }

        fqdn_matches(str, pattern) {
          str_parts := split(str, ".")
          count(str_parts) == 4
          str_parts[1] == pattern
        }

---

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredIngress
metadata:
  name: ns-must-have-gk
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Ingress"]

---

apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: "gatekeeper-system"
spec:
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Namespace"
  • 部署上述配置时持续返回报错:
kubectl apply -f constraint_template.yaml

Error from server: error when creating "constraint_template.yaml": admission webhook "validation.gatekeeper.sh" denied the request: invalid ConstraintTemplate: invalid data references: check refs failed on module {template}: errors (2):
disallowed ref data.kubernetes.namespaces[ns].labels.project
disallowed ref data.kubernetes.namespaces[ns].labels.project
报错原因

报错由三个配置问题共同导致:

  1. 动态索引缓存被安全规则拦截:Gatekeeper默认禁止在Rego规则中直接用动态变量作为键,索引本地同步缓存的集群资源。配置中data.kubernetes.namespaces[ns].labels.project的ns是从准入请求动态获取的变量,直接作为键索引命名空间列表,触发内置校验拒绝。
  2. 缓存引用路径错误:data.kubernetes是Gatekeeper极早期版本的资源缓存路径,新版本统一将同步的集群资源放在data.inventory路径下,旧路径默认不生效。
  3. 资源匹配规则错误:Ingress资源不属于核心API组(""),Constraint中配置的apiGroups参数错误,即使模板校验通过,也无法匹配到实际的Ingress资源。另外Rego中取请求对象的路径有误,Gatekeeper封装后的准入请求对象存放在input.review路径下,而非原生AdmissionReview的input.request路径。
修复方案

1. 修正ConstraintTemplate配置

将动态索引命名空间的逻辑改为遍历匹配,修正缓存路径和请求对象路径,补全域名后缀校验逻辑,修正后的配置如下:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredingress
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredIngress
      validation:
        openAPIV3Schema:
          type: object
          properties:
            rootDomain:
              type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredingress

        operations := {"CREATE", "UPDATE"}
        ns := input.review.object.metadata.namespace
        # 遍历同步的命名空间缓存,匹配当前请求所属命名空间(替代动态键索引)
        ns_obj := data.inventory.cluster["v1"].Namespace[_]
        ns_obj.metadata.name == ns
        project := ns_obj.metadata.labels.project
        root_domain := input.parameters.rootDomain

        # 校验命名空间必须存在project标签
        violation[{"msg": msg}] {
          input.review.kind.kind == "Ingress"
          input.review.kind.group == "networking.k8s.io"
          not project
          msg := sprintf("Ingress denied as namespace '%v' is missing required 'project' label", [ns])
        }

        # 校验所有Ingress域名符合规则
        violation[{"msg": msg}] {
          input.review.kind.kind == "Ingress"
          input.review.kind.group == "networking.k8s.io"
          operations[input.review.operation]
          project

          host := input.review.object.spec.rules[_].host
          not fqdn_matches(host, project, root_domain)
          msg := sprintf("Invalid ingress host '%v', must follow format *.%v.%v", [host, project, root_domain])
        }

        # 域名匹配逻辑:拆分后段数匹配,项目标识、根域名符合预期
        fqdn_matches(host, expected_project, root_domain) {
          domain_parts := split(root_domain, ".")
          host_parts := split(host, ".")
          # 计算总段数:服务名 + 项目标识 + 根域名段数
          count(host_parts) == count(domain_parts) + 2
          # 校验项目标识位置正确
          host_parts[1] == expected_project
          # 校验根域名后缀匹配
          suffix := array.slice(host_parts, 2, count(host_parts))
          suffix == domain_parts
        }

2. 修正Constraint匹配规则

将apiGroups改为Ingress实际所属的networking.k8s.io,同时传入根域名参数:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredIngress
metadata:
  name: require-ingress-host-match-project
spec:
  match:
    kinds:
      - apiGroups: ["networking.k8s.io"]
        kinds: ["Ingress"]
  parameters:
    rootDomain: "example.com"

3. 确认Config同步配置

原有Namespace同步配置无需修改,部署后等待Gatekeeper完成命名空间缓存同步即可正常生效。

补充落地方案

仅靠Gatekeeper准入校验属于第一道管控防线,建议增加两层兜底避免规则被绕过:

  • 在Nginx反向代理层新增校验规则,判断请求Host头与后端服务所在命名空间的project标签是否匹配,不符合规则的请求直接返回403
  • 限制普通用户创建LoadBalancer/NodePort类型Service的权限,所有对外暴露的服务必须通过指定的Nginx Ingress出口,避免用户绕开反向代理直接暴露服务
  • 若使用ExternalDNS做域名自动管理,可给每个项目命名空间配置固定的域名后缀注解,强制自动生成的DNS记录符合命名规则,减少人工配置错误

内容的提问来源于stack exchange,提问作者tobias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:24:23