forked from hswaw/hscloud
app/matrix: add coturn deployment
TURN server is required for proper cross-NAT voice/video calls via Matrix. Change-Id: I8182292dd8ef30690ae4b9487c22aedcff098710 Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1387 Reviewed-by: informatic <informatic@hackerspace.pl>master
parent
b39edc3256
commit
690ed45f66
|
@ -0,0 +1,177 @@
|
|||
local kube = import "../../../kube/kube.libsonnet";
|
||||
|
||||
{
|
||||
local app = self,
|
||||
local cfg = app.cfg,
|
||||
cfg:: {
|
||||
image: error "cfg.image must be set",
|
||||
realm: error "cfg.realm must be set",
|
||||
authSecret: error "cfg.authSecret must be set",
|
||||
storageClassName: error "cfg.storageClassName must be set",
|
||||
|
||||
portStart: 49152,
|
||||
portEnd: 49172,
|
||||
loadBalancerIP: null,
|
||||
},
|
||||
|
||||
ns:: error "ns needs to be provided",
|
||||
|
||||
configMap: app.ns.Contain(kube.ConfigMap("coturn")) {
|
||||
data: {
|
||||
"coturn.conf": |||
|
||||
# VoIP traffic is all UDP. There is no reason to let users connect to arbitrary TCP endpoints via the relay.
|
||||
no-tcp-relay
|
||||
|
||||
no-tls
|
||||
no-dtls
|
||||
|
||||
# don't let the relay ever try to connect to private IP address ranges within your network (if any)
|
||||
# given the turn server is likely behind your firewall, remember to include any privileged public IPs too.
|
||||
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||
|
||||
# recommended additional local peers to block, to mitigate external access to internal services.
|
||||
# https://www.rtcsec.com/article/slack-webrtc-turn-compromise-and-bug-bounty/#how-to-fix-an-open-turn-relay-to-address-this-vulnerability
|
||||
no-multicast-peers
|
||||
denied-peer-ip=0.0.0.0-0.255.255.255
|
||||
denied-peer-ip=100.64.0.0-100.127.255.255
|
||||
denied-peer-ip=127.0.0.0-127.255.255.255
|
||||
denied-peer-ip=169.254.0.0-169.254.255.255
|
||||
denied-peer-ip=192.0.0.0-192.0.0.255
|
||||
denied-peer-ip=192.0.2.0-192.0.2.255
|
||||
denied-peer-ip=192.88.99.0-192.88.99.255
|
||||
denied-peer-ip=198.18.0.0-198.19.255.255
|
||||
denied-peer-ip=198.51.100.0-198.51.100.255
|
||||
denied-peer-ip=203.0.113.0-203.0.113.255
|
||||
denied-peer-ip=240.0.0.0-255.255.255.255
|
||||
|
||||
# special case the turn server itself so that client->TURN->TURN->client flows work
|
||||
# this should be one of the turn server's listening IPs
|
||||
# FIXME allowed-peer-ip=10.0.0.1
|
||||
|
||||
# consider whether you want to limit the quota of relayed streams per user (or total) to avoid risk of DoS.
|
||||
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
|
||||
total-quota=1200
|
||||
|
||||
use-auth-secret
|
||||
|||,
|
||||
},
|
||||
},
|
||||
|
||||
dataVolume: app.ns.Contain(kube.PersistentVolumeClaim("coturn-data")) {
|
||||
spec+: {
|
||||
storageClassName: cfg.storageClassName,
|
||||
resources: {
|
||||
requests: {
|
||||
storage: "10Gi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
deployment: app.ns.Contain(kube.Deployment("coturn")) {
|
||||
spec+: {
|
||||
replicas: 1,
|
||||
template+: {
|
||||
spec+: {
|
||||
volumes_: {
|
||||
config: kube.ConfigMapVolume(app.configMap),
|
||||
data: kube.PersistentVolumeClaimVolume(app.dataVolume),
|
||||
},
|
||||
containers_: {
|
||||
coturn: kube.Container("coturn") {
|
||||
image: cfg.image,
|
||||
ports_: {
|
||||
turn: { containerPort: 3478 },
|
||||
} + {
|
||||
["fwd-%d" % [n]]: { containerPort: n }
|
||||
for n in std.range(cfg.portStart, cfg.portEnd)
|
||||
},
|
||||
|
||||
command: [
|
||||
# This disgusting hack comes from the fact that
|
||||
# official coturn containers have turnserver
|
||||
# binary set up with CAP_NET_BIND_SERVICE=+ep,
|
||||
# while there's really no use that in our case.
|
||||
#
|
||||
# Due to our PSP we can't exec said binary, nor
|
||||
# can we chmod/chown/setcap on it, as we are
|
||||
# running as an unprivileged user.
|
||||
#
|
||||
# Copying it over is the easiest method of
|
||||
# stripping said spurious cap.
|
||||
"/bin/sh", "-c",
|
||||
"cp /usr/bin/turnserver /tmp/turnserver && \\
|
||||
exec /tmp/turnserver \\
|
||||
-c /config/coturn.conf \\
|
||||
--log-binding \\
|
||||
--realm=$(COTURN_REALM) \\
|
||||
--static-auth-secret=$(COTRN_STATIC_AUTH_SECRET) \\
|
||||
--min-port $(COTURN_MIN_PORT) \\
|
||||
--max-port $(COTURN_MAX_PORT) \\
|
||||
" + if cfg.loadBalancerIP != null then "-X $(COTURN_EXTERNAL_IP)" else "",
|
||||
],
|
||||
volumeMounts_: {
|
||||
config: { mountPath: "/config" },
|
||||
data: { mountPath: "/var/lib/coturn" },
|
||||
},
|
||||
env_: {
|
||||
COTURN_REALM: cfg.realm,
|
||||
COTURN_STATIC_AUTH_SECRET: cfg.authSecret,
|
||||
COTURN_EXTERNAL_IP: cfg.loadBalancerIP,
|
||||
COTURN_MIN_PORT: cfg.portStart,
|
||||
COTURN_MAX_PORT: cfg.portEnd,
|
||||
},
|
||||
},
|
||||
},
|
||||
securityContext: {
|
||||
runAsUser: 1000,
|
||||
runAsGroup: 1000,
|
||||
fsGroup: 2000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
svcTCP: app.ns.Contain(kube.Service("coturn-tcp")) {
|
||||
target_pod:: app.deployment.spec.template,
|
||||
metadata+: {
|
||||
annotations+: {
|
||||
"metallb.universe.tf/allow-shared-ip": "coturn",
|
||||
},
|
||||
},
|
||||
spec+: {
|
||||
type: "LoadBalancer",
|
||||
loadBalancerIP: cfg.loadBalancerIP,
|
||||
externalTrafficPolicy: "Local",
|
||||
ports: [
|
||||
{ name: "turn", port: 3478, protocol: "TCP" },
|
||||
] + [
|
||||
{ name: "fwd-%d" % [n], port: n, protocol: "TCP" }
|
||||
for n in std.range(cfg.portStart, cfg.portEnd)
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
svcUDP: app.ns.Contain(kube.Service("coturn-udp")) {
|
||||
target_pod:: app.deployment.spec.template,
|
||||
metadata+: {
|
||||
annotations+: {
|
||||
"metallb.universe.tf/allow-shared-ip": "coturn",
|
||||
},
|
||||
},
|
||||
spec+: {
|
||||
type: "LoadBalancer",
|
||||
loadBalancerIP: cfg.loadBalancerIP,
|
||||
externalTrafficPolicy: "Local",
|
||||
ports: [
|
||||
{ name: "turn", port: 3478, protocol: "UDP" },
|
||||
] + [
|
||||
{ name: "fwd-%d" % [n], port: n, protocol: "UDP" }
|
||||
for n in std.range(cfg.portStart, cfg.portEnd)
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
|
@ -95,6 +95,7 @@ local cas = import "./cas.libsonnet";
|
|||
local wellKnown = import "./wellknown.libsonnet";
|
||||
local synapse = import "./synapse.libsonnet";
|
||||
local mediaRepo = import "./media-repo.libsonnet";
|
||||
local coturn = import "./coturn.libsonnet";
|
||||
|
||||
{
|
||||
local app = self,
|
||||
|
@ -115,6 +116,7 @@ local mediaRepo = import "./media-repo.libsonnet";
|
|||
appserviceTelegram: "dock.mau.dev/tulir/mautrix-telegram@sha256:c6e25cb57e1b67027069e8dc2627338df35d156315c004a6f2b34b6aeaa79f77",
|
||||
wellKnown: "registry.k0.hswaw.net/q3k/wellknown:1611960794-adbf560851a46ad0e58b42f0daad7ef19535687c",
|
||||
mediaRepo: "turt2live/matrix-media-repo:v1.2.8",
|
||||
coturn: "coturn/coturn:4.5.2-r11-alpine",
|
||||
},
|
||||
|
||||
# OpenID Connect provider configuration.
|
||||
|
@ -193,6 +195,23 @@ local mediaRepo = import "./media-repo.libsonnet";
|
|||
|
||||
# List of administrative users MXIDs (used in matrix-media-repo only)
|
||||
admins: [],
|
||||
|
||||
# Deploy coturn STUN/TURN server
|
||||
coturn: {
|
||||
enable: false,
|
||||
config: {
|
||||
domain: error "coturn.config.domain must be set",
|
||||
|
||||
# Default to public domain - this may be adjusted when multiple
|
||||
# turn servers are deployed.
|
||||
realm: self.domain,
|
||||
|
||||
# Set this to assigned LoadBalacer IP for correct NAT resolution
|
||||
loadBalancerIP: null,
|
||||
|
||||
authSecret: { secretKeyRef: { name: "coturn", key: "auth_secret" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
# DEPRECATED: this needs to be removed in favor of namespace.Contain() in
|
||||
|
@ -279,6 +298,17 @@ local mediaRepo = import "./media-repo.libsonnet";
|
|||
},
|
||||
} else {},
|
||||
|
||||
coturn: if cfg.coturn.enable then coturn {
|
||||
ns: app.namespace,
|
||||
cfg+: {
|
||||
storageClassName: cfg.storageClassName,
|
||||
image: cfg.images.coturn,
|
||||
realm: cfg.coturn.config.realm,
|
||||
loadBalancerIP: cfg.coturn.config.loadBalancerIP,
|
||||
authSecret: cfg.coturn.config.authSecret,
|
||||
},
|
||||
} else null,
|
||||
|
||||
synapse: synapse {
|
||||
ns: app.namespace,
|
||||
postgres: app.postgres3,
|
||||
|
|
|
@ -69,6 +69,13 @@ local kube = import "../../../kube/kube.libsonnet";
|
|||
server_url: "https://%s/_cas" % [cfg.webDomain],
|
||||
service_url: "https://%s" % [cfg.webDomain],
|
||||
},
|
||||
} else {}) + (if cfg.coturn.enable then {
|
||||
turn_uris: [ "turn:%s?transport=udp" % cfg.coturn.config.domain, "turn:%s?transport=tcp" % cfg.coturn.config.domain ],
|
||||
|
||||
# Lifetime of single TURN user credentials - 1 day, recommended by TURN REST
|
||||
# spec, see https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00#section-2.2
|
||||
turn_user_lifetime: 24 * 60 * 60 * 1000,
|
||||
turn_allow_guests: true,
|
||||
} else {}),
|
||||
|
||||
configMap: app.ns.Contain(kube.ConfigMap("synapse")) {
|
||||
|
@ -87,6 +94,8 @@ local kube = import "../../../kube/kube.libsonnet";
|
|||
enabled: true,
|
||||
client_secret: "$(OIDC_CLIENT_SECRET)",
|
||||
},
|
||||
} else {}) + (if cfg.coturn.enable then {
|
||||
turn_shared_secret: "$(TURN_SHARED_SECRET)",
|
||||
} else {}),
|
||||
|
||||
# Synapse process Deployment/StatefulSet base resource.
|
||||
|
@ -151,6 +160,7 @@ local kube = import "../../../kube/kube.libsonnet";
|
|||
REDIS_PASSWORD: app.redis.cfg.password,
|
||||
POD_NAME: { fieldRef: { fieldPath: "metadata.name" } },
|
||||
OIDC_CLIENT_SECRET: if cfg.oidc.enable then cfg.oidc.config.client_secret else "",
|
||||
TURN_SHARED_SECRET: if cfg.coturn.enable then cfg.coturn.config.authSecret else "",
|
||||
|
||||
X_SECRETS_CONFIG: std.manifestYamlDoc(app.secretsConfig),
|
||||
X_LOCAL_CONFIG: std.manifestYamlDoc(worker.cfg.localConfig),
|
||||
|
|
|
@ -36,6 +36,13 @@ matrix {
|
|||
password: std.strReplace(importstr "secrets/plain/media-repo-matrix-postgres", "\n", ""),
|
||||
},
|
||||
},
|
||||
coturn+: {
|
||||
enable: true,
|
||||
config+: {
|
||||
domain: "turn.hackerspace.pl",
|
||||
loadBalancerIP: "185.236.240.59",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
riot+: {
|
||||
|
|
Loading…
Reference in New Issue