zking2000 / NotePad

1 stars 0 forks source link

webhook #80

Open zking2000 opened 5 days ago

zking2000 commented 5 days ago
# cert.sh

#!/bin/bash

set -e
set -x

# 设置变量
NAMESPACE="injector"
DEST_NAMESPACE="injection-payload"
SERVICE="otelcol-injector"
SECRET="otelcol-injector-secret"
WEBHOOK_NAME="otelcol-injection-webhook"
CSR_NAME="${SERVICE}.${NAMESPACE}"
TMP_DIR=$(mktemp -d)
CERT_DIR="${TMP_DIR}/certs"
CONFIGMAP_NAME="otelcol-config"
mkdir -p ${CERT_DIR}

# 生成私钥
openssl genrsa -out ${CERT_DIR}/server.key 2048

# 创建 OpenSSL 配置文件
cat > ${CERT_DIR}/csr.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${SERVICE}
DNS.2 = ${SERVICE}.${NAMESPACE}
DNS.3 = ${SERVICE}.${NAMESPACE}.svc
EOF

# 生成CSR
openssl req -new -key ${CERT_DIR}/server.key -subj "/CN=${SERVICE}.${NAMESPACE}.svc" -out ${CERT_DIR}/server.csr -config ${CERT_DIR}/csr.conf

# 创建CSR资源
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: ${CSR_NAME}
spec:
  request: $(cat ${CERT_DIR}/server.csr | base64 | tr -d '\n')
  signerName: kubernetes.io/kubelet-serving
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

# 批准CSR
kubectl certificate approve ${CSR_NAME}

# 获取签名的证书
kubectl get csr ${CSR_NAME} -o jsonpath='{.status.certificate}' | base64 --decode > ${CERT_DIR}/server.crt

# 检查 namespace 是否存在,如果不存在则创建
if ! kubectl get namespace ${NAMESPACE} &> /dev/null; then
  kubectl create namespace ${NAMESPACE}
fi

# 创建Kubernetes Secret
kubectl create secret tls ${SECRET} \
  --cert=${CERT_DIR}/server.crt \
  --key=${CERT_DIR}/server.key \
  --namespace=${NAMESPACE} \
  --dry-run=client -o yaml | kubectl apply -f -

# 创建ConfigMap
cat <<EOF > ${TMP_DIR}/otelcol-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:
exporters:
  console:
    verbosity: detailed
processors:
  batch: {}
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [console]
EOF

# 检查 DEST_NAMESPACE 是否存在,如果不存在则创建
if ! kubectl get namespace ${DEST_NAMESPACE} &> /dev/null; then
  kubectl create namespace ${DEST_NAMESPACE}
fi

kubectl create configmap ${CONFIGMAP_NAME} \
  --from-file=otelcol-config.yaml=${TMP_DIR}/otelcol-config.yaml \
  --namespace=${DEST_NAMESPACE} \
  --dry-run=client -o yaml | kubectl apply -f -

# 获取CA_BUNDLE
CA_BUNDLE=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

# 创建MutatingWebhookConfiguration
cat <<EOF | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: ${WEBHOOK_NAME}
webhooks:
  - name: otelcol-injector.${NAMESPACE}.svc
    clientConfig:
      service:
        name: ${SERVICE}
        namespace: ${NAMESPACE}
        path: "/mutate"
      caBundle: ${CA_BUNDLE}
    namespaceSelector:
      matchLabels:
        otelcol-injection: enabled
    rules:
      - operations: ["CREATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
    admissionReviewVersions: ["v1"]
    sideEffects: None
EOF

# 创建namespace的label
kubectl label namespace ${DEST_NAMESPACE} otelcol-injection=enabled --overwrite

# 应用 RBAC 资源
kubectl apply -f sa.yaml

# 清理临时文件
rm -rf ${TMP_DIR}

echo "证书签发、Secret创建、ConfigMap创建和MutatingWebhookConfiguration配置已完成。"
# app.py

from flask import Flask, request, jsonify
import json
import base64
from kubernetes import client, config
import logging
import os

app = Flask(__name__)

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 加载 Kubernetes 配置
config.load_incluster_config()
api = client.CoreV1Api()

@app.route('/mutate', methods=['POST'])
def mutate():
    request_info = request.get_json()
    namespace = request_info['request']['namespace']
    logger.info(f"Processing request for namespace: {namespace}")

    # 获取命名空间的标签
    namespace_labels = api.read_namespace(namespace).metadata.labels
    logger.info(f"Namespace labels: {namespace_labels}")

    # 检查命名空间标签
    if namespace_labels.get('otelcol-injection') == 'enabled':
        logger.info("Injection enabled for this namespace")
        # 注入otelcol sidecar容器
        patch = [
            {
                "op": "add",
                "path": "/spec/containers/-",
                "value": {
                    "name": "otelcol-sidecar",
                    "image": "otel/opentelemetry-collector-k8s:0.110.0",
                    "args": ["--config", "/etc/otelcol/config.yaml"],
                    "ports": [{"containerPort": 4317}, {"containerPort": 4318}],
                    "volumeMounts": [
                        {
                            "name": "otelcol-config-volume",
                            "mountPath": "/etc/otelcol/config.yaml",
                            "subPath": "otelcol-config.yaml"
                        }
                    ]
                }
            },
            {
                "op": "add",
                "path": "/spec/volumes/-",
                "value": {
                    "name": "otelcol-config-volume",
                    "configMap": {
                        "name": "otelcol-config"
                    }
                }
            }
        ]
        admission_response = {
            "apiVersion": "admission.k8s.io/v1",
            "kind": "AdmissionReview",
            "response": {
                "uid": request_info['request']['uid'],
                "allowed": True,
                "patchType": "JSONPatch",
                "patch": base64.b64encode(json.dumps(patch).encode()).decode()
            }
        }
    else:
        logger.info("Injection not enabled for this namespace")
        admission_response = {
            "apiVersion": "admission.k8s.io/v1",
            "kind": "AdmissionReview",
            "response": {
                "uid": request_info['request']['uid'],
                "allowed": True
            }
        }

    logger.info(f"Admission response: {admission_response}")
    return jsonify(admission_response)

@app.route('/healthz', methods=['GET'])
def healthz():
    return "OK", 200

# 确保应用程序配置正确
# 例如,确保应用程序监听正确的端口
if __name__ == "__main__":
    cert_path = os.environ.get('SSL_CERT_PATH', '/tls/tls.crt')
    key_path = os.environ.get('SSL_KEY_PATH', '/tls/tls.key')

    if os.path.exists(cert_path) and os.path.exists(key_path):
        ssl_context = (cert_path, key_path)
        app.run(host="0.0.0.0", port=9443, ssl_context=ssl_context)
    else:
        logger.warning("SSL证书文件未找到。以非SSL模式运行。")
        app.run(host="0.0.0.0", port=9443)
FROM python:3.11-slim

WORKDIR /app

COPY app.py /app
RUN pip3 install flask kubernetes

CMD ["python3", "app.py"]
tag=v1.0.10

docker build -t zking2000/webhook:$tag .

docker push zking2000/webhook:$tag
apiVersion: apps/v1
kind: Deployment
metadata:
  name: otelcol-injector
  namespace: injector
spec:
  replicas: 1
  selector:
    matchLabels:
      app: otelcol-injector
  template:
    metadata:
      labels:
        app: otelcol-injector
    spec:
      serviceAccountName: otelcol-sidecar-sa
      containers:
        - name: otelcol-injector
          image: zking2000/webhook:v1.0.7
          ports:
            - containerPort: 9443
          volumeMounts:
            - name: webhook-certs
              mountPath: /tls
              readOnly: true
      volumes:
        - name: webhook-certs
          secret:
            secretName: otelcol-injector-secret
---
apiVersion: v1
kind: Service
metadata:
  name: otelcol-injector
  namespace: injector
spec:
  ports:
    - port: 443
      targetPort: 9443
  selector:
    app: otelcol-injector
zking2000 commented 5 days ago
apiVersion: v1
kind: ServiceAccount
metadata:
  name: otelcol-sidecar-sa
  namespace: injector
  annotations:
    iam.gke.io/gcp-service-account: "tempo-gcs-access@observability-436022.iam.gserviceaccount.com"

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-reader
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: otelcol-sidecar-namespace-reader
subjects:
- kind: ServiceAccount
  name: otelcol-sidecar-sa
  namespace: injector
roleRef:
  kind: ClusterRole
  name: namespace-reader
  apiGroup: rbac.authorization.k8s.io
zking2000 commented 5 days ago
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "request": {
    "uid": "12345",
    "kind": {
      "group": "",
      "version": "v1",
      "kind": "Pod"
    },
    "resource": {
      "group": "",
      "version": "v1",
      "resource": "pods"
    },
    "namespace": "injection-payload",
    "object": {
      "metadata": {
        "name": "test-pod",
        "labels": {
          "otelcol-injection": "enabled"
        }
      },
      "spec": {
        "containers": [
          {
            "name": "test-container",
            "image": "nginx"
          }
        ]
      }
    }
  }
}
zking2000 commented 5 days ago

!/bin/bash

set -e

Set variables

NAMESPACE="grafana-stack" DEST_NAMESPACE="injection-payload" SERVICE="otelcol-injector" SECRET="otelcol-injector-secret" WEBHOOK_NAME="otelcol-injection-webhook" CSR_NAME="${SERVICE}.${NAMESPACE}" CERT_DIR="${TMP_DIR}/certs" CONFIGMAP_NAME="otelcol-config" SERVICE_ACCOUNT="pft-uk-grafana-gke-sa"

export HTTPS_PROXY=http://localhost:8888

Function to check if resource exists and delete if it does

delete_if_exists() { local resource_type=$1 local resource_name=$2 local extra_args=$3

if kubectl get $resource_type $resource_name $extra_args &> /dev/null; then
    echo "Deleting $resource_type $resource_name"
    kubectl delete $resource_type $resource_name $extra_args
else
    echo "$resource_type $resource_name not found, skipping deletion"
fi

}

Cleanup approved csr

delete_if_exists CertificateSigningRequest ${CSR_NAME}

Delete deployment

if [ -f deployment.yaml ]; then echo "Deleting resources defined in deployment.yaml" kubectl delete -f deployment.yaml || echo "Failed to delete some resources from deployment.yaml" else echo "deployment.yaml not found, skipping" fi

Delete namespace

delete_if_exists namespace ${DEST_NAMESPACE}

Delete MutatingWebhookConfiguration

delete_if_exists MutatingWebhookConfiguration ${WEBHOOK_NAME}

Delete ClusterRoleBinding

delete_if_exists ClusterRoleBinding otelcol-sidecar-namespace-reader

Delete ClusterRole

delete_if_exists ClusterRole namespace-reader

echo "Cleanup completed."

zking2000 commented 5 days ago

#!/bin/bash

set -e

# Set variables
NAMESPACE="grafana-stack"
DEST_NAMESPACE="injection-payload"
SERVICE="otelcol-injector"
SECRET="otelcol-injector-secret"
WEBHOOK_NAME="otelcol-injection-webhook"
CSR_NAME="${SERVICE}.${NAMESPACE}"
CERT_DIR="${TMP_DIR}/certs"
CONFIGMAP_NAME="otelcol-config"
SERVICE_ACCOUNT="pft-uk-grafana-gke-sa"

export HTTPS_PROXY=http://localhost:8888

# Function to check if resource exists and delete if it does
delete_if_exists() {
    local resource_type=$1
    local resource_name=$2
    local extra_args=$3

    if kubectl get $resource_type $resource_name $extra_args &> /dev/null; then
        echo "Deleting $resource_type $resource_name"
        kubectl delete $resource_type $resource_name $extra_args
    else
        echo "$resource_type $resource_name not found, skipping deletion"
    fi
}

# Cleanup approved csr
delete_if_exists CertificateSigningRequest ${CSR_NAME}

# Delete deployment
if [ -f deployment.yaml ]; then
    echo "Deleting resources defined in deployment.yaml"
    kubectl delete -f deployment.yaml || echo "Failed to delete some resources from deployment.yaml"
else
    echo "deployment.yaml not found, skipping"
fi

# Delete namespace
delete_if_exists namespace ${DEST_NAMESPACE}

# Delete MutatingWebhookConfiguration
delete_if_exists MutatingWebhookConfiguration ${WEBHOOK_NAME}

# Delete ClusterRoleBinding
delete_if_exists ClusterRoleBinding otelcol-sidecar-namespace-reader

# Delete ClusterRole
delete_if_exists ClusterRole namespace-reader

echo "Cleanup completed."