diff --git a/flake.nix b/flake.nix index 77e5b4e..2187d2d 100644 --- a/flake.nix +++ b/flake.nix @@ -154,14 +154,13 @@ grimm-nixos-server-2 = customNixosSystem "x86_64-linux" { modules = [ agenix.nixosModules.default - # agenix.nixosModules.default # nixos-matrix-modules.nixosModules.default - # nixos-mailserver.nixosModules.default + nixos-mailserver.nixosModules.default ./configuration.nix ./specific/grimm-nixos-server-2/configuration.nix - # ./modules + ./modules2 home-manager.nixosModules.home-manager ./hm ]; diff --git a/modules/matrix_legacy.nix b/modules/matrix_legacy.nix index 1d20f9f..c74f23b 100644 --- a/modules/matrix_legacy.nix +++ b/modules/matrix_legacy.nix @@ -75,7 +75,10 @@ in }; }; settings.log_config = ./matrix_synapse_log_config.yaml; - settings.enable_registration = false; + settings.enable_registration = true; + services.matrix-synapse.settings.enable_metrics = false; + settings.max_upload_size = "500M"; + configureRedisLocally = true; settings.redis.enabled = true; @@ -89,48 +92,6 @@ in # "/var/lib/matrix-synapse/discord-registration.yaml" ]; }; - - # services.matrix-synapse-next = { - # enable = true; - # - # workers.federationSenders = 1; - # workers.federationReceivers = 1; - # workers.initialSyncers = 1; - # workers.normalSyncers = 1; - # workers.eventPersisters = 2; - # workers.useUserDirectoryWorker = true; - # mainLogConfig = ./matrix_synapse_log_config.yaml; - # - # enableNginx = true; - # enableSlidingSync = false; - # - # settings = { - # suppress_key_server_warning = true; - # server_name = domain; - # public_baseurl = "https://${domain}"; - # enable_registration = true; - # registration_requires_token = true; - # registration_shared_secret_path = config.age.secrets.synapse_registration_shared_secret.path; - # # enable_registration_without_verification = true; - # # mainLogConfig = ./matrix_synapse_log_config.yaml; - # - # # registrations_require_3pid = [ "email" ]; - # - # database = { - # name = "psycopg2"; - # args = { - # host = "localhost"; - # port = config.services.postgresql.settings.port; - # dbname = "synapse"; - # user = "synapse"; - # cp_min = 5; - # cp_max = 10; - # client_encoding = "auto"; - # passfile = config.age.secrets.synapse_db_pass_prepared.path; - # }; - # }; - # }; - # }; services.redis.servers."".enable = true; age.secrets.synapse_db_pass = { @@ -194,61 +155,4 @@ in locations."/_synapse/client".proxyPass = synapse_backend; }; }; - - # services.nginx = { - # enable = true; - # virtualHosts."${domain}" = { - # forceSSL = true; - # enableACME = lib.mkForce false; # use the cert above, not some weird one that matrix-synapse module supplies - # useACMEHost = domain; - # locations."/.well-known/matrix/server" = { - # return = "200 '{\"m.server\":\"${vhosts.matrix_host.host}:443\"}'"; - # extraConfig = '' - # default_type application/json; - # add_header Access-Control-Allow-Origin *; - # add_header Accept-Ranges bytes;''; - # }; - # locations."/.well-known/matrix/client" = { - # return = "200 '{\"m.homeserver\": {\"base_url\": \"https://${vhosts.matrix_host.host}\"}}'"; - # extraConfig = '' - # add_header Access-Control-Allow-Origin *; - # default_type application/json; - # ''; - # }; - # locations."/_matrix" = { - # proxyPass = "http://$synapse_backend"; - # extraConfig = '' - # add_header X-debug-backend $synapse_backend; - # add_header X-debug-group $synapse_uri_group; - # client_max_body_size ${config.services.matrix-synapse-next.settings.max_upload_size}; - # proxy_read_timeout 10m; - # ''; - # }; - # locations."/_synapse/client" = { - # proxyPass = "http://$synapse_backend"; - # }; - # locations."~ ^/_matrix/client/(r0|v3)/sync$" = { - # proxyPass = "http://$synapse_backend"; - # extraConfig = '' - # proxy_read_timeout 1h; - # ''; - # }; - # locations."~ ^/_matrix/client/(api/v1|r0|v3)/initialSync$" = { - # proxyPass = "http://synapse_worker_initial_sync"; - # extraConfig = '' - # proxy_read_timeout 1h; - # ''; - # }; - # locations."~ ^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$" = { - # proxyPass = "http://synapse_worker_initial_sync"; - # extraConfig = '' - # proxy_read_timeout 1h; - # ''; - # }; - # # locations."/.well-known/matrix" = { - # proxyPass = "http://$synapse_backend"; - # }; - # }; - # }; - # networking.firewall.allowedTCPPorts = [ 8448 8008 ]; } diff --git a/modules2/auth.nix b/modules2/auth.nix new file mode 100644 index 0000000..f6ddad7 --- /dev/null +++ b/modules2/auth.nix @@ -0,0 +1,131 @@ +{ + config, + pkgs, + lib, + ... +}: +let + inherit (config.serverConfig) vhosts; + inherit (config.networking) domain; + inherit (lib) remove concatStringsSep; +in +{ + age.secrets.openldap_admin = + let + inherit (config.services.openldap) user group; + in + { + file = ../secrets/openldap_admin.age; + inherit group; + owner = user; + mode = "0444"; + }; + + age.secrets.keycloak_db_pass = { + file = ../secrets/keycloak_db_pass.age; + group = "keycloak"; + owner = "keycloak"; + mode = "0444"; + }; + + users.users.keycloak = { + isSystemUser = true; + group = "keycloak"; + }; + users.groups.keycloak = { }; + + services.postgresql = + let + inherit (config.services.keycloak.database) name username; + in + { + enable = true; + ensureDatabases = [ name ]; + ensureUsers = [ + { + name = username; + passFile = config.age.secrets.keycloak_db_pass.path; + ensureDBOwnership = true; + } + ]; + }; + + services.keycloak = { + enable = true; + + database = { + type = "postgresql"; + createLocally = false; + + username = "keycloak"; + passwordFile = config.age.secrets.keycloak_db_pass.path; + }; + + settings = { + hostname = vhosts.auth_host.host; + http-host = "127.0.0.1"; + http-port = vhosts.auth_host.port; + proxy = "edge"; # passthrough"; + }; + }; + + services.openldap = + let + localDc = concatStringsSep "," (map (s: "dc=${s}") (remove [ ] (builtins.split "\\." domain))); + in + { + enable = true; + urlList = [ + "ldap:///" + "ldapi:///" + ]; + + # declarativeContents = { + # "${localDc}" = import ./ldapConf.nix { inherit localDc; }; + # }; + + settings = { + attrs = { + olcLogLevel = "conns config"; + }; + + children = { + "cn=schema".includes = [ + "${pkgs.openldap}/etc/schema/core.ldif" + "${pkgs.openldap}/etc/schema/cosine.ldif" + "${pkgs.openldap}/etc/schema/inetorgperson.ldif" + ]; + + "olcDatabase={1}mdb".attrs = { + objectClass = [ + "olcDatabaseConfig" + "olcMdbConfig" + ]; + + olcDatabase = "{1}mdb"; + olcDbDirectory = "/var/lib/openldap/data"; + + olcSuffix = localDc; + + olcRootDN = "cn=admin,${localDc}"; + # olcRootPW.path = config.age.secrets.openldap_admin.path; + olcRootPW = "{SSHA}D1U1E6Xz07DGYLjke1YcCsVF6ddSLyLr"; + + olcAccess = [ + # custom access rules for userPassword attributes + '' + {0}to attrs=userPassword + by self write + by anonymous auth + by * none'' + + # allow read on anything else + '' + {1}to * + by * read'' + ]; + }; + }; + }; + }; +} diff --git a/modules2/default.nix b/modules2/default.nix new file mode 100644 index 0000000..f709a6d --- /dev/null +++ b/modules2/default.nix @@ -0,0 +1,231 @@ +{ lib, config, ... }: +let + inherit (config.networking) domain; + root_email = "contact@${domain}"; +in +{ + imports = [ + ./wireguard.nix + ./matrix_legacy.nix +# ./puffer.nix + ./gitea.nix +# ./grafana.nix + ./nextcloud.nix +# ./prometheus.nix + ./fail2ban.nix + ./email.nix + ./mastodon.nix +# ./nix_cache.nix + ./hedgedoc.nix + ]; + + options.serverConfig = + let + inherit (lib) mkOption types mkEnableOption; + in + { + ports = mkOption { + type = types.attrsOf ( + types.submodule ( + { ... }: + { + options = { + port = mkOption { + type = types.int; + description = "port to define"; + }; + open = mkEnableOption "whether to open the port" // { + default = true; + }; + }; + } + ) + ); + default = { }; + description = "ports associated with services"; + }; + + vhosts = mkOption { + type = types.attrsOf ( + types.submodule ( + { config, ... }: + let + type_lookup = { + proxy = { + locations."/".proxyPass = "http://127.0.0.1:${builtins.toString config.port}"; + }; + redirect = { + locations."/".return = "307 https://${domain}"; + }; + custom = { }; + none = { }; + }; + in + { + options = { + port = mkOption { + type = types.int; + default = 80; + description = "port to redirect to this vhost"; + }; + host = mkOption { + type = types.nonEmptyStr; + description = "name if the vhost"; + }; + accessType = mkOption { + type = types.enum (lib.attrNames type_lookup); + default = "none"; + description = "nginx template to use"; + }; + extraNginx = mkOption { + type = types.attrs; + default = type_lookup.${config.accessType}; + description = "location definition for nginx"; + }; + }; + } + ) + ); + default = { }; + description = "vhosts associated with services"; + }; + }; + + config = { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ] ++ (lib.mapAttrsToList (n: v: v.port) (lib.filterAttrs (n: v: v.open) config.serverConfig.ports)); + # ++ (lib.mapAttrsToList (n: v: v.port) (lib.filterAttrs (n: v: !v.disableWebAccess) config.serverConfig.vhosts)); + + services.nginx.virtualHosts = + { + "${domain}" = { + forceSSL = true; + enableACME = lib.mkForce false; # use the correct cert, not some weird one that matrix-synapse module supplies + useACMEHost = domain; + locations."/" = { + root = "/var/www/${domain}"; + }; + }; + } + // (lib.concatMapAttrs (_: host: { + "${host.host}" = { + serverName = host.host; + forceSSL = true; + useACMEHost = domain; + enableACME = lib.mkForce false; + } // host.extraNginx; + }) (lib.filterAttrs (n: v: v.accessType != "none") config.serverConfig.vhosts)); + + serverConfig = { + ports = { +# puffer_sftp_port.port = 5657; + gitea_ssh_port.port = 2222; +# node_exporter = { +# port = 9002; +# open = false; +# }; +# discord_matrix_bridge_port = { +# port = 9005; +# open = false; +# }; + redis_nextcloud_port = { + port = 6379; + open = false; + }; +# open_ldap_port = { +# port = 389; +# open = false; +# }; + }; + + vhosts = { +# puffer_host = { +# port = 8080; +# host = "puffer.${domain}"; +# accessType = "proxy"; +# }; +# ooye = { +# port = 6693; +# host = "ooye.${domain}"; +# accessType = "proxy"; +# }; + hedgedoc_host = { + port = 8048; + host = "hedgedoc.${domain}"; + accessType = "proxy"; + }; +# tlemap_host = { +# port = 8100; +# host = "tlemap.${domain}"; +# accessType = "proxy"; +# }; + mail_host = { + host = "mail.${domain}"; + accessType = "redirect"; + }; + gitea_host = { + host = "git.${domain}"; + port = 8081; + accessType = "proxy"; + }; + matrix_host = { + # accessType = "redirect"; + host = "matrix.${domain}"; + # port = 8008; + }; + prometheus_host = { + host = "prometheus.${domain}"; + port = 9090; + accessType = "redirect"; + }; + grafana_host = { + host = "grafana.${domain}"; + accessType = "proxy"; + port = 8082; + }; + nextcloud_host = rec { + host = "cloud.${domain}"; + port = 8083; + accessType = "custom"; + extraNginx.serverName = host; + }; + mastodon_host = { + host = "mastodon.${domain}"; + }; +# nix_cache_host = { +# host = "nixcache.${domain}"; +# port = 5000; +# accessType = "proxy"; +# }; +# auth_host = { +# host = "auth.${domain}"; +# port = 38080; +# accessType = "proxy"; +# }; + }; + }; + + security.acme = { + acceptTerms = true; + defaults.email = root_email; + certs."${domain}" = { + webroot = "/var/lib/acme/acme-challenge/"; + extraDomainNames = lib.mapAttrsToList (n: v: v.host) config.serverConfig.vhosts; + }; + }; + + services.nginx = { + # package = pkgs.nginxStable.override { openssl = pkgs.libressl; }; + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL"; + }; + + users.users.nginx.extraGroups = [ "acme" ]; + }; +} diff --git a/modules2/discord-matrix-bridge.nix b/modules2/discord-matrix-bridge.nix new file mode 100644 index 0000000..f55644d --- /dev/null +++ b/modules2/discord-matrix-bridge.nix @@ -0,0 +1,36 @@ +{ config, pkgs, ... }: +let + inherit (config.serverConfig) ports; +in +{ + age.secrets.matrix_discord_bridge_token.file = ../secrets/matrix_discord_bridge_token.age; + + services.matrix-synapse-next.settings.app_service_config_files = [ + "/var/lib/matrix-synapse/discord-registration.yaml" + ]; + + services.matrix-appservice-discord = { + enable = false; + settings = { + auth = { + usePrivilegedIntents = true; # typing status and stuff + }; + bridge = { + enableSelfServiceBridging = true; + domain = config.services.matrix-synapse.settings.server_name; + homeserverUrl = config.services.matrix-synapse.settings.public_baseurl; + disablePresence = true; + disableTypingNotifications = true; + }; + inactiveAfterDays = 14; + # logging.console = "silly"; + }; + serviceDependencies = [ "matrix-synapse.target" ]; + port = ports.discord_matrix_bridge_port.port; + localpart = "_discord_"; + package = pkgs.matrix-appservice-discord; + environmentFile = config.age.secrets.matrix_discord_bridge_token.path; + }; + + environment.systemPackages = with pkgs; [ matrix-appservice-discord ]; +} diff --git a/modules2/email.nix b/modules2/email.nix new file mode 100644 index 0000000..b3835a8 --- /dev/null +++ b/modules2/email.nix @@ -0,0 +1,34 @@ +{ config, ... }: +let + inherit (config.serverConfig) vhosts; + inherit (config.networking) domain; +in +{ + # services.dovecot2.sieve.extensions = [ "fileinto" ]; # sives break without this for some reason + mailserver = { + enable = true; + fqdn = vhosts.mail_host.host; + domains = [ domain ]; + + # A list of all login accounts. To create the password hashes, use + # nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt' + loginAccounts = { + "contact@${domain}" = { + hashedPasswordFile = ./mailpass/contact; + aliases = [ "kontakt@${domain}" ]; + }; + "admin@${domain}" = { + hashedPasswordFile = ./mailpass/admin; + }; + "grimmauld@${domain}" = { + hashedPasswordFile = ./mailpass/grimmauld; + }; + }; + + # Use Let's Encrypt certificates. Note that this needs to set up a stripped + # down nginx and opens port 80. + certificateScheme = "manual"; + certificateFile = "/var/lib/acme/${domain}/fullchain.pem"; + keyFile = "/var/lib/acme/${domain}/key.pem"; + }; +} diff --git a/modules2/factorio.nix b/modules2/factorio.nix new file mode 100644 index 0000000..6a0abb8 --- /dev/null +++ b/modules2/factorio.nix @@ -0,0 +1,43 @@ +{ + pkgs, + config, + lib, + ... +}: +{ + + networking.firewall.allowedTCPPorts = [ + 34197 + 34198 + ]; + + networking.firewall.allowedUDPPorts = [ + 34198 + 34197 + ]; + + services.prometheus.scrapeConfigs = [ + { + job_name = "clusterio-trangar"; + static_configs = [ { targets = [ "trang.ar:8080" ]; } ]; + } + ]; + + systemd.services.clusterio-trangar = { + description = "clusterio pulling its config from trang.ar"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + serviceConfig.Type = "simple"; + # serviceConfig.PassEnvironment = "NIX_PATH"; + #serviceConfig.User = "grimmauld"; + #serviceConfig.Group = "users"; + serviceConfig.WorkingDirectory = "/home/grimmauld/clusterio"; + script = '' + export NIXPKGS_ALLOW_UNFREE=1 + ${lib.getExe' config.nix.package "nix-shell"} -I nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos /home/grimmauld/clusterio-nonfhs/shell.nix --run "cd /home/grimmauld/clusterio-nonfhs/install && nice -19 bash run-host.sh" + # /home/grimmauld/clusterio/shell.nix + ''; + wantedBy = [ "multi-user.target" ]; # starts after login + enable = true; + }; +} diff --git a/modules2/fail2ban.nix b/modules2/fail2ban.nix new file mode 100644 index 0000000..f91b47b --- /dev/null +++ b/modules2/fail2ban.nix @@ -0,0 +1,22 @@ +{ ... }: +{ + services.fail2ban = { + enable = true; + maxretry = 5; + ignoreIP = [ + # Whitelist some subnets + "10.0.0.0/8" + "172.16.0.0/12" + "192.168.0.0/16" + "matrix.org" + "app.element.io" # don't ratelimit matrix users + ]; + bantime = "1h"; # Ban IPs for 1h at first. + bantime-increment = { + enable = true; # Enable increment of bantime after each violation + multipliers = "1 2 4 8 16 32 64 128 256"; + maxtime = "48h"; # Do not ban for more than 1 week + overalljails = true; # Calculate the bantime based on all the violations + }; + }; +} diff --git a/modules2/gitea.nix b/modules2/gitea.nix new file mode 100644 index 0000000..963005c --- /dev/null +++ b/modules2/gitea.nix @@ -0,0 +1,40 @@ +{ config, pkgs, ... }: +let + inherit (config.networking) domain; + inherit (config.serverConfig) ports vhosts; +in +{ + services.forgejo = { + enable = true; + package = pkgs.forgejo; + + database = { + user = "gitea"; + path = "${config.services.forgejo.stateDir}/data/gitea.db"; + name = "gitea"; + }; + + dump.enable = true; + + settings = { + service.DISABLE_REGISTRATION = true; + server = { + HTTP_PORT = vhosts.gitea_host.port; + ROOT_URL = "https://${vhosts.gitea_host.host}/"; + DISABLE_SSH = false; + SSH_DOMAIN = domain; + START_SSH_SERVER = true; + BUILTIN_SSH_SERVER_USER = "git"; + SSH_PORT = ports.gitea_ssh_port.port; + # SSH_LISTEN_HOST="::"; # fixme? + # SSH_AUTHORIZED_PRINCIPALS_ALLOW="username"; + }; + # log.LEVEL = "Debug"; + "ssh.minimum_key_sizes".RSA = 2048; + "git.timeout".MIGRATE = 6000; + }; + lfs.enable = true; + }; + + environment.systemPackages = with pkgs; [ gitea ]; +} diff --git a/modules2/grafana.nix b/modules2/grafana.nix new file mode 100644 index 0000000..2887495 --- /dev/null +++ b/modules2/grafana.nix @@ -0,0 +1,29 @@ +{ config, ... }: +let + inherit (config.serverConfig) ports vhosts; + inherit (config.networking) domain; +in +{ + age.secrets.grafana_admin_pass = { + file = ../secrets/grafana_admin_pass.age; + owner = "grafana"; + group = "grafana"; + mode = "0600"; + }; + + services.grafana = { + enable = true; + settings = { + security = { + admin_user = "admin"; + admin_email = "admin@${domain}"; + admin_password = "$__file{${config.age.secrets.grafana_admin_pass.path}}"; + }; + server = { + domain = vhosts.grafana_host.host; + root_url = "https://${vhosts.grafana_host.host}"; + http_port = vhosts.grafana_host.port; + }; + }; + }; +} diff --git a/modules2/hedgedoc.nix b/modules2/hedgedoc.nix new file mode 100644 index 0000000..40a71df --- /dev/null +++ b/modules2/hedgedoc.nix @@ -0,0 +1,18 @@ +{ config, ... }: +let + inherit (config.serverConfig) vhosts; +in +{ + services.hedgedoc = { + enable = true; + settings = { + domain = vhosts.hedgedoc_host.host; + inherit (vhosts.hedgedoc_host) port; + host = "127.0.0.1"; + protocolUseSSL = true; + allowEmailRegister = false; # no registrations for now + allowAnonymousEdits = true; # anonymous can edit select files + allowAnonymous = false; # anonymous can't actually create notes + }; + }; +} diff --git a/modules2/mailpass/admin b/modules2/mailpass/admin new file mode 100644 index 0000000..ed12fba --- /dev/null +++ b/modules2/mailpass/admin @@ -0,0 +1 @@ +$2b$05$9E2phVa/06fZW3daV3CeYuLTCLcBBDY7xF5TOpeHdCBGU5yNemBgy diff --git a/modules2/mailpass/contact b/modules2/mailpass/contact new file mode 100644 index 0000000..081b4b9 --- /dev/null +++ b/modules2/mailpass/contact @@ -0,0 +1 @@ +$2b$05$WsEwEXHa3kzDdMJdluirn.ExpK5BGJENEf3iH2AAjW6IFUPSpBWVa diff --git a/modules2/mailpass/grimmauld b/modules2/mailpass/grimmauld new file mode 100644 index 0000000..8e2798c --- /dev/null +++ b/modules2/mailpass/grimmauld @@ -0,0 +1 @@ +$2b$05$nmY9QnYyOhhhXn3OOalxkeWWLZtlaxD2vGwr0f6gtHNUz5EfZXvsa diff --git a/modules2/mastodon.nix b/modules2/mastodon.nix new file mode 100644 index 0000000..9a1ad5b --- /dev/null +++ b/modules2/mastodon.nix @@ -0,0 +1,17 @@ +{ config, ... }: +let + inherit (config.serverConfig) vhosts; + inherit (config.networking) domain; +in +{ + services.mastodon = { + enable = true; + localDomain = vhosts.mastodon_host.host; + streamingProcesses = 7; + configureNginx = true; + smtp = { + fromAddress = "noreply@${domain}"; + }; + extraConfig.SINGLE_USER_MODE = "true"; + }; +} diff --git a/modules2/matrix.nix b/modules2/matrix.nix new file mode 100644 index 0000000..fdc4ba3 --- /dev/null +++ b/modules2/matrix.nix @@ -0,0 +1,146 @@ +{ + lib, + config, + pkgs, + ... +}: +let + inherit (config.networking) domain; + inherit (config.serverConfig) vhosts; +in +{ + services.postgresql = { + enable = true; + ensureDatabases = [ "synapse" ]; + ensureUsers = [ + { + name = "synapse"; + passFile = config.age.secrets.synapse_db_pass.path; + ensureDBOwnership = true; + } + ]; + }; + + services.matrix-synapse-next = { + enable = true; + + workers.federationSenders = 1; + workers.federationReceivers = 1; + workers.initialSyncers = 1; + workers.normalSyncers = 1; + workers.eventPersisters = 1; + workers.useUserDirectoryWorker = true; + mainLogConfig = ./matrix_synapse_log_config.yaml; + + enableNginx = true; + enableSlidingSync = false; + + settings = { + suppress_key_server_warning = true; + server_name = domain; + public_baseurl = "https://${vhosts.matrix_host.host}"; + enable_registration = true; + registration_requires_token = true; + registration_shared_secret_path = config.age.secrets.synapse_registration_shared_secret.path; + # enable_registration_without_verification = true; + # mainLogConfig = ./matrix_synapse_log_config.yaml; + + # registrations_require_3pid = [ "email" ]; + + database = { + name = "psycopg2"; + args = { + host = "localhost"; + port = config.services.postgresql.settings.port; + dbname = "synapse"; + user = "synapse"; + cp_min = 5; + cp_max = 10; + client_encoding = "auto"; + passfile = config.age.secrets.synapse_db_pass_prepared.path; + }; + }; + }; + }; + services.redis.servers."".enable = true; + + age.secrets.synapse_db_pass = { + file = ../secrets/synapse_db_pass.age; + owner = "postgres"; + group = "postgres"; + }; + age.secrets.synapse_db_pass_prepared = { + file = ../secrets/synapse_db_pass_prepared.age; + owner = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0600"; + }; + age.secrets.synapse_registration_shared_secret = { + file = ../secrets/synapse_registration_shared_secret.age; + owner = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0600"; + }; + + environment.systemPackages = with pkgs; [ + matrix-synapse-tools.synadm + matrix-synapse + ]; + + services.nginx = { + enable = true; + virtualHosts."${domain}" = { + forceSSL = true; + enableACME = lib.mkForce false; # use the cert above, not some weird one that matrix-synapse module supplies + useACMEHost = domain; + locations."/.well-known/matrix/server" = { + return = "200 '{\"m.server\":\"${vhosts.matrix_host.host}:443\"}'"; + extraConfig = '' + default_type application/json; + add_header Access-Control-Allow-Origin *; + add_header Accept-Ranges bytes;''; + }; + locations."/.well-known/matrix/client" = { + return = "200 '{\"m.homeserver\": {\"base_url\": \"https://${vhosts.matrix_host.host}\"}}'"; + extraConfig = '' + add_header Access-Control-Allow-Origin *; + default_type application/json; + ''; + }; + locations."/_matrix" = { + proxyPass = "http://$synapse_backend"; + extraConfig = '' + add_header X-debug-backend $synapse_backend; + add_header X-debug-group $synapse_uri_group; + client_max_body_size ${config.services.matrix-synapse-next.settings.max_upload_size}; + proxy_read_timeout 10m; + ''; + }; + locations."~ ^/_matrix/client/(r0|v3)/sync$" = { + proxyPass = "http://$synapse_backend"; + extraConfig = '' + proxy_read_timeout 1h; + ''; + }; + locations."~ ^/_matrix/client/(api/v1|r0|v3)/initialSync$" = { + proxyPass = "http://synapse_worker_initial_sync"; + extraConfig = '' + proxy_read_timeout 1h; + ''; + }; + locations."~ ^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$" = { + proxyPass = "http://synapse_worker_initial_sync"; + extraConfig = '' + proxy_read_timeout 1h; + ''; + }; + locations."/_synapse/client" = { + proxyPass = "http://$synapse_backend"; + }; + locations."/.well-known/matrix" = { + proxyPass = "http://$synapse_backend"; + }; + }; + }; + # networking.firewall.allowedTCPPorts = [ 8448 8008 ]; +} diff --git a/modules2/matrix_legacy.nix b/modules2/matrix_legacy.nix new file mode 100644 index 0000000..00e79f0 --- /dev/null +++ b/modules2/matrix_legacy.nix @@ -0,0 +1,255 @@ +{ + lib, + config, + pkgs, + ... +}: +let + inherit (config.networking) domain; + inherit (config.serverConfig) vhosts; + + fqdn = vhosts.matrix_host.host; + base_url = "https://${fqdn}"; + + clientConfig."m.homeserver" = { + inherit base_url; + }; # = "https://${vhosts.matrix_host.host}"; + serverConfig."m.server" = "${vhosts.matrix_host.host}:443"; + mkWellKnown = data: '' + default_type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON data}'; + ''; + synapse_backend = "http://[::1]:8008"; +in +{ + services.postgresql = { + enable = true; + ensureDatabases = [ "synapse" ]; + ensureUsers = [ + { + name = "synapse"; + passFile = config.age.secrets.synapse_db_pass.path; + ensureDBOwnership = true; + } + ]; + }; + + services.matrix-synapse = { + enable = true; + settings.server_name = domain; + # The public base URL value must match the `base_url` value set in `clientConfig` above. + # The default value here is based on `server_name`, so if your `server_name` is different + # from the value of `fqdn` above, you will likely run into some mismatched domain names + # in client applications. + settings.public_baseurl = "https://${vhosts.matrix_host.host}"; + settings.listeners = [ + { + port = 8008; + bind_addresses = [ "::1" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ + "client" + "federation" + ]; + compress = true; + } + ]; + } + ]; + + settings.database = { + name = "psycopg2"; + args = { + user = "synapse"; + database = "synapse"; + port = config.services.postgresql.settings.port; + cp_max = 10; + cp_min = 5; + client_encoding = "auto"; + passfile = config.age.secrets.synapse_db_pass_prepared.path; + }; + }; + settings.log_config = ./matrix_synapse_log_config.yaml; + settings.enable_registration = true; + settings.registration_requires_token = true; + configureRedisLocally = true; + settings.redis.enabled = true; + + settings.app_service_config_files = [ + # The registration file is automatically generated after starting the + # appservice for the first time. + # cp /var/lib/mautrix-telegram/telegram-registration.yaml \ + # /var/lib/matrix-synapse/ + # chown matrix-synapse:matrix-synapse \ + # /var/lib/matrix-synapse/telegram-registration.yaml + # "/var/lib/matrix-synapse/discord-registration.yaml" + ]; + }; + + # services.matrix-synapse-next = { + # enable = true; + # + # workers.federationSenders = 1; + # workers.federationReceivers = 1; + # workers.initialSyncers = 1; + # workers.normalSyncers = 1; + # workers.eventPersisters = 2; + # workers.useUserDirectoryWorker = true; + # mainLogConfig = ./matrix_synapse_log_config.yaml; + # + # enableNginx = true; + # enableSlidingSync = false; + # + # settings = { + # suppress_key_server_warning = true; + # server_name = domain; + # public_baseurl = "https://${domain}"; + # enable_registration = true; + # registration_requires_token = true; + # registration_shared_secret_path = config.age.secrets.synapse_registration_shared_secret.path; + # # enable_registration_without_verification = true; + # # mainLogConfig = ./matrix_synapse_log_config.yaml; + # + # # registrations_require_3pid = [ "email" ]; + # + # database = { + # name = "psycopg2"; + # args = { + # host = "localhost"; + # port = config.services.postgresql.settings.port; + # dbname = "synapse"; + # user = "synapse"; + # cp_min = 5; + # cp_max = 10; + # client_encoding = "auto"; + # passfile = config.age.secrets.synapse_db_pass_prepared.path; + # }; + # }; + # }; + # }; + services.redis.servers."".enable = true; + + age.secrets.synapse_db_pass = { + file = ../secrets/synapse_db_pass.age; + owner = "postgres"; + group = "postgres"; + }; + age.secrets.synapse_db_pass_prepared = { + file = ../secrets/synapse_db_pass_prepared.age; + owner = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0600"; + }; + age.secrets.synapse_registration_shared_secret = { + file = ../secrets/synapse_registration_shared_secret.age; + owner = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0600"; + }; + + environment.systemPackages = with pkgs; [ + matrix-synapse-tools.synadm + matrix-synapse + ]; + + services.nginx = { + enable = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + # This section is not needed if the server_name of matrix-synapse is equal to + # the domain (i.e. example.org from @foo:example.org) and the federation port + # is 8448. + # Further reference can be found in the docs about delegation under + # https://element-hq.github.io/synapse/latest/delegate.html + locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig; + # This is usually needed for homeserver discovery (from e.g. other Matrix clients). + # Further reference can be found in the upstream docs at + # https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient + locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig; + }; + + virtualHosts."${fqdn}" = { + enableACME = true; + forceSSL = true; + + locations."/_matrix" = { + proxyPass = synapse_backend; + #extraConfig = '' + # add_header X-debug-backend ${synapse_backend}; + # add_header X-debug-group $synapse_uri_group; + # client_max_body_size ${config.services.matrix-synapse-next.settings.max_upload_size}; + # proxy_read_timeout 10m; + #''; + }; + locations."/_synapse/client".proxyPass = synapse_backend; + }; + }; + + # services.nginx = { + # enable = true; + # virtualHosts."${domain}" = { + # forceSSL = true; + # enableACME = lib.mkForce false; # use the cert above, not some weird one that matrix-synapse module supplies + # useACMEHost = domain; + # locations."/.well-known/matrix/server" = { + # return = "200 '{\"m.server\":\"${vhosts.matrix_host.host}:443\"}'"; + # extraConfig = '' + # default_type application/json; + # add_header Access-Control-Allow-Origin *; + # add_header Accept-Ranges bytes;''; + # }; + # locations."/.well-known/matrix/client" = { + # return = "200 '{\"m.homeserver\": {\"base_url\": \"https://${vhosts.matrix_host.host}\"}}'"; + # extraConfig = '' + # add_header Access-Control-Allow-Origin *; + # default_type application/json; + # ''; + # }; + # locations."/_matrix" = { + # proxyPass = "http://$synapse_backend"; + # extraConfig = '' + # add_header X-debug-backend $synapse_backend; + # add_header X-debug-group $synapse_uri_group; + # client_max_body_size ${config.services.matrix-synapse-next.settings.max_upload_size}; + # proxy_read_timeout 10m; + # ''; + # }; + # locations."/_synapse/client" = { + # proxyPass = "http://$synapse_backend"; + # }; + # locations."~ ^/_matrix/client/(r0|v3)/sync$" = { + # proxyPass = "http://$synapse_backend"; + # extraConfig = '' + # proxy_read_timeout 1h; + # ''; + # }; + # locations."~ ^/_matrix/client/(api/v1|r0|v3)/initialSync$" = { + # proxyPass = "http://synapse_worker_initial_sync"; + # extraConfig = '' + # proxy_read_timeout 1h; + # ''; + # }; + # locations."~ ^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$" = { + # proxyPass = "http://synapse_worker_initial_sync"; + # extraConfig = '' + # proxy_read_timeout 1h; + # ''; + # }; + # # locations."/.well-known/matrix" = { + # proxyPass = "http://$synapse_backend"; + # }; + # }; + # }; + # networking.firewall.allowedTCPPorts = [ 8448 8008 ]; +} diff --git a/modules2/matrix_synapse_log_config.yaml b/modules2/matrix_synapse_log_config.yaml new file mode 100644 index 0000000..f412638 --- /dev/null +++ b/modules2/matrix_synapse_log_config.yaml @@ -0,0 +1,25 @@ +version: 1 + +# In systemd's journal, loglevel is implicitly stored, so let's omit it +# from the message text. +formatters: + journal_fmt: + format: '%(name)s: [%(request)s] %(message)s' + +filters: + context: + (): synapse.util.logcontext.LoggingContextFilter + request: "" + +handlers: + journal: + class: systemd.journal.JournalHandler + formatter: journal_fmt + filters: [context] + SYSLOG_IDENTIFIER: synapse + +root: + level: WARNING + handlers: [journal] + +disable_existing_loggers: False diff --git a/modules2/mjolnir.nix b/modules2/mjolnir.nix new file mode 100644 index 0000000..2e38ce4 --- /dev/null +++ b/modules2/mjolnir.nix @@ -0,0 +1,69 @@ +{ ... }: +let +in +{ + age.secrets = { + matrix_mjolnir_pass = { + file = ../secrets/matrix_mjolnir_pass.age; + owner = "mjolnir"; + group = "mjolnir"; + mode = "0600"; + }; + + matrix_mjolnir_tle_pass = { + file = ../secrets/matrix_mjolnir_tle_pass.age; + owner = "mjolnir"; + group = "mjolnir"; + mode = "0777"; # not ideal, but containers are weird + }; + }; + + # global mjolnir + services.mjolnir = { + enable = true; + homeserverUrl = config.services.matrix-synapse-next.settings.public_baseurl; + protectedRooms = [ "https://matrix.to/#/!zDkrFrfuMIKbqYFbFv:grimmauld.de" ]; + managementRoom = "!kgfXXqEYHGgToIwhMP:grimmauld.de"; + pantalaimon = { + enable = true; + username = "mjolnir"; + options = { + homeserver = config.services.matrix-synapse-next.settings.public_baseurl; + }; + passwordFile = config.age.secrets.matrix_mjolnir_pass.path; + }; + }; + + services.logrotate.checkConfig = false; # needed or this explodes + containers.mjolnirtle = + let + baseurl = config.services.matrix-synapse-next.settings.public_baseurl; + pass_file = config.age.secrets.matrix_mjolnir_tle_pass.path; + in + { + privateNetwork = false; # don't want nat + autoStart = true; + bindMounts."${pass_file}".isReadOnly = true; + config = + { config, ... }: + { + system.stateVersion = "unstable"; + # tle mjolnir + services.logrotate.checkConfig = false; + services.mjolnir = { + enable = true; + homeserverUrl = baseurl; + protectedRooms = [ "https://matrix.to/#/!BgDBnHgMgilMMnPMyp:grimmauld.de" ]; + managementRoom = "!NQedmlMeoQErGgAwxm:grimmauld.de"; + pantalaimon = { + enable = true; + username = "mjolnir_tle"; + options = { + homeserver = baseurl; + }; + passwordFile = pass_file; + }; + }; + }; + }; +} diff --git a/modules2/nextcloud.nix b/modules2/nextcloud.nix new file mode 100644 index 0000000..d9b3bee --- /dev/null +++ b/modules2/nextcloud.nix @@ -0,0 +1,88 @@ +{ pkgs, config, ... }: +let + inherit (config.serverConfig) ports vhosts; +in +{ + services.postgresql = { + enable = true; + ensureDatabases = [ "nextcloud" ]; + ensureUsers = [ + { + name = "nextcloud"; + passFile = config.age.secrets.nextcloud_admin_pass.path; + ensureDBOwnership = true; + } + ]; + }; + + age.secrets = { + nextcloud_admin_pass = { + file = ../secrets/nextcloud_admin_pass.age; + owner = "nextcloud"; + group = "nextcloud"; + mode = "0600"; + }; + nextcloud_server_key = { + file = ../secrets/nextcloud_server_key.age; + owner = "nextcloud"; + group = "nextcloud"; + mode = "0600"; + }; + nextcloud_db_key = { + file = ../secrets/nextcloud_db_pass.age; + owner = "nextcloud"; + group = "nextcloud"; + mode = "0600"; + }; + }; + + services.redis.servers.nextcloud = { + enable = true; + bind = "::1"; + port = ports.redis_nextcloud_port.port; + }; + + systemd.services.nextcloud-setup.serviceConfig.ExecStartPost = + pkgs.writeScript "nextcloud-redis.sh" '' + #!${pkgs.runtimeShell} + nextcloud-occ config:system:set redis 'host' --value '::1' --type string + nextcloud-occ config:system:set redis '${builtins.toString config.services.redis.servers.nextcloud.port}' --value 6379 --type integer + nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string + nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string + ''; + + services.nextcloud = { + enable = true; + https = true; + hostName = vhosts.nextcloud_host.host; + package = pkgs.nextcloud30; + caching.redis = true; + + extraApps = { + inherit (config.services.nextcloud.package.packages.apps) calendar tasks; + }; + + config = { + adminpassFile = config.age.secrets.nextcloud_admin_pass.path; + dbuser = "nextcloud"; + dbhost = "localhost:${builtins.toString config.services.postgresql.settings.port}"; + dbtype = "pgsql"; + dbpassFile = config.age.secrets.nextcloud_db_key.path; + }; + settings = { + overwriteProtocol = "https"; + # config_is_read_only = true; + defaultPhoneRegion = "DE"; + filelocking.enabled = true; + sseCKeyFile = config.age.secrets.nextcloud_server_key; + redis = { + host = "localhost"; + port = config.services.redis.servers.nextcloud.port; + timeout = 0.0; + }; + }; + phpOptions = { + "opcache.interned_strings_buffer" = "12"; + }; + }; +} diff --git a/modules2/nix_cache.nix b/modules2/nix_cache.nix new file mode 100644 index 0000000..43d1e35 --- /dev/null +++ b/modules2/nix_cache.nix @@ -0,0 +1,11 @@ +{ config, ... }: +let + inherit (config.serverConfig) vhosts; +in +{ + services.harmonia = { + enable = true; + signKeyPaths = [ "/var/cache-priv-key.pem" ]; + settings.bind = "[::]:${builtins.toString vhosts.nix_cache_host.port}"; + }; +} diff --git a/modules2/ooye/default.nix b/modules2/ooye/default.nix new file mode 100644 index 0000000..9a70f30 --- /dev/null +++ b/modules2/ooye/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ ooye ]; + + +services.matrix-synapse-next.settings.app_service_config_files = [ ./registration.yaml ]; +} diff --git a/modules2/prometheus.nix b/modules2/prometheus.nix new file mode 100644 index 0000000..32488fe --- /dev/null +++ b/modules2/prometheus.nix @@ -0,0 +1,47 @@ +{ config, lib, ... }: +let + inherit (config.serverConfig) ports vhosts; +in +{ + services.prometheus = { + enable = true; + port = vhosts.prometheus_host.port; + globalConfig.scrape_interval = "15s"; + scrapeConfigs = [ + { + job_name = "chrysalis"; + static_configs = [ + { + targets = + let + inherit (lib) + filter + isAttrs + attrValues + filterAttrs + ; + in + map (v: "127.0.0.1:${builtins.toString v.port}") ( + filter (v: (isAttrs v) && v.enable) ( + attrValues (filterAttrs (n: v: n != "minio" && n != "tor") config.services.prometheus.exporters) + ) + ); + } + ]; + } + ]; + exporters = { + # nginx.enable = true; + redis.enable = true; + domain.enable = true; + postgres.enable = true; + nginxlog.enable = true; + jitsi.enable = true; + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + port = ports.node_exporter.port; + }; + }; + }; +} diff --git a/modules2/puffer.nix b/modules2/puffer.nix new file mode 100644 index 0000000..14b8024 --- /dev/null +++ b/modules2/puffer.nix @@ -0,0 +1,30 @@ +{ config, pkgs, ... }: +let + inherit (config.serverConfig) ports vhosts; +in +{ + services.pufferpanel = { + enable = true; + environment = { + PUFFER_WEB_HOST = ":${builtins.toString vhosts.puffer_host.port}"; + PUFFER_DAEMON_SFTP_HOST = ":${builtins.toString ports.puffer_sftp_port.port}"; + }; + extraGroups = [ "docker" ]; + }; + + networking.firewall.allowedTCPPorts = [ + 25565 + 25566 + 25567 + 25568 + 7270 + ]; + + # virtualisation.podman.enable = true; + virtualisation.docker.enable = true; + + environment.systemPackages = with pkgs; [ + pufferpanel + (writeShellScriptBin "pufferpanel-nix" "pufferpanel --workDir /var/lib/pufferpanel $@") + ]; +} diff --git a/modules2/wireguard.nix b/modules2/wireguard.nix new file mode 100644 index 0000000..2467d0e --- /dev/null +++ b/modules2/wireguard.nix @@ -0,0 +1,42 @@ +{ pkgs, ... }: +{ + # enable NAT + networking.nat.enable = true; + networking.nat.externalInterface = "eth0"; + networking.nat.internalInterfaces = [ "wg0" ]; + networking.firewall = { + allowedUDPPorts = [ 51820 ]; + }; + + networking.wireguard.interfaces = { + # "wg0" is the network interface name. You can name the interface + # arbitrarily.} + wg0 = { + privateKeyFile = "/home/grimmauld/wireguard.priv"; + # Determines the IP address and subnet of the server's end of the tunnel + # interface. + ips = [ "10.100.0.1/24" ]; + # The port that WireGuard listens to. Must be accessible by the client. + listenPort = 51820; + # This allows the wireguard server to route your traffic to the internet and + # hence be like a VPN For this to work you have to set the dnsserver IP of + # your router (or dnsserver of choice) in your clients + postSetup = '' + ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o ens18 -j MASQUERADE + ''; + # This undoes the above command + postShutdown = '' + ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.0/24 -o ens18 -j MASQUERADE + ''; + + generatePrivateKeyFile = true; + peers = [ + { + publicKey = "2aANdnPYtf78iXfwNVAtYjIlE5k/yDWvbdXZ2jw0hXk="; + allowedIPs = [ "10.100.0.2/32" ]; + } + ]; + }; + }; + environment.systemPackages = with pkgs; [ wireguard-tools ]; +} diff --git a/modules2/wireguard.nix.save b/modules2/wireguard.nix.save new file mode 100644 index 0000000..683f70f --- /dev/null +++ b/modules2/wireguard.nix.save @@ -0,0 +1,2 @@ +{ +