From bbf9f72fd3a84840be9aa4c96d68e3765d11f182 Mon Sep 17 00:00:00 2001 From: mrhid6 Date: Fri, 31 Jul 2026 09:28:54 +0100 Subject: [PATCH] feat: Docker and helm charts --- .gitignore | 4 +- deploy/agent.service | 23 ---- deploy/chart/vantage/Chart.yaml | 6 + deploy/chart/vantage/templates/NOTES.txt | 17 +++ deploy/chart/vantage/templates/_helpers.tpl | 11 ++ deploy/chart/vantage/templates/guacd.yaml | 44 +++++++ deploy/chart/vantage/templates/mongo.yaml | 90 ++++++++++++++ deploy/chart/vantage/templates/redis.yaml | 90 ++++++++++++++ deploy/chart/vantage/templates/server.yaml | 123 ++++++++++++++++++++ deploy/chart/vantage/templates/web.yaml | 57 +++++++++ deploy/chart/vantage/values.yaml | 66 +++++++++++ deploy/{ => docker}/.env.example | 2 +- deploy/{ => docker}/docker-compose.site.yml | 0 deploy/{ => docker}/docker-compose.yml | 2 +- 14 files changed, 509 insertions(+), 26 deletions(-) delete mode 100644 deploy/agent.service create mode 100644 deploy/chart/vantage/Chart.yaml create mode 100644 deploy/chart/vantage/templates/NOTES.txt create mode 100644 deploy/chart/vantage/templates/_helpers.tpl create mode 100644 deploy/chart/vantage/templates/guacd.yaml create mode 100644 deploy/chart/vantage/templates/mongo.yaml create mode 100644 deploy/chart/vantage/templates/redis.yaml create mode 100644 deploy/chart/vantage/templates/server.yaml create mode 100644 deploy/chart/vantage/templates/web.yaml create mode 100644 deploy/chart/vantage/values.yaml rename deploy/{ => docker}/.env.example (94%) rename deploy/{ => docker}/docker-compose.site.yml (100%) rename deploy/{ => docker}/docker-compose.yml (97%) diff --git a/.gitignore b/.gitignore index fa0ea7b..208ec05 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ dist build .env .env.bck +.env.live docs/* !docs/superpowers/ .superpowers @@ -12,4 +13,5 @@ installer/nssm.zip installer/checksums-msi.txt .next *.tsbuildinfo -graphify-out \ No newline at end of file +graphify-out +docker-compose.live.yml \ No newline at end of file diff --git a/deploy/agent.service b/deploy/agent.service deleted file mode 100644 index 60699fd..0000000 --- a/deploy/agent.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=Vantage Agent -Documentation=https://github.com/your-org/vantage -After=network.target -Wants=network-online.target - -[Service] -Type=simple -ExecStart=/usr/local/bin/vantage-agent -Restart=always -RestartSec=10 -User=root -StandardOutput=journal -StandardError=journal -SyslogIdentifier=vantage-agent - -# Security hardening -NoNewPrivileges=true -ProtectSystem=false -ProtectHome=false - -[Install] -WantedBy=multi-user.target diff --git a/deploy/chart/vantage/Chart.yaml b/deploy/chart/vantage/Chart.yaml new file mode 100644 index 0000000..b3802c0 --- /dev/null +++ b/deploy/chart/vantage/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: vantage +description: Helm chart for the Vantage stack (Redis, MongoDB, guacd, server, web) +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/deploy/chart/vantage/templates/NOTES.txt b/deploy/chart/vantage/templates/NOTES.txt new file mode 100644 index 0000000..8948f50 --- /dev/null +++ b/deploy/chart/vantage/templates/NOTES.txt @@ -0,0 +1,17 @@ +Vantage has been deployed as release "{{ .Release.Name }}" in namespace "{{ .Release.Namespace }}". + +Services created: + - {{ .Release.Name }}-redis (ClusterIP {{ .Values.redis.port }}) + - {{ .Release.Name }}-mongo (ClusterIP {{ .Values.mongo.port }}) + - {{ .Release.Name }}-guacd ({{ .Values.guacd.service.type }} {{ .Values.guacd.service.port }}) + - {{ .Release.Name }}-server ({{ .Values.server.service.type }} http:{{ .Values.server.service.httpPort }} grpc:{{ .Values.server.service.grpcPort }}) + - {{ .Release.Name }}-web ({{ .Values.web.service.type }} {{ .Values.web.service.port }}) + +By default the server/web/guacd services are ClusterIP only (no host port publishing, +unlike the original docker-compose file). To expose them externally, set +server.service.type / web.service.type / guacd.service.type to NodePort or LoadBalancer, +or add an Ingress on top of the -web and -server services. + +Quick access via port-forward, e.g.: + kubectl port-forward svc/{{ .Release.Name }}-web {{ .Values.web.service.port }}:{{ .Values.web.service.port }} + kubectl port-forward svc/{{ .Release.Name }}-server {{ .Values.server.service.httpPort }}:{{ .Values.server.service.httpPort }} diff --git a/deploy/chart/vantage/templates/_helpers.tpl b/deploy/chart/vantage/templates/_helpers.tpl new file mode 100644 index 0000000..f1063cb --- /dev/null +++ b/deploy/chart/vantage/templates/_helpers.tpl @@ -0,0 +1,11 @@ +{{/* +Common name helpers +*/}} +{{- define "vantage.fullname" -}} +{{ .Release.Name }} +{{- end -}} + +{{- define "vantage.labels" -}} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} diff --git a/deploy/chart/vantage/templates/guacd.yaml b/deploy/chart/vantage/templates/guacd.yaml new file mode 100644 index 0000000..7a6ed4f --- /dev/null +++ b/deploy/chart/vantage/templates/guacd.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-guacd + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: guacd +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: guacd + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: guacd + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: guacd + image: "{{ .Values.guacd.image.repository }}:{{ .Values.guacd.image.tag }}" + ports: + - containerPort: 4822 +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-guacd + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: guacd +spec: + type: {{ .Values.guacd.service.type }} + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: guacd + ports: + - port: {{ .Values.guacd.service.port }} + targetPort: 4822 diff --git a/deploy/chart/vantage/templates/mongo.yaml b/deploy/chart/vantage/templates/mongo.yaml new file mode 100644 index 0000000..a27b238 --- /dev/null +++ b/deploy/chart/vantage/templates/mongo.yaml @@ -0,0 +1,90 @@ +{{- if .Values.mongo.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-mongo-data + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: mongo +spec: + accessModes: + - {{ .Values.mongo.persistence.accessMode }} + {{- if .Values.mongo.persistence.storageClass }} + storageClassName: {{ .Values.mongo.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.mongo.persistence.size }} +--- +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-mongo + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: mongo +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: mongo + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: mongo + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: mongo + image: "{{ .Values.mongo.image.repository }}:{{ .Values.mongo.image.tag }}" + ports: + - containerPort: {{ .Values.mongo.port }} + volumeMounts: + - name: mongo-data + mountPath: /data/db + livenessProbe: + exec: + command: ["mongosh", "--quiet", "--eval", "db.adminCommand('ping')"] + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + readinessProbe: + exec: + command: ["mongosh", "--quiet", "--eval", "db.adminCommand('ping')"] + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + volumes: + - name: mongo-data + {{- if .Values.mongo.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Release.Name }}-mongo-data + {{- else }} + emptyDir: {} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-mongo + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: mongo +spec: + type: ClusterIP + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: mongo + ports: + - port: {{ .Values.mongo.port }} + targetPort: {{ .Values.mongo.port }} diff --git a/deploy/chart/vantage/templates/redis.yaml b/deploy/chart/vantage/templates/redis.yaml new file mode 100644 index 0000000..5c41cee --- /dev/null +++ b/deploy/chart/vantage/templates/redis.yaml @@ -0,0 +1,90 @@ +{{- if .Values.redis.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-redis-data + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: redis +spec: + accessModes: + - {{ .Values.redis.persistence.accessMode }} + {{- if .Values.redis.persistence.storageClass }} + storageClassName: {{ .Values.redis.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.redis.persistence.size }} +--- +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-redis + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: redis +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: redis + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: redis + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: redis + image: "{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}" + ports: + - containerPort: {{ .Values.redis.port }} + volumeMounts: + - name: redis-data + mountPath: /data + livenessProbe: + exec: + command: ["redis-cli", "ping"] + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + readinessProbe: + exec: + command: ["redis-cli", "ping"] + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + volumes: + - name: redis-data + {{- if .Values.redis.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Release.Name }}-redis-data + {{- else }} + emptyDir: {} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-redis + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: redis +spec: + type: ClusterIP + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: redis + ports: + - port: {{ .Values.redis.port }} + targetPort: {{ .Values.redis.port }} diff --git a/deploy/chart/vantage/templates/server.yaml b/deploy/chart/vantage/templates/server.yaml new file mode 100644 index 0000000..79dd8a2 --- /dev/null +++ b/deploy/chart/vantage/templates/server.yaml @@ -0,0 +1,123 @@ +{{- if and .Values.server.persistence.enabled (not .Values.server.persistence.useHostPath) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-server-data + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: server +spec: + accessModes: + - {{ .Values.server.persistence.accessMode }} + {{- if .Values.server.persistence.storageClass }} + storageClassName: {{ .Values.server.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.server.persistence.size }} +--- +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-server + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: server +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: server + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: server + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + # Wait for redis & mongo to be reachable, approximating compose's + # `depends_on: condition: service_healthy` + initContainers: + - name: wait-for-redis + image: busybox:1.36 + command: + - sh + - -c + - | + until nc -z {{ .Release.Name }}-redis {{ .Values.redis.port }}; do + echo "waiting for redis..."; sleep 2; + done + - name: wait-for-mongo + image: busybox:1.36 + command: + - sh + - -c + - | + until nc -z {{ .Release.Name }}-mongo {{ .Values.mongo.port }}; do + echo "waiting for mongo..."; sleep 2; + done + containers: + - name: server + image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}" + ports: + - containerPort: {{ .Values.server.service.httpPort }} + - containerPort: {{ .Values.server.service.grpcPort }} + env: + - name: MONGO_URI + value: {{ tpl .Values.server.env.mongoUri . | quote }} + - name: REDIS_ADDR + value: "{{ .Release.Name }}-redis:{{ .Values.redis.port }}" + - name: GRPC_HOST + value: {{ .Values.server.env.grpcHost | quote }} + - name: GRPC_PORT + value: {{ .Values.server.service.grpcPort | quote }} + - name: HTTP_PORT + value: {{ .Values.server.service.httpPort | quote }} + - name: KEY_ENCRYPTION_KEY + value: {{ .Values.server.env.keyEncryptionKey | quote }} + - name: VANTAGE_WORKFLOW_LOG_DIR + value: {{ .Values.server.env.vantageWorkflowLogDir | quote }} + - name: GUACD_ADDR + value: "{{ .Release.Name }}-guacd:{{ .Values.guacd.service.port }}" + - name: APP_ROOT_LABEL + value: {{ .Values.server.env.appRootLabel | quote }} + - name: PROXY_ADVERTISE_HOST + value: {{ .Values.server.env.proxyAdvertiseHost | quote }} + - name: PROXY_LISTEN_HOST + value: {{ .Values.server.env.proxyListenHost | quote }} + volumeMounts: + - name: server-data + mountPath: /data + volumes: + - name: server-data + {{- if .Values.server.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Release.Name }}-server-data + {{- else }} + emptyDir: {} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-server + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: server +spec: + type: {{ .Values.server.service.type }} + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: server + ports: + - name: http + port: {{ .Values.server.service.httpPort }} + targetPort: {{ .Values.server.service.httpPort }} + - name: grpc + port: {{ .Values.server.service.grpcPort }} + targetPort: {{ .Values.server.service.grpcPort }} diff --git a/deploy/chart/vantage/templates/web.yaml b/deploy/chart/vantage/templates/web.yaml new file mode 100644 index 0000000..cebf903 --- /dev/null +++ b/deploy/chart/vantage/templates/web.yaml @@ -0,0 +1,57 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-web + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: web +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: web + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: web + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + initContainers: + - name: wait-for-server + image: busybox:1.36 + command: + - sh + - -c + - | + until nc -z {{ .Release.Name }}-server {{ .Values.server.service.httpPort }}; do + echo "waiting for server..."; sleep 2; + done + containers: + - name: web + image: "{{ .Values.web.image.repository }}:{{ .Values.web.image.tag }}" + ports: + - containerPort: {{ .Values.web.service.port }} + env: + - name: API_URL + value: {{ tpl .Values.web.env.apiUrl . | quote }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-web + labels: + {{- include "vantage.labels" . | nindent 4 }} + app.kubernetes.io/component: web +spec: + type: {{ .Values.web.service.type }} + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: web + ports: + - port: {{ .Values.web.service.port }} + targetPort: {{ .Values.web.service.port }} diff --git a/deploy/chart/vantage/values.yaml b/deploy/chart/vantage/values.yaml new file mode 100644 index 0000000..9d0bee5 --- /dev/null +++ b/deploy/chart/vantage/values.yaml @@ -0,0 +1,66 @@ +# Default values for the vantage chart. + +redis: + image: + repository: redis + tag: "8" + persistence: + enabled: true + size: 1Gi + storageClass: "" + accessMode: ReadWriteOnce + port: 6379 + +mongo: + image: + repository: mongo + tag: "7" + persistence: + enabled: true + size: 5Gi + storageClass: "" + accessMode: ReadWriteOnce + port: 27017 + +guacd: + image: + repository: docker.io/guacamole/guacd + tag: "1.6.0" + service: + type: ClusterIP + port: 4822 + +server: + image: + repository: gitea.hostxtra.co.uk/mrhid6/vantage/server + tag: latest + service: + type: ClusterIP + httpPort: 8080 + grpcPort: 9090 + env: + mongoUri: "mongodb://{{ .Release.Name }}-mongo:27017/vantage" + grpcHost: "{{ .Release.Name }}-server:9090" + keyEncryptionKey: "" + vantageWorkflowLogDir: "" + appRootLabel: vantage + proxyAdvertiseHost: "{{ .Release.Name }}-server" + proxyListenHost: "0.0.0.0" + persistence: + enabled: true + size: 1Gi + storageClass: "" + accessMode: ReadWriteOnce + hostPath: /data + +web: + image: + repository: gitea.hostxtra.co.uk/mrhid6/vantage/web + tag: latest + service: + type: ClusterIP + port: 3000 + env: + apiUrl: "http://{{ .Release.Name }}-server:8080" + +imagePullSecrets: [] diff --git a/deploy/.env.example b/deploy/docker/.env.example similarity index 94% rename from deploy/.env.example rename to deploy/docker/.env.example index 04843c9..95a7500 100644 --- a/deploy/.env.example +++ b/deploy/docker/.env.example @@ -5,7 +5,7 @@ # host:port agents dial for gRPC. No default; boot fails without it. # Must be reachable from managed servers. Use the public host, port 9090. -GRPC_HOST=192.168.1.250:9090 +GRPC_HOST=vantage.yourdomain.com:9090 # 64-char hex (32 bytes) for AES-256-GCM. Required for private keys, # secrets, OIDC secrets, RDP/VNC credentials. diff --git a/deploy/docker-compose.site.yml b/deploy/docker/docker-compose.site.yml similarity index 100% rename from deploy/docker-compose.site.yml rename to deploy/docker/docker-compose.site.yml diff --git a/deploy/docker-compose.yml b/deploy/docker/docker-compose.yml similarity index 97% rename from deploy/docker-compose.yml rename to deploy/docker/docker-compose.yml index 264d345..1783d48 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -47,7 +47,7 @@ services: KEY_ENCRYPTION_KEY: ${KEY_ENCRYPTION_KEY:-} VANTAGE_WORKFLOW_LOG_DIR: ${VANTAGE_WORKFLOW_LOG_DIR:-} GUACD_ADDR: guacd:4822 - APP_ROOT_LABEL: vantage + PROXY_ADVERTISE_HOST: server depends_on: redis: condition: service_healthy