Issue #38 discussion surfaced a real bug: the TURN shared secret was duplicated - correctly SOPS-encrypted in coturn-secret.yaml, but also hardcoded in plaintext in synapse-values.yaml (a plain, non-SOPS ConfigMap), visible in git history. Also found turn_user_lifetime is 86400000ms (24h), not "short-lived" as previously assumed - raising the stakes of the leak somewhat. Extracted the turn config block into its own dedicated SOPS-encrypted Secret (synapse-turn-secret.yaml), wired via a second HelmRelease valuesFrom entry (same pattern already used for ess-mas-values-secret). Rotated the value while doing this, so the leaked plaintext secret is no longer live anywhere. Added checksum/rotated-at annotations (matrix-stack HelmRelease's existing element-config-checksum patch gets a sibling turn-secret-checksum; coturn's Deployment pod template gets a rotated-at annotation) so future rotations actually restart both consumers - Kubernetes doesn't restart running pods when a referenced Secret's content changes on its own. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
164 lines
3.8 KiB
YAML
164 lines
3.8 KiB
YAML
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: coturn-config
|
|
namespace: matrix
|
|
data:
|
|
turnserver.conf: |
|
|
# TURN Server configuration
|
|
realm=axion1337.chat
|
|
|
|
# Listen ports
|
|
listening-port=3478
|
|
listening-ip=0.0.0.0
|
|
alt-listening-port=5349
|
|
alt-listening-ip=0.0.0.0
|
|
|
|
# External IPs (for clients behind NAT)
|
|
relay-ip=49.13.132.245
|
|
external-ip=49.13.132.245
|
|
|
|
# Relay port range
|
|
min-bps=0
|
|
bps-capacity=0
|
|
|
|
# Authentication
|
|
use-auth-secret
|
|
static-auth-secret=$TURN_SECRET
|
|
|
|
# HTTPS/TLS
|
|
cert=/etc/coturn/tls/tls.crt
|
|
pkey=/etc/coturn/tls/tls.key
|
|
|
|
# Performance tuning
|
|
max-bps=0
|
|
bps-capacity=0
|
|
log-file=stdout
|
|
|
|
# Logging
|
|
verbose
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: coturn
|
|
namespace: matrix
|
|
spec:
|
|
type: ClusterIP
|
|
ports:
|
|
- name: stun-udp
|
|
port: 3478
|
|
protocol: UDP
|
|
- name: stun-tcp
|
|
port: 3478
|
|
protocol: TCP
|
|
- name: turns-tcp
|
|
port: 5349
|
|
protocol: TCP
|
|
selector:
|
|
app: coturn
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: coturn
|
|
namespace: matrix
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: coturn
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: coturn
|
|
annotations:
|
|
prometheus.io/scrape: "false"
|
|
# Bumped on every TURN_SECRET rotation (Issue #38) to force a new pod, since
|
|
# Kubernetes doesn't restart running pods when a referenced Secret's content
|
|
# changes and the initContainer that reads it only runs once at pod start.
|
|
rotated-at: "2026-07-28T19:02:00Z"
|
|
spec:
|
|
hostNetwork: true
|
|
dnsPolicy: ClusterFirstWithHostNet
|
|
initContainers:
|
|
- name: init-config
|
|
image: busybox:1.28
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
TURN_SECRET=$(cat /etc/coturn-secret/TURN_SECRET)
|
|
sed "s|\$TURN_SECRET|$TURN_SECRET|g" /etc/coturn-template/turnserver.conf > /etc/coturn/turnserver.conf
|
|
chmod 644 /etc/coturn/turnserver.conf
|
|
resources:
|
|
limits:
|
|
cpu: 100m
|
|
memory: 64Mi
|
|
requests:
|
|
cpu: 50m
|
|
memory: 32Mi
|
|
volumeMounts:
|
|
- name: config-template
|
|
mountPath: /etc/coturn-template
|
|
- name: config
|
|
mountPath: /etc/coturn
|
|
- name: secret
|
|
mountPath: /etc/coturn-secret
|
|
readOnly: true
|
|
containers:
|
|
- name: coturn
|
|
image: coturn/coturn:latest
|
|
imagePullPolicy: IfNotPresent
|
|
ports:
|
|
- name: stun-udp
|
|
containerPort: 3478
|
|
protocol: UDP
|
|
- name: stun-tcp
|
|
containerPort: 3478
|
|
protocol: TCP
|
|
- name: turns-tcp
|
|
containerPort: 5349
|
|
protocol: TCP
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /etc/coturn
|
|
- name: tls
|
|
mountPath: /etc/coturn/tls
|
|
readOnly: true
|
|
resources:
|
|
limits:
|
|
cpu: 500m
|
|
memory: 256Mi
|
|
requests:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
livenessProbe:
|
|
tcpSocket:
|
|
port: 3478
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 10
|
|
volumes:
|
|
- name: config
|
|
emptyDir: {}
|
|
- name: config-template
|
|
configMap:
|
|
name: coturn-config
|
|
- name: secret
|
|
secret:
|
|
secretName: coturn-secret
|
|
defaultMode: 0400
|
|
- name: tls
|
|
secret:
|
|
secretName: turn-axion1337-chat-tls
|
|
affinity:
|
|
nodeAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
preference:
|
|
matchExpressions:
|
|
- key: kubernetes.io/hostname
|
|
operator: In
|
|
values:
|
|
- matrix
|