From b2a77a65729a25c6d38788f898e85b59370e1590 Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Mon, 8 Dec 2025 17:31:51 +0200 Subject: [PATCH] Reworked pasarguard nodes daemonset. --- .../pasarguard/configmap-scripts-ingress.yaml | 275 ++++++++++++++++++ k8s/apps/pasarguard/daemonset-ingress.yaml | 225 ++++++++++++++ k8s/apps/pasarguard/kustomization.yaml | 2 + 3 files changed, 502 insertions(+) create mode 100644 k8s/apps/pasarguard/configmap-scripts-ingress.yaml create mode 100644 k8s/apps/pasarguard/daemonset-ingress.yaml diff --git a/k8s/apps/pasarguard/configmap-scripts-ingress.yaml b/k8s/apps/pasarguard/configmap-scripts-ingress.yaml new file mode 100644 index 0000000..c437f76 --- /dev/null +++ b/k8s/apps/pasarguard/configmap-scripts-ingress.yaml @@ -0,0 +1,275 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: pasarguard-scripts-ingress + labels: + app: pasarguard-node-ingress +data: + init-uuid-ingress.sh: | + #!/bin/bash + set -e + echo "Started" + # NODE_NAME is already set via environment variable + NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) + + # Get DNS name from node label xray-node-address + DNS_NAME=$(kubectl get node "${NODE_NAME}" -o jsonpath='{.metadata.labels.xray-node-address}') + + if [ -z "${DNS_NAME}" ]; then + echo "ERROR: Node ${NODE_NAME} does not have label 'xray-node-address'" + exit 1 + fi + + echo "Node: ${NODE_NAME}" + echo "DNS Name from label: ${DNS_NAME}" + + # Use DNS name for ConfigMap name to ensure uniqueness + CONFIGMAP_NAME="node-uuid-ingress-${DNS_NAME//./-}" + + echo "Checking ConfigMap: ${CONFIGMAP_NAME}" + + # Check if ConfigMap exists and get UUID + if kubectl get configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" &>/dev/null; then + echo "ConfigMap exists, reading UUID..." + API_KEY=$(kubectl get configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" -o jsonpath='{.data.API_KEY}') + + if [ -z "${API_KEY}" ]; then + echo "UUID not found in ConfigMap, generating new one..." + API_KEY=$(cat /proc/sys/kernel/random/uuid) + kubectl patch configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" --type merge -p "{\"data\":{\"API_KEY\":\"${API_KEY}\"}}" + else + echo "Using existing UUID from ConfigMap" + fi + else + echo "ConfigMap does not exist, creating new one..." + API_KEY=$(cat /proc/sys/kernel/random/uuid) + kubectl create configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" \ + --from-literal=API_KEY="${API_KEY}" \ + --from-literal=NODE_NAME="${NODE_NAME}" + fi + + # Save UUID and node info to shared volume for the main container + echo -n "${API_KEY}" > /shared/api-key + echo -n "${NODE_NAME}" > /shared/node-name + echo -n "${CONFIGMAP_NAME}" > /shared/configmap-name + echo "UUID initialized: ${API_KEY}" + echo "Node name: ${NODE_NAME}" + echo "ConfigMap: ${CONFIGMAP_NAME}" + + # Create Certificate for this node using DNS name from label + CERT_NAME="pasarguard-node-ingress-${DNS_NAME//./-}" + + echo "Creating Certificate: ${CERT_NAME} for ${DNS_NAME}" + + # Check if Certificate already exists + if ! kubectl get certificate "${CERT_NAME}" -n "${NAMESPACE}" &>/dev/null; then + echo "Certificate does not exist, creating..." + cat </dev/null; then + echo "Certificate secret is ready!" + break + fi + echo "Waiting for certificate... ($i/600)" + sleep 1 + done + + if ! kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" &>/dev/null; then + echo "WARNING: Certificate secret not ready after 600 seconds" + else + # Extract certificate and key from secret to shared volume + echo "Extracting certificate and key..." + kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" -o jsonpath='{.data.tls\.crt}' | base64 -d > /shared/tls.crt + kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" -o jsonpath='{.data.tls\.key}' | base64 -d > /shared/tls.key + echo "Certificate and key extracted successfully." + cat /shared/tls.crt + fi + + # Create ClusterIP Service for this node (pod selector based) + NODE_SHORT_NAME="${NODE_NAME%%.*}" + SERVICE_NAME="${NODE_SHORT_NAME}-ingress" + + echo "Creating Service: ${SERVICE_NAME} for node ${NODE_NAME} (short: ${NODE_SHORT_NAME})" + + # Create Service with pod selector + cat </dev/null; do + if [ -f /shared/xray-api-port ]; then + NEW_PORT=$(cat /shared/xray-api-port) + if [ "$NEW_PORT" != "$API_PORT" ]; then + echo "API port changed from $API_PORT to $NEW_PORT, restarting exporter" + kill $EXPORTER_PID 2>/dev/null + wait $EXPORTER_PID 2>/dev/null + break + fi + fi + sleep 5 + done + + echo "Exporter stopped, restarting..." + wait $EXPORTER_PID 2>/dev/null + fi + fi + sleep 2 + done + + pasarguard-start.sh: | + #!/bin/sh + # Read API_KEY from shared volume created by init container + if [ -f /shared/api-key ]; then + export API_KEY=$(cat /shared/api-key) + echo "Loaded API_KEY from shared volume" + else + echo "WARNING: API_KEY file not found, using default" + fi + + cd /app + + # Start main process in background + ./main & + MAIN_PID=$! + + # Start continuous port monitoring in background + { + sleep 10 # Wait for xray to start initially + LAST_PORT="" + + while true; do + API_PORT=$(netstat -tlpn | grep xray | grep 127.0.0.1 | awk '{print $4}' | cut -d: -f2 | head -1) + if [ -n "$API_PORT" ] && [ "$API_PORT" != "$LAST_PORT" ]; then + echo "Found xray API port: $API_PORT" + echo -n "$API_PORT" > /shared/xray-api-port + LAST_PORT="$API_PORT" + fi + sleep 5 # Check every 5 seconds + done + } & + PORT_MONITOR_PID=$! + + # Wait for main process to finish + wait $MAIN_PID + + # Clean up port monitor + kill $PORT_MONITOR_PID 2>/dev/null diff --git a/k8s/apps/pasarguard/daemonset-ingress.yaml b/k8s/apps/pasarguard/daemonset-ingress.yaml new file mode 100644 index 0000000..cdb18a5 --- /dev/null +++ b/k8s/apps/pasarguard/daemonset-ingress.yaml @@ -0,0 +1,225 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pasarguard-node-ingress + labels: + app: pasarguard-node-ingress +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pasarguard-node-ingress-configmap + labels: + app: pasarguard-node-ingress +rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "list", "create", "update", "patch"] + - apiGroups: ["cert-manager.io"] + resources: ["certificates"] + verbs: ["get", "list", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + - apiGroups: [""] + resources: ["services", "endpoints"] + verbs: ["get", "list", "create", "update", "patch", "delete"] + - apiGroups: ["traefik.io", "traefik.containo.us"] + resources: ["ingressroutetcps"] + verbs: ["get", "list", "create", "update", "patch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pasarguard-node-ingress-configmap + labels: + app: pasarguard-node-ingress +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pasarguard-node-ingress-configmap +subjects: + - kind: ServiceAccount + name: pasarguard-node-ingress +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: pasarguard-node-ingress-reader + labels: + app: pasarguard-node-ingress +rules: + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: pasarguard-node-ingress-reader + labels: + app: pasarguard-node-ingress +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: pasarguard-node-ingress-reader +subjects: + - kind: ServiceAccount + name: pasarguard-node-ingress + namespace: pasarguard +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: pasarguard-node-ingress + labels: + app: pasarguard-node-ingress +spec: + selector: + matchLabels: + app: pasarguard-node-ingress + revisionHistoryLimit: 3 + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + app: pasarguard-node-ingress + spec: + serviceAccountName: pasarguard-node-ingress + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: xray-node-address + operator: Exists + initContainers: + - name: init-uuid + image: bitnami/kubectl:latest + env: + - name: GODEBUG + value: "x509sha1=1" + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + command: + - /bin/bash + - /scripts/init-uuid-ingress.sh + volumeMounts: + - name: shared-data + mountPath: /shared + - name: scripts + mountPath: /scripts + containers: + - name: xray-exporter + image: alpine:3.18 + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - /scripts/exporter-start.sh + ports: + - name: metrics + containerPort: 9550 + protocol: TCP + livenessProbe: + httpGet: + path: /scrape + port: metrics + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /scrape + port: metrics + initialDelaySeconds: 45 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "150m" + volumeMounts: + - name: shared-data + mountPath: /shared + readOnly: true + - name: scripts + mountPath: /scripts + - name: pasarguard-node + image: 'pasarguard/node:v0.1.3' + imagePullPolicy: Always + command: + - /bin/sh + - /scripts/pasarguard-start.sh + ports: + - name: api + containerPort: 62050 + protocol: TCP + - name: proxy + containerPort: 443 + protocol: TCP + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: NODE_HOST + value: "0.0.0.0" + - name: SERVICE_PORT + value: "62050" + - name: SERVICE_PROTOCOL + value: "grpc" + - name: DEBUG + value: "true" + - name: SSL_CERT_FILE + value: "/shared/tls.crt" + - name: SSL_KEY_FILE + value: "/shared/tls.key" + - name: XRAY_EXECUTABLE_PATH + value: "/usr/local/bin/xray" + - name: XRAY_ASSETS_PATH + value: "/usr/local/share/xray" + - name: API_KEY + value: "change-this-to-a-secure-uuid" + livenessProbe: + tcpSocket: + port: 62050 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + tcpSocket: + port: 62050 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "750m" + volumeMounts: + - name: shared-data + mountPath: /shared + readOnly: false + - name: scripts + mountPath: /scripts + volumes: + - name: shared-data + emptyDir: {} + - name: scripts + configMap: + name: pasarguard-scripts-ingress + defaultMode: 0755 diff --git a/k8s/apps/pasarguard/kustomization.yaml b/k8s/apps/pasarguard/kustomization.yaml index 620f148..9daac29 100644 --- a/k8s/apps/pasarguard/kustomization.yaml +++ b/k8s/apps/pasarguard/kustomization.yaml @@ -9,3 +9,5 @@ resources: - ./certificate.yaml - ./configmap-scripts.yaml - ./servicemonitor.yaml + - ./configmap-scripts-ingress.yaml + - ./daemonset-ingress.yaml