diff --git a/hosts/goldberg/configuration.nix b/hosts/goldberg/configuration.nix index 874d947..df29403 100644 --- a/hosts/goldberg/configuration.nix +++ b/hosts/goldberg/configuration.nix @@ -1,10 +1,11 @@ -{ lib, pkgs, baseDomain, ... }: { +{ lib, pkgs, config, ... }: { cj.deployment.environment = "dev"; imports = [ ./hardware-config.nix ../../services/mumble.nix ../../services/website.nix + ../../services/matrix.nix ../../services/vaultwarden.nix ../../services/dokuwiki.nix ../../services/freescout.nix @@ -32,4 +33,13 @@ registerPassword = lib.mkForce ""; environmentFile = lib.mkForce null; }; + + # This is specific to every host! + systemd.mounts = [{ + what = "/dev/disk/by-id/scsi-0HC_Volume_27793580"; + where = config.services.matrix-synapse.settings.media_store_path; + type = "ext4"; + options = "discard,nofail,defaults"; + wantedBy = [ "multi-user.target" ]; + }]; } diff --git a/packages/default.nix b/packages/default.nix index 2e475b0..c630964 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -145,4 +145,35 @@ final: prev: installPhase = "mkdir -p $out; cp -R * $out/"; }; }; + + pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [( + pfinal: pprev: { + matrix-synapse-saml-mapper = pfinal.buildPythonPackage { + pname = "matrix-synapse-saml-mapper"; + version = "2020-09-21"; + + postPatch = '' + substituteInPlace setup.py \ + --replace "attr>=0.3.1" "attrs" + ''; + + src = final.fetchFromGitHub { + owner = "chaos-jetzt"; + repo = "matrix-synapse-saml-mapper"; + rev = "1aca2bfc73568a1a25d4e63a52b7a8ea9bdb7272"; + hash = "sha256-mieJ8ECYr0hiniMHSnEbQAi/W9x1lsAMqV12qHtql5E="; + leaveDotGit = true; + }; + + nativeBuildInputs = with pfinal; [ + setuptools-scm + final.git + ]; + propagatedBuildInputs = with pfinal; [ + pysaml2 + attrs + final.matrix-synapse + ]; + }; + })]; } diff --git a/services/matrix.nix b/services/matrix.nix new file mode 100644 index 0000000..c93793e --- /dev/null +++ b/services/matrix.nix @@ -0,0 +1,159 @@ +{ lib, config, pkgs, baseDomain, ... }: let + matrixPort = 8008; + isDev = (builtins.substring 0 3 baseDomain) == "dev"; + synapseDb = config.services.matrix-synapse.settings.database.args; + initSynapseDb = ''CREATE DATABASE "${synapseDb.database}" WITH OWNER "${synapseDb.user}" ENCODING "UTF8" TEMPLATE template0 LC_COLLATE = "C" LC_CTYPE = "C";''; +in { + sops.secrets = { + "coturn_static_auth_secret".owner = "turnserver"; + "synapse/signing_key" = { + owner = "matrix-synapse"; + path = config.services.matrix-synapse.settings.signing_key_path; + mode = "0600"; + }; + "synapse/secret_config".owner = "matrix-synapse"; + }; + + services.nginx.virtualHosts = { + "chat.${baseDomain}" = { + enableACME = true; + forceSSL = true; + + root = pkgs.element-web.override { + # Somewhat duplicate of the stuff in website.nix but I am + # not sure if we absolutely need to dedup this, just out of complexity perspective + conf.default_server_config."m.homeserver".base_url = "https://matrix.${baseDomain}/"; + }; + }; + "matrix.${baseDomain}" = { + enableACME = true; + forceSSL = true; + # It's also possible to do a redirect here or something else, this vhost is not + # needed for Matrix. It's recommended though to *not put* element + # here, see also the section about Element. + locations."/".extraConfig = '' + return 404; + ''; + # Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash + # *must not* be used here. + locations."/_matrix".proxyPass = "http://[::1]:${toString matrixPort}"; + # Forward requests for e.g. SSO and password-resets. + locations."/_synapse/client".proxyPass = "http://[::1]:${toString matrixPort}"; + }; + }; + + services.postgresql = { + enable = true; + ensureUsers = [ + { name = synapseDb.user; } + ]; + }; + systemd.services.postgresql = { + postStart = '' + $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = 'matrix-synapse'" | grep -q 1 || $PSQL -tAc '${initSynapseDb}' + ''; + }; + + security.acme.certs."turn.${baseDomain}" = { + group = "turnserver"; + reloadServices = [ "coturn.service" ]; + }; + services.coturn = let + sslDir = config.security.acme.certs."turn.${baseDomain}".directory; + in { + enable = true; + cert = "${sslDir}/fullchain.pem"; + pkey = "${sslDir}/key.pem"; + static-auth-secret-file = config.sops.secrets."coturn_static_auth_secret".path; + }; + + # TODO: Use media storage volume on prod + services.matrix-synapse = { + enable = true; + plugins = [ + pkgs.python3Packages.matrix-synapse-saml-mapper + ]; + settings = { + server_name = baseDomain; + public_baseurl = "https://matrix.${baseDomain}"; + allow_public_rooms_over_federation = true; + enable_registration = false; + database = { + name = "psycopg2"; + args.database = "matrix-synapse"; + }; + federation_ip_range_blacklist = [ + "127.0.0.0/8" + "10.0.0.0/8" + "172.16.0.0/12" + "192.168.0.0/16" + "100.64.0.0/10" + "169.254.0.0/16" + "::1/128" + "fe80::/64" + "fc00::/7" + ]; + + additional_resources."/_matrix/saml2/pick_username".module = "matrix_synapse_saml_mapper.pick_username_resource"; + admin_contact = "mailto:administration@chaos.jetzt"; + url_preview_enabled = true; + media_store_path = "/mnt/synapse_media_store"; + turn_uris = let + turn_base = "turn.${baseDomain}"; + ct = config.services.coturn; + port = builtins.toString ct.listening-port; + tlsPort = builtins.toString ct.tls-listening-port; + in [ + "turn:${turn_base}:${port}?transport=tcp" + "turn:${turn_base}:${port}?transport=udp" + "turns:${turn_base}:${tlsPort}?transport=tcp" + "turns:${turn_base}:${tlsPort}?transport=udp" + ]; + auto_join_rooms = builtins.map (v: "#${v}:${baseDomain}") [ "grosse_halle" "allgemein" ]; + autocreate_auto_join_rooms = true; + enable_metrics = false; + user_directory = { + enabled = true; + search_all_users = true; + }; + saml2_config = { + enabled = true; + sp_config.metadata.remote = [{ + url = "https://sso.chaos.jetzt/auth/realms/${if isDev then "dev" else "chaos-jetzt"}/protocol/saml/descriptor"; + }]; + user_mapping_provider.module = "matrix_synapse_saml_mapper.SamlMappingProvider"; + }; + password_config.enabled = false; + }; + extraConfigFiles = let + format = (pkgs.formats.yaml {}).generate; + in [ + # Contains turn_shared_secret, macaroon_secret_key, and form_secret + config.sops.secrets."synapse/secret_config".path + # For our saml sso stuff we need to have additional_ressouces, but they are not possible with the NixOS module listener + (format "additional_ressources.yaml" { + listeners = [{ + bind_addresses = [ "::1" "127.0.0.1" ]; + port = matrixPort; + type = "http"; + tls = false; + x_forwarded = true; + resources = [{ + names = [ "client" "federation" ]; + compress = false; + }]; + additional_resources."/_matrix/saml2/pick_username".module = "matrix_synapse_saml_mapper.pick_username_resource"; + }]; + }) + ]; + }; + + system.activationScripts."synapse-media-store-mnt".text = '' + mkdir -p ${lib.escapeShellArg config.services.matrix-synapse.settings.media_store_path} + chown matrix-synapse:matrix-synapse ${lib.escapeShellArg config.services.matrix-synapse.settings.media_store_path} + ''; + systemd.services.matrix-synapse = { + unitConfig.RequiresMountsFor = [ config.services.matrix-synapse.settings.media_store_path ]; + serviceConfig.ReadWritePaths = [ config.services.matrix-synapse.settings.media_store_path ]; + }; +}