apiVersion: v1 kind: Namespace metadata: name: ua-edgetranslator-namespace spec: finalizers: - kubernetes --- # OPC UA credentials must be supplied out-of-band — never commit real # values into source control. Provision the secret with: # # kubectl -n ua-edgetranslator-namespace create secret generic ua-edgetranslator-credentials \ # --from-literal=OPCUA_USERNAME='' \ # --from-literal=OPCUA_PASSWORD='' # # Optionally add a UACL{Username,Password,URL} key for cloud library access # and a LICENSE_KEY entry once licensing is enabled. The values below are # placeholders only and MUST be replaced before applying this manifest in # any environment outside of an isolated dev cluster. apiVersion: v1 kind: Secret metadata: name: ua-edgetranslator-credentials namespace: ua-edgetranslator-namespace type: Opaque stringData: # Replace these placeholders or, preferably, delete this Secret block # and provision the secret separately via `kubectl create secret`. OPCUA_USERNAME: "REPLACE_ME" OPCUA_PASSWORD: "REPLACE_ME" --- apiVersion: apps/v1 kind: Deployment metadata: name: ua-edgetranslator namespace: ua-edgetranslator-namespace spec: replicas: 1 selector: matchLabels: app: ua-edgetranslator template: metadata: labels: app: ua-edgetranslator spec: nodeSelector: "kubernetes.io/os": linux securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault initContainers: # hostPath volumes are NOT subject to the pod-level fsGroup, so when a # node-side directory is wiped (or first created), kubelet recreates it # as root:root mode 0755 and the non-root `app` user (UID 1000) inside # the main container cannot write into /app/pki, /app/logs, etc. # Symptom: "UnauthorizedAccessException: Access to the path # '/app/pki/own' is denied." during certificate generation at startup. # This init container runs as root (with only the capabilities chown # actually needs) to fix ownership before the main container starts. - name: fix-hostpath-perms image: busybox:1.36 imagePullPolicy: IfNotPresent command: ["sh", "-c", "chown -R 1000:1000 /app/settings /app/pki /app/logs /app/nodesets"] securityContext: # Override the pod-level runAsNonRoot/runAsUser=1000 so we can chown # files that are currently owned by root. runAsNonRoot: false runAsUser: 0 runAsGroup: 0 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL add: - CHOWN - FOWNER - DAC_OVERRIDE volumeMounts: - name: "settings" mountPath: "/app/settings" - name: "pki" mountPath: "/app/pki" - name: "logs" mountPath: "/app/logs" - name: "nodesets" mountPath: "/app/nodesets" - name: ua-edgetranslator-drivers image: ghcr.io/opcfoundation/ua-edgetranslator-drivers:main imagePullPolicy: Always command: ["/bin/sh", "-c", "cp -a /drivers/. /app/drivers/"] securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: "drivers" mountPath: "/app/drivers" containers: - name: ua-edgetranslator image: ghcr.io/opcfoundation/ua-edgetranslator:main imagePullPolicy: Always securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: false capabilities: drop: - ALL env: - name: OPCUA_USERNAME valueFrom: secretKeyRef: name: ua-edgetranslator-credentials key: OPCUA_USERNAME - name: OPCUA_PASSWORD valueFrom: secretKeyRef: name: ua-edgetranslator-credentials key: OPCUA_PASSWORD ports: - containerPort: 4840 # Diagnostics dashboard (Web UI) served over HTTP by DiagnosticsWebHost. - containerPort: 8081 - containerPort: 5000 - containerPort: 5001 - containerPort: 19520 - containerPort: 19521 resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "1000m" memory: "1Gi" livenessProbe: tcpSocket: port: 4840 initialDelaySeconds: 30 periodSeconds: 30 failureThreshold: 3 readinessProbe: tcpSocket: port: 4840 initialDelaySeconds: 10 periodSeconds: 10 failureThreshold: 3 volumeMounts: - name: "settings" mountPath: "/app/settings" - name: "pki" mountPath: "/app/pki" - name: "logs" mountPath: "/app/logs" - name: "nodesets" mountPath: "/app/nodesets" - name: "drivers" mountPath: "/app/drivers" volumes: - name: "settings" hostPath: path: "/mnt/c/K3s/UAEdgeTranslator/Settings" - name: "pki" hostPath: path: "/mnt/c/K3s/UAEdgeTranslator/PKI" - name: "logs" hostPath: path: "/mnt/c/K3s/UAEdgeTranslator/Logs" - name: "nodesets" hostPath: path: "/mnt/c/K3s/UAEdgeTranslator/Nodesets" - name: "drivers" emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: ua-edgetranslator namespace: ua-edgetranslator-namespace spec: type: LoadBalancer ports: - name: opcua targetPort: 4840 port: 4840 - name: diagnostics targetPort: 8081 port: 8081 - name: lorawan targetPort: 5000 port: 5000 - name: securelorawan targetPort: 5001 port: 5001 - name: ocpp targetPort: 19520 port: 19520 - name: secureocpp targetPort: 19521 port: 19521 selector: app: ua-edgetranslator --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ua-edgetranslator namespace: ua-edgetranslator-namespace spec: rules: - http: paths: - path: / pathType: Prefix backend: service: name: ua-edgetranslator port: number: 8081