comms: add synapse admin ensure job
This commit is contained in:
parent
39fd7adb55
commit
0ef14c67fd
@ -25,6 +25,7 @@ resources:
|
|||||||
- mas-admin-client-secret-ensure-job.yaml
|
- mas-admin-client-secret-ensure-job.yaml
|
||||||
- mas-db-ensure-job.yaml
|
- mas-db-ensure-job.yaml
|
||||||
- comms-secrets-ensure-job.yaml
|
- comms-secrets-ensure-job.yaml
|
||||||
|
- synapse-admin-ensure-job.yaml
|
||||||
- synapse-signingkey-ensure-job.yaml
|
- synapse-signingkey-ensure-job.yaml
|
||||||
- synapse-seeder-admin-ensure-job.yaml
|
- synapse-seeder-admin-ensure-job.yaml
|
||||||
- synapse-user-seed-job.yaml
|
- synapse-user-seed-job.yaml
|
||||||
|
|||||||
177
services/comms/synapse-admin-ensure-job.yaml
Normal file
177
services/comms/synapse-admin-ensure-job.yaml
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# services/comms/synapse-admin-ensure-job.yaml
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: synapse-admin-ensure-1
|
||||||
|
namespace: comms
|
||||||
|
spec:
|
||||||
|
backoffLimit: 1
|
||||||
|
ttlSecondsAfterFinished: 3600
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
serviceAccountName: comms-secrets-ensure
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: node-role.kubernetes.io/worker
|
||||||
|
operator: Exists
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
preference:
|
||||||
|
matchExpressions:
|
||||||
|
- key: kubernetes.io/arch
|
||||||
|
operator: In
|
||||||
|
values: ["arm64"]
|
||||||
|
containers:
|
||||||
|
- name: ensure
|
||||||
|
image: python:3.11-slim
|
||||||
|
env:
|
||||||
|
- name: VAULT_ADDR
|
||||||
|
value: http://vault.vault.svc.cluster.local:8200
|
||||||
|
- name: VAULT_ROLE
|
||||||
|
value: comms-secrets
|
||||||
|
- name: SYNAPSE_ADMIN_URL
|
||||||
|
value: http://othrys-synapse-matrix-synapse.comms.svc.cluster.local:8008
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
python - <<'PY'
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
VAULT_ADDR = os.environ.get("VAULT_ADDR", "http://vault.vault.svc.cluster.local:8200").rstrip("/")
|
||||||
|
VAULT_ROLE = os.environ.get("VAULT_ROLE", "comms-secrets")
|
||||||
|
SYNAPSE_ADMIN_URL = os.environ.get(
|
||||||
|
"SYNAPSE_ADMIN_URL",
|
||||||
|
"http://othrys-synapse-matrix-synapse.comms.svc.cluster.local:8008",
|
||||||
|
).rstrip("/")
|
||||||
|
SA_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||||
|
|
||||||
|
def log(msg: str) -> None:
|
||||||
|
print(msg, flush=True)
|
||||||
|
|
||||||
|
def request_json(url: str, payload: dict | None = None) -> dict:
|
||||||
|
data = None
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
if payload is not None:
|
||||||
|
data = json.dumps(payload).encode("utf-8")
|
||||||
|
req = urllib.request.Request(url, data=data, headers=headers, method="POST" if data else "GET")
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
return json.loads(resp.read().decode("utf-8"))
|
||||||
|
|
||||||
|
def vault_login() -> str:
|
||||||
|
with open(SA_TOKEN_PATH, "r", encoding="utf-8") as f:
|
||||||
|
jwt = f.read().strip()
|
||||||
|
payload = {"jwt": jwt, "role": VAULT_ROLE}
|
||||||
|
resp = request_json(f"{VAULT_ADDR}/v1/auth/kubernetes/login", payload)
|
||||||
|
token = resp.get("auth", {}).get("client_token")
|
||||||
|
if not token:
|
||||||
|
raise RuntimeError("vault login failed")
|
||||||
|
return token
|
||||||
|
|
||||||
|
def vault_get(token: str, path: str) -> dict:
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f"{VAULT_ADDR}/v1/kv/data/atlas/{path}",
|
||||||
|
headers={"X-Vault-Token": token},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
payload = json.loads(resp.read().decode("utf-8"))
|
||||||
|
return payload.get("data", {}).get("data", {})
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
if exc.code == 404:
|
||||||
|
return {}
|
||||||
|
raise
|
||||||
|
|
||||||
|
def vault_put(token: str, path: str, data: dict) -> None:
|
||||||
|
payload = {"data": data}
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f"{VAULT_ADDR}/v1/kv/data/atlas/{path}",
|
||||||
|
data=json.dumps(payload).encode("utf-8"),
|
||||||
|
headers={"X-Vault-Token": token, "Content-Type": "application/json"},
|
||||||
|
method="POST",
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
resp.read()
|
||||||
|
|
||||||
|
def random_password(length: int = 32) -> str:
|
||||||
|
alphabet = string.ascii_letters + string.digits
|
||||||
|
return "".join(secrets.choice(alphabet) for _ in range(length))
|
||||||
|
|
||||||
|
def ensure_registration_secret(token: str) -> str:
|
||||||
|
data = vault_get(token, "comms/synapse-registration")
|
||||||
|
secret = (data.get("registration_shared_secret") or "").strip()
|
||||||
|
if not secret:
|
||||||
|
secret = secrets.token_urlsafe(32)
|
||||||
|
data["registration_shared_secret"] = secret
|
||||||
|
vault_put(token, "comms/synapse-registration", data)
|
||||||
|
log("registration secret created")
|
||||||
|
return secret
|
||||||
|
|
||||||
|
def ensure_admin_creds(token: str) -> dict:
|
||||||
|
data = vault_get(token, "comms/synapse-admin")
|
||||||
|
username = (data.get("username") or "").strip() or "synapse-admin"
|
||||||
|
password = (data.get("password") or "").strip()
|
||||||
|
if not password:
|
||||||
|
password = random_password()
|
||||||
|
data["username"] = username
|
||||||
|
data["password"] = password
|
||||||
|
vault_put(token, "comms/synapse-admin", data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def register_admin(secret: str, username: str, password: str) -> str:
|
||||||
|
nonce_payload = request_json(f"{SYNAPSE_ADMIN_URL}/_synapse/admin/v1/register")
|
||||||
|
nonce = nonce_payload.get("nonce")
|
||||||
|
if not nonce:
|
||||||
|
raise RuntimeError("synapse register nonce missing")
|
||||||
|
admin_flag = "admin"
|
||||||
|
user_type = ""
|
||||||
|
mac_payload = "\x00".join([nonce, username, password, admin_flag, user_type])
|
||||||
|
mac = hmac.new(secret.encode("utf-8"), mac_payload.encode("utf-8"), hashlib.sha1).hexdigest()
|
||||||
|
payload = {
|
||||||
|
"nonce": nonce,
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"admin": True,
|
||||||
|
"mac": mac,
|
||||||
|
}
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f"{SYNAPSE_ADMIN_URL}/_synapse/admin/v1/register",
|
||||||
|
data=json.dumps(payload).encode("utf-8"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
method="POST",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
payload = json.loads(resp.read().decode("utf-8"))
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
body = exc.read().decode("utf-8")
|
||||||
|
raise RuntimeError(f"synapse admin register failed: {exc.code} {body}") from exc
|
||||||
|
access_token = payload.get("access_token")
|
||||||
|
if not access_token:
|
||||||
|
raise RuntimeError("synapse admin token missing")
|
||||||
|
return access_token
|
||||||
|
|
||||||
|
vault_token = vault_login()
|
||||||
|
reg_secret = ensure_registration_secret(vault_token)
|
||||||
|
admin_data = ensure_admin_creds(vault_token)
|
||||||
|
if admin_data.get("access_token"):
|
||||||
|
log("synapse admin token already present")
|
||||||
|
raise SystemExit(0)
|
||||||
|
access_token = register_admin(reg_secret, admin_data["username"], admin_data["password"])
|
||||||
|
admin_data["access_token"] = access_token
|
||||||
|
vault_put(vault_token, "comms/synapse-admin", admin_data)
|
||||||
|
log("synapse admin user ensured")
|
||||||
|
PY
|
||||||
@ -69,6 +69,9 @@ spec:
|
|||||||
export COMMS_BOT_PASSWORD="{{ index .Data.data "bot-password" }}"
|
export COMMS_BOT_PASSWORD="{{ index .Data.data "bot-password" }}"
|
||||||
export COMMS_SEEDER_PASSWORD="{{ index .Data.data "seeder-password" }}"
|
export COMMS_SEEDER_PASSWORD="{{ index .Data.data "seeder-password" }}"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ with secret "kv/data/atlas/comms/synapse-admin" }}
|
||||||
|
export COMMS_SYNAPSE_ADMIN_TOKEN="{{ .Data.data.access_token }}"
|
||||||
|
{{ end }}
|
||||||
{{ with secret "kv/data/atlas/comms/synapse-db" }}
|
{{ with secret "kv/data/atlas/comms/synapse-db" }}
|
||||||
export COMMS_SYNAPSE_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}"
|
export COMMS_SYNAPSE_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@ -231,7 +231,7 @@ write_policy_and_role "crypto" "crypto" "crypto-vault-sync" \
|
|||||||
write_policy_and_role "health" "health" "health-vault-sync" \
|
write_policy_and_role "health" "health" "health-vault-sync" \
|
||||||
"health/*" ""
|
"health/*" ""
|
||||||
write_policy_and_role "maintenance" "maintenance" "ariadne,maintenance-vault-sync" \
|
write_policy_and_role "maintenance" "maintenance" "ariadne,maintenance-vault-sync" \
|
||||||
"maintenance/ariadne-db portal/atlas-portal-db portal/bstein-dev-home-keycloak-admin mailu/mailu-db-secret mailu/mailu-initial-account-secret nextcloud/nextcloud-db nextcloud/nextcloud-admin health/wger-admin finance/firefly-secrets comms/mas-admin-client-runtime comms/atlasbot-credentials-runtime comms/synapse-db vault/vault-oidc-config shared/harbor-pull" ""
|
"maintenance/ariadne-db portal/atlas-portal-db portal/bstein-dev-home-keycloak-admin mailu/mailu-db-secret mailu/mailu-initial-account-secret nextcloud/nextcloud-db nextcloud/nextcloud-admin health/wger-admin finance/firefly-secrets comms/mas-admin-client-runtime comms/atlasbot-credentials-runtime comms/synapse-db comms/synapse-admin vault/vault-oidc-config shared/harbor-pull" ""
|
||||||
write_policy_and_role "finance" "finance" "finance-vault" \
|
write_policy_and_role "finance" "finance" "finance-vault" \
|
||||||
"finance/* shared/postmark-relay" ""
|
"finance/* shared/postmark-relay" ""
|
||||||
write_policy_and_role "finance-secrets" "finance" "finance-secrets-ensure" \
|
write_policy_and_role "finance-secrets" "finance" "finance-secrets-ensure" \
|
||||||
@ -253,4 +253,4 @@ write_policy_and_role "crypto-secrets" "crypto" "crypto-secrets-ensure" \
|
|||||||
write_policy_and_role "comms-secrets" "comms" \
|
write_policy_and_role "comms-secrets" "comms" \
|
||||||
"comms-secrets-ensure,mas-db-ensure,mas-admin-client-secret-writer,othrys-synapse-signingkey-job" \
|
"comms-secrets-ensure,mas-db-ensure,mas-admin-client-secret-writer,othrys-synapse-signingkey-job" \
|
||||||
"" \
|
"" \
|
||||||
"comms/turn-shared-secret comms/livekit-api comms/synapse-redis comms/synapse-macaroon comms/atlasbot-credentials-runtime comms/synapse-db comms/mas-db comms/mas-admin-client-runtime comms/mas-secrets-runtime comms/othrys-synapse-signingkey"
|
"comms/turn-shared-secret comms/livekit-api comms/synapse-redis comms/synapse-macaroon comms/atlasbot-credentials-runtime comms/synapse-db comms/synapse-admin comms/synapse-registration comms/mas-db comms/mas-admin-client-runtime comms/mas-secrets-runtime comms/othrys-synapse-signingkey"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user