diff --git a/README.md b/README.md index adb0ef2..5e27bbd 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ The PostgreSQL subchart is optional. With `postgresql.enabled=true`, set `postgr The homelab deployment is selected in [homelab-gitops](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/mumble). It uses the optional PostgreSQL subchart and keeps the existing `default/mumble` Service and Deployment names, the `mumble-secrets` and TLS Secrets, and TCP/UDP port 64738. The old shared PostgreSQL database is deliberately left intact as rollback data. `bootstrap-homelab-secrets.sh` is a guarded, one-time secret recovery/bootstrap helper; it does not deploy workloads. Back up the dedicated PostgreSQL PVC and both Mumble Secrets together. +`directAccess.enabled` optionally creates a second `mumble-direct` Service on the same TCP/UDP ports. Set `directAccess.externalIPs` to addresses already routed to a cluster node. This Service uses cluster-wide endpoint routing, so an address on one node can reach a ready Mumble pod on another node. It leaves the primary LoadBalancer Service and its client-IP policy unchanged. The homelab uses this for the Funkfeuer/OLSR address on `arschrock`. + Chart package versions come from Git tags using `git describe --tags --long`. The tag `v1.5.939` denotes the tested upstream Mumble release; a later chart-only commit publishes a distinct `1.5.939-N-gHASH` chart version. Source CI validates the chart, publishes it to the indexed `brunner.ninja` repository, and promotes only this chart's selected version in GitOps. CI has no Kubernetes credential. The old `mumble-server.ini` file is ignored because it contains obsolete plaintext credentials. Never commit it, kubeconfigs, generated Secret values, or database dumps. diff --git a/chart/mumble/templates/direct-service.yaml b/chart/mumble/templates/direct-service.yaml new file mode 100644 index 0000000..380f739 --- /dev/null +++ b/chart/mumble/templates/direct-service.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.mumble.enabled .Values.directAccess.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: mumble-direct + labels: + app.kubernetes.io/name: mumble +spec: + type: ClusterIP + externalIPs: +{{ toYaml .Values.directAccess.externalIPs | indent 4 }} + selector: + app.kubernetes.io/name: mumble + ports: + - name: mumble-tcp + port: 64738 + targetPort: mumble-tcp + protocol: TCP + - name: mumble-udp + port: 64738 + targetPort: mumble-udp + protocol: UDP +{{- end }} diff --git a/chart/mumble/values.schema.json b/chart/mumble/values.schema.json index de525ef..d42c5ea 100644 --- a/chart/mumble/values.schema.json +++ b/chart/mumble/values.schema.json @@ -4,6 +4,7 @@ "properties": { "image": {"type": "object", "properties": {"repository": {"type": "string", "minLength": 1}, "tag": {"type": "string"}}}, "service": {"type": "object", "properties": {"type": {"enum": ["LoadBalancer", "ClusterIP"]}, "loadBalancerIP": {"type": "string"}, "externalTrafficPolicy": {"enum": ["Local", "Cluster"]}, "annotations": {"type": "object", "additionalProperties": {"type": "string"}}}}, + "directAccess": {"type": "object", "properties": {"enabled": {"type": "boolean"}, "externalIPs": {"type": "array", "items": {"type": "string", "format": "ipv4"}}}, "if": {"properties": {"enabled": {"const": true}}}, "then": {"required": ["externalIPs"], "properties": {"externalIPs": {"minItems": 1}}}}, "database": {"type": "object", "properties": {"host": {"type": "string", "minLength": 1}}}, "secrets": {"type": "object", "properties": {"mumble": {"type": "string", "minLength": 1}, "postgresql": {"type": "string", "minLength": 1}, "tls": {"type": "string", "minLength": 1}}}, "config": {"type": "object", "properties": {"welcomeText": {"type": "string"}, "registrationName": {"type": "string"}, "registrationURL": {"type": "string"}, "registrationHostname": {"type": "string"}, "registrationLocation": {"type": "string"}}}, diff --git a/chart/mumble/values.yaml b/chart/mumble/values.yaml index 556fec8..298e49a 100644 --- a/chart/mumble/values.yaml +++ b/chart/mumble/values.yaml @@ -11,6 +11,9 @@ service: annotations: {} loadBalancerIP: "" externalTrafficPolicy: Local +directAccess: + enabled: false + externalIPs: [] database: host: postgresql secrets: diff --git a/test.sh b/test.sh index 972d6af..50aaa02 100755 --- a/test.sh +++ b/test.sh @@ -29,5 +29,11 @@ pod = deployment['spec']['template']['spec'] assert [container['name'] for container in pod['initContainers']] == ['render-config'] assert pod['containers'][0]['image'].endswith('v1.5.939') assert any(volume.get('secret', {}).get('secretName') == 'mumble-tls' for volume in pod['volumes']) +direct = render('--set', 'directAccess.enabled=true', '--set', 'directAccess.externalIPs[0]=10.12.4.170') +direct_service = next(doc for doc in direct if doc['kind'] == 'Service' and doc['metadata']['name'] == 'mumble-direct') +assert direct_service['spec']['externalIPs'] == ['10.12.4.170'] +assert direct_service['spec']['type'] == 'ClusterIP' +assert 'externalTrafficPolicy' not in direct_service['spec'] +assert {port['protocol'] for port in direct_service['spec']['ports']} == {'TCP', 'UDP'} print('Mumble chart renders staged PostgreSQL and active Mumble without cleartext credentials.') PY