diff --git a/machines/gerd.nix b/machines/gerd.nix index 5dc34e0..59c791c 100644 --- a/machines/gerd.nix +++ b/machines/gerd.nix @@ -19,6 +19,8 @@ ./gerd/services/cyberchef.nix ./gerd/services/nextcloud.nix ./gerd/services/stalwart + + ./gerd/services/matrix-synapse.nix ]; networking.hostName = "gerd"; @@ -33,6 +35,7 @@ "safe/svcs/hedgedoc" = { mountpoint = "/srv/hedgedoc"; extra.options.quota = "5G"; }; "safe/svcs/nextcloud" = { mountpoint = "/srv/nextcloud"; extra.options.quota = "5G"; }; "safe/svcs/stalwart" = { mountpoint = "/srv/stalwart"; extra.options.quota = "5G"; }; + "safe/svcs/synapse" = { mountpoint = "/srv/synapse"; extra.options.quota = "5G"; }; "safe/svcs/postgresql" = { mountpoint = "/srv/postgresql"; extra.options.quota = "5G"; }; "backup/postgresql" = { mountpoint = "/media/backup/postgresqlbackup"; extra.options.quota = "5G"; }; }; diff --git a/machines/gerd/services/matrix-synapse.nix b/machines/gerd/services/matrix-synapse.nix new file mode 100644 index 0000000..032041d --- /dev/null +++ b/machines/gerd/services/matrix-synapse.nix @@ -0,0 +1,182 @@ +{ config, lib, pkgs, ... }: + +let + svc_domain = "matrix.${config.mine.shared.settings.domain}"; + + max_upload_size = "50M"; + + name = "matrix-synapse"; + + matrix_synapse_user = name; + matrix_synapse_group = name; + + oidcConfigPath = "/run/${name}/oidc.yaml"; + + stateDir = config.mine.zfsMounts."rpool/safe/svcs/synapse"; + matrix_port = 8448; +in { + services.matrix-synapse = { + enable = true; + dataDir = stateDir; + + extras = [ "oidc" ]; + + extraConfigFiles = [ + oidcConfigPath + ]; + + settings = { + server_name = config.mine.shared.settings.domain; + public_baseurl = "https://${svc_domain}"; + + enable_registration = false; + password_config.enabled = false; + + listeners = [ + { + port = matrix_port; + bind_addresses = [ "localhost" ]; + x_forwarded = true; + tls = false; + resources = [{ + names = [ "federation" "client" ]; + compress = false; + }]; + } + ]; + + # database + # shut up shut up shut up shut up!!! + database.allow_unsafe_locale = true; + database_type = "psycopg2"; + database_args.database = "matrix-synapse"; + + # increase max_upload_size, otherwise might not + # be able to watch cat videos + max_upload_size = max_upload_size; + + # retentien policies + media_retention = { + local_media_lifetime = "90d"; + remote_media_lifetime = "14d"; + }; + + # keep messages MIN 1 day, MAX 1 year + retention = { + enabled = true; + default_policy = { + min_lifetime = "1d"; + max_lifetime = "1y"; + }; + allowed_lifetime_min = "1d"; + allowed_lifetime_max = "1y"; + + }; + + }; + }; + + # setup OIDC/OpenID/OAUTH2/whatever for synapse + # do this way, instead of having EVERYTHING in a secret file. + systemd.services.matrix-synapse = { + # https://linux-audit.com/systemd/systemd-syscall-filtering/ + # https://github.com/restic/rest-server/pull/249 + # https://github.com/golang/go/issues/46279 + serviceConfig.SystemCallFilter = [ "setrlimit" ]; + serviceConfig.EnvironmentFile = config.age.secrets.matrix-synapse-config-authelia-secret.path; + preStart = let + oidc_config = (pkgs.formats.yaml {}).generate "matrix-synapse-oidc-config" { + oidc_providers = [{ + idp_id = "authelia"; + idp_name = "Authelia"; + idp_icon = "mxc://authelia.com/cKlrTPsGvlpKxAYeHWJsdVHI"; + client_id = "synapse"; + client_secret = "$CLIENT_SECRET"; + issuer = "https://${config.mine.shared.settings.authelia.domain}"; + discover = true; + + scopes = [ + "openid" + "profile" + "email" + ]; + + user_mapping_provider.config = { + subject_claim = "sub"; + localpart_template = "{{ user.preferred_username }}"; + display_name_template = "{{ user.name }}"; + email_template = "{{ user.email }}"; + }; + }]; + }; + in '' + ${pkgs.envsubst}/bin/envsubst \ + -o ${oidcConfigPath} \ + -i ${oidc_config} + ''; + }; + + # setup for oidc + services.authelia.instances.main.settings.identity_providers.oidc.clients = [{ + client_id = "synapse"; + client_name = "Synapse"; + client_secret = "$pbkdf2-sha512$310000$dO6Qohc.TolD5kFu5bOiOg$O4Bt519VVDDHeUkmH4vzu0/ea/T7H9rCTgjIyLl7mx9CDZ/TP8ZQtbuOKMjKv8anBKs85a/nTS4b2S9SG7mnkg"; + redirect_uris = [ "https://${svc_domain}/_synapse/client/oidc/callback" ]; + scopes = [ + "openid" + "profile" + "email" + ]; + }]; + + # ensure databases + user is setup + services.postgresql = { + ensureDatabases = [ matrix_synapse_user ]; + ensureUsers = [{ + name = matrix_synapse_user; + ensureDBOwnership = true; + }]; + }; + + services.nginx = { + virtualHosts."${svc_domain}" = { + enableACME = true; + forceSSL = true; + + # only proxy `_matrix` and `_synapse/client`, it is not ideal to proxy `_synapse/admin` + locations."~ ^(/_matrix|/_synapse/client)" = { + proxyPass = "http://localhost:${builtins.toString matrix_port}"; + + extraConfig = "client_max_body_size ${max_upload_size};"; + }; + }; + + # serve `.well-known/matrix/{server,client}` because matrix synapse runs on subdomain + virtualHosts."${config.mine.shared.settings.domain}" = let + client = { + "m.homeserver" = { "base_url" = "https://${svc_domain}"; }; + "m.identity_server" = { "base_url" = "https://matrix.org"; }; + }; + server = { "m.server" = "${svc_domain}:443"; }; + in { + locations."/.well-known/matrix/server".extraConfig = '' + add_header Content-Type application/json; + return 200 '${builtins.toJSON server}'; + ''; + + locations."/.well-known/matrix/client".extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON client}'; + ''; + }; + }; + + systemd.tmpfiles.rules = [ + "Z ${stateDir} 0770 ${matrix_synapse_user} ${matrix_synapse_group} -" + ]; + + age.secrets = { + matrix-synapse-config-authelia-secret.owner = matrix_synapse_user; + }; +} diff --git a/secrets/default.nix b/secrets/default.nix index bb514ff..458e408 100644 --- a/secrets/default.nix +++ b/secrets/default.nix @@ -35,6 +35,9 @@ # stalwart stalwart-admin-fallback-password.file = ./stalwart/admin-fallback-password.age; + + # matrix-synapse + matrix-synapse-config-authelia-secret.file = ./matrix-synapse/config-authelia-secret.age; }; users.groups.secrets-lldap-bind-user-pass = {}; diff --git a/secrets/matrix-synapse/config-authelia-secret.age b/secrets/matrix-synapse/config-authelia-secret.age new file mode 100644 index 0000000..59dc463 --- /dev/null +++ b/secrets/matrix-synapse/config-authelia-secret.age @@ -0,0 +1,10 @@ +age-encryption.org/v1 +-> ssh-ed25519 QSDXqg 0jVC1Y1IcRGcf1MyzlKhRRmIly5kZJVvwS7ENtyAhCs +dBX55nxL2QPRweNhA6SaEJCp6WC4Sez3c8g/Ms72uis +-> ssh-ed25519 n8n9DQ 2OdafHqXFjQPRcyb2aE0WTrx8mDnT0i0ggbQ5rOlGiE +RYs11Z+3ZhqTAH8GtLNiJuaIHco7eM0eV+pvzVI5Mkc +-> ssh-ed25519 BTp6UA xNy6a5bOAlNUosHZ8+vLYys6WSqREbYaOk42oruiVzc +NSYAozntUTDf1no5zXTFu+37r9O+5Fixjxod8/6NwAw +--- /nM6Wr/EQ+XwrVgRWKRbbKourneNaexwSdX0GkfLMZo + +m^.ǻN۴81::Ο 6v5өI_.xks%ŗr ofyםq'-v(W9(:EN?ʇZO֕ \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 4f6006e..c046626 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -39,4 +39,7 @@ in # mailserver/stalwart "stalwart/admin-fallback-password.age".publicKeys = defaultAccess; + + # matrix-synapse + "matrix-synapse/config-authelia-secret.age".publicKeys = defaultAccess; }