diff --git a/hosts/vm/neo/default.nix b/hosts/vm/neo/default.nix index b85a37c..8416710 100644 --- a/hosts/vm/neo/default.nix +++ b/hosts/vm/neo/default.nix @@ -6,11 +6,12 @@ ./networking.nix ../../../modules + ../../../modules/services/matrix.nix ]; boot.loader.grub.devices = [ "/dev/sda" ]; networking.hostName = "neo"; - system.stateVersion = "23.11"; + system.stateVersion = "24.11"; } diff --git a/hosts/vm/neo/hardware-configuration.nix b/hosts/vm/neo/hardware-configuration.nix index be3f8eb..065d77c 100644 --- a/hosts/vm/neo/hardware-configuration.nix +++ b/hosts/vm/neo/hardware-configuration.nix @@ -1,6 +1,3 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. { config, lib, @@ -27,7 +24,7 @@ boot.extraModulePackages = [ ]; fileSystems."/" = { - device = "/dev/disk/by-uuid/89589639-21f1-4899-97e9-d1de6eb16d45"; + device = "/dev/disk/by-uuid/d7e64c03-51b3-415c-8e6f-241a996b16f5"; fsType = "ext4"; }; diff --git a/hosts/vm/neo/networking.nix b/hosts/vm/neo/networking.nix index f9139aa..363ead7 100644 --- a/hosts/vm/neo/networking.nix +++ b/hosts/vm/neo/networking.nix @@ -4,11 +4,10 @@ networking = { interfaces = { ens18 = { - ipv4 = { addresses = [ { - address = "172.16.10.137"; + address = "172.16.10.141"; prefixLength = 24; } ]; @@ -17,20 +16,18 @@ ipv6 = { addresses = [ { - address = "fd00::10:0:ff:fe01:3710"; + address = "fd00::10:0:ff:fe01:4110"; prefixLength = 64; } ]; }; - }; ens19 = { - ipv4 = { addresses = [ { - address = "185.230.79.38"; + address = "185.230.79.5"; prefixLength = 26; } ]; @@ -42,24 +39,24 @@ } ]; }; - ipv6 = { addresses = [ { - address = "2a0c:700:2::ff:fe01:3702"; - prefixLength = 64; - } - ]; - routes = [ - { - address = "::"; - via = "2a0c:700:2::ff:fe00:9902"; - prefixLength = 0; + address = "2a0c:700:2::ff:fe01:4102"; + prefixLength = 64; } ]; + routes = [{ + address = "::"; + via = "2a0c:700:2::ff:fe00:9902"; + prefixLength = 0; + }]; }; - }; }; + + firewall = { + enable = true; + }; }; } diff --git a/modules/services/acme.nix b/modules/services/acme.nix index c315344..236ddb0 100644 --- a/modules/services/acme.nix +++ b/modules/services/acme.nix @@ -12,6 +12,7 @@ email = "root@crans.org"; dnsPropagationCheck = false; }; + certs."crans.org" = { domain = "*.crans.org"; dnsProvider = "rfc2136"; diff --git a/modules/services/coturn.nix b/modules/services/coturn.nix new file mode 100644 index 0000000..8382c11 --- /dev/null +++ b/modules/services/coturn.nix @@ -0,0 +1,59 @@ +{ config, ... }: + +{ + services.coturn = { + enable = true; + no-cli = true; + no-tcp-relay = true; + min-port = 49000; + max-port = 50000; + use-auth-secret = true; + static-auth-secret-file = config.age.secrets.coturn_auth_secret.path; + realm = "crans.org"; + cert = "/var/lib/acme/crans.org/full.pem"; + pkey = "/var/lib/acme/crans.org/key.pem"; + extraConfig = '' + verbose + no-multicast-peers + denied-peer-ip=0.0.0.0-0.255.255.255 + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=100.64.0.0-100.127.255.255 + denied-peer-ip=127.0.0.0-127.255.255.255 + denied-peer-ip=169.254.0.0-169.254.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + denied-peer-ip=192.0.0.0-192.0.0.255 + denied-peer-ip=192.0.2.0-192.0.2.255 + denied-peer-ip=192.88.99.0-192.88.99.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=198.18.0.0-198.19.255.255 + denied-peer-ip=198.51.100.0-198.51.100.255 + denied-peer-ip=203.0.113.0-203.0.113.255 + denied-peer-ip=240.0.0.0-255.255.255.255 + denied-peer-ip=::1 + denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff + denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 + denied-peer-ip=100::-100::ffff:ffff:ffff:ffff + denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff + ''; + }; + + networking.firewall = { + allowedTCPPorts = [ + 3478 + 5349 + ]; + allowedUDPPorts = [ + 3478 + 5349 + ]; + allowedUDPPortRanges = [ + { + from = config.services.coturn.min-port; + to = config.services.coturn.max-port; + } + ]; + }; +} diff --git a/modules/services/matrix-appservice-irc.nix b/modules/services/matrix-appservice-irc.nix new file mode 100644 index 0000000..6352c7c --- /dev/null +++ b/modules/services/matrix-appservice-irc.nix @@ -0,0 +1,198 @@ +{ config +, pkgs +, lib +, ... +}: + +let + cfg = config.services.matrix-appservice-irc; + pkg = pkgs.matrix-appservice-irc; + + # Recopié de https://github.com/NixOS/nixpkgs/blob/nixos-24.11/nixos/modules/services/matrix/appservice-irc.nix + # Permet de ne pas avoir un secret dans le store + matrix-appservice-irc-config-file = + pkgs.runCommand "matrix-appservice-irc.yml" + { + nativeBuildInputs = [ + (pkgs.python3.withPackages (ps: [ ps.jsonschema ])) + pkgs.remarshal + ]; + preferLocalBuild = true; + + config = builtins.toJSON cfg.settings; + passAsFile = [ "config" ]; + } + '' + remarshal --if yaml --of json -i ${pkg}/config.schema.yml -o config.schema.json + # desactive le check sinon on a des probleme avec envsubst + # python -m jsonschema config.schema.json -i $configPath + cp "$configPath" "$out" + ''; + + configFile = "/var/lib/matrix-appservice-irc/config.yaml"; + registrationFile = "/var/lib/matrix-appservice-irc/registration.yml"; + bin = "${pkg}/bin/matrix-appservice-irc"; +in + +{ + services.matrix-appservice-irc = { + enable = true; + + registrationUrl = "http://localhost:9999"; + port = 9999; + + settings = { + homeserver = { + url = "https://matrix.crans.org:443"; + domain = "crans.org"; + + dropMatrixMessagesAfterSecs = 3000; + enablePresence = true; + }; + + database = { + engine = "postgres"; + connectionString = "$MATRIX_APPSERVICE_IRC_DB_CONNECTION_STRING"; + }; + + ircService = { + servers = { + "irc.crans.org" = { + name = "Crans"; + onlyAdditionalAddresses = false; + networkId = "crans"; + port = 6697; + ssl = true; + sslselfsign = true; + sasl = false; + allowExpiredCerts = false; + sendConnectionMessages = true; + passwordEncryptionKeyPath = "/var/lib/matrix-appservice-irc/passkey.pem"; + + modePowerMap = { + o = 50; + v = 1; + }; + + botConfig = { + enabled = false; + nick = "IrcBot"; + username = "ircbot"; + joinChannelsIfNoUsers = true; + }; + + privateMessages = { + enabled = true; + federate = true; + }; + + dynamicChannels = { + enabled = true; + createAlias = true; + publish = true; + useHomeserverDirectory = true; + joinRule = "public"; + aliasTemplate = "#irc_\$\$CHANNEL"; + }; + + membershipLists = { + enabled = true; + floodDelayMs = 100; + global = { + ircToMatrix = { + initial = true; + incremental = true; + requireMatrixJoined = true; + }; + matrixToIrc = { + initial = true; + incremental = true; + }; + }; + + ignoreIdleUsersOnStartup = { + enabled = true; + idleForHours = 720; + }; + }; + + matrixClients = { + userTemplate = "@irc_\$\$NICK"; + displayName = "\$\$NICK[irc]"; + }; + + ircClients = { + nickTemplate = "\$\$DISPLAY[m]"; + allowNickChanges = true; + maxClients = 300; + ipv6.enabled = false; + idleTimeout = 10800; + realnameFormat = "mxid"; + kickOn = { + channelJoinFailure = true; + ircConnectionFailure = true; + userQuit = true; + }; + # nombre de ligne avant de transformer un message matrix en liens pour IRC + lineLimit = 5; + }; + }; + }; + + bridgeInfoState = { + enabled = false; + initial = false; + }; + + logging = { + level = "info"; + logging = "debug.log"; + errfile = "error.log"; + toConsole = true; + maxFiles = 2; + }; + + metrics = { + enabled = false; + }; + + matrixHandler = { + eventCacheSize = 4096; + shortReplyTemplate = "\$\$NICK: \$\$REPLY"; + longReplyTemplate = "<\$\$NICK> \"\$\$ORIGINAL\" <- \$\$REPLY"; + shortReplyTresholdSeconds = 300; + }; + + mediaProxy = { + publicUrl = "https://matrix.crans.org/media"; + ttlSeconds = 2629800; # media matrix dispo ~1mois via IRC + }; + + permissions = { + "@lzebulon:crans.org" = "admin"; + "@pigeonmoelleux:crans.org" = "admin"; + }; + }; + + advanced = { + maxHttpSockets = 1000; + maxTxnSize = 10000000; + }; + }; + }; + + systemd.services = { + matrix-appservice-irc = { + path = [ pkgs.envsubst ]; + serviceConfig = { + ExecStartPre = lib.mkForce "${lib.getExe pkgs.envsubst} -i ${matrix-appservice-irc-config-file} -o ${configFile}"; + ExecStart = lib.mkForce "${bin} --config ${configFile} --file ${registrationFile} --port ${toString config.services.matrix-appservice-irc.port}"; + + EnvironmentFile = config.age.secrets.appservice_irc_db_env.path; + WorkingDirectory = "/var/lib/matrix-appservice-irc"; + + SystemCallFilter = lib.mkForce [ ]; + }; + }; + }; +} diff --git a/modules/services/matrix.nix b/modules/services/matrix.nix index e2dcc12..f2654c2 100644 --- a/modules/services/matrix.nix +++ b/modules/services/matrix.nix @@ -1,44 +1,217 @@ { config, ... }: { - services.postgresql = { - enable = true; - ensureUsers = [ - { - name = "matrix-synapse"; - ensureDBOwnership = true; - } + imports = [ + ./acme.nix + ./coturn.nix + ./matrix-appservice-irc.nix + ./nginx.nix + ]; + + age.secrets = { + ldap_synapse_password = { + file = ../../secrets/neo/ldap_synapse_password.age; + owner = "matrix-synapse"; + }; + + database_extra_config = { + file = ../../secrets/neo/database_extra_config.age; + owner = "matrix-synapse"; + }; + + note_oidc_extra_config = { + file = ../../secrets/neo/note_oidc_extra_config.age; + owner = "matrix-synapse"; + }; + + appservice_irc_db_env = { + file = ../../secrets/neo/appservice_irc_db_env.age; + }; + + coturn_auth_secret = { + file = ../../secrets/neo/coturn_auth_secret.age; + owner = "turnserver"; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ + 80 + 443 + 8008 + 8448 ]; - ensureDatabases = [ "matrix-synapse" ]; }; services.matrix-synapse = { - enable = false; + enable = true; + + plugins = with config.services.matrix-synapse.package.plugins; [ + matrix-synapse-ldap3 + ]; settings = { server_name = "crans.org"; + report_stats = false; + + public_baseurl = "https://matrix.crans.org/"; + listeners = [ { port = 8008; + tls = false; bind_addresses = [ - "127.0.0.1" "::1" + "127.0.0.1" ]; type = "http"; - tls = false; x_forwarded = true; resources = [ { - name = [ - "client" - "federation" - ]; + names = [ "client" ]; compress = true; } + { + names = [ "federation" ]; + compress = false; + } ]; } ]; + + database = { + name = "psycopg2"; + args = { + user = "synapse"; + database = "synapse"; + # Password is declared in extra config + host = "172.16.10.1"; + cp_min = 5; + cp_max = 10; + }; + }; + + modules = [ + { + module = "ldap_auth_provider.LdapAuthProviderModule"; + config = { + enabled = true; + uri = "ldap://172.16.10.157:389"; + start_tls = false; + base = "cn=Utilisateurs,dc=crans,dc=org"; + attributes = { + uid = "uid"; + mail = "mail"; + name = "sn"; + }; + bind_dn = "cn=synapse,ou=service-users,dc=crans,dc=org"; + bind_password_file = config.age.secrets.ldap_synapse_password.path; + filter = "(&(objectclass=inetOrgPerson)(objectclass=posixAccount))"; + }; + } + ]; + + turn_uris = [ + "turn:${config.services.coturn.realm}:3478?transport=udp" + "turn:${config.services.coturn.realm}:3478?transport=tcp" + ]; + turn_shared_secret = config.age.secrets.coturn_auth_secret.path; + turn_user_lifetime = "1h"; + + app_service_config_files = [ + "/var/lib/matrix-appservice-irc/registration.yml" + ]; }; + + extraConfigFiles = [ + config.age.secrets.database_extra_config.path + config.age.secrets.note_oidc_extra_config.path + ]; + + extras = [ + "oidc" + "postgres" + "systemd" + "url-preview" + "user-search" + ]; + }; + + services.nginx.virtualHosts."matrix.crans.org" = { + enableACME = true; + forceSSL = true; + + listen = [ + { + addr = "0.0.0.0"; + port = 80; + ssl = false; + } + { + addr = "[::]"; + port = 80; + ssl = false; + } + { + addr = "0.0.0.0"; + port = 443; + ssl = true; + } + { + addr = "[::]"; + port = 443; + ssl = true; + } + { + addr = "0.0.0.0"; + port = 8448; + ssl = true; + } + { + addr = "[::]"; + port = 8448; + ssl = true; + } + ]; + + locations."/_matrix" = { + proxyPass = "http://localhost:8008"; + extraConfig = '' + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + ''; + }; + + locations."/_synapse/client" = { + proxyPass = "http://localhost:8008"; + extraConfig = '' + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + ''; + }; + + locations."/_synapse/admin" = { + proxyPass = "http://localhost:8008"; + extraConfig = '' + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + ''; + }; + + locations."/media" = { + proxyPass = "http://localhost:11111"; + extraConfig = '' + rewrite ^/media(.*)$ $1 break; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + ''; + }; + + }; } diff --git a/secrets.nix b/secrets.nix index 70877b7..b14e26f 100644 --- a/secrets.nix +++ b/secrets.nix @@ -96,15 +96,20 @@ in let key = hosts.${name}; in - genAttrs [ - "restic/${name}/base-repo" - "restic/${name}/base-password" - ] [ key ] + genAttrs + [ + "restic/${name}/base-repo" + "restic/${name}/base-password" + ] + [ key ] ) ) { } (remove "thot" hostnames) // builtins.mapAttrs (name: value: { publicKeys = value.publicKeys ++ nounous; }) { "secrets/common/root.age".publicKeys = remove apprentix all; "secrets/apprentix/root.age".publicKeys = [ apprentix ]; - "secrets/neo/database-extra-config.age".publicKeys = [ neo ]; - "secrets/neo/matrix-appservice-irc-password.age".publicKeys = [ neo ]; + "secrets/neo/appservice_irc_db_env.age".publicKeys = [ neo ]; + "secrets/neo/coturn_auth_secret.age".publicKeys = [ neo ]; + "secrets/neo/database_extra_config.age".publicKeys = [ neo ]; + "secrets/neo/note_oidc_extra_config.age".publicKeys = [ neo ]; + "secrets/neo/ldap_synapse_password.age".publicKeys = [ neo ]; } diff --git a/secrets/neo/appservice_irc_db_env.age b/secrets/neo/appservice_irc_db_env.age new file mode 100644 index 0000000..2b54a13 Binary files /dev/null and b/secrets/neo/appservice_irc_db_env.age differ diff --git a/secrets/neo/coturn_auth_secret.age b/secrets/neo/coturn_auth_secret.age new file mode 100644 index 0000000..a3b608c Binary files /dev/null and b/secrets/neo/coturn_auth_secret.age differ diff --git a/secrets/neo/database_extra_config.age b/secrets/neo/database_extra_config.age new file mode 100644 index 0000000..3297a97 Binary files /dev/null and b/secrets/neo/database_extra_config.age differ diff --git a/secrets/neo/ldap_synapse_password.age b/secrets/neo/ldap_synapse_password.age new file mode 100644 index 0000000..d1b0851 --- /dev/null +++ b/secrets/neo/ldap_synapse_password.age @@ -0,0 +1,11 @@ +age-encryption.org/v1 +-> ssh-ed25519 /Gpyew GGtk6DYlauerByL2ia9uqYRRnwqwn+oeZZUfRpDzhh8 +OJ0qDoPCz5FXCXDOHJyGlcYhBRvMPIyrDuTXVR6pYiE +-> ssh-ed25519 I2EdxQ rHELcLTEsfu0sL3Aw2c290Zf9EmdOIO5gmhLS6lRMiU +AKX6RMwbLn3J1IKsjSTfxn0u/XlT0W76JKXfcfMCkqc +-> ssh-ed25519 GNhSGw LPx7cnjBfMcDwZ4hqfP6y++D2FVtlYbzMxfVkfF86hY +QjXtb0IX9wtvCw1ms4A+kG4Nx6URhIT9e2nzyRSpI0U +-> ssh-ed25519 eXMAtA sB1Ew2t6yjQoYW6OpH/bFCo5PO+a23nF/OrCrl9d+iY +73LkKS8y0bYR+hGPVjHxHc6VDZ5mscAMPfLwS+a0slo +--- B5T496c9WhW9A7EzOhy7vshIjNFgTr/kfW1mi5Cc5fc +MZϖD7Up{Z~*Xѐ \ No newline at end of file diff --git a/secrets/neo/note_oidc_extra_config.age b/secrets/neo/note_oidc_extra_config.age new file mode 100644 index 0000000..fcee140 Binary files /dev/null and b/secrets/neo/note_oidc_extra_config.age differ