diff options
Diffstat (limited to '')
-rw-r--r-- | modules/by-name/at/atuin-sync/module.nix | 45 | ||||
-rw-r--r-- | modules/by-name/ba/back/module.nix | 92 | ||||
-rw-r--r-- | modules/by-name/co/constants/module.nix | 21 | ||||
-rw-r--r-- | modules/by-name/gi/git-back/module.nix | 41 | ||||
-rw-r--r-- | modules/by-name/ma/mastodon/module.nix | 27 | ||||
-rw-r--r-- | modules/by-name/ma/matrix/module.nix | 78 | ||||
-rw-r--r-- | modules/by-name/ne/nextcloud/module.nix | 82 | ||||
-rw-r--r-- | modules/by-name/ng/nginx/module.nix | 5 | ||||
-rw-r--r-- | modules/by-name/re/redlib/module.nix | 5 | ||||
-rw-r--r-- | modules/by-name/ru/rust-motd/module.nix | 32 | ||||
-rw-r--r-- | modules/by-name/sh/sharkey/module.nix | 298 | ||||
-rw-r--r-- | modules/by-name/st/stalwart-mail/module.nix | 102 | ||||
-rw-r--r-- | modules/by-name/st/stalwart-mail/settings.nix | 56 | ||||
-rw-r--r-- | modules/by-name/sy/system-info/module.nix | 11 | ||||
-rw-r--r-- | modules/by-name/ta/taskchampion-sync/module.nix | 53 | ||||
-rw-r--r-- | modules/by-name/us/users/module.nix | 90 |
16 files changed, 776 insertions, 262 deletions
diff --git a/modules/by-name/at/atuin-sync/module.nix b/modules/by-name/at/atuin-sync/module.nix new file mode 100644 index 0000000..0db2e29 --- /dev/null +++ b/modules/by-name/at/atuin-sync/module.nix @@ -0,0 +1,45 @@ +{ + config, + lib, + vhackPackages, + ... +}: let + cfg = config.vhack.atuin-sync; +in { + options.vhack.atuin-sync = { + enable = lib.mkEnableOption "atuin sync server"; + + fqdn = lib.mkOption { + description = "The fully qualified domain name of this instance."; + type = lib.types.str; + example = "atuin-sync.atuin.sh"; + }; + }; + + config = lib.mkIf cfg.enable { + vhack.nginx.enable = true; + + services = { + nginx.virtualHosts."${cfg.fqdn}" = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString config.services.atuin.port}"; + recommendedProxySettings = true; + }; + + enableACME = true; + forceSSL = true; + }; + + atuin = { + enable = true; + package = vhackPackages.atuin-server-only; + host = "127.0.0.1"; + + # Nobody knows about the fqdn and even if, they can only upload encrypted blobs. + openRegistration = true; + + database.createLocally = true; + }; + }; + }; +} diff --git a/modules/by-name/ba/back/module.nix b/modules/by-name/ba/back/module.nix deleted file mode 100644 index d47ffce..0000000 --- a/modules/by-name/ba/back/module.nix +++ /dev/null @@ -1,92 +0,0 @@ -{ - config, - lib, - vhackPackages, - pkgs, - ... -}: let - cfg = config.vhack.back; -in { - options.vhack.back = { - enable = lib.mkEnableOption "Back issue tracker (inspired by tvix's panettone)"; - - domain = lib.mkOption { - type = lib.types.str; - description = "The domain to host this `back` instance on."; - }; - - settings = { - scan_path = lib.mkOption { - type = lib.types.path; - description = "The path to the directory under which all the repositories reside"; - }; - project_list = lib.mkOption { - type = lib.types.path; - description = "The path to the `projects.list` file."; - }; - - source_code_repository_url = lib.mkOption { - description = "The url to the source code of this instance of back"; - default = "https://git.foss-syndicate.org/vhack.eu/nixos-server/tree/pkgs/by-name/ba/back"; - type = lib.types.str; - }; - - root_url = lib.mkOption { - type = lib.types.str; - description = "The url to this instance of back."; - default = "https://${cfg.domain}"; - }; - }; - }; - - config = lib.mkIf cfg.enable { - systemd.services."back" = { - description = "Back issue tracking system."; - requires = ["network-online.target"]; - after = ["network-online.target"]; - wantedBy = ["default.target"]; - - serviceConfig = { - ExecStart = "${lib.getExe vhackPackages.back} ${(pkgs.formats.json {}).generate "config.json" cfg.settings}"; - - # Ensure that the service can read the repository - # FIXME(@bpeetz): This has the implied assumption, that all the exposed git - # repositories are readable for the git group. This should not be necessary. <2024-12-23> - User = "git"; - Group = "git"; - - DynamicUser = true; - Restart = "always"; - - # Sandboxing - ProtectSystem = "strict"; - ProtectHome = true; - PrivateTmp = true; - PrivateDevices = true; - ProtectHostname = true; - ProtectClock = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectKernelLogs = true; - ProtectControlGroups = true; - RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; - RestrictNamespaces = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - RemoveIPC = true; - PrivateMounts = true; - # System Call Filtering - SystemCallArchitectures = "native"; - SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"]; - }; - }; - services.nginx.virtualHosts."${cfg.domain}" = { - locations."/".proxyPass = "http://127.0.0.1:8000"; - - enableACME = true; - forceSSL = true; - }; - }; -} diff --git a/modules/by-name/co/constants/module.nix b/modules/by-name/co/constants/module.nix index d601e70..2115a37 100644 --- a/modules/by-name/co/constants/module.nix +++ b/modules/by-name/co/constants/module.nix @@ -25,6 +25,7 @@ config.vhack.constants = { ids.uids = { # Keep this sorted with `!sort --numeric-sort --key=2 --field-separator="="` + systemd-coredump = 151; # GROUP opendkim = 221; mautrix-whatsapp = 222; etebase-server = 223; @@ -43,13 +44,22 @@ nscd = 330; sshd = 331; systemd-oom = 332; + resolvconf = 333; # GROUP nix-sync = 334; + nextcloud = 335; + redis-nextcloud = 336; + taskchampion = 337; + stalwart-mail-certificates = 338; # GROUP + sharkey = 339; + redis-sharkey = 340; # As per the NixOS file, the uids should not be greater or equal to 400; }; ids.gids = let inherit (config.vhack.constants.ids) uids; in { + # Please add your groups to the users and inherit them here. + # This avoids having an user/group id mismatch. inherit (uids) acme @@ -59,11 +69,13 @@ mastodon matrix-synapse mautrix-whatsapp + nextcloud nix-sync nscd opendkim peertube redis-mastodon + redis-nextcloud redis-peertube redis-rspamd redis-stalwart-mail @@ -71,12 +83,13 @@ sshd stalwart-mail systemd-oom + sharkey + redis-sharkey + systemd-coredump # matches systemd-coredump user + resolvconf # This group is not matched to an user? + stalwart-mail-certificates # This group is used to connect nginx and stalwart-mail ; - # Keep this sorted with `!sort --numeric-sort --key=2 --field-separator="="` - systemd-coredump = 151; # matches systemd-coredump user - resolvconf = 333; # This group is not matched to an user? - # The gid should match the uid. Thus should not be >= 400; }; }; diff --git a/modules/by-name/gi/git-back/module.nix b/modules/by-name/gi/git-back/module.nix new file mode 100644 index 0000000..96f4913 --- /dev/null +++ b/modules/by-name/gi/git-back/module.nix @@ -0,0 +1,41 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.git-back; +in { + options.vhack.git-back = { + enable = lib.mkEnableOption "Back integration into git-server"; + + domain = lib.mkOption { + type = lib.types.str; + description = "The domain where to deploy back"; + }; + }; + + config = lib.mkIf cfg.enable { + vhack.back = { + enable = true; + + user = "git"; + group = "git"; + + settings = { + scan_path = "${config.services.gitolite.dataDir}/repositories"; + project_list = "${config.services.gitolite.dataDir}/projects.list"; + root_url = "https://${cfg.domain}"; + }; + }; + + services.nginx = { + enable = true; + virtualHosts."${cfg.domain}" = { + locations."/".proxyPass = "http://127.0.0.1:8000"; + + enableACME = true; + forceSSL = true; + }; + }; + }; +} diff --git a/modules/by-name/ma/mastodon/module.nix b/modules/by-name/ma/mastodon/module.nix index 895428d..84f3ec8 100644 --- a/modules/by-name/ma/mastodon/module.nix +++ b/modules/by-name/ma/mastodon/module.nix @@ -37,16 +37,22 @@ in { owner = "mastodon"; group = "mastodon"; }; - vhack.persist.directories = [ - { - directory = "/var/lib/mastodon"; - user = "mastodon"; - group = "mastodon"; - mode = "0700"; - } - ]; - vhack.postgresql.enable = true; + vhack = { + persist.directories = [ + { + directory = "/var/lib/mastodon"; + user = "mastodon"; + group = "mastodon"; + mode = "0700"; + } + ]; + + postgresql.enable = true; + + nginx.enable = true; + }; + services.mastodon = { enable = true; @@ -54,7 +60,7 @@ in { # Unstable Mastodon package, used if # security updates aren't backported. - #package = applyPatches pkgs-unstable.mastodon; + #package = applyPatches pkgsUnstable.mastodon; localDomain = if cfg.enableTLD @@ -75,7 +81,6 @@ in { }; }; - vhack.nginx.enable = true; services.nginx = { enable = true; recommendedProxySettings = true; # required for redirections to work diff --git a/modules/by-name/ma/matrix/module.nix b/modules/by-name/ma/matrix/module.nix index 4b730da..ae3f04e 100644 --- a/modules/by-name/ma/matrix/module.nix +++ b/modules/by-name/ma/matrix/module.nix @@ -1,6 +1,5 @@ { config, - pkgs, lib, ... }: let @@ -29,6 +28,7 @@ in { description = "The age encrypted shared secret file for synapse, passed to agenix"; }; }; + config = lib.mkIf cfg.enable { age.secrets.matrix-synapse_registration_shared_secret = { file = cfg.sharedSecretFile; @@ -38,45 +38,53 @@ in { }; networking.firewall.allowedTCPPorts = [80 443]; - vhack.persist.directories = [ - { - directory = "/var/lib/matrix"; - user = "matrix-synapse"; - group = "matrix-synapse"; - mode = "0700"; - } - { - directory = "/var/lib/mautrix-whatsapp"; - user = "mautrix-whatsapp"; - group = "matrix-synapse"; - mode = "0750"; - } - ]; - systemd.tmpfiles.rules = [ - "d /etc/matrix 0755 matrix-synapse matrix-synapse" - ]; + vhack = { + persist.directories = [ + { + directory = "/var/lib/matrix"; + user = "matrix-synapse"; + group = "matrix-synapse"; + mode = "0700"; + } + { + directory = "/var/lib/mautrix-whatsapp"; + user = "mautrix-whatsapp"; + group = "matrix-synapse"; + mode = "0750"; + } + ]; - vhack.postgresql.enable = true; - vhack.nginx.enable = true; + postgresql.enable = true; + nginx.enable = true; + }; + + systemd = { + tmpfiles.rules = [ + "d /etc/matrix 0755 matrix-synapse matrix-synapse" + ]; + services.postgresql.postStart = '' + $PSQL -tAc "ALTER ROLE \"matrix-synapse\" WITH PASSWORD 'synapse';" + $PSQL -tAc "ALTER ROLE \"mautrix-whatsapp\" WITH PASSWORD 'whatsapp';" + ''; + }; services = { postgresql = { enable = true; - initialScript = pkgs.writeText "synapse-init.sql" '' - --Matrix: - CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; - CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" - TEMPLATE template0 - LC_COLLATE = "C" - LC_CTYPE = "C"; - - --Whatsapp-bridge: - CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp'; - CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp" - TEMPLATE template0 - LC_COLLATE = "C" - LC_CTYPE = "C"; - ''; + ensureUsers = [ + { + name = "matrix-synapse"; + ensureDBOwnership = true; + } + { + name = "mautrix-whatsapp"; + ensureDBOwnership = true; + } + ]; + ensureDatabases = [ + "matrix-synapse" + "mautrix-whatsapp" + ]; }; nginx = { diff --git a/modules/by-name/ne/nextcloud/module.nix b/modules/by-name/ne/nextcloud/module.nix new file mode 100644 index 0000000..e0d7cb3 --- /dev/null +++ b/modules/by-name/ne/nextcloud/module.nix @@ -0,0 +1,82 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.vhack.nextcloud; +in { + options.vhack.nextcloud = { + enable = lib.mkEnableOption "a sophisticated nextcloud setup"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.nextcloud31; + description = "The nextcloud package to use"; + }; + hostname = lib.mkOption { + type = lib.types.str; + description = "The nextcloud hostname (fqdn)"; + }; + adminpassFile = lib.mkOption { + type = lib.types.path; + description = "The age encrypted admin password file"; + }; + }; + config = lib.mkIf cfg.enable { + vhack = { + nginx.enable = true; + postgresql.enable = true; + persist.directories = [ + "/var/lib/nextcloud" + ]; + }; + age.secrets = { + adminpassFile = { + file = cfg.adminpassFile; + mode = "0700"; + owner = "nextcloud"; + group = "nextcloud"; + }; + }; + + services = { + nextcloud = { + enable = true; + extraApps = { + inherit (cfg.package.packages.apps) calendar contacts tasks; + }; + extraAppsEnable = true; + configureRedis = true; + config = { + adminuser = "admin"; + adminpassFile = config.age.secrets.adminpassFile.path; + dbname = "nextcloud"; + dbuser = "nextcloud"; + dbtype = "pgsql"; + }; + database.createLocally = true; + hostName = cfg.hostname; + https = true; + maxUploadSize = "5G"; + package = cfg.package; + settings = { + default_phone_region = "DE"; + }; + }; + nginx.virtualHosts.${cfg.hostname} = { + forceSSL = true; + enableACME = true; + }; + }; + users = { + users = { + "nextcloud".uid = config.vhack.constants.ids.uids.nextcloud; + "redis-nextcloud".uid = config.vhack.constants.ids.uids.redis-nextcloud; + }; + groups = { + "nextcloud".gid = config.vhack.constants.ids.gids.nextcloud; + "redis-nextcloud".gid = config.vhack.constants.ids.gids.redis-nextcloud; + }; + }; + }; +} diff --git a/modules/by-name/ng/nginx/module.nix b/modules/by-name/ng/nginx/module.nix index 1cb4e46..fa3337d 100644 --- a/modules/by-name/ng/nginx/module.nix +++ b/modules/by-name/ng/nginx/module.nix @@ -44,7 +44,10 @@ in { ]; users = { - users.acme.uid = config.vhack.constants.ids.uids.acme; + users.acme = { + uid = config.vhack.constants.ids.uids.acme; + group = "acme"; + }; groups.acme.gid = config.vhack.constants.ids.gids.acme; }; diff --git a/modules/by-name/re/redlib/module.nix b/modules/by-name/re/redlib/module.nix index eb5edba..909c9f1 100644 --- a/modules/by-name/re/redlib/module.nix +++ b/modules/by-name/re/redlib/module.nix @@ -32,10 +32,5 @@ in { forceSSL = true; }; }; - - # TODO(@bpeetz): Remove this at some point. <2025-02-04> - vhack.nginx.redirects = { - "libreddit.vhack.eu" = "${domain}"; - }; }; } diff --git a/modules/by-name/ru/rust-motd/module.nix b/modules/by-name/ru/rust-motd/module.nix index a6998f4..8d0939a 100644 --- a/modules/by-name/ru/rust-motd/module.nix +++ b/modules/by-name/ru/rust-motd/module.nix @@ -19,6 +19,13 @@ || v.openssh.authorizedKeys.keyFiles != [] ); userList = builtins.mapAttrs (n: v: 2) (lib.filterAttrs pred config.users.users); + + bannerFile = + pkgs.runCommandNoCCLocal "banner-file" { + nativeBuildInputs = [pkgs.figlet]; + } '' + echo "${config.system.name}" | figlet -f slant > "$out" + ''; in { options.vhack.rust-motd = { enable = lib.mkEnableOption "rust-motd"; @@ -49,25 +56,22 @@ in { banner = { color = "red"; - command = "${pkgs.hostname}/bin/hostname | ${pkgs.figlet}/bin/figlet -f slant"; - # if you don't want a dependency on figlet, you can generate your - # banner however you want, put it in a file, and then use something like: - # command = "cat banner.txt" + # Avoid some runtime dependencies. + command = "cat ${bannerFile}"; + }; + + cg_stats = { + state_file = "/var/lib/rust-motd/cg_stats_state"; + threshold = 0.02; # When to start generating output for a cgroup + }; + load_avg = { + format = "Load (1, 5, 15 min.): {one:.02}, {five:.02}, {fifteen:.02}"; }; uptime = { prefix = "Uptime:"; }; - # ssl_certificates = { - # sort_method = "manual"; - # - # certs = { - # "server1.vhack.eu" = "/var/lib/acme/server1.vhack.eu/cert.pem"; - # "vhack.eu" = "/var/lib/acme/vhack.eu/cert.pem"; - # }; - # }; - filesystems = { root = "/"; persistent = "/srv"; @@ -79,7 +83,7 @@ in { swap_pos = "beside"; # or "below" or "none" }; - fail2_ban = { + fail_2_ban = { jails = ["sshd"]; #, "anotherjail"] }; diff --git a/modules/by-name/sh/sharkey/module.nix b/modules/by-name/sh/sharkey/module.nix new file mode 100644 index 0000000..2b50cf0 --- /dev/null +++ b/modules/by-name/sh/sharkey/module.nix @@ -0,0 +1,298 @@ +# Source: https://github.com/sodiboo/system/blob/b63c7b27f49043e8701b3ff5e1441cd27d5a2fff/sharkey/module.nix +{ + config, + lib, + pkgs, + vhackPackages, + ... +}: let + cfg = config.vhack.sharkey; + + createDB = cfg.database.host == "127.0.0.1" && cfg.database.createLocally; + + settingsFormat = pkgs.formats.yaml {}; + configFile = settingsFormat.generate "sharkey-config.yml" cfg.settings; +in { + options.vhack.sharkey = { + enable = lib.mkEnableOption "sharkey"; + + fqdn = lib.mkOption { + description = "The fully qualified domain name of this instance."; + type = lib.types.str; + example = "sharkey.shonk.social"; + }; + + package = lib.mkOption { + type = lib.types.package; + default = vhackPackages.sharkey; + defaultText = lib.literalExpression "vhackPackages.sharkey"; + description = "Sharkey package to use."; + }; + + dataDirectory = lib.mkOption { + type = lib.types.path; + default = "/var/lib/sharkey"; + description = "The directory where sharkey stores it's data."; + + # This is already set in the package. + readOnly = true; + }; + + database = { + createLocally = lib.mkOption { + description = "Whether to enable local db creation."; + type = lib.types.bool; + default = true; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "The database host."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 5432; + description = "The database port."; + }; + + name = lib.mkOption { + type = lib.types.str; + default = "sharkey"; + description = "The database name in postgresql."; + }; + }; + + settings = lib.mkOption { + inherit (settingsFormat) type; + default = {}; + description = '' + Configuration for Sharkey, see + <link xlink:href="https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/.config/example.yml"/> + for supported settings. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + vhack = { + nginx.enable = true; + + sharkey.settings = { + id = "aidx"; + + url = "https://${cfg.fqdn}/"; + port = 5312; + + db = { + inherit (cfg.database) host port; + db = cfg.database.name; + user = cfg.database.name; + pass = "sharkey-password"; + }; + redis = { + path = config.services.redis.servers."sharkey".unixSocket; + }; + }; + + persist.directories = [ + { + directory = "${config.services.redis.servers."sharkey".settings.dir}"; + user = "sharkey"; + group = "redis-sharey"; + mode = "0770"; + } + { + directory = "${cfg.dataDirectory}"; + user = "sharkey"; + group = "sharkey"; + mode = "0770"; + } + ]; + }; + + services = { + nginx.virtualHosts."${cfg.fqdn}" = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.settings.port}"; + proxyWebsockets = true; + }; + + # proxy_set_header Host $host; + # proxy_http_version 1.1; + # proxy_redirect off; + # + # # If it's behind another reverse proxy or CDN, remove the following. + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto https; + # + # # For WebSocket + # proxy_set_header Upgrade $http_upgrade; + # proxy_set_header Connection $connection_upgrade; + # + # # Cache settings + # proxy_cache cache1; + # proxy_cache_lock on; + # proxy_cache_use_stale updating; + # proxy_force_ranges on; + # add_header X-Cache $upstream_cache_status; + + enableACME = true; + forceSSL = true; + }; + + postgresql = lib.mkIf createDB { + enable = true; + settings.port = cfg.database.port; + ensureUsers = [ + { + inherit (cfg.database) name; + ensureDBOwnership = true; + } + ]; + ensureDatabases = [cfg.database.name]; + }; + + redis = { + servers."sharkey" = { + enable = true; + + user = "sharkey"; + + # Disable TCP listening. (We have a UNIX socket) + port = 0; + bind = null; + + settings = { + protected-mode = true; + enable-protected-configs = false; + enable-debug-command = false; + enable-module-command = false; + + supervised = "systemd"; + stop-writes-on-bgsave-error = true; + sanitize-dump-payload = "clients"; + }; + }; + }; + }; + + systemd.services.postgresql.postStart = '' + $PSQL -tAc "ALTER ROLE ${cfg.database.name} WITH ENCRYPTED PASSWORD 'sharkey-password';" + ''; + + systemd.services.sharkey = { + requires = + [ + "redis-sharkey.service" + "network-online.target" + ] + ++ lib.optionals createDB ["postgresql.service"]; + + after = + [ + "redis-sharkey.service" + "network-online.target" + ] + ++ lib.optionals createDB ["postgresql.service"]; + + wantedBy = ["multi-user.target"]; + + environment = { + MISSKEY_CONFIG_YML = "${configFile}"; + NODE_ENV = "production"; + }; + + serviceConfig = { + Type = "simple"; + + StateDirectory = "sharkey"; + StateDirectoryMode = "0700"; + CacheDirectory = "sharkey"; + RuntimeDirectory = "sharkey"; + RuntimeDirectoryMode = "0700"; + ExecStart = "${lib.getExe cfg.package} migrateandstart"; + + TimeoutSec = 60; + Restart = "no"; + + StandardOutput = "journal"; + StandardError = "journal"; + SyslogIdentifier = "sharkey"; + + User = "sharkey"; + Group = "sharkey"; + + # Bind standard privileged ports + AmbientCapabilities = []; + CapabilityBoundingSet = []; + + ReadWritePaths = [ + "${cfg.dataDirectory}" + ]; + + # Hardening + DeviceAllow = [""]; + LockPersonality = true; + # Probably needed for v8's JIT (crashes with it on). + MemoryDenyWriteExecute = false; + PrivateDevices = true; + PrivateUsers = true; + # Sharkey needs access to the hosts CPUs + ProcSubset = "all"; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ + "AF_UNIX" # Local communication unix(7) + "AF_INET" # IPv4 Internet protocols ip(7) + "AF_INET6" # IPv6 Internet protocols ipv6(7) + # Needed for nodes `os.networkInterfaces()` function. + "AF_NETLINK" # Kernel user interface device netlink(7) + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "@chown" + "~@mount" + ]; + UMask = "0077"; + }; + }; + + users = { + groups.sharkey = { + gid = config.vhack.constants.ids.gids.sharkey; + }; + users.sharkey = { + isSystemUser = true; + group = "sharkey"; + uid = config.vhack.constants.ids.uids.sharkey; + home = cfg.package; + packages = [cfg.package]; + }; + + groups.redis-sharkey = { + gid = config.vhack.constants.ids.gids.redis-sharkey; + }; + users.redis-sharkey = { + group = "redis-sharkey"; + uid = config.vhack.constants.ids.uids.redis-sharkey; + }; + }; + }; +} diff --git a/modules/by-name/st/stalwart-mail/module.nix b/modules/by-name/st/stalwart-mail/module.nix index 1ad76c7..4565bf4 100644 --- a/modules/by-name/st/stalwart-mail/module.nix +++ b/modules/by-name/st/stalwart-mail/module.nix @@ -18,7 +18,7 @@ in { options.vhack.stalwart-mail = { enable = lib.mkEnableOption "starwart-mail"; - package = lib.mkPackageOption vhackPackages "stalwart-mail-free" {}; + package = lib.mkPackageOption vhackPackages "stalwart-mail-patched" {}; admin = lib.mkOption { description = '' @@ -41,8 +41,8 @@ in { }; principals = lib.mkOption { - default = []; - type = lib.types.listOf (lib.types.submodule { + default = null; + type = lib.types.nullOr (lib.types.listOf (lib.types.submodule { options = { name = lib.mkOption { type = lib.types.str; @@ -61,7 +61,32 @@ in { }; secret = lib.mkOption { - type = lib.types.str; + type = let + prefix = pre: lib.types.strMatching "^${lib.strings.escapeRegex pre}.*"; + in + lib.types.oneOf [ + (prefix "$argon2") + (prefix "$pbkdf2") + (prefix "$scrypt") + (prefix "$2") # bcrypt + (prefix "$6$") # sha-512 + (prefix "$5$") # sha-256 + (prefix "$sha1") + (prefix "$1") # md5 + (prefix "_") # BSDi crypt + (prefix "{SHA}") # base64 sha + (prefix "{SSHA}") # base64 salted sha + + # unix crypt + (prefix "{CRYPT}") + (prefix "{crypt}") + + # Plain text + (prefix "{PLAIN}") + (prefix "{plain}") + (prefix "{CLEAR}") + (prefix "{clear}") + ]; description = '' Sets the password for the user account. Passwords can be stored hashed or in plain text (not recommended). @@ -77,7 +102,7 @@ in { ''; }; }; - }); + })); }; dataDirectory = lib.mkOption { @@ -160,25 +185,16 @@ in { # However, this decision could obviously be reversed in the future. <2025-02-08> enable = false; inherit (cfg) package; - # dataDir = cfg.dataDirectory; }; - # FIXME(@bpeetz): This is currently needed for a successful acme http-01 challenge. - # We could also use the DNS challenge. <2025-03-01> nginx.virtualHosts."${cfg.fqdn}" = { - enableACME = false; - extraConfig = - # This is copied directly from the nixos nginx module. - # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx) - # We use ^~ here, so that we don't check any regexes (which could - # otherwise easily override this intended match accidentally). - '' - location ^~ /.well-known/acme-challenge/ { - root ${config.security.acme.certs.${cfg.fqdn}.webroot}; - auth_basic off; - auth_request off; - } - ''; + locations."/" = { + proxyPass = "http://${builtins.elemAt config.services.stalwart-mail.settings.server.listener.http.bind 0}"; + recommendedProxySettings = true; + }; + + useACMEHost = "${cfg.fqdn}"; + forceSSL = true; }; redis = { @@ -209,7 +225,7 @@ in { security.acme.certs = { "${cfg.fqdn}" = { domain = cfg.fqdn; - group = "stalwart-mail"; + group = "stalwart-mail-certificates"; }; }; @@ -239,7 +255,7 @@ in { { directory = "${config.services.redis.servers."stalwart-mail".settings.dir}"; user = "stalwart-mail"; - group = "redis"; + group = "redis-stalwart-mail"; mode = "0770"; } ]; @@ -249,20 +265,31 @@ in { # service is restarted on a potentially large number of files. # That would cause unnecessary and unwanted delays. users = { - groups.stalwart-mail = { - gid = config.vhack.constants.ids.gids.stalwart-mail; - }; - users.stalwart-mail = { - isSystemUser = true; - group = "stalwart-mail"; - uid = config.vhack.constants.ids.uids.stalwart-mail; - }; - groups.redis-stalwart-mail = { - gid = config.vhack.constants.ids.gids.redis-stalwart-mail; + groups = { + stalwart-mail = { + gid = config.vhack.constants.ids.gids.stalwart-mail; + }; + stalwart-mail-certificates = { + gid = config.vhack.constants.ids.gids.stalwart-mail-certificates; + }; + redis-stalwart-mail = { + gid = config.vhack.constants.ids.gids.redis-stalwart-mail; + }; }; - users.redis-stalwart-mail = { - group = "redis-stalwart-mail"; - uid = config.vhack.constants.ids.uids.redis-stalwart-mail; + users = { + nginx = { + extraGroups = ["stalwart-mail-certificates"]; + }; + stalwart-mail = { + isSystemUser = true; + group = "stalwart-mail"; + uid = config.vhack.constants.ids.uids.stalwart-mail; + extraGroups = ["stalwart-mail-certificates"]; + }; + redis-stalwart-mail = { + group = "redis-stalwart-mail"; + uid = config.vhack.constants.ids.uids.redis-stalwart-mail; + }; }; }; @@ -321,8 +348,7 @@ in { ${lib.getExe cfg.package} --config="$CACHE_DIRECTORY/mutable_config_file.toml" ''; - Restart = "on-failure"; - RestartSec = 5; + Restart = "no"; KillMode = "process"; KillSignal = "SIGINT"; diff --git a/modules/by-name/st/stalwart-mail/settings.nix b/modules/by-name/st/stalwart-mail/settings.nix index 7032ae0..907cea9 100644 --- a/modules/by-name/st/stalwart-mail/settings.nix +++ b/modules/by-name/st/stalwart-mail/settings.nix @@ -18,6 +18,11 @@ if cfg.security != null then cfg.security.verificationMode else "disable"; + + directory = + if cfg.principals == null + then "internal" + else "in-memory"; in { config.services.stalwart-mail.settings = lib.mkIf cfg.enable { # https://www.rfc-editor.org/rfc/rfc6376.html#section-3.3 @@ -219,7 +224,7 @@ in { require = true; }; rcpt = { - directory = "'in-memory'"; + directory = "'${directory}'"; catch-all = true; subaddressing = true; }; @@ -236,7 +241,7 @@ in { }; auth = { mechanisms = ["LOGIN" "PLAIN"]; - directory = "'in-memory'"; + directory = "'${directory}'"; require = true; must-match-sender = true; errors = { @@ -339,13 +344,13 @@ in { hostname = cfg.fqdn; listener = { - # TODO(@bpeetz): Add this <2025-02-08> - # # HTTP (used for jmap) - # "http" = { - # bind = ["[::]:8080"]; - # protocol = "http"; - # tls.implicit = true; - # }; + # HTTP (used for jmap) + "http" = { + bind = ["127.0.0.1:8112"]; + protocol = "http"; + # handled by ngnix + tls.implicit = false; + }; # IMAP "imap" = { @@ -401,11 +406,12 @@ in { certificate = "default"; }; - # TODO(@bpeetz): Configure that <2025-02-07> - # http = { - # url = ""; - # allowed-endpoint = ["404"]; - # }; + http = { + url = "protocol + '://' + config_get('server.hostname') + ':' + local_port"; + + # We are behind a nginx proxy, and can thus trust this header. + use-x-forwarded = true; + }; auto-ban = { # Ban if the same IP fails to login 10 times in a day @@ -467,6 +473,14 @@ in { # Perform “maintenance” every day at 3 am local time. purge.frequency = "0 3 *"; }; + "rocksdb-directory" = lib.mkIf (cfg.principals == null) { + type = "rocksdb"; + path = "${cfg.dataDirectory}/storage/directory"; + compression = "lz4"; + + # Perform “maintenance” every day at 1 am local time. + purge.frequency = "0 1 *"; + }; "rocksdb-full-text-search" = { type = "rocksdb"; path = "${cfg.dataDirectory}/storage/full-text-search"; @@ -505,7 +519,7 @@ in { full-text.default-language = "en"; fts = "rocksdb-full-text-search"; - directory = "in-memory"; + directory = "${directory}"; lookup = "redis"; @@ -516,9 +530,15 @@ in { encryption.enable = false; }; - directory."in-memory" = { - type = "memory"; - inherit (cfg) principals; + directory = { + "in-memory" = lib.mkIf (cfg.principals != null) { + type = "memory"; + inherit (cfg) principals; + }; + "internal" = lib.mkIf (cfg.principals == null) { + type = "internal"; + store = "rocksdb-directory"; + }; }; certificate = { diff --git a/modules/by-name/sy/system-info/module.nix b/modules/by-name/sy/system-info/module.nix index f04eb49..8136ae5 100644 --- a/modules/by-name/sy/system-info/module.nix +++ b/modules/by-name/sy/system-info/module.nix @@ -34,11 +34,20 @@ "993" = checkEnabled "mail" "mail-imap-tls"; "995" = checkEnabled "mail" "mail-pop3-tls"; + "10222" = checkEnabled "taskchampion-sync" "taskchampion-sync"; + # TODO(@bpeetz): Check which service opens these ports: <2025-01-28> "64738" = "???"; + "4190" = "???"; }; in '' - ${mode} ${builtins.toString port}: ${mappings.${builtins.toString port}} + ${mode} ${builtins.toString port}: ${ + if (builtins.hasAttr "${builtins.toString port}" mappings) + then mappings.${builtins.toString port} + else + builtins.throw + "'${builtins.toString port}' is still missing from the system info port -> name map. Maybe add it?" + } ''; # TODO(@bpeetz): This should probably also include the allowed TCP/UDP port ranges. <2025-01-28> diff --git a/modules/by-name/ta/taskchampion-sync/module.nix b/modules/by-name/ta/taskchampion-sync/module.nix new file mode 100644 index 0000000..a722883 --- /dev/null +++ b/modules/by-name/ta/taskchampion-sync/module.nix @@ -0,0 +1,53 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.taskchampion-sync; + dataDirectory = "/var/lib/taskchampion-sync-server"; +in { + options.vhack.taskchampion-sync = { + enable = lib.mkEnableOption "taskchampion-sync"; + + fqdn = lib.mkOption { + description = "The fully qualified domain name of this instance."; + type = lib.types.str; + example = "task-sync.tw.online"; + }; + }; + + config = lib.mkIf cfg.enable { + users = { + users.taskchampion.uid = config.vhack.constants.ids.uids.taskchampion; + groups.taskchampion.gid = config.vhack.constants.ids.uids.taskchampion; + }; + + vhack = { + persist.directories = [ + { + directory = dataDirectory; + user = "taskchampion"; + group = "taskchampion"; + mode = "0700"; + } + ]; + nginx.enable = true; + }; + + services = { + taskchampion-sync-server = { + enable = true; + dataDir = dataDirectory; + }; + + nginx.virtualHosts."${cfg.fqdn}" = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString config.services.taskchampion-sync-server.port}"; + recommendedProxySettings = true; + }; + enableACME = true; + forceSSL = true; + }; + }; + }; +} diff --git a/modules/by-name/us/users/module.nix b/modules/by-name/us/users/module.nix index a197b13..4be3f41 100644 --- a/modules/by-name/us/users/module.nix +++ b/modules/by-name/us/users/module.nix @@ -3,45 +3,51 @@ lib, pkgs, ... -}: let +}: +let cfg = config.vhack.users; - mkUser = { - name, - password, - uid, - sshKey, - }: { - inherit name; - value = { - inherit name uid; - isNormalUser = true; - home = "/home/${name}"; - hashedPassword = password; - extraGroups = [ - "wheel" - ]; - openssh.authorizedKeys.keys = [ - sshKey - ]; - }; - }; - - extraUsers = lib.listToAttrs (builtins.map mkUser [ + mkUser = { - name = "soispha"; - password = "$y$jFT$3.8XmUyukZvpExMUxDZkI.$IVrJgm8ysNDF/0vDD2kF6w73ozXgr1LMVRNN4Bq7pv1"; - sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIME4ZVa+IoZf6T3U08JG93i6QIAJ4amm7mkBzO14JSkz cardno:000F_18F83532"; - uid = 1000; - } + name, + password, + uid, + sshKey, + }: { - name = "sils"; - password = "$y$jFT$KpFnahVCE9JbE.5P3us8o.$ZzSxCusWqe3sL7b6DLgOXNNUf114tiiptM6T8lDxtKC"; - sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAe4o1PM6VasT3KZNl5NYvgkkBrPOg36dqsywd10FztS openpgp:0x21D20D6A"; - uid = 1001; - } - ]); -in { + inherit name; + value = { + inherit name uid; + isNormalUser = true; + home = "/home/${name}"; + hashedPassword = password; + extraGroups = [ + "wheel" + ]; + openssh.authorizedKeys.keys = [ + sshKey + ]; + }; + }; + + extraUsers = lib.listToAttrs ( + builtins.map mkUser [ + { + name = "soispha"; + password = "$y$jFT$3.8XmUyukZvpExMUxDZkI.$IVrJgm8ysNDF/0vDD2kF6w73ozXgr1LMVRNN4Bq7pv1"; + sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIME4ZVa+IoZf6T3U08JG93i6QIAJ4amm7mkBzO14JSkz cardno:000F_18F83532"; + uid = 1000; + } + { + name = "sils"; + password = "$y$jFT$KpFnahVCE9JbE.5P3us8o.$ZzSxCusWqe3sL7b6DLgOXNNUf114tiiptM6T8lDxtKC"; + sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILn7Oumr5IYtTTIKRFvDnofGXXiDLBQE9jVF+7UE+4G5 vhack.eu"; + uid = 1001; + } + ] + ); +in +{ options.vhack.users = { enable = lib.mkEnableOption "user setup"; }; @@ -51,14 +57,12 @@ in { mutableUsers = false; defaultUserShell = pkgs.bashInteractive; - users = - { - root = { - hashedPassword = lib.mkForce null; # to lock root - openssh.authorizedKeys.keys = lib.mkForce []; - }; - } - // extraUsers; + users = { + root = { + hashedPassword = lib.mkForce null; # to lock root + openssh.authorizedKeys.keys = lib.mkForce [ ]; + }; + } // extraUsers; # TODO(@bpeetz): Is this still relevant? # If it is, it should be moved to a separate module. <2024-12-24> |