使用webhook禁止k8s创建顶级域名的命名空间

通过创建与公共顶级域名同名的名字空间, 这些名字空间中的服务可以拥有与公共 DNS 记录重叠的、较短的 DNS 名称。 所有名字空间中的负载在执行 DNS 查找时, 如果查找的名称没有尾部句点, 就会被重定向到这些服务上,因此呈现出比公共 DNS 更高的优先序。

为了缓解这类问题,需要将创建名字空间的权限授予可信的用户。

或者额外部署第三方的安全控制机制, 例如以准入 Webhook 的形式,阻止用户创建与公共 TLD 同名的名字空间,也就是本文的内容。

环境

ubuntu 22.04

kubernetes 1.28.2

步骤一:准备证书

说明,apiserver访问webhook服务必须走https,需要准备证书(域名是webhook的service)。ca我们复用k8s的自签ca。

1.生成私钥和证书签名请求 (CSR)

首先,生成一个私钥和证书签名请求 (CSR)。

1.生成私钥:

openssl genrsa -out /etc/kubernetes/pki/webhook-service.key 2048

2.创建一个 CSR 配置文件 csr.conf,内容如下:

req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
CN = webhook-service.default.svc

[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = webhook-service.default.svc
DNS.2 = webhook-service.default.svc.cluster.local

3.使用私钥和 CSR 配置文件生成证书签名请求:

openssl req -new -key /etc/kubernetes/pki/webhook-service.key -out /etc/kubernetes/pki/webhook-service.csr -config csr.conf
2.使用 Kubernetes CA 签发证书

现在我们使用 Kubernetes 的 CA 证书 /etc/kubernetes/pki/ca.crt 和 CA 密钥 /etc/kubernetes/pki/ca.key 来签发证书。

使用 openssl 签发证书:

openssl x509 -req -in /etc/kubernetes/pki/webhook-service.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/webhook-service.crt -days 365 -extensions v3_req -extfile csr.conf

这会生成一个自签名的证书 /etc/kubernetes/pki/webhook-service.crt,有效期为 365 天。

验证证书

openssl x509 -in /etc/kubernetes/pki/webhook-service.crt -text -noout
步骤二:写webhook的程序(golang)
package main

import (
    "encoding/json"
    "net/http"

    admv1 "k8s.io/api/admission/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// 可根据需要扩展,TLD清单参考:https://data.iana.org/TLD/tlds-alpha-by-domain.txt
var publicTLDs = []string{"com", "org", "net", "gov", "edu", "mil", "cn", "jp"} 

func admitNamespace(ar *admv1.AdmissionReview) *admv1.AdmissionResponse {
    nsName := ar.Request.Name
    for _, tld := range publicTLDs {
        fmt.Println(nsName,1,tld)
        if nsName==tld {
            return &admv1.AdmissionResponse{
                Allowed: false,
                Result: &metav1.Status{
                    Message: "Namespace name matches a public TLD.",
                },
            }
        }
    }
    return &admv1.AdmissionResponse{Allowed: true}
}

func serve(w http.ResponseWriter, r *http.Request) {
    var admissionReview admv1.AdmissionReview
    if err := json.NewDecoder(r.Body).Decode(&admissionReview); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    admissionResponse := admitNamespace(&admissionReview)
    admissionReview.Response = admissionResponse
    admissionReview.Response.UID = admissionReview.Request.UID
    json.NewEncoder(w).Encode(admissionReview)
}

func main() {
    http.HandleFunc("/validate", serve)
    http.ListenAndServeTLS(":8443", "/etc/kubernetes/pki/webhook-service.crt", "/etc/kubernetes/pki/webhook-service.key", nil)

打包出的执行程序为:main

步骤三:部署服务
1. 部署webhook的dp和service

说明:本程序仅为测试,所以直接用nodeSelector直接把pod锁定到指定node上了。该node是golang程序所在的node,为了测试方便。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webhook-server
  namespace: default 
  labels:
    app: webhook-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webhook-server
  template:
    metadata:
      labels:
        app: webhook-server
    spec:
      containers:
      - name: webhook-server
        image: busybox:latest
        command: ["/opt/main"]
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: tls-certs
          mountPath: /etc/kubernetes/pki
          readOnly: true
        - name: program
          mountPath: /opt
          readOnly: true
      volumes:
      - name: tls-certs
        hostPath:
          path: /etc/kubernetes/pki
      - name: program
        hostPath:
          path: /root/yang/go
      nodeSelector:
        kubernetes.io/hostname: vm38
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        effect: "NoSchedule"
---
apiVersion: v1
kind: Service
metadata:
  name: webhook-service
  namespace: default
  labels:
    app: webhook-server
spec:
  ports:
    - port: 443
      targetPort: 8443
      protocol: TCP
  selector:
    app: webhook-server

kubectl apply -f 上面的yaml文件

2.部署ValidatingWebhookConfiguration

官方文档:ValidatingWebhookConfiguration | Kubernetes

官方文档:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/extensible-admission-controllers/

说明:

​ 1.请参阅官方文档

​ 2.下面yaml中clientConfig部分就是webhook服务的配置

​ 3.caBundle是步骤一中ca证书的Base64编码格式,生成的命令为:cat /etc/kubernetes/pki/ca.crt | base64 | tr -d '\n'

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: prevent-public-tld-ns
webhooks:
  - name: prevent-public-tld-ns.k8s.io
    clientConfig:
      service:
        name: webhook-service
        namespace: default
        path: "/validate"
      caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJR1k2NDdMdnZjTGt3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzV***********************************ZjhhTk0yNmZQTzdSK2gxTDBjCmRXbk1QdzR3cXU5emFYSEJyK2pneUsyRHVIOER6QnFYWjZXSVp4ZXRmRm1KMjRRV3dITCs0OEdtUGpWbGFRMUYKbkhYdkV5clhZclVJCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    rules:
      - operations: ["CREATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["namespaces"]
    admissionReviewVersions: ["v1"]
    sideEffects: None

kubectl apply -f 上面的yaml文件

步骤四:验证
# 执行下面命令尝试创建com命名空间,失败
kubectl create namespace com
# 输出报错提示如下:
Error from server: admission webhook "prevent-public-tld-ns.k8s.io" denied the request: Namespace name matches a public TLD.

# 执行下面命令尝试创建普通命名空间,成功
kubectl create namespace yeluomen
# 正常
namespace/yeluomen created

kubectl get namespaces

验证com这个命名空间确实没有创建出来,yeluomen命名空间创建出来了,完美!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值