Adding Justfile and helm chart

This commit is contained in:
Ian Roddis
2026-03-24 12:52:00 -03:00
parent 34b9b21950
commit 715a0a70b1
20 changed files with 3873 additions and 1 deletions
+19
View File
@@ -0,0 +1,19 @@
apiVersion: v2
name: sovereign
description: >
Self-hosted infrastructure stack for small businesses — Traefik, Graylog,
Authentik, MinIO, Nextcloud, Stalwart Mail, Roundcube, Matrix/Element,
Jitsi, Headscale, Wazuh, Vaultwarden, Forgejo, and a static website.
type: application
version: 0.1.0
appVersion: "1.0.0"
keywords:
- self-hosted
- infrastructure
- authentik
- nextcloud
- matrix
- forgejo
home: https://github.com/your-org/sovereign
maintainers:
- name: Sovereign Maintainers
+176
View File
@@ -0,0 +1,176 @@
{{/*
Expand the chart name.
*/}}
{{- define "sovereign.name" -}}
{{- .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully-qualified app name using release name.
*/}}
{{- define "sovereign.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels applied to every resource.
*/}}
{{- define "sovereign.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: sovereign
{{- end }}
{{/*
Selector labels used in matchLabels and Service selectors.
Pass a dict with "root" (.Values context) and "component" (string).
Usage: {{ include "sovereign.selectorLabels" (dict "root" . "component" "graylog") }}
*/}}
{{- define "sovereign.selectorLabels" -}}
app.kubernetes.io/instance: {{ .root.Release.Name }}
app.kubernetes.io/component: {{ .component }}
{{- end }}
{{/*
Base domain.
*/}}
{{- define "sovereign.baseDomain" -}}
{{- .Values.global.baseDomain -}}
{{- end }}
{{/*
SMTP host: configured value or the stalwart Service name.
*/}}
{{- define "sovereign.smtpHost" -}}
{{- if .Values.smtp.host -}}
{{- .Values.smtp.host -}}
{{- else -}}
{{- .Release.Name }}-stalwart
{{- end -}}
{{- end }}
{{/*
SMTP from address.
*/}}
{{- define "sovereign.smtpFrom" -}}
{{- if .Values.smtp.from -}}
{{- .Values.smtp.from -}}
{{- else -}}
{{- printf "noreply@%s" .Values.global.baseDomain -}}
{{- end -}}
{{- end }}
{{/*
SMTP username.
*/}}
{{- define "sovereign.smtpUser" -}}
{{- if .Values.smtp.user -}}
{{- .Values.smtp.user -}}
{{- else -}}
{{- printf "noreply@%s" .Values.global.baseDomain -}}
{{- end -}}
{{- end }}
{{/*
Graylog GELF host release-name-graylog service, unless overridden.
*/}}
{{- define "sovereign.graylogHost" -}}
{{- .Release.Name }}-graylog
{{- end }}
{{/*
Authentik base URL.
*/}}
{{- define "sovereign.authentikURL" -}}
{{- printf "https://auth.%s" .Values.global.baseDomain -}}
{{- end }}
{{/*
Traefik ACME email.
*/}}
{{- define "sovereign.acmeEmail" -}}
{{- if .Values.traefik.acmeEmail -}}
{{- .Values.traefik.acmeEmail -}}
{{- else -}}
{{- printf "admin@%s" .Values.global.baseDomain -}}
{{- end -}}
{{- end }}
{{/*
Authentik admin email.
*/}}
{{- define "sovereign.authentikAdminEmail" -}}
{{- if .Values.authentik.adminEmail -}}
{{- .Values.authentik.adminEmail -}}
{{- else -}}
{{- printf "admin@%s" .Values.global.baseDomain -}}
{{- end -}}
{{- end }}
{{/*
Forgejo admin email.
*/}}
{{- define "sovereign.forgejoAdminEmail" -}}
{{- if .Values.forgejo.adminEmail -}}
{{- .Values.forgejo.adminEmail -}}
{{- else -}}
{{- printf "admin@%s" .Values.global.baseDomain -}}
{{- end -}}
{{- end }}
{{/*
Standard Ingress annotations (cert-manager if configured).
*/}}
{{- define "sovereign.ingressAnnotations" -}}
{{- if .Values.global.clusterIssuer }}
cert-manager.io/cluster-issuer: {{ .Values.global.clusterIssuer }}
{{- end }}
{{- end }}
{{/*
PVC storageClassName snippet omitted entirely when empty so the cluster default is used.
Usage: {{ include "sovereign.storageClass" .Values.global.storageClass }}
*/}}
{{- define "sovereign.storageClass" -}}
{{- if . }}
storageClassName: {{ . }}
{{- end -}}
{{- end }}
{{/*
Standard Ingress TLS block for a single host.
Usage: {{ include "sovereign.ingressTLS" (dict "host" "foo.example.com" "secretName" "foo-tls") }}
*/}}
{{/*
Shared environment variables for Authentik server and worker containers.
*/}}
{{- define "authentik.commonEnv" -}}
- name: AUTHENTIK_REDIS__HOST
value: {{ .Release.Name }}-authentik-redis
- name: AUTHENTIK_POSTGRESQL__HOST
value: {{ .Release.Name }}-authentik-postgres
- name: AUTHENTIK_POSTGRESQL__USER
value: authentik
- name: AUTHENTIK_POSTGRESQL__NAME
value: authentik
- name: AUTHENTIK_POSTGRESQL__PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-authentik
key: dbPassword
- name: AUTHENTIK_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-authentik
key: secretKey
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
value: "false"
{{- end }}
{{- define "sovereign.ingressTLS" -}}
tls:
- hosts:
- {{ .host }}
secretName: {{ .secretName }}
{{- end }}
+275
View File
@@ -0,0 +1,275 @@
{{- if .Values.authentik.enabled }}
# -------------------------------------------------------------------------
# AUTHENTIK — PostgreSQL + Redis + Server + Worker
# -------------------------------------------------------------------------
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-authentik-postgres
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.authentik.persistence.postgresSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-authentik-media
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.authentik.persistence.mediaSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-authentik-certs
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.authentik.persistence.certsSize }}
---
# --- PostgreSQL ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-authentik-postgres
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: authentik-postgres
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-postgres") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-postgres") | nindent 8 }}
spec:
containers:
- name: postgres
image: postgres:16-alpine
env:
- name: POSTGRES_USER
value: authentik
- name: POSTGRES_DB
value: authentik
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-authentik
key: dbPassword
livenessProbe:
exec:
command: [sh, -c, "pg_isready -d authentik -U authentik"]
initialDelaySeconds: 30
periodSeconds: 30
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-authentik-postgres
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-authentik-postgres
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-postgres") | nindent 4 }}
ports:
- port: 5432
targetPort: 5432
---
# --- Redis ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-authentik-redis
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: authentik-redis
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-redis") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-redis") | nindent 8 }}
spec:
containers:
- name: redis
image: redis:alpine
livenessProbe:
exec:
command: [redis-cli, ping]
initialDelaySeconds: 10
periodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-authentik-redis
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-redis") | nindent 4 }}
ports:
- port: 6379
targetPort: 6379
---
# --- Authentik Server ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-authentik-server
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: authentik-server
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-server") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-server") | nindent 8 }}
spec:
containers:
- name: server
image: ghcr.io/goauthentik/server:{{ .Values.authentik.version }}
command: [server]
env:
{{- include "authentik.commonEnv" . | nindent 12 }}
- name: AUTHENTIK_EMAIL__HOST
value: {{ include "sovereign.smtpHost" . | quote }}
- name: AUTHENTIK_EMAIL__PORT
value: {{ .Values.smtp.port | quote }}
- name: AUTHENTIK_EMAIL__USERNAME
value: {{ include "sovereign.smtpUser" . | quote }}
- name: AUTHENTIK_EMAIL__FROM
value: {{ include "sovereign.smtpFrom" . | quote }}
- name: AUTHENTIK_EMAIL__USE_TLS
value: "true"
- name: AUTHENTIK_EMAIL__PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-smtp
key: password
ports:
- name: http
containerPort: 9000
volumeMounts:
- name: media
mountPath: /media
- name: certs
mountPath: /certs
volumes:
- name: media
persistentVolumeClaim:
claimName: {{ .Release.Name }}-authentik-media
- name: certs
persistentVolumeClaim:
claimName: {{ .Release.Name }}-authentik-certs
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-authentik-server
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-server") | nindent 4 }}
ports:
- name: http
port: 9000
targetPort: http
---
# --- Authentik Worker ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-authentik-worker
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: authentik-worker
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-worker") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "authentik-worker") | nindent 8 }}
spec:
containers:
- name: worker
image: ghcr.io/goauthentik/server:{{ .Values.authentik.version }}
command: [worker]
env:
{{- include "authentik.commonEnv" . | nindent 12 }}
volumeMounts:
- name: media
mountPath: /media
- name: certs
mountPath: /certs
volumes:
- name: media
persistentVolumeClaim:
claimName: {{ .Release.Name }}-authentik-media
- name: certs
persistentVolumeClaim:
claimName: {{ .Release.Name }}-authentik-certs
---
# --- Ingress ---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-authentik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: auth.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-authentik-server
port:
name: http
tls:
- hosts:
- auth.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-authentik-tls
{{- end }}
+246
View File
@@ -0,0 +1,246 @@
{{- if .Values.forgejo.enabled }}
# -------------------------------------------------------------------------
# FORGEJO — PostgreSQL + Forgejo Git
# -------------------------------------------------------------------------
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-forgejo-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.forgejo.persistence.dbSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-forgejo-data
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.forgejo.persistence.dataSize }}
---
# --- PostgreSQL ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-forgejo-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: forgejo-db
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo-db") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo-db") | nindent 8 }}
spec:
containers:
- name: postgres
image: postgres:16-alpine
env:
- name: POSTGRES_DB
value: forgejo
- name: POSTGRES_USER
value: forgejo
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-forgejo
key: dbPassword
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-forgejo-db
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-forgejo-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo-db") | nindent 4 }}
ports:
- port: 5432
targetPort: 5432
---
# --- Forgejo ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-forgejo
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: forgejo
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo") | nindent 8 }}
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: forgejo
image: codeberg.org/forgejo/forgejo:{{ .Values.forgejo.version }}
env:
- name: FORGEJO__database__DB_TYPE
value: postgres
- name: FORGEJO__database__HOST
value: {{ .Release.Name }}-forgejo-db:5432
- name: FORGEJO__database__NAME
value: forgejo
- name: FORGEJO__database__USER
value: forgejo
- name: FORGEJO__server__DOMAIN
value: git.{{ .Values.global.baseDomain }}
- name: FORGEJO__server__ROOT_URL
value: https://git.{{ .Values.global.baseDomain }}
- name: FORGEJO__server__SSH_DOMAIN
value: git.{{ .Values.global.baseDomain }}
- name: FORGEJO__server__SSH_PORT
value: {{ .Values.forgejo.sshPort | quote }}
- name: FORGEJO__server__SSH_LISTEN_PORT
value: "22"
- name: FORGEJO__mailer__ENABLED
value: "true"
- name: FORGEJO__mailer__SMTP_ADDR
value: {{ include "sovereign.smtpHost" . | quote }}
- name: FORGEJO__mailer__SMTP_PORT
value: {{ .Values.smtp.port | quote }}
- name: FORGEJO__mailer__FROM
value: {{ include "sovereign.smtpFrom" . | quote }}
- name: FORGEJO__mailer__USER
value: {{ include "sovereign.smtpUser" . | quote }}
- name: FORGEJO__openid__ENABLE_OPENID_SIGNIN
value: "true"
- name: FORGEJO__openid__ENABLE_OPENID_SIGNUP
value: "false"
- name: FORGEJO__oauth2_client__REGISTER_EMAIL_CONFIRM
value: "false"
- name: FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION
value: "true"
- name: FORGEJO____APP_NAME
value: {{ .Values.global.tenantName | quote }}
- name: FORGEJO__log__LEVEL
value: warn
- name: FORGEJO__database__PASSWD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-forgejo
key: dbPassword
- name: FORGEJO__security__SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-forgejo
key: secretKey
- name: FORGEJO__security__INTERNAL_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-forgejo
key: internalToken
- name: FORGEJO__lfs__JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-forgejo
key: lfsJwtSecret
- name: FORGEJO__mailer__PASSWD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-smtp
key: password
ports:
- name: http
containerPort: 3000
- name: ssh
containerPort: 22
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-forgejo-data
---
# HTTP service
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-forgejo
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo") | nindent 4 }}
ports:
- name: http
port: 3000
targetPort: http
---
# SSH service (NodePort on the configured sshPort)
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-forgejo-ssh
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
type: NodePort
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "forgejo") | nindent 4 }}
ports:
- name: ssh
port: {{ .Values.forgejo.sshPort }}
targetPort: ssh
nodePort: {{ .Values.forgejo.sshPort }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-forgejo
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: git.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-forgejo
port:
name: http
tls:
- hosts:
- git.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-forgejo-tls
{{- end }}
+262
View File
@@ -0,0 +1,262 @@
{{- if .Values.graylog.enabled }}
# -------------------------------------------------------------------------
# GRAYLOG — MongoDB + OpenSearch + Graylog
# -------------------------------------------------------------------------
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-graylog-mongodb
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.graylog.persistence.mongodbSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-graylog-opensearch
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.graylog.persistence.opensearchSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-graylog-data
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.graylog.persistence.graylogSize }}
---
# --- MongoDB ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-graylog-mongodb
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: graylog-mongodb
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog-mongodb") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog-mongodb") | nindent 8 }}
spec:
containers:
- name: mongodb
image: mongo:{{ .Values.graylog.mongodbVersion }}
volumeMounts:
- name: data
mountPath: /data/db
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-graylog-mongodb
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-graylog-mongodb
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog-mongodb") | nindent 4 }}
ports:
- port: 27017
targetPort: 27017
---
# --- OpenSearch ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-graylog-opensearch
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: graylog-opensearch
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog-opensearch") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog-opensearch") | nindent 8 }}
spec:
initContainers:
- name: sysctl
image: busybox
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
containers:
- name: opensearch
image: opensearchproject/opensearch:{{ .Values.graylog.opensearchVersion }}
env:
- name: OPENSEARCH_JAVA_OPTS
value: {{ .Values.graylog.opensearchJavaOpts | quote }}
- name: bootstrap.memory_lock
value: "true"
- name: discovery.type
value: single-node
- name: action.auto_create_index
value: "false"
- name: plugins.security.ssl.http.enabled
value: "false"
- name: plugins.security.disabled
value: "true"
- name: OPENSEARCH_INITIAL_ADMIN_PASSWORD
value: "changeme_os_admin"
securityContext:
capabilities:
add: [IPC_LOCK]
volumeMounts:
- name: data
mountPath: /usr/share/opensearch/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-graylog-opensearch
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-graylog-opensearch
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog-opensearch") | nindent 4 }}
ports:
- port: 9200
targetPort: 9200
---
# --- Graylog ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-graylog
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: graylog
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog") | nindent 8 }}
spec:
containers:
- name: graylog
image: graylog/graylog:{{ .Values.graylog.version }}
env:
- name: GRAYLOG_NODE_ID_FILE
value: /usr/share/graylog/data/config/node-id
- name: GRAYLOG_HTTP_BIND_ADDRESS
value: "0.0.0.0:9000"
- name: GRAYLOG_ELASTICSEARCH_HOSTS
value: http://{{ .Release.Name }}-graylog-opensearch:9200
- name: GRAYLOG_MONGODB_URI
value: mongodb://{{ .Release.Name }}-graylog-mongodb:27017/graylog
- name: GRAYLOG_HTTP_EXTERNAL_URI
value: https://logs.{{ .Values.global.baseDomain }}/
- name: GRAYLOG_TRANSPORT_EMAIL_ENABLED
value: "true"
- name: GRAYLOG_TRANSPORT_EMAIL_HOSTNAME
value: {{ include "sovereign.smtpHost" . | quote }}
- name: GRAYLOG_TRANSPORT_EMAIL_PORT
value: {{ .Values.smtp.port | quote }}
- name: GRAYLOG_TRANSPORT_EMAIL_FROM_EMAIL
value: {{ include "sovereign.smtpFrom" . | quote }}
- name: GRAYLOG_PASSWORD_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-graylog
key: passwordSecret
- name: GRAYLOG_ROOT_PASSWORD_SHA2
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-graylog
key: rootPasswordSha2
ports:
- name: http
containerPort: 9000
- name: gelf-udp
containerPort: 12201
protocol: UDP
volumeMounts:
- name: data
mountPath: /usr/share/graylog/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-graylog-data
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-graylog
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "graylog") | nindent 4 }}
ports:
- name: http
port: 9000
targetPort: http
- name: gelf-udp
port: {{ .Values.graylog.gelfPort }}
targetPort: gelf-udp
protocol: UDP
---
# --- Ingress ---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-graylog
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: logs.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-graylog
port:
name: http
tls:
- hosts:
- logs.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-graylog-tls
{{- end }}
+189
View File
@@ -0,0 +1,189 @@
{{- if .Values.headscale.enabled }}
# -------------------------------------------------------------------------
# HEADSCALE — WireGuard mesh VPN coordinator
# -------------------------------------------------------------------------
# ConfigMap: base headscale config
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-headscale-config
labels:
{{- include "sovereign.labels" . | nindent 4 }}
data:
config.yaml: |
server_url: "https://headscale.{{ .Values.global.baseDomain }}"
listen_addr: "0.0.0.0:8080"
grpc_listen_addr: "0.0.0.0:50443"
grpc_allow_insecure: true
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
- 100.64.0.0/10
derp:
server:
enabled: false
urls:
- https://controlplane.tailscale.com/derpmap/default
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: true
ephemeral_node_inactivity_timeout: 30m
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
log:
level: warn
acls_path: /etc/headscale/acls.hujson
oidc:
issuer: "{{ include "sovereign.authentikURL" . }}/application/o/headscale/"
client_id: headscale
client_secret: "changeme_headscale_oidc_secret"
scope: [openid, profile, email]
strip_email_domain: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-headscale-config
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.headscale.persistence.configSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-headscale-data
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.headscale.persistence.dataSize }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-headscale
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: headscale
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "headscale") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "headscale") | nindent 8 }}
spec:
initContainers:
- name: init-config
image: busybox
command:
- sh
- -c
- |
if [ ! -f /etc/headscale/config.yaml ]; then
cp /configmap/config.yaml /etc/headscale/config.yaml
fi
volumeMounts:
- name: config
mountPath: /etc/headscale
- name: configmap
mountPath: /configmap
containers:
- name: headscale
image: headscale/headscale:{{ .Values.headscale.version }}
command: [serve]
ports:
- name: http
containerPort: 8080
- name: grpc
containerPort: 50443
- name: wireguard
containerPort: {{ .Values.headscale.wireguardPort }}
protocol: UDP
volumeMounts:
- name: config
mountPath: /etc/headscale
- name: data
mountPath: /var/lib/headscale
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Release.Name }}-headscale-config
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-headscale-data
- name: configmap
configMap:
name: {{ .Release.Name }}-headscale-config
---
# HTTP service (for Ingress)
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-headscale
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "headscale") | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
---
# WireGuard UDP service
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-headscale-vpn
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
type: NodePort
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "headscale") | nindent 4 }}
ports:
- name: wireguard
port: {{ .Values.headscale.wireguardPort }}
targetPort: wireguard
nodePort: {{ .Values.headscale.wireguardPort }}
protocol: UDP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-headscale
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: headscale.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-headscale
port:
name: http
tls:
- hosts:
- headscale.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-headscale-tls
{{- end }}
+362
View File
@@ -0,0 +1,362 @@
{{- if .Values.jitsi.enabled }}
# -------------------------------------------------------------------------
# JITSI MEET — Web + Prosody + Jicofo + JVB
# -------------------------------------------------------------------------
# --- PVCs ---
{{- range list "web" "prosody" "jicofo" "jvb" }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ $.Release.Name }}-jitsi-{{ . }}
labels:
{{- include "sovereign.labels" $ | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" $.Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ index $.Values.jitsi.persistence (printf "%sSize" .) }}
---
{{- end }}
# --- Prosody (XMPP) ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-jitsi-prosody
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: jitsi-prosody
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-prosody") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-prosody") | nindent 8 }}
spec:
containers:
- name: prosody
image: jitsi/prosody:{{ .Values.jitsi.version }}
env:
- name: XMPP_DOMAIN
value: meet.jitsi
- name: XMPP_AUTH_DOMAIN
value: auth.meet.jitsi
- name: XMPP_MUC_DOMAIN
value: muc.meet.jitsi
- name: XMPP_INTERNAL_MUC_DOMAIN
value: internal-muc.meet.jitsi
- name: XMPP_RECORDER_DOMAIN
value: recorder.meet.jitsi
- name: JICOFO_AUTH_USER
value: focus
- name: JVB_AUTH_USER
value: jvb
- name: JIBRI_RECORDER_USER
value: recorder
- name: JIBRI_XMPP_USER
value: jibri
- name: JWT_APP_ID
value: jitsi
- name: ENABLE_AUTH
value: "1"
- name: ENABLE_GUESTS
value: "1"
- name: AUTH_TYPE
value: jwt
- name: TZ
value: UTC
- name: JICOFO_AUTH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: jicofoAuthPassword
- name: JVB_AUTH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: jvbAuthPassword
- name: JIBRI_RECORDER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: jibriRecorderPassword
- name: JIBRI_XMPP_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: jibriXmppPassword
- name: JWT_APP_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: turnSecret
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Release.Name }}-jitsi-prosody
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-jitsi-prosody
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-prosody") | nindent 4 }}
ports:
- name: xmpp-c2s
port: 5222
targetPort: 5222
- name: xmpp-s2s
port: 5269
targetPort: 5269
- name: bosh
port: 5280
targetPort: 5280
- name: xmpp-component
port: 5347
targetPort: 5347
---
# --- Jicofo ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-jitsi-jicofo
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: jitsi-jicofo
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-jicofo") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-jicofo") | nindent 8 }}
spec:
containers:
- name: jicofo
image: jitsi/jicofo:{{ .Values.jitsi.version }}
env:
- name: XMPP_SERVER
value: {{ .Release.Name }}-jitsi-prosody
- name: XMPP_DOMAIN
value: meet.jitsi
- name: XMPP_AUTH_DOMAIN
value: auth.meet.jitsi
- name: XMPP_INTERNAL_MUC_DOMAIN
value: internal-muc.meet.jitsi
- name: JICOFO_AUTH_USER
value: focus
- name: JVB_BREWERY_MUC
value: jvbbrewery
- name: TZ
value: UTC
- name: JICOFO_AUTH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: jicofoAuthPassword
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Release.Name }}-jitsi-jicofo
---
# --- JVB (video bridge) ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-jitsi-jvb
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: jitsi-jvb
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-jvb") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-jvb") | nindent 8 }}
spec:
containers:
- name: jvb
image: jitsi/jvb:{{ .Values.jitsi.version }}
env:
- name: XMPP_SERVER
value: {{ .Release.Name }}-jitsi-prosody
- name: XMPP_DOMAIN
value: meet.jitsi
- name: XMPP_AUTH_DOMAIN
value: auth.meet.jitsi
- name: XMPP_INTERNAL_MUC_DOMAIN
value: internal-muc.meet.jitsi
- name: JVB_AUTH_USER
value: jvb
- name: JVB_BREWERY_MUC
value: jvbbrewery
- name: JVB_PORT
value: "10000"
- name: JVB_TCP_HARVESTER_DISABLED
value: "true"
- name: TZ
value: UTC
- name: JVB_AUTH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: jvbAuthPassword
ports:
- name: jvb-udp
containerPort: 10000
protocol: UDP
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Release.Name }}-jitsi-jvb
---
# JVB service — needs to be reachable by clients (UDP)
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-jitsi-jvb
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
type: NodePort
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-jvb") | nindent 4 }}
ports:
- name: jvb-udp
port: 10000
targetPort: jvb-udp
protocol: UDP
---
# --- Jitsi Web ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-jitsi-web
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: jitsi-web
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-web") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-web") | nindent 8 }}
spec:
containers:
- name: web
image: jitsi/web:{{ .Values.jitsi.version }}
env:
- name: PUBLIC_URL
value: https://meet.{{ .Values.global.baseDomain }}
- name: JITSI_WATERMARKLINK
value: https://{{ .Values.global.baseDomain }}
- name: XMPP_SERVER
value: {{ .Release.Name }}-jitsi-prosody
- name: XMPP_DOMAIN
value: meet.jitsi
- name: XMPP_AUTH_DOMAIN
value: auth.meet.jitsi
- name: XMPP_INTERNAL_MUC_DOMAIN
value: internal-muc.meet.jitsi
- name: XMPP_MUC_DOMAIN
value: muc.meet.jitsi
- name: XMPP_BOSH_URL_BASE
value: http://{{ .Release.Name }}-jitsi-prosody:5280
- name: XMPP_RECORDER_DOMAIN
value: recorder.meet.jitsi
- name: ENABLE_AUTH
value: "1"
- name: ENABLE_GUESTS
value: "1"
- name: AUTH_TYPE
value: jwt
- name: JWT_APP_ID
value: jitsi
- name: TZ
value: UTC
- name: JWT_APP_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: turnSecret
- name: TURN_CREDENTIALS
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-jitsi
key: turnSecret
ports:
- name: http
containerPort: 80
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Release.Name }}-jitsi-web
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-jitsi-web
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "jitsi-web") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-jitsi
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: meet.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-jitsi-web
port:
name: http
tls:
- hosts:
- meet.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-jitsi-tls
{{- end }}
+328
View File
@@ -0,0 +1,328 @@
{{- if .Values.matrix.enabled }}
# -------------------------------------------------------------------------
# MATRIX / ELEMENT — PostgreSQL + Synapse + Element Web
#
# NOTE: Synapse requires a homeserver.yaml in its data volume.
# On first run, exec into the synapse pod and run:
# python -m synapse.app.homeserver --generate-config \
# -H matrix.<baseDomain> --report-stats=no \
# -c /data/homeserver.yaml
# Or provide your own via the configMap below.
# -------------------------------------------------------------------------
# ConfigMap: basic homeserver.yaml — customize before deploying
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-synapse-config
labels:
{{- include "sovereign.labels" . | nindent 4 }}
data:
homeserver.yaml: |
server_name: "{{ .Values.global.baseDomain }}"
public_baseurl: "https://matrix.{{ .Values.global.baseDomain }}"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
args:
user: synapse
password: "<set via SYNAPSE_POSTGRES_PASSWORD env — see Deployment>"
database: synapse
host: {{ .Release.Name }}-matrix-db
cp_min: 5
cp_max: 10
log_config: "/data/log.config"
media_store_path: /data/media_store
registration_shared_secret: "<set via SYNAPSE_REGISTRATION_SHARED_SECRET env>"
report_stats: false
signing_key_path: "/data/{{ .Values.global.baseDomain }}.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
email:
smtp_host: {{ include "sovereign.smtpHost" . | quote }}
smtp_port: {{ .Values.smtp.port }}
notif_from: "{{ include "sovereign.smtpFrom" . }}"
log.config: |
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
formatter: precise
loggers:
synapse.storage.SQL:
level: WARNING
root:
level: WARNING
handlers: [console]
disable_existing_loggers: false
element-config.json: |
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.{{ .Values.global.baseDomain }}",
"server_name": "{{ .Values.global.baseDomain }}"
}
},
"brand": "{{ .Values.global.tenantName }}",
"disable_guests": true,
"room_directory": { "servers": ["{{ .Values.global.baseDomain }}"] }
}
---
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-matrix-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.matrix.persistence.dbSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-synapse-data
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.matrix.persistence.synapseSize }}
---
# --- PostgreSQL ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-matrix-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: matrix-db
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "matrix-db") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "matrix-db") | nindent 8 }}
spec:
containers:
- name: postgres
image: postgres:16-alpine
env:
- name: POSTGRES_USER
value: synapse
- name: POSTGRES_DB
value: synapse
- name: POSTGRES_INITDB_ARGS
value: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-matrix
key: dbPassword
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-matrix-db
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-matrix-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "matrix-db") | nindent 4 }}
ports:
- port: 5432
targetPort: 5432
---
# --- Synapse ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-synapse
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: synapse
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "synapse") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "synapse") | nindent 8 }}
spec:
initContainers:
# Copy config from ConfigMap into writable data volume on first run
- name: init-config
image: busybox
command:
- sh
- -c
- |
if [ ! -f /data/homeserver.yaml ]; then
cp /config/homeserver.yaml /data/homeserver.yaml
cp /config/log.config /data/log.config
fi
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /config
containers:
- name: synapse
image: ghcr.io/element-hq/synapse:{{ .Values.matrix.synapseVersion }}
env:
- name: SYNAPSE_CONFIG_PATH
value: /data/homeserver.yaml
- name: SYNAPSE_POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-matrix
key: dbPassword
- name: SYNAPSE_REGISTRATION_SHARED_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-matrix
key: registrationSecret
ports:
- name: http
containerPort: 8008
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-synapse-data
- name: config
configMap:
name: {{ .Release.Name }}-synapse-config
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-synapse
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "synapse") | nindent 4 }}
ports:
- name: http
port: 8008
targetPort: http
---
# --- Element Web ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-element
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: element
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "element") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "element") | nindent 8 }}
spec:
containers:
- name: element
image: vectorim/element-web:latest
ports:
- name: http
containerPort: 80
volumeMounts:
- name: config
mountPath: /app/config.json
subPath: element-config.json
volumes:
- name: config
configMap:
name: {{ .Release.Name }}-synapse-config
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-element
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "element") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: http
---
# --- Ingress (matrix + chat subdomains) ---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-matrix
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: matrix.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-synapse
port:
name: http
- host: chat.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-element
port:
name: http
tls:
- hosts:
- matrix.{{ .Values.global.baseDomain }}
- chat.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-matrix-tls
{{- end }}
+144
View File
@@ -0,0 +1,144 @@
{{- if .Values.minio.enabled }}
# -------------------------------------------------------------------------
# MINIO — S3-compatible object storage
# -------------------------------------------------------------------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-minio
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.minio.persistence.size }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-minio
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: minio
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "minio") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "minio") | nindent 8 }}
spec:
containers:
- name: minio
image: quay.io/minio/minio:{{ .Values.minio.version }}
command: [server, /data, --console-address, ":9001"]
env:
- name: MINIO_ROOT_USER
value: {{ .Values.minio.rootUser | quote }}
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-minio
key: rootPassword
- name: MINIO_BROWSER_REDIRECT_URL
value: https://minio.{{ .Values.global.baseDomain }}
- name: MINIO_IDENTITY_OPENID_CONFIG_URL
value: {{ include "sovereign.authentikURL" . }}/application/o/minio/.well-known/openid-configuration
- name: MINIO_IDENTITY_OPENID_CLIENT_ID
value: minio
- name: MINIO_IDENTITY_OPENID_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-minio
key: oidcSecret
- name: MINIO_IDENTITY_OPENID_CLAIM_NAME
value: policy
- name: MINIO_IDENTITY_OPENID_REDIRECT_URI
value: https://minio.{{ .Values.global.baseDomain }}/oauth_callback
ports:
- name: api
containerPort: 9000
- name: console
containerPort: 9001
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-minio
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-minio
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "minio") | nindent 4 }}
ports:
- name: api
port: 9000
targetPort: api
- name: console
port: 9001
targetPort: console
---
# Two Ingresses: API (s3.<domain>) and Console (minio.<domain>)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-minio-api
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: s3.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-minio
port:
name: api
tls:
- hosts:
- s3.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-minio-api-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-minio-console
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: minio.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-minio
port:
name: console
tls:
- hosts:
- minio.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-minio-console-tls
{{- end }}
+292
View File
@@ -0,0 +1,292 @@
{{- if .Values.nextcloud.enabled }}
# -------------------------------------------------------------------------
# NEXTCLOUD — MariaDB + Redis + Nextcloud + Cron
# -------------------------------------------------------------------------
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-nextcloud-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.nextcloud.persistence.dbSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-nextcloud-data
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.nextcloud.persistence.dataSize }}
---
# --- MariaDB ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nextcloud-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: nextcloud-db
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-db") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-db") | nindent 8 }}
spec:
containers:
- name: mariadb
image: mariadb:10.11
args:
- --transaction-isolation=READ-COMMITTED
- --log-bin=binlog
- --binlog-format=ROW
env:
- name: MYSQL_DATABASE
value: nextcloud
- name: MYSQL_USER
value: nextcloud
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-nextcloud
key: dbRootPassword
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-nextcloud
key: dbPassword
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-nextcloud-db
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-nextcloud-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-db") | nindent 4 }}
ports:
- port: 3306
targetPort: 3306
---
# --- Redis ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nextcloud-redis
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: nextcloud-redis
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-redis") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-redis") | nindent 8 }}
spec:
containers:
- name: redis
image: redis:alpine
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-nextcloud-redis
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-redis") | nindent 4 }}
ports:
- port: 6379
targetPort: 6379
---
# --- Nextcloud ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nextcloud
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: nextcloud
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud") | nindent 8 }}
spec:
containers:
- name: nextcloud
image: nextcloud:{{ .Values.nextcloud.version }}
env:
- name: MYSQL_HOST
value: {{ .Release.Name }}-nextcloud-db
- name: MYSQL_DATABASE
value: nextcloud
- name: MYSQL_USER
value: nextcloud
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-nextcloud
key: dbPassword
- name: REDIS_HOST
value: {{ .Release.Name }}-nextcloud-redis
- name: NEXTCLOUD_ADMIN_USER
value: {{ .Values.nextcloud.adminUser | quote }}
- name: NEXTCLOUD_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-nextcloud
key: adminPassword
- name: NEXTCLOUD_TRUSTED_DOMAINS
value: cloud.{{ .Values.global.baseDomain }}
- name: OVERWRITEPROTOCOL
value: https
- name: OVERWRITECLIURL
value: https://cloud.{{ .Values.global.baseDomain }}
- name: SMTP_HOST
value: {{ include "sovereign.smtpHost" . | quote }}
- name: SMTP_PORT
value: {{ .Values.smtp.port | quote }}
- name: SMTP_NAME
value: {{ include "sovereign.smtpUser" . | quote }}
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-smtp
key: password
- name: MAIL_FROM_ADDRESS
value: noreply
- name: MAIL_DOMAIN
value: {{ .Values.global.baseDomain | quote }}
- name: OBJECTSTORE_S3_HOST
value: {{ .Release.Name }}-minio
- name: OBJECTSTORE_S3_PORT
value: "9000"
- name: OBJECTSTORE_S3_SSL
value: "false"
- name: OBJECTSTORE_S3_BUCKET
value: {{ .Values.minio.nextcloudBucket | quote }}
- name: OBJECTSTORE_S3_KEY
value: {{ .Values.minio.nextcloudAccessKey | quote }}
- name: OBJECTSTORE_S3_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-minio
key: nextcloudSecretKey
- name: OBJECTSTORE_S3_USEPATH_STYLE
value: "true"
ports:
- name: http
containerPort: 80
volumeMounts:
- name: data
mountPath: /var/www/html
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-nextcloud-data
---
# Cron sidecar deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nextcloud-cron
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: nextcloud-cron
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-cron") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud-cron") | nindent 8 }}
spec:
containers:
- name: cron
image: nextcloud:{{ .Values.nextcloud.version }}
command: [/cron.sh]
volumeMounts:
- name: data
mountPath: /var/www/html
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-nextcloud-data
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-nextcloud
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "nextcloud") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-nextcloud
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
# CardDAV / CalDAV redirect
nginx.ingress.kubernetes.io/rewrite-target: /remote.php/dav
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: cloud.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-nextcloud
port:
name: http
tls:
- hosts:
- cloud.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-nextcloud-tls
{{- end }}
+187
View File
@@ -0,0 +1,187 @@
{{- if .Values.roundcube.enabled }}
# -------------------------------------------------------------------------
# ROUNDCUBE — PostgreSQL + Roundcube webmail
# -------------------------------------------------------------------------
# ConfigMap for custom.inc.php overrides
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-roundcube-config
labels:
{{- include "sovereign.labels" . | nindent 4 }}
data:
custom.inc.php: |
<?php
// Additional Roundcube configuration overrides.
// Add site-specific settings here.
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-roundcube-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.roundcube.persistence.dbSize }}
---
# --- PostgreSQL ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-roundcube-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: roundcube-db
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "roundcube-db") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "roundcube-db") | nindent 8 }}
spec:
containers:
- name: postgres
image: postgres:16-alpine
env:
- name: POSTGRES_DB
value: roundcube
- name: POSTGRES_USER
value: roundcube
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-roundcube
key: dbPassword
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-roundcube-db
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-roundcube-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "roundcube-db") | nindent 4 }}
ports:
- port: 5432
targetPort: 5432
---
# --- Roundcube ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-roundcube
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: roundcube
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "roundcube") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "roundcube") | nindent 8 }}
spec:
containers:
- name: roundcube
image: roundcube/roundcubemail:{{ .Values.roundcube.version }}
env:
- name: ROUNDCUBEMAIL_DB_TYPE
value: pgsql
- name: ROUNDCUBEMAIL_DB_HOST
value: {{ .Release.Name }}-roundcube-db
- name: ROUNDCUBEMAIL_DB_NAME
value: roundcube
- name: ROUNDCUBEMAIL_DB_USER
value: roundcube
- name: ROUNDCUBEMAIL_DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-roundcube
key: dbPassword
- name: ROUNDCUBEMAIL_DEFAULT_HOST
value: ssl://{{ .Release.Name }}-stalwart
- name: ROUNDCUBEMAIL_DEFAULT_PORT
value: "993"
- name: ROUNDCUBEMAIL_SMTP_SERVER
value: tls://{{ .Release.Name }}-stalwart
- name: ROUNDCUBEMAIL_SMTP_PORT
value: "587"
- name: ROUNDCUBEMAIL_DES_KEY
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-roundcube
key: desKey
- name: ROUNDCUBEMAIL_PLUGINS
value: "archive,zipdownload,managesieve,jqueryui"
- name: ROUNDCUBEMAIL_SKIN
value: {{ .Values.roundcube.skin | quote }}
ports:
- name: http
containerPort: 80
volumeMounts:
- name: config
mountPath: /var/roundcube/config/custom.inc.php
subPath: custom.inc.php
volumes:
- name: config
configMap:
name: {{ .Release.Name }}-roundcube-config
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-roundcube
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "roundcube") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-roundcube
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: webmail.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-roundcube
port:
name: http
tls:
- hosts:
- webmail.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-roundcube-tls
{{- end }}
+179
View File
@@ -0,0 +1,179 @@
{{/* -----------------------------------------------------------------------
One Secret per service, containing only the credentials that service needs.
Reference these in Deployments with secretKeyRef.
----------------------------------------------------------------------- */}}
# -- Shared SMTP credentials (referenced by multiple services)
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-smtp
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
password: {{ .Values.smtp.password | quote }}
---
{{- if .Values.traefik.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
dashboardPassword: {{ .Values.traefik.dashboardPassword | quote }}
---
{{- end }}
{{- if .Values.graylog.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-graylog
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
passwordSecret: {{ .Values.graylog.passwordSecret | quote }}
rootPasswordSha2: {{ .Values.graylog.rootPasswordSha2 | quote }}
---
{{- end }}
{{- if .Values.authentik.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-authentik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
secretKey: {{ .Values.authentik.secretKey | quote }}
dbPassword: {{ .Values.authentik.dbPassword | quote }}
adminPassword: {{ .Values.authentik.adminPassword | quote }}
---
{{- end }}
{{- if .Values.minio.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-minio
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
rootPassword: {{ .Values.minio.rootPassword | quote }}
oidcSecret: {{ .Values.minio.oidcSecret | quote }}
nextcloudSecretKey: {{ .Values.minio.nextcloudSecretKey | quote }}
---
{{- end }}
{{- if .Values.nextcloud.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-nextcloud
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
adminPassword: {{ .Values.nextcloud.adminPassword | quote }}
dbPassword: {{ .Values.nextcloud.dbPassword | quote }}
dbRootPassword: {{ .Values.nextcloud.dbRootPassword | quote }}
---
{{- end }}
{{- if .Values.stalwart.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-stalwart
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
adminPassword: {{ .Values.stalwart.adminPassword | quote }}
---
{{- end }}
{{- if .Values.roundcube.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-roundcube
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
dbPassword: {{ .Values.roundcube.dbPassword | quote }}
desKey: {{ .Values.roundcube.desKey | quote }}
---
{{- end }}
{{- if .Values.matrix.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-matrix
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
registrationSecret: {{ .Values.matrix.registrationSecret | quote }}
dbPassword: {{ .Values.matrix.dbPassword | quote }}
---
{{- end }}
{{- if .Values.jitsi.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-jitsi
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
jicofoAuthPassword: {{ .Values.jitsi.jicofoAuthPassword | quote }}
jvbAuthPassword: {{ .Values.jitsi.jvbAuthPassword | quote }}
jibriRecorderPassword: {{ .Values.jitsi.jibriRecorderPassword | quote }}
jibriXmppPassword: {{ .Values.jitsi.jibriXmppPassword | quote }}
turnSecret: {{ .Values.jitsi.turnSecret | quote }}
---
{{- end }}
{{- if .Values.wazuh.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-wazuh
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
adminPassword: {{ .Values.wazuh.adminPassword | quote }}
apiPassword: {{ .Values.wazuh.apiPassword | quote }}
---
{{- end }}
{{- if .Values.vaultwarden.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-vaultwarden
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
adminToken: {{ .Values.vaultwarden.adminToken | quote }}
dbPassword: {{ .Values.vaultwarden.dbPassword | quote }}
oidcSecret: {{ .Values.vaultwarden.oidcSecret | quote }}
---
{{- end }}
{{- if .Values.forgejo.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-forgejo
labels:
{{- include "sovereign.labels" . | nindent 4 }}
type: Opaque
stringData:
dbPassword: {{ .Values.forgejo.dbPassword | quote }}
secretKey: {{ .Values.forgejo.secretKey | quote }}
internalToken: {{ .Values.forgejo.internalToken | quote }}
lfsJwtSecret: {{ .Values.forgejo.lfsJwtSecret | quote }}
adminPassword: {{ .Values.forgejo.adminPassword | quote }}
{{- end }}
+133
View File
@@ -0,0 +1,133 @@
{{- if .Values.stalwart.enabled }}
# -------------------------------------------------------------------------
# STALWART MAIL — SMTP/IMAP mail server
# -------------------------------------------------------------------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-stalwart
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.stalwart.persistence.size }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-stalwart
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: stalwart
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "stalwart") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "stalwart") | nindent 8 }}
spec:
containers:
- name: stalwart
image: stalwartlabs/mail-server:{{ .Values.stalwart.version }}
env:
- name: TZ
value: UTC
ports:
- name: http
containerPort: 8080
- name: smtp
containerPort: 25
- name: smtps
containerPort: 465
- name: submission
containerPort: 587
- name: imaps
containerPort: 993
- name: sieve
containerPort: 4190
volumeMounts:
- name: data
mountPath: /opt/stalwart-mail
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-stalwart
---
# HTTP service for the web admin UI (via Ingress)
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-stalwart
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "stalwart") | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
- name: smtp
port: 587
targetPort: submission
---
# Mail service — LoadBalancer or NodePort to expose raw TCP/UDP mail ports
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-stalwart-mail
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
type: {{ .Values.stalwart.mailServiceType }}
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "stalwart") | nindent 4 }}
ports:
- name: smtp
port: 25
targetPort: smtp
- name: smtps
port: 465
targetPort: smtps
- name: submission
port: 587
targetPort: submission
- name: imaps
port: 993
targetPort: imaps
- name: sieve
port: 4190
targetPort: sieve
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-stalwart
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: mail.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-stalwart
port:
name: http
tls:
- hosts:
- mail.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-stalwart-tls
{{- end }}
+165
View File
@@ -0,0 +1,165 @@
{{- if .Values.traefik.enabled }}
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Release.Name }}-traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
---
# ClusterRole — allow Traefik to watch Ingress / Service / Endpoint resources
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ .Release.Name }}-traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
rules:
- apiGroups: [""]
resources: [services, endpoints, secrets]
verbs: [get, list, watch]
- apiGroups: [networking.k8s.io]
resources: [ingresses, ingressclasses]
verbs: [get, list, watch]
- apiGroups: [networking.k8s.io]
resources: [ingresses/status]
verbs: [update]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ .Release.Name }}-traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Release.Name }}-traefik
subjects:
- kind: ServiceAccount
name: {{ .Release.Name }}-traefik
namespace: {{ .Release.Namespace }}
---
# IngressClass
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
controller: traefik.io/ingress-controller
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: traefik
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "traefik") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "traefik") | nindent 8 }}
spec:
serviceAccountName: {{ .Release.Name }}-traefik
containers:
- name: traefik
image: traefik:{{ .Values.traefik.version }}
args:
- "--api.dashboard=true"
- "--providers.kubernetesingress=true"
- "--providers.kubernetesingress.ingressclass=traefik"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email={{ include "sovereign.acmeEmail" . }}"
- "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--log.level=INFO"
ports:
- name: web
containerPort: 80
- name: websecure
containerPort: 443
- name: dashboard
containerPort: 8080
volumeMounts:
- name: acme
mountPath: /acme
env:
- name: TRAEFIK_API_DASHBOARD
value: "true"
volumes:
- name: acme
persistentVolumeClaim:
claimName: {{ .Release.Name }}-traefik-acme
---
# PVC for ACME certificate storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-traefik-acme
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: 128Mi
---
# Service
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-traefik
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
type: {{ .Values.traefik.service.type }}
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "traefik") | nindent 4 }}
ports:
- name: web
port: 80
targetPort: web
- name: websecure
port: 443
targetPort: websecure
---
# Dashboard Ingress (protected by basic auth via Traefik middleware annotation)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-traefik-dashboard
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
traefik.ingress.kubernetes.io/router.middlewares: "{{ .Release.Namespace }}-{{ .Release.Name }}-traefik-auth@kubernetescrd"
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: traefik.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-traefik
port:
name: dashboard
tls:
- hosts:
- traefik.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-traefik-tls
{{- end }}
+203
View File
@@ -0,0 +1,203 @@
{{- if .Values.vaultwarden.enabled }}
# -------------------------------------------------------------------------
# VAULTWARDEN — PostgreSQL + Vaultwarden
# -------------------------------------------------------------------------
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-vaultwarden-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.vaultwarden.persistence.dbSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-vaultwarden-data
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.vaultwarden.persistence.dataSize }}
---
# --- PostgreSQL ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-vaultwarden-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: vaultwarden-db
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "vaultwarden-db") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "vaultwarden-db") | nindent 8 }}
spec:
containers:
- name: postgres
image: postgres:16-alpine
env:
- name: POSTGRES_DB
value: vaultwarden
- name: POSTGRES_USER
value: vaultwarden
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-vaultwarden
key: dbPassword
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-vaultwarden-db
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-vaultwarden-db
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "vaultwarden-db") | nindent 4 }}
ports:
- port: 5432
targetPort: 5432
---
# --- Vaultwarden ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-vaultwarden
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: vaultwarden
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "vaultwarden") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "vaultwarden") | nindent 8 }}
spec:
containers:
- name: vaultwarden
image: vaultwarden/server:{{ .Values.vaultwarden.version }}
env:
- name: DOMAIN
value: https://vault.{{ .Values.global.baseDomain }}
- name: SIGNUPS_ALLOWED
value: "false"
- name: SSO_ENABLED
value: "true"
- name: SSO_ONLY
value: "false"
- name: SSO_AUTHORITY
value: {{ include "sovereign.authentikURL" . }}/application/o/vaultwarden/
- name: SSO_CLIENT_ID
value: vaultwarden
- name: SMTP_HOST
value: {{ include "sovereign.smtpHost" . | quote }}
- name: SMTP_FROM
value: {{ include "sovereign.smtpFrom" . | quote }}
- name: SMTP_PORT
value: {{ .Values.smtp.port | quote }}
- name: SMTP_SECURITY
value: {{ .Values.smtp.tls | quote }}
- name: SMTP_USERNAME
value: {{ include "sovereign.smtpUser" . | quote }}
- name: LOG_LEVEL
value: warn
- name: DATABASE_URL
value: postgresql://vaultwarden:$(VAULTWARDEN_DB_PASSWORD)@{{ .Release.Name }}-vaultwarden-db/vaultwarden
- name: VAULTWARDEN_DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-vaultwarden
key: dbPassword
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-vaultwarden
key: adminToken
- name: SSO_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-vaultwarden
key: oidcSecret
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-smtp
key: password
ports:
- name: http
containerPort: 80
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-vaultwarden-data
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-vaultwarden
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "vaultwarden") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-vaultwarden
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: vault.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-vaultwarden
port:
name: http
tls:
- hosts:
- vault.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-vaultwarden-tls
{{- end }}
+325
View File
@@ -0,0 +1,325 @@
{{- if .Values.wazuh.enabled }}
# -------------------------------------------------------------------------
# WAZUH — Manager + Indexer + Dashboard
#
# IMPORTANT: Wazuh requires mutual TLS between its components.
# Before deploying, generate certificates using the Wazuh cert tool and
# create a Secret named <release>-wazuh-certs with these keys:
#
# root-ca.pem root-ca-manager.pem
# wazuh.manager.pem wazuh.manager-key.pem
# wazuh.indexer.pem wazuh.indexer.key
# admin.pem admin-key.pem
# wazuh.dashboard.pem wazuh.dashboard-key.pem
#
# Example:
# kubectl create secret generic <release>-wazuh-certs \
# --from-file=root-ca.pem=./certs/root-ca.pem \
# ... (repeat for each cert)
# -------------------------------------------------------------------------
# --- PVCs ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-wazuh-manager
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.wazuh.persistence.managerSize }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-wazuh-indexer
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.wazuh.persistence.indexerSize }}
---
# --- Wazuh Indexer (OpenSearch-based) ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-wazuh-indexer
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: wazuh-indexer
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-indexer") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-indexer") | nindent 8 }}
spec:
initContainers:
- name: sysctl
image: busybox
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
containers:
- name: wazuh-indexer
image: wazuh/wazuh-indexer:{{ .Values.wazuh.version }}
env:
- name: OPENSEARCH_JAVA_OPTS
value: {{ .Values.wazuh.indexerJavaOpts | quote }}
securityContext:
capabilities:
add: [IPC_LOCK]
volumeMounts:
- name: data
mountPath: /var/lib/wazuh-indexer
- name: certs
mountPath: /usr/share/wazuh-indexer/certs/root-ca.pem
subPath: root-ca.pem
- name: certs
mountPath: /usr/share/wazuh-indexer/certs/wazuh.indexer.key
subPath: wazuh.indexer.key
- name: certs
mountPath: /usr/share/wazuh-indexer/certs/wazuh.indexer.pem
subPath: wazuh.indexer.pem
- name: certs
mountPath: /usr/share/wazuh-indexer/certs/admin.pem
subPath: admin.pem
- name: certs
mountPath: /usr/share/wazuh-indexer/certs/admin-key.pem
subPath: admin-key.pem
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-wazuh-indexer
- name: certs
secret:
secretName: {{ .Release.Name }}-wazuh-certs
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-wazuh-indexer
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-indexer") | nindent 4 }}
ports:
- port: 9200
targetPort: 9200
---
# --- Wazuh Manager ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-wazuh-manager
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: wazuh-manager
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-manager") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-manager") | nindent 8 }}
spec:
containers:
- name: wazuh-manager
image: wazuh/wazuh-manager:{{ .Values.wazuh.version }}
env:
- name: INDEXER_URL
value: https://{{ .Release.Name }}-wazuh-indexer:9200
- name: INDEXER_USERNAME
value: admin
- name: FILEBEAT_SSL_VERIFICATION_MODE
value: full
- name: SSL_CERTIFICATE_AUTHORITIES
value: /etc/ssl/root-ca.pem
- name: SSL_CERTIFICATE
value: /etc/ssl/filebeat.pem
- name: SSL_KEY
value: /etc/ssl/filebeat.key
- name: API_USERNAME
value: wazuh-wui
- name: INDEXER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-wazuh
key: adminPassword
- name: API_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-wazuh
key: apiPassword
ports:
- name: agent
containerPort: 1514
- name: enrollment
containerPort: 1515
- name: syslog
containerPort: 514
protocol: UDP
- name: api
containerPort: 55000
volumeMounts:
- name: data
mountPath: /var/ossec/data
- name: certs
mountPath: /etc/ssl/root-ca.pem
subPath: root-ca-manager.pem
- name: certs
mountPath: /etc/ssl/filebeat.pem
subPath: wazuh.manager.pem
- name: certs
mountPath: /etc/ssl/filebeat.key
subPath: wazuh.manager-key.pem
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Release.Name }}-wazuh-manager
- name: certs
secret:
secretName: {{ .Release.Name }}-wazuh-certs
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-wazuh-manager
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-manager") | nindent 4 }}
ports:
- name: agent
port: 1514
targetPort: agent
- name: enrollment
port: 1515
targetPort: enrollment
- name: syslog
port: 514
targetPort: syslog
protocol: UDP
- name: api
port: 55000
targetPort: api
---
# --- Wazuh Dashboard ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-wazuh-dashboard
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: wazuh-dashboard
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-dashboard") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-dashboard") | nindent 8 }}
spec:
containers:
- name: wazuh-dashboard
image: wazuh/wazuh-dashboard:{{ .Values.wazuh.version }}
env:
- name: INDEXER_USERNAME
value: admin
- name: WAZUH_API_URL
value: https://{{ .Release.Name }}-wazuh-manager
- name: DASHBOARD_USERNAME
value: kibanaserver
- name: API_USERNAME
value: wazuh-wui
- name: INDEXER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-wazuh
key: adminPassword
- name: DASHBOARD_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-wazuh
key: adminPassword
- name: API_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-wazuh
key: apiPassword
ports:
- name: https
containerPort: 5601
volumeMounts:
- name: certs
mountPath: /usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
subPath: wazuh.dashboard.pem
- name: certs
mountPath: /usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
subPath: wazuh.dashboard-key.pem
- name: certs
mountPath: /usr/share/wazuh-dashboard/certs/root-ca.pem
subPath: root-ca.pem
volumes:
- name: certs
secret:
secretName: {{ .Release.Name }}-wazuh-certs
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-wazuh-dashboard
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "wazuh-dashboard") | nindent 4 }}
ports:
- name: https
port: 5601
targetPort: https
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-wazuh
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
# Dashboard speaks HTTPS internally
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
traefik.ingress.kubernetes.io/service.serversscheme: "https"
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: wazuh.{{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-wazuh-dashboard
port:
name: https
tls:
- hosts:
- wazuh.{{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-wazuh-tls
{{- end }}
+89
View File
@@ -0,0 +1,89 @@
{{- if .Values.website.enabled }}
# -------------------------------------------------------------------------
# WEBSITE — static site served by nginx
# -------------------------------------------------------------------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Release.Name }}-website-html
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
{{- include "sovereign.storageClass" .Values.global.storageClass | nindent 2 }}
resources:
requests:
storage: {{ .Values.website.persistence.htmlSize }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-website
labels:
{{- include "sovereign.labels" . | nindent 4 }}
app.kubernetes.io/component: website
spec:
replicas: 1
selector:
matchLabels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "website") | nindent 6 }}
template:
metadata:
labels:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "website") | nindent 8 }}
spec:
containers:
- name: nginx
image: nginx:{{ .Values.website.nginxVersion }}
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
volumes:
- name: html
persistentVolumeClaim:
claimName: {{ .Release.Name }}-website-html
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-website
labels:
{{- include "sovereign.labels" . | nindent 4 }}
spec:
selector:
{{- include "sovereign.selectorLabels" (dict "root" . "component" "website") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-website
labels:
{{- include "sovereign.labels" . | nindent 4 }}
annotations:
{{- include "sovereign.ingressAnnotations" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.global.ingressClassName }}
rules:
- host: {{ .Values.global.baseDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-website
port:
name: http
tls:
- hosts:
- {{ .Values.global.baseDomain }}
secretName: {{ .Release.Name }}-website-tls
{{- end }}
+244
View File
@@ -0,0 +1,244 @@
# =============================================================================
# SOVEREIGN HELM VALUES
# Mirrors the structure of inventories/production/group_vars/all.yml.
# For a new deployment: copy this file, update baseDomain, and replace all
# changeme_* values with secure secrets.
# =============================================================================
# -- Global settings shared by all services
global:
# Base domain — all service subdomains derive from this
baseDomain: "example.com"
# Display name shown in service UIs and email subjects
tenantName: "Example Corp"
# Brand colours (hex)
primaryColor: "#2563eb"
accentColor: "#1e40af"
# Kubernetes IngressClass name used for all Ingress resources
ingressClassName: "traefik"
# cert-manager ClusterIssuer name; leave empty to disable cert-manager integration
clusterIssuer: ""
# Default StorageClass for PVCs; empty string = cluster default
storageClass: ""
# -- SMTP (shared across services that send email)
smtp:
# Service name or hostname for the SMTP server.
# Defaults to the release-name-stalwart service when empty.
host: ""
port: 587
# Sender address; defaults to noreply@<baseDomain> when empty
from: ""
# Login username; defaults to noreply@<baseDomain> when empty
user: ""
password: "changeme_smtp"
# starttls | tls | plain
tls: "starttls"
# =============================================================================
# TRAEFIK — reverse proxy / ingress controller
# Set enabled: false to use an existing ingress controller (nginx, etc.)
# and still deploy all other services with standard Ingress resources.
# =============================================================================
traefik:
enabled: true
version: "v3.1"
# ACME / Let's Encrypt email; defaults to admin@<baseDomain> when empty
acmeEmail: ""
# htpasswd-hashed password for the Traefik dashboard
dashboardPassword: "changeme"
service:
# LoadBalancer | NodePort
type: LoadBalancer
# =============================================================================
# GRAYLOG — centralised logging (Graylog + OpenSearch + MongoDB)
# =============================================================================
graylog:
enabled: true
version: "6.0"
opensearchVersion: "2.15.0"
opensearchJavaOpts: "-Xms1g -Xmx1g"
mongodbVersion: "6.0"
# Minimum 16 characters
passwordSecret: "changeme_graylog_secret_min_16_chars"
# sha256 of the root password: echo -n yourpassword | sha256sum
rootPasswordSha2: "changeme_sha256_of_password"
persistence:
mongodbSize: "10Gi"
opensearchSize: "50Gi"
graylogSize: "10Gi"
# =============================================================================
# AUTHENTIK — identity provider (OIDC / OAuth2 / SAML)
# =============================================================================
authentik:
enabled: true
version: "2024.10.5"
# 50-character random string
secretKey: "change-me-to-a-50-char-random-string"
dbPassword: "changeme_authentik_db"
# Defaults to admin@<baseDomain> when empty
adminEmail: ""
adminPassword: "changeme_admin"
persistence:
postgresSize: "10Gi"
mediaSize: "5Gi"
certsSize: "1Gi"
# =============================================================================
# MINIO — S3-compatible object storage
# =============================================================================
minio:
enabled: true
version: "latest"
rootUser: "minioadmin"
rootPassword: "changeme_minio"
# OIDC client secret configured in Authentik
oidcSecret: "changeme_minio_oidc_secret"
# Bucket / credentials used by Nextcloud
nextcloudBucket: "nextcloud"
nextcloudAccessKey: "nextcloud"
nextcloudSecretKey: "changeme_nextcloud_s3"
persistence:
size: "100Gi"
# =============================================================================
# NEXTCLOUD — file sync and share
# =============================================================================
nextcloud:
enabled: true
version: "29"
adminUser: "admin"
adminPassword: "changeme_nextcloud"
dbPassword: "changeme_nextcloud_db"
dbRootPassword: "changeme_nextcloud_db_root"
persistence:
dbSize: "10Gi"
dataSize: "50Gi"
# =============================================================================
# STALWART MAIL — SMTP / IMAP mail server
# =============================================================================
stalwart:
enabled: true
version: "latest"
adminPassword: "changeme_mail_admin"
persistence:
size: "20Gi"
# Service type for external mail ports (25/465/587/993/4190)
# LoadBalancer | NodePort
mailServiceType: LoadBalancer
# =============================================================================
# ROUNDCUBE — webmail client
# =============================================================================
roundcube:
enabled: true
version: "latest"
dbPassword: "changeme_roundcube_db"
# Exactly 24 characters
desKey: "changeme_24_char_des_key____"
skin: "elastic"
persistence:
dbSize: "5Gi"
# =============================================================================
# MATRIX / ELEMENT — federated chat (Synapse + Element Web)
# =============================================================================
matrix:
enabled: true
synapseVersion: "v1.118.0"
# Used for user registration via the admin API
registrationSecret: "changeme_registration_secret"
dbPassword: "changeme_matrix_db"
persistence:
dbSize: "10Gi"
synapseSize: "20Gi"
# =============================================================================
# JITSI — video conferencing
# =============================================================================
jitsi:
enabled: true
version: "stable-9753"
jicofoAuthPassword: "changeme_jicofo"
jvbAuthPassword: "changeme_jvb"
jibriRecorderPassword: "changeme_jibri_recorder"
jibriXmppPassword: "changeme_jibri_xmpp"
turnSecret: "changeme_turn"
persistence:
webSize: "1Gi"
prosodySize: "1Gi"
jicofoSize: "1Gi"
jvbSize: "1Gi"
# =============================================================================
# HEADSCALE — WireGuard mesh VPN coordinator
# =============================================================================
headscale:
enabled: true
version: "0.23.0"
wireguardPort: 51820
persistence:
configSize: "1Gi"
dataSize: "5Gi"
# =============================================================================
# WAZUH — endpoint security (Manager + Indexer + Dashboard)
# NOTE: Wazuh requires pre-generated TLS certificates stored in a Kubernetes
# Secret named <release>-wazuh-certs with the keys documented in
# templates/wazuh.yaml before first deployment.
# =============================================================================
wazuh:
enabled: true
version: "4.9.0"
adminPassword: "changeme_wazuh_admin"
apiPassword: "changeme_wazuh_api"
indexerJavaOpts: "-Xms512m -Xmx512m"
persistence:
managerSize: "20Gi"
indexerSize: "50Gi"
# =============================================================================
# VAULTWARDEN — Bitwarden-compatible password manager
# =============================================================================
vaultwarden:
enabled: true
version: "latest"
adminToken: "changeme_vaultwarden_admin_token"
dbPassword: "changeme_vaultwarden_db"
# OIDC client secret configured in Authentik
oidcSecret: "changeme_vaultwarden_oidc_secret"
persistence:
dbSize: "5Gi"
dataSize: "10Gi"
# =============================================================================
# FORGEJO — self-hosted Git
# =============================================================================
forgejo:
enabled: true
version: "latest"
dbPassword: "changeme_forgejo_db"
secretKey: "changeme_forgejo_secret"
internalToken: "changeme_forgejo_internal_token"
lfsJwtSecret: "changeme_forgejo_lfs_jwt"
adminUser: "admin"
adminPassword: "changeme_forgejo_admin"
# Defaults to admin@<baseDomain> when empty
adminEmail: ""
# NodePort number for SSH git access
sshPort: 2222
persistence:
dbSize: "10Gi"
dataSize: "20Gi"
# =============================================================================
# WEBSITE — static site (nginx)
# =============================================================================
website:
enabled: true
nginxVersion: "alpine"
persistence:
htmlSize: "1Gi"