Compare commits

...
3 Commits
Author SHA1 Message Date
Scrublord MacBadandClaude Haiku 4.5 8ff438bd24 Implement TURN server (coturn) for WebRTC video calls
Add coturn Deployment with hostNetwork mode and init container for secret substitution. Include SOPS-encrypted shared secret, TLS certificate for turn.axion1337.chat, and Synapse TURN configuration with proper relay URIs and credentials.

Resolves DTLS timeout issues in RTC video calls by providing media relay for clients behind NAT/Firewall.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-29 23:07:52 +02:00
Scrublord MacBadandClaude Haiku 4.5 01ad3f2d71 Fix HelmRepository API version
- Change from v1beta2 to v1 (v1beta2 is deprecated)
- Resolves dry-run failure in monitoring Kustomization

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 16:18:17 +02:00
Scrublord MacBadandClaude Haiku 4.5 34bffe40be Add monitoring stack: kube-state-metrics, node-exporter, and Alloy
- New monitoring namespace
- HelmRepositories: prometheus-community, grafana
- kube-state-metrics: Kubernetes state metrics
- prometheus-node-exporter: Host metrics DaemonSet
- Alloy: Multi-target scraper pushing to Selendis (10.0.0.3:9090 Prometheus, 10.0.0.3:3100 Loki)
  - Scrapes Flux controllers (flux-system:8080)
  - Scrapes kube-state-metrics (monitoring:8080)
  - Scrapes node-exporter DaemonSet (monitoring:9100)
  - Scrapes Synapse metrics (matrix:9000)
  - Pushes pod logs to Loki
- Monitoring Kustomization added to flux-system

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 16:14:17 +02:00
14 changed files with 476 additions and 1 deletions
+135
View File
@@ -0,0 +1,135 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: alloy-config
namespace: monitoring
data:
config.alloy: |
// Kubernetes pod discovery
discovery.kubernetes "k8s_pods" {
role = "pod"
}
// Relabel for Prometheus scrape
discovery.relabel "prometheus_pods" {
targets = discovery.kubernetes.k8s_pods.targets
rule {
source_labels = ["__meta_kubernetes_pod_annotation_prometheus_io_scrape"]
action = "keep"
regex = "true"
}
rule {
source_labels = ["__meta_kubernetes_pod_annotation_prometheus_io_path"]
action = "replace"
target_label = "__metrics_path__"
regex = "(.+)"
}
rule {
source_labels = ["__address__", "__meta_kubernetes_pod_annotation_prometheus_io_port"]
action = "replace"
regex = "([^:]+)(?::\\d+)?;(\\d+)"
replacement = "$1:$2"
target_label = "__address__"
}
rule {
source_labels = ["__meta_kubernetes_namespace"]
action = "replace"
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
action = "replace"
target_label = "pod"
}
}
// Scrape Flux controllers (flux-system namespace, port 8080)
discovery.kubernetes "flux_pods" {
role = "pod"
namespaces {
names = ["flux-system"]
}
}
discovery.relabel "flux_scrape" {
targets = discovery.kubernetes.flux_pods.targets
rule {
source_labels = ["__meta_kubernetes_pod_container_port_number"]
action = "keep"
regex = "8080"
}
rule {
source_labels = ["__meta_kubernetes_namespace"]
action = "replace"
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
action = "replace"
target_label = "pod"
}
}
// Scrape kube-state-metrics
prometheus.scrape "kube_state_metrics" {
targets = [{
__address__ = "kube-state-metrics.monitoring.svc.cluster.local:8080",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Scrape Flux controllers
prometheus.scrape "flux" {
targets = discovery.relabel.flux_scrape.output
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
job_name = "flux"
}
// Scrape node-exporter DaemonSet
prometheus.scrape "node_exporter" {
targets = [{
__address__ = "prometheus-node-exporter.monitoring.svc.cluster.local:9100",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Scrape Synapse metrics
prometheus.scrape "synapse" {
targets = [{
__address__ = "matrix-stack-synapse-main.matrix.svc.cluster.local:9000",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Kubernetes pod logs to Loki
loki.source.kubernetes "k8s_logs" {
targets = discovery.kubernetes.k8s_pods.targets
forward_to = [loki.write.selendis.receiver]
}
// Remote write to Selendis Prometheus
prometheus.remote_write "selendis" {
endpoint {
url = "http://10.0.0.3:9090/api/v1/write"
write_relabel_config {
source_labels = ["__name__"]
regex = "go_.*|process_.*"
action = "drop"
}
}
}
// Remote write logs to Selendis Loki
loki.write "selendis" {
endpoint {
url = "http://10.0.0.3:3100/loki/api/v1/push"
}
}
+33
View File
@@ -0,0 +1,33 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: alloy
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: alloy
version: "0.x"
sourceRef:
kind: HelmRepository
name: grafana
namespace: flux-system
values:
alloy:
configMap:
name: alloy-config
replicaCount: 1
serviceAccount:
create: true
name: alloy
rbac:
create: true
podAnnotations:
prometheus.io/scrape: "false"
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
+18
View File
@@ -0,0 +1,18 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: prometheus-community
namespace: flux-system
spec:
interval: 1h
url: https://prometheus-community.github.io/helm-charts
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: grafana
namespace: flux-system
spec:
interval: 1h
url: https://grafana.github.io/helm-charts
+22
View File
@@ -0,0 +1,22 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-state-metrics
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: kube-state-metrics
version: "5.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
values:
replicas: 1
service:
port: 8080
prometheus:
monitor:
enabled: false
+9
View File
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helm-repos.yaml
- kube-state-metrics.yaml
- node-exporter.yaml
- alloy-config.yaml
- alloy.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
+29
View File
@@ -0,0 +1,29 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: prometheus-node-exporter
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: prometheus-node-exporter
version: "4.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
values:
hostNetwork: true
hostPID: true
hostRootFsMount:
enabled: true
service:
port: 9100
targetPort: 9100
prometheus:
monitor:
enabled: false
tolerations:
- effect: NoSchedule
operator: Exists
+22
View File
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Secret
metadata:
name: coturn-secret
namespace: matrix
stringData:
TURN_SECRET: ENC[AES256_GCM,data:SILIqMB+fmAMFITAL7lG1hOgICec6BJf1mOcK0gdmnCHWYqRuJv7jgjfGylG25xzQKi+zE7Qual9PnkZG2KiOA==,iv:+GZqLGusE4Q0x2jEEtFxj06rryyQmQhXdkTy4eE8ZHw=,tag:OpSZkinPTAi1ZKWyo8OX3A==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyRk1mK3NWc1l4T0JCOFpF
S0RuQ3ViZmo3QTNVL2JvZ0hzMy91R2l0TEhzCk01a1VGdk1sdVg4aWswTzRibXI4
ZlJtNFF5MjBONEZOaWVpeU5taHl2bkEKLS0tIGxpUHY3NUFLWFBaWm1QSlZiVFkx
MEJleHFnd3oyT3VPL2dsYkpMUlRkOWMKcKUIgsQ/ff49pGGXMnYwJmwqPVC7woAR
IEzvhcNX97xx746SnrxZe5t2YadsYMkYIl0nvqBPJhSlvqMNafpQbQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-29T21:06:21Z"
mac: ENC[AES256_GCM,data:UhyR5m1HYWrZHwNLW5sg2PxbpaydWbP5cekghGlzSpQg7CYEcvZw3tJ/qB8zA19xZSM7tdSHOXdD+QytRq6qW59M1unqMaumA43B6JxQg1C1NdXAW0mkSc2WiNchvgpVii9P/TVlzSSIRwC3YGCQUsfa3SSfNzI4Z6fMuBnhYLE=,iv:4HYxbrYSRJLe1KcQ6q8bpee8/Lx1m3pPmisb/L2Mu64=,tag:l7n3u+Pg6533OzwtNUZvNw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
+162
View File
@@ -0,0 +1,162 @@
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"
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:
exec:
command:
- /bin/sh
- -c
- "netstat -uln | grep 3478 || exit 1"
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
@@ -57,4 +57,13 @@ data:
config: |
room_list_publication_rules:
- user_id: "*"
action: allow
action: allow
turn:
config: |
turn_uris:
- "turn:turn.axion1337.chat?transport=udp"
- "turn:turn.axion1337.chat?transport=tcp"
- "turns:turn.axion1337.chat?transport=tcp"
turn_shared_secret: "cab3c8408363515d9b4cdc3384a1f76ca17a973242fdfdc72b67ac4d86158527"
turn_user_lifetime: 86400000
turn_allow_guests: false
+4
View File
@@ -3,12 +3,16 @@ kind: Kustomization
resources:
- matrix-postgres-auth.yaml
- cert-issuer.yaml
- matrix-certificates.yaml
# Neue Dateien:
- custom-configs/synapse-values.yaml
- custom-configs/element-values.yaml
- custom-configs/mas-secret.yaml
- element-web-docs-configmap.yaml
- element-web-docs-server.yaml
# TURN Server für WebRTC
- coturn-secret.yaml
- coturn.yaml
# HelmRelease (muss ganz unten stehen, damit die ConfigMaps vorher da sind!)
- element-server-suite.yaml
# Custom Apex Ingress für Element Web + Well-Known auf axion1337.chat
+13
View File
@@ -62,3 +62,16 @@ spec:
kind: ClusterIssuer
dnsNames:
- admin.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: turn-axion1337-chat-cert
namespace: matrix
spec:
secretName: turn-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- turn.axion1337.chat
@@ -4,4 +4,5 @@ resources:
- gotk-components.yaml
- gotk-sync.yaml
- infra-sync.yaml
- monitoring-sync.yaml
- production-sync.yaml
@@ -0,0 +1,14 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: monitoring-apps
namespace: flux-system
spec:
interval: 10m
path: ./apps/monitoring
prune: true
sourceRef:
kind: GitRepository
name: flux-system
dependsOn:
- name: infra-apps