feat: Docker and helm charts
Server Deploy / deploy (push) Successful in 5m26s
Agent Release / build (push) Successful in 10m45s
Agent Release / msi (push) Successful in 1m31s

This commit is contained in:
2026-07-31 09:28:54 +01:00
parent 978b665aa6
commit bbf9f72fd3
14 changed files with 509 additions and 26 deletions
+3 -1
View File
@@ -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
graphify-out
docker-compose.live.yml
-23
View File
@@ -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
+6
View File
@@ -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"
+17
View File
@@ -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 }}
@@ -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 -}}
+44
View File
@@ -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
+90
View File
@@ -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 }}
+90
View File
@@ -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 }}
+123
View File
@@ -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 }}
+57
View File
@@ -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 }}
+66
View File
@@ -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: []
@@ -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.
@@ -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