communication: remove plaintext secrets
This commit is contained in:
parent
aca05266fc
commit
d8d741bbd9
@ -1,9 +0,0 @@
|
|||||||
# services/communication/atlasbot-credentials.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: atlasbot-credentials
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
bot-password: "x8eU9xwsjJ2S7Xv1G4mQ"
|
|
||||||
seeder-password: "Qv5sjyH8nD6pPz7Lk3R0"
|
|
||||||
@ -33,7 +33,7 @@ spec:
|
|||||||
- name: BOT_PASS
|
- name: BOT_PASS
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: atlasbot-credentials
|
name: atlasbot-credentials-runtime
|
||||||
key: bot-password
|
key: bot-password
|
||||||
- name: CHAT_API_KEY
|
- name: CHAT_API_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
|
|||||||
@ -22,7 +22,7 @@ spec:
|
|||||||
- name: SEEDER_PASS
|
- name: SEEDER_PASS
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: atlasbot-credentials
|
name: atlasbot-credentials-runtime
|
||||||
key: seeder-password
|
key: seeder-password
|
||||||
command:
|
command:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
|
|||||||
@ -16,7 +16,6 @@ resources:
|
|||||||
- element-call-deployment.yaml
|
- element-call-deployment.yaml
|
||||||
- pin-othrys-job.yaml
|
- pin-othrys-job.yaml
|
||||||
- guest-name-job.yaml
|
- guest-name-job.yaml
|
||||||
- atlasbot-credentials.yaml
|
|
||||||
- atlasbot-configmap.yaml
|
- atlasbot-configmap.yaml
|
||||||
- atlasbot-deployment.yaml
|
- atlasbot-deployment.yaml
|
||||||
- seed-othrys-room.yaml
|
- seed-othrys-room.yaml
|
||||||
|
|||||||
@ -20,7 +20,7 @@ spec:
|
|||||||
- name: SEEDER_PASS
|
- name: SEEDER_PASS
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: atlasbot-credentials
|
name: atlasbot-credentials-runtime
|
||||||
key: seeder-password
|
key: seeder-password
|
||||||
command:
|
command:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
|
|||||||
@ -18,45 +18,30 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: SYNAPSE_BASE
|
- name: SYNAPSE_BASE
|
||||||
value: http://othrys-synapse-matrix-synapse:8008
|
value: http://othrys-synapse-matrix-synapse:8008
|
||||||
- name: REG_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: othrys-synapse-matrix-synapse
|
|
||||||
key: config.yaml
|
|
||||||
- name: SEEDER_USER
|
- name: SEEDER_USER
|
||||||
value: othrys-seeder
|
value: othrys-seeder
|
||||||
- name: SEEDER_PASS
|
- name: SEEDER_PASS
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: atlasbot-credentials
|
name: atlasbot-credentials-runtime
|
||||||
key: seeder-password
|
key: seeder-password
|
||||||
- name: BOT_USER
|
- name: BOT_USER
|
||||||
value: atlasbot
|
value: atlasbot
|
||||||
- name: BOT_PASS
|
- name: BOT_PASS
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: atlasbot-credentials
|
name: atlasbot-credentials-runtime
|
||||||
key: bot-password
|
key: bot-password
|
||||||
command:
|
command:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
pip install --no-cache-dir requests pyyaml matrix-synapse >/dev/null
|
pip install --no-cache-dir requests pyyaml >/dev/null
|
||||||
python - <<'PY'
|
python - <<'PY'
|
||||||
import os, subprocess, requests, yaml
|
import os, requests, urllib.parse
|
||||||
|
|
||||||
BASE = os.environ["SYNAPSE_BASE"]
|
BASE = os.environ["SYNAPSE_BASE"]
|
||||||
CONFIG = "/config/config.yaml"
|
|
||||||
|
|
||||||
def register(user, password, admin=False):
|
|
||||||
args = ["register_new_matrix_user", "-c", CONFIG, "-u", user, "-p", password]
|
|
||||||
if admin:
|
|
||||||
args.append("-a")
|
|
||||||
args.append(BASE)
|
|
||||||
res = subprocess.run(args, capture_output=True, text=True)
|
|
||||||
if res.returncode not in (0, 1): # 1 = already exists
|
|
||||||
raise SystemExit(f"register {user} failed: {res.returncode} {res.stderr}")
|
|
||||||
|
|
||||||
def login(user, password):
|
def login(user, password):
|
||||||
r = requests.post(f"{BASE}/_matrix/client/v3/login", json={
|
r = requests.post(f"{BASE}/_matrix/client/v3/login", json={
|
||||||
@ -68,6 +53,18 @@ spec:
|
|||||||
raise SystemExit(f"login failed: {r.status_code} {r.text}")
|
raise SystemExit(f"login failed: {r.status_code} {r.text}")
|
||||||
return r.json()["access_token"]
|
return r.json()["access_token"]
|
||||||
|
|
||||||
|
def ensure_user(token, localpart, password, admin):
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
user_id = f"@{localpart}:live.bstein.dev"
|
||||||
|
url = f"{BASE}/_synapse/admin/v2/users/{urllib.parse.quote(user_id)}"
|
||||||
|
res = requests.get(url, headers=headers)
|
||||||
|
if res.status_code == 200:
|
||||||
|
return
|
||||||
|
payload = {"password": password, "admin": admin, "deactivated": False}
|
||||||
|
create = requests.put(url, headers=headers, json=payload)
|
||||||
|
if create.status_code not in (200, 201):
|
||||||
|
raise SystemExit(f"create user {user_id} failed: {create.status_code} {create.text}")
|
||||||
|
|
||||||
def ensure_room(token):
|
def ensure_room(token):
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
alias = "#othrys:live.bstein.dev"
|
alias = "#othrys:live.bstein.dev"
|
||||||
@ -100,7 +97,7 @@ spec:
|
|||||||
|
|
||||||
def join_user(token, room_id, user_id):
|
def join_user(token, room_id, user_id):
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
requests.post(f"{BASE}/_synapse/admin/v1/join/{room_id}", headers=headers, json={"user_id": user_id})
|
requests.post(f"{BASE}/_synapse/admin/v1/join/{urllib.parse.quote(room_id)}", headers=headers, json={"user_id": user_id})
|
||||||
|
|
||||||
def join_all_locals(token, room_id):
|
def join_all_locals(token, room_id):
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
@ -118,9 +115,9 @@ spec:
|
|||||||
for uid in users:
|
for uid in users:
|
||||||
join_user(token, room_id, uid)
|
join_user(token, room_id, uid)
|
||||||
|
|
||||||
register(os.environ["SEEDER_USER"], os.environ["SEEDER_PASS"], admin=True)
|
|
||||||
register(os.environ["BOT_USER"], os.environ["BOT_PASS"], admin=False)
|
|
||||||
token = login(os.environ["SEEDER_USER"], os.environ["SEEDER_PASS"])
|
token = login(os.environ["SEEDER_USER"], os.environ["SEEDER_PASS"])
|
||||||
|
ensure_user(token, os.environ["SEEDER_USER"], os.environ["SEEDER_PASS"], admin=True)
|
||||||
|
ensure_user(token, os.environ["BOT_USER"], os.environ["BOT_PASS"], admin=False)
|
||||||
room_id = ensure_room(token)
|
room_id = ensure_room(token)
|
||||||
join_user(token, room_id, f"@{os.environ['BOT_USER']}:live.bstein.dev")
|
join_user(token, room_id, f"@{os.environ['BOT_USER']}:live.bstein.dev")
|
||||||
join_all_locals(token, room_id)
|
join_all_locals(token, room_id)
|
||||||
|
|||||||
@ -27,8 +27,6 @@ stringData:
|
|||||||
config.yaml: |
|
config.yaml: |
|
||||||
## Registration ##
|
## Registration ##
|
||||||
|
|
||||||
registration_shared_secret: "PlxXRFAiRfLDp8RbAS6aHN7b"
|
|
||||||
|
|
||||||
## API Configuration ##
|
## API Configuration ##
|
||||||
|
|
||||||
## Database configuration ##
|
## Database configuration ##
|
||||||
|
|||||||
@ -1,231 +0,0 @@
|
|||||||
# services/jitsi/deployment.yaml
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: jitsi-prosody
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: jitsi-prosody }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: jitsi-prosody }
|
|
||||||
spec:
|
|
||||||
serviceAccountName: jitsi
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: titan-22
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
containers:
|
|
||||||
- name: prosody
|
|
||||||
image: jitsi/prosody:stable
|
|
||||||
ports:
|
|
||||||
- { name: c2s, containerPort: 5222, protocol: TCP }
|
|
||||||
- { name: http, containerPort: 5280, protocol: TCP }
|
|
||||||
- { name: comp, containerPort: 5347, protocol: TCP }
|
|
||||||
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: ENABLE_AUTH, value: "1" }
|
|
||||||
- { name: ENABLE_GUESTS, value: "1" }
|
|
||||||
- { name: AUTH_TYPE, value: "jwt" }
|
|
||||||
- { name: XMPP_GUEST_DOMAIN, value: "guest.meet.jitsi" }
|
|
||||||
- { name: JWT_ACCEPTED_ISSUERS, value: "https://sso.bstein.dev/realms/atlas" }
|
|
||||||
- { name: JWT_ACCEPTED_AUDIENCES, value: "jitsi" }
|
|
||||||
- { name: JWT_APP_ID, value: "jitsi" }
|
|
||||||
- name: JWT_APP_SECRET
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-jwt, key: app_secret } }
|
|
||||||
- { name: JICOFO_AUTH_USER, value: "focus" }
|
|
||||||
- { name: JVB_AUTH_USER, value: "jvb" }
|
|
||||||
- name: JICOFO_AUTH_PASSWORD
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_AUTH_PASSWORD } }
|
|
||||||
- name: JICOFO_COMPONENT_SECRET
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_COMPONENT_SECRET } }
|
|
||||||
- name: JVB_AUTH_PASSWORD
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JVB_AUTH_PASSWORD } }
|
|
||||||
volumeMounts:
|
|
||||||
- { name: cfg, mountPath: /config }
|
|
||||||
- { name: jwt, mountPath: /var/lib/jitsi-jwt, readOnly: true }
|
|
||||||
volumes:
|
|
||||||
- name: cfg
|
|
||||||
persistentVolumeClaim: { claimName: jitsi-prosody-config }
|
|
||||||
- name: jwt
|
|
||||||
csi:
|
|
||||||
driver: secrets-store.csi.k8s.io
|
|
||||||
readOnly: true
|
|
||||||
volumeAttributes:
|
|
||||||
secretProviderClass: jitsi-jwt
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jicofo
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: jitsi-jicofo }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: jitsi-jicofo }
|
|
||||||
spec:
|
|
||||||
serviceAccountName: jitsi
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: titan-22
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
containers:
|
|
||||||
- name: jicofo
|
|
||||||
image: jitsi/jicofo:stable
|
|
||||||
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_GUEST_DOMAIN, value: "guest.meet.jitsi" }
|
|
||||||
- { name: ENABLE_AUTH, value: "1" }
|
|
||||||
- { name: ENABLE_GUESTS, value: "1" }
|
|
||||||
- { name: AUTH_TYPE, value: "jwt" }
|
|
||||||
- { name: XMPP_SERVER, value: "jitsi-prosody.jitsi.svc.cluster.local" }
|
|
||||||
- { name: JICOFO_AUTH_USER, value: "focus" }
|
|
||||||
- name: JICOFO_AUTH_PASSWORD
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_AUTH_PASSWORD } }
|
|
||||||
- name: JICOFO_COMPONENT_SECRET
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_COMPONENT_SECRET } }
|
|
||||||
- { name: JVB_BREWERY_MUC, value: "jvbbrewery" }
|
|
||||||
volumeMounts:
|
|
||||||
- { name: cfg, mountPath: /config }
|
|
||||||
volumes:
|
|
||||||
- name: cfg
|
|
||||||
persistentVolumeClaim: { claimName: jitsi-jicofo-config }
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jvb
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: jitsi-jvb }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: jitsi-jvb }
|
|
||||||
spec:
|
|
||||||
serviceAccountName: jitsi
|
|
||||||
initContainers:
|
|
||||||
- name: jvb-custom-config
|
|
||||||
image: busybox:1.36
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
set -euo pipefail
|
|
||||||
cp /custom-config/custom-jvb.conf /config/custom-jvb.conf
|
|
||||||
cp /custom-config/sip-communicator.properties /config/sip-communicator.properties
|
|
||||||
volumeMounts:
|
|
||||||
- { name: cfg, mountPath: /config }
|
|
||||||
- { name: jvb-custom, mountPath: /custom-config }
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: titan-22
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
containers:
|
|
||||||
- name: jvb
|
|
||||||
image: jitsi/jvb:stable
|
|
||||||
ports:
|
|
||||||
- { name: colibri-ws, containerPort: 9090, protocol: TCP } # WebSocket control channel
|
|
||||||
- { name: rtp-udp, containerPort: 10000, hostPort: 10000, protocol: UDP } # media
|
|
||||||
- { name: rtp-tcp, containerPort: 4443, hostPort: 4443, protocol: TCP }
|
|
||||||
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_GUEST_DOMAIN, value: "guest.meet.jitsi" }
|
|
||||||
- { name: XMPP_SERVER, value: "jitsi-prosody.jitsi.svc.cluster.local" }
|
|
||||||
- { name: JVB_AUTH_USER, value: "jvb" }
|
|
||||||
- name: JVB_AUTH_PASSWORD
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JVB_AUTH_PASSWORD } }
|
|
||||||
- { name: JVB_BREWERY_MUC, value: "jvbbrewery" }
|
|
||||||
- { name: JVB_PORT, value: "10000" } # matches hostPort above
|
|
||||||
- { name: ENABLE_COLIBRI_WEBSOCKET, value: "1" } # enables /colibri-ws
|
|
||||||
# - { name: JVB_STUN_SERVERS, value: "stun.l.google.com:19302,stun1.l.google.com:19302,meet-jit-si-turnrelay.jitsi.net:443" }
|
|
||||||
- { name: JVB_ENABLE_APIS, value: "rest,colibri" }
|
|
||||||
- { name: JVB_WS_DOMAIN, value: "meet.bstein.dev" }
|
|
||||||
- { name: JVB_WS_TLS, value: "true" } # advertise wss:// for bridge channel
|
|
||||||
- { name: JVB_ADVERTISE_IPS, value: "38.28.125.112,192.168.22.22" }
|
|
||||||
- { name: JVB_TCP_HARVESTER_DISABLED, value: "false" }
|
|
||||||
- { name: JVB_TCP_PORT, value: "4443" }
|
|
||||||
- { name: AUTH_TYPE, value: "jwt" }
|
|
||||||
- name: JVB_OPTS
|
|
||||||
value: "-Dorg.jitsi.videobridge.DISABLE_TCP_HARVESTER=false -Dorg.ice4j.ice.harvest.DISABLE_TCP_HARVESTER=false -Dorg.jitsi.videobridge.TCP_HARVESTER_PORT=4443 -Dorg.jitsi.videobridge.TCP_HARVESTER_MAPPED_PORT=4443"
|
|
||||||
volumeMounts:
|
|
||||||
- { name: cfg, mountPath: /config }
|
|
||||||
volumes:
|
|
||||||
- name: cfg
|
|
||||||
persistentVolumeClaim: { claimName: jitsi-jvb-config }
|
|
||||||
- name: jvb-custom
|
|
||||||
configMap:
|
|
||||||
name: jitsi-jvb-custom-config
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: jitsi-web
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: jitsi-web }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: jitsi-web }
|
|
||||||
spec:
|
|
||||||
serviceAccountName: jitsi
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: titan-22
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
containers:
|
|
||||||
- name: web
|
|
||||||
image: jitsi/web:stable
|
|
||||||
ports:
|
|
||||||
- { name: http, containerPort: 80, protocol: TCP }
|
|
||||||
env:
|
|
||||||
- { name: PUBLIC_URL, value: "https://meet.bstein.dev" }
|
|
||||||
- { 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_GUEST_DOMAIN, value: "guest.meet.jitsi" }
|
|
||||||
- { name: ENABLE_AUTH, value: "1" }
|
|
||||||
- { name: ENABLE_GUESTS, value: "1" }
|
|
||||||
- { name: AUTH_TYPE, value: "jwt" }
|
|
||||||
- { name: JWT_APP_ID, value: "jitsi" }
|
|
||||||
- { name: JWT_ACCEPTED_ISSUERS, value: "https://sso.bstein.dev/realms/atlas" }
|
|
||||||
- { name: JWT_ACCEPTED_AUDIENCES, value: "jitsi" }
|
|
||||||
- name: JWT_APP_SECRET
|
|
||||||
valueFrom: { secretKeyRef: { name: jitsi-jwt, key: app_secret } }
|
|
||||||
- { name: XMPP_BOSH_URL_BASE, value: "https://meet.bstein.dev" }
|
|
||||||
- { name: ENABLE_XMPP_WEBSOCKET, value: "1" }
|
|
||||||
- { name: ENABLE_COLIBRI_WEBSOCKET, value: "1" }
|
|
||||||
volumeMounts:
|
|
||||||
- { name: cfg, mountPath: /config }
|
|
||||||
- { name: jwt, mountPath: /var/lib/jitsi-jwt, readOnly: true }
|
|
||||||
volumes:
|
|
||||||
- name: cfg
|
|
||||||
persistentVolumeClaim: { claimName: jitsi-web-config }
|
|
||||||
- name: jwt
|
|
||||||
csi:
|
|
||||||
driver: secrets-store.csi.k8s.io
|
|
||||||
readOnly: true
|
|
||||||
volumeAttributes:
|
|
||||||
secretProviderClass: jitsi-jwt
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
# services/jitsi/ingress.yaml
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: jitsi
|
|
||||||
namespace: jitsi
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: letsencrypt
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
tls:
|
|
||||||
- hosts: [ "meet.bstein.dev" ]
|
|
||||||
secretName: jitsi-meet-tls
|
|
||||||
rules:
|
|
||||||
- host: meet.bstein.dev
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /colibri-ws
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: jitsi-jvb
|
|
||||||
port: { number: 9090 }
|
|
||||||
- path: /xmpp-websocket
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: jitsi-prosody
|
|
||||||
port: { number: 5280 }
|
|
||||||
- path: /http-bind
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: jitsi-prosody
|
|
||||||
port: { number: 5280 }
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: jitsi-web
|
|
||||||
port: { number: 80 }
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
# services/jitsi/jvb-configmap.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jvb-custom-config
|
|
||||||
namespace: jitsi
|
|
||||||
data:
|
|
||||||
custom-jvb.conf: |
|
|
||||||
videobridge {
|
|
||||||
ice {
|
|
||||||
tcp {
|
|
||||||
enabled = true
|
|
||||||
port = 4443
|
|
||||||
mapped-port = 4443
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sip-communicator.properties: |
|
|
||||||
org.jitsi.videobridge.DISABLE_TCP_HARVESTER=false
|
|
||||||
org.ice4j.ice.harvest.DISABLE_TCP_HARVESTER=false
|
|
||||||
org.jitsi.videobridge.TCP_HARVESTER_PORT=4443
|
|
||||||
org.jitsi.videobridge.TCP_HARVESTER_MAPPED_PORT=4443
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# services/jitsi/kustomization.yaml
|
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- namespace.yaml
|
|
||||||
- serviceaccount.yaml
|
|
||||||
- secretproviderclass.yaml
|
|
||||||
- deployment.yaml
|
|
||||||
- launcher-configmap.yaml
|
|
||||||
- launcher-deployment.yaml
|
|
||||||
- launcher-service.yaml
|
|
||||||
- launcher-ingress.yaml
|
|
||||||
- service.yaml
|
|
||||||
- pvc.yaml
|
|
||||||
- ingress.yaml
|
|
||||||
- secret.yaml
|
|
||||||
- jvb-configmap.yaml
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
# services/jitsi/launcher-configmap.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: jitsi-launcher
|
|
||||||
namespace: jitsi
|
|
||||||
data:
|
|
||||||
app.py: |
|
|
||||||
import base64
|
|
||||||
import hashlib
|
|
||||||
import hmac
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
from fastapi import FastAPI, HTTPException, Request
|
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
|
||||||
|
|
||||||
ISSUER = os.getenv("JWT_ISSUER", "https://sso.bstein.dev/realms/atlas")
|
|
||||||
AUDIENCE = os.getenv("JWT_AUDIENCE", "jitsi")
|
|
||||||
APP_ID = os.getenv("JWT_APP_ID", "jitsi")
|
|
||||||
PUBLIC_URL = os.getenv("PUBLIC_URL", "https://meet.bstein.dev")
|
|
||||||
SECRET_FILE = os.getenv("JWT_SECRET_FILE", "/var/lib/jitsi-jwt/jwt")
|
|
||||||
ALLOWED_GROUPS = {g for g in os.getenv("ALLOWED_GROUPS", "").split(",") if g}
|
|
||||||
TOKEN_TTL = int(os.getenv("JWT_TTL_SECONDS", "600"))
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
def _b64url(data: bytes) -> bytes:
|
|
||||||
return base64.urlsafe_b64encode(data).rstrip(b"=")
|
|
||||||
|
|
||||||
|
|
||||||
def _read_secret() -> bytes:
|
|
||||||
raw = open(SECRET_FILE, "rb").read().strip()
|
|
||||||
try:
|
|
||||||
return bytes.fromhex(raw.decode())
|
|
||||||
except ValueError:
|
|
||||||
return raw
|
|
||||||
|
|
||||||
|
|
||||||
def _sign(room: str, user: str, groups: list[str]) -> str:
|
|
||||||
now = int(time.time())
|
|
||||||
header = {"alg": "HS256", "typ": "JWT"}
|
|
||||||
payload = {
|
|
||||||
"iss": ISSUER,
|
|
||||||
"aud": AUDIENCE,
|
|
||||||
"sub": "meet.jitsi",
|
|
||||||
"room": room,
|
|
||||||
"exp": now + TOKEN_TTL,
|
|
||||||
"nbf": now - 10,
|
|
||||||
"context": {
|
|
||||||
"user": {
|
|
||||||
"name": user,
|
|
||||||
"email": user,
|
|
||||||
"affiliation": "owner",
|
|
||||||
"groups": groups,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app_id": APP_ID,
|
|
||||||
}
|
|
||||||
secret = _read_secret()
|
|
||||||
signing_input = b".".join(
|
|
||||||
[
|
|
||||||
_b64url(json.dumps(header, separators=(",", ":")).encode()),
|
|
||||||
_b64url(json.dumps(payload, separators=(",", ":")).encode()),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
sig = _b64url(hmac.new(secret, signing_input, hashlib.sha256).digest())
|
|
||||||
return b".".join([signing_input, sig]).decode()
|
|
||||||
|
|
||||||
|
|
||||||
def _render_form(message: str = "") -> HTMLResponse:
|
|
||||||
body = f"""
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h2>Start a Jitsi room</h2>
|
|
||||||
{'<p style="color:red;">'+message+'</p>' if message else ''}
|
|
||||||
<form action="/launch" method="get">
|
|
||||||
<label>Room name:</label>
|
|
||||||
<input name="room" required autofocus />
|
|
||||||
<button type="submit">Launch</button>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
return HTMLResponse(body)
|
|
||||||
|
|
||||||
|
|
||||||
def _extract_groups(request: Request) -> set[str]:
|
|
||||||
raw = request.headers.get("x-auth-request-groups", "")
|
|
||||||
# Traefik forwardAuth returns comma-separated groups
|
|
||||||
return {g.strip() for g in raw.split(",") if g.strip()}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/launch")
|
|
||||||
async def launch(request: Request, room: str | None = None):
|
|
||||||
user = request.headers.get("x-auth-request-email") or request.headers.get(
|
|
||||||
"x-auth-request-user", ""
|
|
||||||
)
|
|
||||||
groups = _extract_groups(request)
|
|
||||||
if ALLOWED_GROUPS and not (groups & ALLOWED_GROUPS):
|
|
||||||
raise HTTPException(status_code=403, detail="forbidden")
|
|
||||||
if not room:
|
|
||||||
return _render_form()
|
|
||||||
room = room.strip()
|
|
||||||
if not room or "/" in room or ".." in room:
|
|
||||||
raise HTTPException(status_code=400, detail="invalid room")
|
|
||||||
token = _sign(room, user or "moderator", sorted(groups))
|
|
||||||
join_url = f"{PUBLIC_URL}/{room}#config.jwt={token}"
|
|
||||||
accept = request.headers.get("accept", "")
|
|
||||||
if "text/html" in accept:
|
|
||||||
return RedirectResponse(join_url, status_code=302)
|
|
||||||
return JSONResponse({"room": room, "join_url": join_url, "token": token})
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
async def root():
|
|
||||||
return RedirectResponse("/launch")
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
|
||||||
async def health():
|
|
||||||
return {"status": "ok"}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
# services/jitsi/launcher-deployment.yaml
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: jitsi-launcher
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: jitsi-launcher }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: jitsi-launcher }
|
|
||||||
spec:
|
|
||||||
serviceAccountName: jitsi
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: titan-22
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
containers:
|
|
||||||
- name: launcher
|
|
||||||
image: docker.io/tiangolo/uvicorn-gunicorn-fastapi:python3.11-slim
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
env:
|
|
||||||
- { name: JWT_SECRET_FILE, value: "/var/lib/jitsi-jwt/jwt" }
|
|
||||||
- { name: JWT_ISSUER, value: "https://sso.bstein.dev/realms/atlas" }
|
|
||||||
- { name: JWT_AUDIENCE, value: "jitsi" }
|
|
||||||
- { name: JWT_APP_ID, value: "jitsi" }
|
|
||||||
- { name: PUBLIC_URL, value: "https://meet.bstein.dev" }
|
|
||||||
# Allow any authenticated user to mint; tighten later by setting comma list
|
|
||||||
- { name: ALLOWED_GROUPS, value: "" }
|
|
||||||
- { name: JWT_TTL_SECONDS, value: "600" }
|
|
||||||
ports:
|
|
||||||
- { name: http, containerPort: 80 }
|
|
||||||
volumeMounts:
|
|
||||||
- { name: app, mountPath: /app/main.py, subPath: app.py }
|
|
||||||
- { name: jwt, mountPath: /var/lib/jitsi-jwt, readOnly: true }
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 80
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
volumes:
|
|
||||||
- name: app
|
|
||||||
configMap:
|
|
||||||
name: jitsi-launcher
|
|
||||||
defaultMode: 0444
|
|
||||||
- name: jwt
|
|
||||||
csi:
|
|
||||||
driver: secrets-store.csi.k8s.io
|
|
||||||
readOnly: true
|
|
||||||
volumeAttributes:
|
|
||||||
secretProviderClass: jitsi-jwt
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
# services/jitsi/launcher-ingress.yaml
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: jitsi-launcher
|
|
||||||
namespace: jitsi
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: letsencrypt
|
|
||||||
traefik.ingress.kubernetes.io/router.middlewares: sso-oauth2-proxy-forward-auth@kubernetescrd,sso-oauth2-proxy-errors@kubernetescrd
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
tls:
|
|
||||||
- hosts: [ "meet.bstein.dev" ]
|
|
||||||
secretName: jitsi-meet-tls
|
|
||||||
rules:
|
|
||||||
- host: meet.bstein.dev
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /launch
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: jitsi-launcher
|
|
||||||
port: { number: 80 }
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
# services/jitsi/launcher-service.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: jitsi-launcher
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
selector: { app: jitsi-launcher }
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
targetPort: 80
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# services/jitsi/namespace.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: jitsi
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
# services/jitsi/pvc.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: jitsi-web-config
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
accessModes: ["ReadWriteOnce"]
|
|
||||||
resources: { requests: { storage: 10Gi } }
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: jitsi-prosody-config
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
accessModes: ["ReadWriteOnce"]
|
|
||||||
resources: { requests: { storage: 10Gi } }
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jicofo-config
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
accessModes: ["ReadWriteOnce"]
|
|
||||||
resources: { requests: { storage: 10Gi } }
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jvb-config
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
accessModes: ["ReadWriteOnce"]
|
|
||||||
resources: { requests: { storage: 10Gi } }
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
# services/jitsi/secret.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: jitsi-internal-secrets
|
|
||||||
namespace: jitsi
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
JICOFO_COMPONENT_SECRET: bEg5Y09hZFJBem5PUFliQlp4RHkwRTRP
|
|
||||||
JICOFO_AUTH_PASSWORD: VVkyUmczaVRDWUZ0MzdQdmN3UDN1SFc5
|
|
||||||
JVB_AUTH_PASSWORD: d0M5aWJ4dWlPTnhFak9lRHJqSHdYa0g5
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# services/jitsi/secretproviderclass.yaml
|
|
||||||
apiVersion: secrets-store.csi.x-k8s.io/v1
|
|
||||||
kind: SecretProviderClass
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jwt
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
provider: vault
|
|
||||||
parameters:
|
|
||||||
vaultAddress: "http://vault.vault.svc.cluster.local:8200"
|
|
||||||
roleName: jitsi-jwt
|
|
||||||
objects: |
|
|
||||||
- objectName: "jwt"
|
|
||||||
secretPath: "kv/data/jitsi/jwt-hs256"
|
|
||||||
secretKey: "app_secret"
|
|
||||||
secretObjects:
|
|
||||||
- secretName: jitsi-jwt
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
- objectName: "jwt"
|
|
||||||
key: app_secret
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
# services/jitsi/service.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: jitsi-prosody
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
selector: { app: jitsi-prosody }
|
|
||||||
ports:
|
|
||||||
- { name: c2s, port: 5222, targetPort: 5222, protocol: TCP }
|
|
||||||
- { name: http, port: 5280, targetPort: 5280, protocol: TCP }
|
|
||||||
- { name: comp, port: 5347, targetPort: 5347, protocol: TCP }
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: jitsi-jvb
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
selector: { app: jitsi-jvb }
|
|
||||||
ports:
|
|
||||||
- { name: colibri-ws, port: 9090, targetPort: 9090, protocol: TCP }
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: jitsi-web
|
|
||||||
namespace: jitsi
|
|
||||||
spec:
|
|
||||||
selector: { app: jitsi-web }
|
|
||||||
ports:
|
|
||||||
- { name: http, port: 80, targetPort: 80, protocol: TCP }
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
# services/jitsi/serviceaccount.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: jitsi
|
|
||||||
namespace: jitsi
|
|
||||||
Loading…
x
Reference in New Issue
Block a user