forked from hswaw/hscloud
402 lines
16 KiB
Plaintext
402 lines
16 KiB
Plaintext
# Matrix server (synapse).
|
|
# This needs a secret provisioned, create with:
|
|
# ns=matrix
|
|
# kubectl -n $ns create secret generic synapse --from-literal=postgres_password=$(pwgen 24 1) --from-literal=macaroon_secret_key=$(pwgen 32 1) --from-literal=registration_shared_secret=$(pwgen 32 1)
|
|
# kubectl -n $ns create secret generic oauth2-cas-proxy --from-literal=oauth2_secret=...
|
|
#
|
|
# After starting, re-create the postgres database (because docker/postgres won't let you set ENCODING):
|
|
# postgres=# drop database synapse;
|
|
# DROP DATABASE
|
|
# postgres=# CREATE DATABASE synapse
|
|
# postgres-# ENCODING 'UTF8'
|
|
# postgres-# LC_COLLATE='C'
|
|
# postgres-# LC_CTYPE='C'
|
|
# postgres-# template=template0
|
|
# postgres-# OWNER synapse;
|
|
# CREATE DATABASE
|
|
#
|
|
# Sequencing appservices is fun. The appservice needs to run first (for
|
|
# instance, via a bootstrap job), and on startup it will spit out a
|
|
# registration file. This registration file then needs to be fed to synapse -
|
|
# this is done via specialy named secrets (appservice-X-registration, for X key
|
|
# in the appservices object).
|
|
#
|
|
# For appservice-irc instances, you can use this oneliner magic to get the
|
|
# registration YAML from logs.
|
|
# kubectl -n matrix create secret generic appservice-irc-freenode-registration --from-file=registration.yaml=<(kubectl -n matrix logs job/appservice-irc-freenode-bootstrap | tail -n +4 | sed -r 's/(.*aliases:.*)/ group_id: "+freenode:hackerspace.pl"\n\1/')
|
|
#
|
|
# For appservice-telegram instances, you can use this oneliner magic:
|
|
# kubectl -n matrix create secret generic appservice-telegram-prod-registration --from-file=registration.yaml=<(kubectl -n matrix logs job/appservice-telegram-prod-bootstrap | grep -A 100 SNIPSNIP | grep -v SNIPSNIP)
|
|
|
|
local kube = import "../../../kube/hscloud.libsonnet";
|
|
local postgres = import "../../../kube/postgres.libsonnet";
|
|
|
|
{
|
|
local top = self,
|
|
local cfg = top.cfg,
|
|
cfg:: {
|
|
namespace: error "cfg.namespace must be set",
|
|
# webDomain is the domain name at which element will run
|
|
webDomain: error "cfg.webDomain must be set",
|
|
# serverName is the server part of the MXID this homeserver will cover
|
|
serverName: error "cfg.serverName must be set",
|
|
storageClassName: "waw-hdd-redundant-3",
|
|
|
|
images: {
|
|
synapse: "matrixdotorg/synapse:v1.37.1",
|
|
riot: "vectorim/riot-web:v1.7.16",
|
|
casProxy: "registry.k0.hswaw.net/q3k/oauth2-cas-proxy:0.1.4",
|
|
appserviceIRC: "matrixdotorg/matrix-appservice-irc:release-0.17.1",
|
|
# :latest tag on 2021/04/10.
|
|
appserviceTelegram: "dock.mau.dev/tulir/mautrix-telegram@sha256:c6e25cb57e1b67027069e8dc2627338df35d156315c004a6f2b34b6aeaa79f77",
|
|
wellKnown: "registry.k0.hswaw.net/q3k/wellknown:1611960794-adbf560851a46ad0e58b42f0daad7ef19535687c",
|
|
},
|
|
|
|
# Central Authentication Scheme, a single-sign-on system. Note: this flow is now called 'SSO' in Matrix, we keep this name for legacy reasons.
|
|
# Refer to https://matrix.org/docs/spec/client_server/r0.6.1#sso-client-login
|
|
cas: {
|
|
# whether to enable the CAS proxy (ie. connect to hswaw sso via OAuth)
|
|
enable: false,
|
|
# generate client ID and secret in with your OAuth2 provider, refer to https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/
|
|
oauth2: {
|
|
clientID: error "cas.oauth2.clientID must be set",
|
|
clientSecret: error "cas.oauth2.clientSecret must be set",
|
|
scope: error "cas.oauth2.scope must be set",
|
|
authorizeURL: error "cas.oauth2.authorizeURL must be set",
|
|
tokenURL: error "cas.oauth2.tokenURL must be set",
|
|
userinfoURL: error "cas.oauth2.userinfoURL must be set",
|
|
},
|
|
},
|
|
|
|
wellKnown: false,
|
|
},
|
|
|
|
metadata(component):: {
|
|
namespace: cfg.namespace,
|
|
labels: {
|
|
"app.kubernetes.io/name": "matrix",
|
|
"app.kubernetes.io/managed-by": "kubecfg",
|
|
"app.kubernetes.io/component": component,
|
|
},
|
|
},
|
|
|
|
local ns = kube.Namespace(cfg.namespace),
|
|
|
|
postgres3: postgres {
|
|
cfg+: {
|
|
namespace: cfg.namespace,
|
|
appName: "synapse",
|
|
database: "synapse",
|
|
username: "synapse",
|
|
prefix: "waw3-",
|
|
password: { secretKeyRef: { name: "synapse", key: "postgres_password" } },
|
|
storageClassName: cfg.storageClassName,
|
|
storageSize: "100Gi",
|
|
},
|
|
},
|
|
|
|
dataVolume: kube.PersistentVolumeClaim("synapse-data-waw3") {
|
|
metadata+: top.metadata("synapse-data"),
|
|
storage:: "50Gi",
|
|
storageClass:: cfg.storageClassName,
|
|
},
|
|
|
|
// homeserver.yaml that will be used to run synapse (in synapseConfigMap).
|
|
// This is based off of //app/matrix/lib/synapse/homeserver.yaml with some fields overriden per
|
|
// deployment.
|
|
// Note this is a templated yaml - {{}}/{%%} style. This templatization is consumed by the Docker
|
|
// container startup magic.
|
|
synapseConfig:: (std.native("parseYaml"))(importstr "synapse/homeserver.yaml")[0] {
|
|
server_name: cfg.serverName,
|
|
public_baseurl: "https://%s" % [cfg.webDomain],
|
|
signing_key_path: "/data/%s.signing.key" % [cfg.serverName],
|
|
app_service_config_files: [
|
|
"/appservices/%s/registration.yaml" % [k]
|
|
for k in std.objectFields(top.appservices)
|
|
],
|
|
} + (if cfg.cas.enable then {
|
|
cas_config: {
|
|
enabled: true,
|
|
server_url: "https://%s/_cas" % [cfg.webDomain],
|
|
service_url: "https://%s" % [cfg.webDomain],
|
|
},
|
|
} else {}),
|
|
|
|
synapseConfigMap: kube.ConfigMap("synapse") {
|
|
metadata+: top.metadata("synapse"),
|
|
data: {
|
|
"homeserver.yaml": std.manifestYamlDoc(top.synapseConfig),
|
|
"log.config": importstr "synapse/log.config",
|
|
},
|
|
},
|
|
|
|
casDeployment: if cfg.cas.enable then kube.Deployment("oauth2-cas-proxy") {
|
|
metadata+: top.metadata("oauth2-cas-proxy"),
|
|
spec+: {
|
|
replicas: 1,
|
|
template+: {
|
|
spec+: {
|
|
containers_: {
|
|
proxy: kube.Container("oauth2-cas-proxy") {
|
|
image: cfg.images.casProxy,
|
|
ports_: {
|
|
http: { containerPort: 5000 },
|
|
},
|
|
env_: {
|
|
BASE_URL: "https://%s" % [cfg.webDomain],
|
|
SERVICE_URL: "https://%s" % [cfg.webDomain],
|
|
OAUTH2_CLIENT: cfg.cas.oauth2.clientID,
|
|
OAUTH2_SECRET: cfg.cas.oauth2.clientSecret,
|
|
OAUTH2_SCOPE: cfg.cas.oauth2.scope,
|
|
OAUTH2_AUTHORIZE: cfg.cas.oauth2.authorizeURL,
|
|
OAUTH2_TOKEN: cfg.cas.oauth2.tokenURL,
|
|
OAUTH2_USERINFO: cfg.cas.oauth2.userinfoURL,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
casSvc: if cfg.cas.enable then kube.Service("oauth2-cas-proxy") {
|
|
metadata+: top.metadata("oauth2-cas-proxy"),
|
|
target:: top.casDeployment,
|
|
},
|
|
|
|
synapseDeployment: kube.Deployment("synapse") {
|
|
metadata+: top.metadata("synapse"),
|
|
spec+: {
|
|
replicas: 1,
|
|
template+: {
|
|
spec+: {
|
|
volumes_: {
|
|
data: kube.PersistentVolumeClaimVolume(top.dataVolume),
|
|
config: kube.ConfigMapVolume(top.synapseConfigMap),
|
|
} + {
|
|
[k]: { secret: { secretName: "appservice-%s-registration" % [k] } }
|
|
for k in std.objectFields(top.appservices)
|
|
},
|
|
containers_: {
|
|
web: kube.Container("synapse") {
|
|
image: cfg.images.synapse,
|
|
command: ["/bin/sh", "-c", "/start.py migrate_config && exec /start.py"],
|
|
ports_: {
|
|
http: { containerPort: 8008 },
|
|
metrics: { containerPort: 9092 },
|
|
},
|
|
env_: {
|
|
SYNAPSE_CONFIG_DIR: "/tmp/config",
|
|
SYNAPSE_CONFIG_PATH: "/tmp/config/homeserver.yaml",
|
|
|
|
# These values are not used in a template, but
|
|
# are required by /start.py migrate_config
|
|
SYNAPSE_SERVER_NAME: cfg.serverName,
|
|
SYNAPSE_REPORT_STATS: "no",
|
|
|
|
SYNAPSE_MACAROON_SECRET_KEY: { secretKeyRef: { name: "synapse", key: "macaroon_secret_key" } },
|
|
SYNAPSE_REGISTRATION_SHARED_SECRET: { secretKeyRef: { name: "synapse", key: "registration_shared_secret" } },
|
|
POSTGRES_PASSWORD: { secretKeyRef: { name: "synapse", key: "postgres_password" } },
|
|
},
|
|
volumeMounts_: {
|
|
data: { mountPath: "/data" },
|
|
config: { mountPath: "/conf", },
|
|
} + {
|
|
[k]: { mountPath: "/appservices/%s" % [k] }
|
|
for k in std.objectFields(top.appservices)
|
|
},
|
|
},
|
|
},
|
|
securityContext: {
|
|
runAsUser: 991,
|
|
runAsGroup: 991,
|
|
fsGroup: 991,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
synapseSvc: kube.Service("synapse") {
|
|
metadata+: top.metadata("synapse"),
|
|
target:: top.synapseDeployment,
|
|
},
|
|
|
|
riotConfig:: {
|
|
"default_hs_url": "https://%s" % [cfg.webDomain],
|
|
"disable_custom_urls": false,
|
|
"disable_guests": false,
|
|
"disable_login_language_selector": false,
|
|
"disable_3pid_login": true,
|
|
"brand": "Riot",
|
|
"integrations_ui_url": "https://scalar.vector.im/",
|
|
"integrations_rest_url": "https://scalar.vector.im/api",
|
|
"integrations_jitsi_widget_url": "https://scalar.vector.im/api/widgets/jitsi.html",
|
|
|
|
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
|
|
"features": {
|
|
"feature_groups": "labs",
|
|
"feature_pinning": "labs",
|
|
"feature_reactions": "labs"
|
|
},
|
|
"default_federate": true,
|
|
"default_theme": "light",
|
|
"roomDirectory": {
|
|
"servers": [
|
|
cfg.serverName,
|
|
]
|
|
},
|
|
"welcomeUserId": "@riot-bot:matrix.org",
|
|
"enable_presence_by_hs_url": {
|
|
"https://matrix.org": false
|
|
}
|
|
},
|
|
|
|
riotConfigMap: kube.ConfigMap("riot-web-config") {
|
|
metadata+: top.metadata("riot-web-config"),
|
|
data: {
|
|
"config.json": std.manifestJsonEx(top.riotConfig, ""),
|
|
// Standard nginx.conf, made to work when running as unprivileged user.
|
|
"nginx.conf": |||
|
|
worker_processes auto;
|
|
|
|
error_log /tmp/nginx_error.log warn;
|
|
pid /tmp/nginx.pid;
|
|
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
|
|
http {
|
|
client_body_temp_path /tmp/nginx_client_temp;
|
|
proxy_temp_path /tmp/nginx_proxy_temp;
|
|
fastcgi_temp_path /tmp/nginx_fastcgi_temp;
|
|
uwsgi_temp_path /tmp/nginx_uwsgi_temp;
|
|
scgi_temp_path /tmp/nginx_scgi_temp;
|
|
|
|
include /etc/nginx/mime.types;
|
|
default_type application/octet-stream;
|
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
'$status $body_bytes_sent "$http_referer" '
|
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
access_log /tmp/nginx_access.log main;
|
|
sendfile on;
|
|
keepalive_timeout 65;
|
|
|
|
server {
|
|
listen 8080;
|
|
server_name localhost;
|
|
|
|
location / {
|
|
root /usr/share/nginx/html;
|
|
index index.html index.htm;
|
|
}
|
|
|
|
error_page 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root /usr/share/nginx/html;
|
|
}
|
|
}
|
|
}
|
|
|||,
|
|
},
|
|
},
|
|
|
|
riotDeployment: kube.Deployment("riot-web") {
|
|
metadata+: top.metadata("riot-web"),
|
|
spec+: {
|
|
replicas: 1,
|
|
template+: {
|
|
spec+: {
|
|
volumes_: {
|
|
config: kube.ConfigMapVolume(top.riotConfigMap),
|
|
},
|
|
containers_: {
|
|
web: kube.Container("riot-web") {
|
|
image: cfg.images.riot,
|
|
ports_: {
|
|
http: { containerPort: 8080 },
|
|
},
|
|
volumeMounts: [
|
|
{
|
|
name: "config",
|
|
mountPath: "/app/config.json",
|
|
subPath: "config.json",
|
|
},
|
|
{
|
|
name: "config",
|
|
mountPath: "/etc/nginx/nginx.conf",
|
|
subPath: "nginx.conf",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
securityContext: {
|
|
// nginx:nginx
|
|
runAsUser: 101,
|
|
runAsGroup: 101,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
riotSvc: kube.Service("riot-web") {
|
|
metadata+: top.metadata("riot-web"),
|
|
target:: top.riotDeployment,
|
|
},
|
|
|
|
wellKnown: if cfg.wellKnown then {
|
|
deployment: kube.Deployment("wellknown") {
|
|
metadata+: top.metadata("wellknown"),
|
|
spec+: {
|
|
replicas: 1,
|
|
template+: {
|
|
spec+: {
|
|
containers_: {
|
|
web: kube.Container("wellknown") {
|
|
image: cfg.images.wellKnown,
|
|
ports_: {
|
|
http: { containerPort: 8080 },
|
|
},
|
|
command: ["/app/matrix/wellknown"],
|
|
args: ["-hspki_disable", "-domain", cfg.webDomain],
|
|
},
|
|
},
|
|
securityContext: {
|
|
runAsUser: 101,
|
|
runAsGroup: 101,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
svc: kube.Service("wellknown") {
|
|
metadata+: top.metadata("wellknown"),
|
|
target:: top.wellKnown.deployment,
|
|
},
|
|
} else {},
|
|
|
|
// Any appservice you add here will require an appservice-X-registration
|
|
// secret containing a registration.yaml file. Adding something to this
|
|
// dictionary will cause Synapse to not start until that secret is
|
|
// available - so change things carefully!
|
|
// If bootstrapping a new appservice, just keep it out of this dictionary
|
|
// until it spits you a registration YAML and you feed that to a secret.
|
|
appservices: {},
|
|
|
|
ingress: kube.SimpleIngress("matrix") {
|
|
hosts:: [cfg.webDomain],
|
|
target_service:: top.riotSvc,
|
|
metadata+: top.metadata("matrix"),
|
|
extra_paths:: [
|
|
{ path: "/_matrix", backend: top.synapseSvc.name_port },
|
|
] + (if cfg.cas.enable then [
|
|
{ path: "/_cas", backend: top.casSvc.name_port },
|
|
] else []) + (if cfg.wellKnown then [
|
|
{ path: "/.well-known/matrix", backend: top.wellKnown.svc.name_port },
|
|
] else [])
|
|
},
|
|
}
|