From 2a6f0a8db3beb86dd2b21d0585e885834d0e68ef Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 8 Jan 2026 05:34:03 -0300 Subject: [PATCH] comms: tidy stack and guest naming --- .../kustomization.yaml | 2 +- .../applications/kustomization.yaml | 2 +- services/comms/NOTES.md | 32 ++++++ services/comms/guest-name-job.yaml | 105 +++++++++++++++++- services/comms/guest-register-configmap.yaml | 12 +- services/comms/kustomization.yaml | 48 ++++---- services/comms/mas-db-secret.yaml | 7 -- 7 files changed, 171 insertions(+), 37 deletions(-) rename clusters/atlas/flux-system/applications/{communication => comms}/kustomization.yaml (94%) create mode 100644 services/comms/NOTES.md delete mode 100644 services/comms/mas-db-secret.yaml diff --git a/clusters/atlas/flux-system/applications/communication/kustomization.yaml b/clusters/atlas/flux-system/applications/comms/kustomization.yaml similarity index 94% rename from clusters/atlas/flux-system/applications/communication/kustomization.yaml rename to clusters/atlas/flux-system/applications/comms/kustomization.yaml index ab2e7d8..0fb664a 100644 --- a/clusters/atlas/flux-system/applications/communication/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/comms/kustomization.yaml @@ -2,7 +2,7 @@ apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: - name: communication + name: comms namespace: flux-system spec: interval: 10m diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index d8e27af..4c9fb58 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -5,7 +5,7 @@ resources: - gitea/kustomization.yaml - vault/kustomization.yaml - vaultwarden/kustomization.yaml - - communication/kustomization.yaml + - comms/kustomization.yaml - crypto/kustomization.yaml - monerod/kustomization.yaml - pegasus/kustomization.yaml diff --git a/services/comms/NOTES.md b/services/comms/NOTES.md new file mode 100644 index 0000000..e6868fe --- /dev/null +++ b/services/comms/NOTES.md @@ -0,0 +1,32 @@ +# services/comms/NOTES.md + +Purpose: Matrix + Element + LiveKit stack for Othrys (live.bstein.dev). + +Core flow +- Matrix Authentication Service (MAS) handles login/SSO and issues Matrix access tokens. +- Synapse is the homeserver; MAS fronts login, Synapse serves client/server APIs. +- Element Web provides the main UI; Element Call embeds LiveKit for group video. +- LiveKit handles SFU media; Coturn provides TURN for NAT traversal. +- matrix-guest-register provides guest accounts + guest sessions (no Keycloak). + +Operational jobs +- mas-db-ensure-job: ensures MAS database role/database + secret in comms. +- comms-secrets-ensure-job: creates runtime secrets (TURN, LiveKit, Synapse, atlasbot). +- synapse-signingkey-ensure-job: ensures Synapse signing key secret. +- synapse-seeder-admin-ensure-job: ensures Synapse admin user exists. +- synapse-user-seed-job: seeds atlasbot + othrys-seeder users/passwords. +- mas-local-users-ensure-job: ensures MAS local users exist (seeder/bot). +- seed-othrys-room: (suspended) creates Othrys + joins locals. +- reset-othrys-room: one-off room reset + pin invite. +- pin-othrys-invite: (suspended) pin invite message if missing. +- guest-name-randomizer: renames numeric/guest users to adj-noun names. +- bstein-force-leave: one-off room leave cleanup. + +Manual re-runs +- Bump the job name suffix (e.g., reset-othrys-room-9) to re-run a one-off job. +- Unsuspend a CronJob only when needed; re-suspend after completion. + +Ports +- Traefik (HTTPS) via LB on 192.168.22.9. +- Coturn LB on 192.168.22.5 (3478/5349 + UDP range). +- LiveKit LB on 192.168.22.6 (7880/7881/7882/7883). diff --git a/services/comms/guest-name-job.yaml b/services/comms/guest-name-job.yaml index 10dde37..f3ea00b 100644 --- a/services/comms/guest-name-job.yaml +++ b/services/comms/guest-name-job.yaml @@ -7,6 +7,8 @@ metadata: spec: schedule: "*/1 * * * *" suspend: false + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 jobTemplate: spec: backoffLimit: 0 @@ -30,6 +32,8 @@ spec: env: - name: SYNAPSE_BASE value: http://othrys-synapse-matrix-synapse:8008 + - name: AUTH_BASE + value: http://matrix-authentication-service:8080 - name: MAS_ADMIN_CLIENT_ID value: 01KDXMVQBQ5JNY6SEJPZW6Z8BM - name: MAS_ADMIN_CLIENT_SECRET_FILE @@ -40,6 +44,11 @@ spec: value: http://matrix-authentication-service:8080/oauth2/token - name: SEEDER_USER value: othrys-seeder + - name: SEEDER_PASS + valueFrom: + secretKeyRef: + name: atlasbot-credentials-runtime + key: seeder-password command: - /bin/sh - -c @@ -66,11 +75,13 @@ spec: ] BASE = os.environ["SYNAPSE_BASE"] + AUTH_BASE = os.environ.get("AUTH_BASE", BASE) MAS_ADMIN_CLIENT_ID = os.environ["MAS_ADMIN_CLIENT_ID"] MAS_ADMIN_CLIENT_SECRET_FILE = os.environ["MAS_ADMIN_CLIENT_SECRET_FILE"] MAS_ADMIN_API_BASE = os.environ["MAS_ADMIN_API_BASE"].rstrip("/") MAS_TOKEN_URL = os.environ["MAS_TOKEN_URL"] SEEDER_USER = os.environ["SEEDER_USER"] + SEEDER_PASS = os.environ["SEEDER_PASS"] ROOM_ALIAS = "#othrys:live.bstein.dev" def mas_admin_token(): @@ -126,6 +137,19 @@ spec: timeout=30, ) + def login(user, password): + r = requests.post( + f"{AUTH_BASE}/_matrix/client/v3/login", + json={ + "type": "m.login.password", + "identifier": {"type": "m.id.user", "user": user}, + "password": password, + }, + timeout=30, + ) + r.raise_for_status() + return r.json()["access_token"] + def resolve_alias(token, alias): headers = {"Authorization": f"Bearer {token}"} enc = urllib.parse.quote(alias) @@ -167,6 +191,23 @@ spec: break return users + def synapse_list_users(token): + headers = {"Authorization": f"Bearer {token}"} + users = [] + from_token = None + while True: + url = f"{BASE}/_synapse/admin/v2/users?local=true&deactivated=false&limit=100" + if from_token: + url += f"&from={urllib.parse.quote(from_token)}" + r = requests.get(url, headers=headers, timeout=30) + r.raise_for_status() + payload = r.json() + users.extend(payload.get("users", [])) + from_token = payload.get("next_token") + if not from_token: + break + return users + def user_id_for_username(username): return f"@{username}:live.bstein.dev" @@ -176,6 +217,18 @@ spec: r.raise_for_status() return r.json().get("displayname") + def get_displayname_admin(token, user_id): + headers = {"Authorization": f"Bearer {token}"} + r = requests.get( + f"{BASE}/_synapse/admin/v2/users/{urllib.parse.quote(user_id)}", + headers=headers, + timeout=30, + ) + if r.status_code == 404: + return None + r.raise_for_status() + return r.json().get("displayname") + def set_displayname(token, room_id, user_id, name, in_room): headers = {"Authorization": f"Bearer {token}"} payload = {"displayname": name} @@ -191,6 +244,25 @@ spec: content = {"membership": "join", "displayname": name} requests.put(state_url, headers=headers, json=content, timeout=30) + def set_displayname_admin(token, user_id, name): + headers = {"Authorization": f"Bearer {token}"} + payload = {"displayname": name} + r = requests.put( + f"{BASE}/_synapse/admin/v2/users/{urllib.parse.quote(user_id)}", + headers=headers, + json=payload, + timeout=30, + ) + if r.status_code in (200, 201, 204): + return True + return False + + def needs_rename_username(username): + return username.isdigit() or username.startswith("guest-") + + def needs_rename_display(display): + return not display or display.isdigit() or display.startswith("guest-") + admin_token = mas_admin_token() seeder_id = mas_user_id(admin_token, SEEDER_USER) seeder_token, seeder_session = mas_personal_session(admin_token, seeder_id) @@ -201,19 +273,22 @@ spec: mas_revoke_session(admin_token, seeder_session) users = mas_list_users(admin_token) + mas_usernames = set() for user in users: attrs = user.get("attributes") or {} username = attrs.get("username") or "" + if username: + mas_usernames.add(username) legacy_guest = attrs.get("legacy_guest") if not username: continue - if not (legacy_guest or username.isdigit() or username.startswith("guest-")): + if not (legacy_guest or needs_rename_username(username)): continue user_id = user_id_for_username(username) access_token, session_id = mas_personal_session(admin_token, user["id"]) try: display = get_displayname(access_token, user_id) - if display and (not display.isdigit()) and (not display.startswith("guest-")): + if display and not needs_rename_display(display): continue new = None for _ in range(30): @@ -227,4 +302,30 @@ spec: set_displayname(access_token, room_id, user_id, new, user_id in members) finally: mas_revoke_session(admin_token, session_id) + + seeder_token = login(SEEDER_USER, SEEDER_PASS) + for entry in synapse_list_users(seeder_token): + user_id = entry.get("name") or "" + if not user_id.startswith("@"): + continue + localpart = user_id.split(":", 1)[0].lstrip("@") + if localpart in mas_usernames: + continue + is_guest = entry.get("is_guest") + if not (is_guest or needs_rename_username(localpart)): + continue + display = get_displayname_admin(seeder_token, user_id) + if display and not needs_rename_display(display): + continue + new = None + for _ in range(30): + candidate = f"{random.choice(ADJ)}-{random.choice(NOUN)}" + if candidate not in existing: + new = candidate + existing.add(candidate) + break + if not new: + continue + if not set_displayname_admin(seeder_token, user_id, new): + continue PY diff --git a/services/comms/guest-register-configmap.yaml b/services/comms/guest-register-configmap.yaml index ded54ec..b5bc803 100644 --- a/services/comms/guest-register-configmap.yaml +++ b/services/comms/guest-register-configmap.yaml @@ -27,8 +27,16 @@ data: RATE_MAX = int(os.environ.get("RATE_MAX", "30")) _rate = {} # ip -> [window_start, count] - ADJ = ["brisk", "calm", "eager", "gentle", "merry", "nifty", "rapid", "sunny", "witty", "zesty"] - NOUN = ["otter", "falcon", "comet", "ember", "grove", "harbor", "meadow", "raven", "river", "summit"] + ADJ = [ + "brisk","calm","eager","gentle","merry","nifty","rapid","sunny","witty","zesty", + "amber","bold","bright","crisp","daring","frosty","glad","jolly","lively","mellow", + "quiet","ripe","serene","spry","tidy","vivid","warm","wild","clever","kind", + ] + NOUN = [ + "otter","falcon","comet","ember","grove","harbor","meadow","raven","river","summit", + "breeze","cedar","cinder","cove","delta","forest","glade","lark","marsh","peak", + "pine","quartz","reef","ridge","sable","sage","shore","thunder","vale","zephyr", + ] def _json(method, url, *, headers=None, body=None, timeout=20): hdrs = {"Content-Type": "application/json"} diff --git a/services/comms/kustomization.yaml b/services/comms/kustomization.yaml index 206dda1..9f95958 100644 --- a/services/comms/kustomization.yaml +++ b/services/comms/kustomization.yaml @@ -4,41 +4,41 @@ kind: Kustomization namespace: comms resources: - namespace.yaml - - atlasbot-rbac.yaml - - synapse-rendered.yaml - - synapse-signingkey-ensure-job.yaml - - synapse-seeder-admin-ensure-job.yaml - mas-configmap.yaml - - mas-admin-client-secret-ensure-job.yaml - - mas-secrets-ensure-rbac.yaml - - comms-secrets-ensure-rbac.yaml - - mas-db-ensure-rbac.yaml - - mas-db-ensure-job.yaml - - comms-secrets-ensure-job.yaml - - synapse-user-seed-job.yaml - - mas-local-users-ensure-job.yaml - - mas-deployment.yaml - element-rendered.yaml - livekit-config.yaml - - livekit.yaml - - coturn.yaml - - livekit-token-deployment.yaml - - livekit-ingress.yaml - - livekit-middlewares.yaml - element-call-config.yaml - element-call-deployment.yaml - - reset-othrys-room-job.yaml - - bstein-force-leave-job.yaml - - pin-othrys-job.yaml - - guest-name-job.yaml - guest-register-configmap.yaml - guest-register-deployment.yaml - guest-register-service.yaml - - matrix-ingress.yaml - atlasbot-configmap.yaml - atlasbot-deployment.yaml - - seed-othrys-room.yaml - wellknown.yaml + - atlasbot-rbac.yaml + - mas-secrets-ensure-rbac.yaml + - comms-secrets-ensure-rbac.yaml + - mas-db-ensure-rbac.yaml + - mas-admin-client-secret-ensure-job.yaml + - mas-db-ensure-job.yaml + - comms-secrets-ensure-job.yaml + - synapse-signingkey-ensure-job.yaml + - synapse-seeder-admin-ensure-job.yaml + - synapse-user-seed-job.yaml + - mas-local-users-ensure-job.yaml + - synapse-rendered.yaml + - mas-deployment.yaml + - livekit-token-deployment.yaml + - livekit.yaml + - coturn.yaml + - seed-othrys-room.yaml + - guest-name-job.yaml + - pin-othrys-job.yaml + - reset-othrys-room-job.yaml + - bstein-force-leave-job.yaml + - livekit-ingress.yaml + - livekit-middlewares.yaml + - matrix-ingress.yaml patches: - path: synapse-deployment-strategy-patch.yaml diff --git a/services/comms/mas-db-secret.yaml b/services/comms/mas-db-secret.yaml deleted file mode 100644 index 21b408d..0000000 --- a/services/comms/mas-db-secret.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# services/comms/mas-db-secret.yaml -apiVersion: v1 -kind: Secret -metadata: - name: mas-db - namespace: comms -type: Opaque