diff options
| author | Jakub Stachurski <j.stachurski@student.utwente.nl> | 2026-01-25 14:11:14 +0100 |
|---|---|---|
| committer | Jakub Stachurski <j.stachurski@student.utwente.nl> | 2026-01-25 14:11:14 +0100 |
| commit | 645e86a443d3f74cc8f9c95cb433cbefed5263cb (patch) | |
| tree | 65ab53d20b29792af60a6c7b1894c4cb192832a5 /services | |
| parent | 24ae3d26155fc70cf381422f9dedbbbb33b425d8 (diff) | |
Inital commit for working omega-relay host.
This commit includes:
- Mysql module from umbriel
- Disko configuration for the Ferox VM
- Freshrss module
- Stalwart module
- Vaultwarden module
- Wakapi module
- Uptime Kuma module
- Support for using mysql socket
- Server user that does not depend on full home-manager preset.
- ACME for wilkuu.xyz domains, including all the services.
Diffstat (limited to '')
| -rw-r--r-- | services/email.nix | 142 | ||||
| -rw-r--r-- | services/freshrss.nix | 81 | ||||
| -rw-r--r-- | services/mysql.nix | 125 | ||||
| -rw-r--r-- | services/uptimekuma.nix | 78 | ||||
| -rw-r--r-- | services/vaultwarden.nix | 76 | ||||
| -rw-r--r-- | services/wakapi.nix | 110 |
6 files changed, 612 insertions, 0 deletions
diff --git a/services/email.nix b/services/email.nix new file mode 100644 index 0000000..d3f60ab --- /dev/null +++ b/services/email.nix @@ -0,0 +1,142 @@ +{config, pkgs, lib, ...}: +let + cfg = config.wilkuu.services.stalwart; + hostname = config.networking.hostName; +in +{ + options.wilkuu.services.stalwart = with lib; { + domain = mkOption { + type = types.str; + default = "mail.${hostname}.local"; + example = "mail.wilkuu.xyz"; + description = "Domain for http connections."; + }; + wellKnownDomains = mkOption { + type = types.listOf types.str; + default = [ "${hostname}.local" ]; + example = [ "wilkuu.xyz"] ; + description = "Domain for well-known items"; + }; + doACME = mkEnableOption "Enable ACME for stalwart here"; + enable = mkEnableOption "Enable the email service"; + dataDir = mkOption { + type = types.path; + description = "Storage localtion for Stalwart user data"; + default = "/srv/data/stalwart"; + example = "/srv/data/stalwart"; + }; + }; + + config = lib.mkIf cfg.enable (let + sopsPath = ../secrets/${hostname}/stalwart.yaml; + secrets = ["user_admin_password"]; + toSops = (sname: "stalwart/${sname}"); + toCredfilePath = (name: config.sops.secrets.${toSops name}.path); + toStalwartCred = name: "%{file:/run/credentials/stalwart-mail.service/${name}}%"; + + basicListener = proto: port: tls: { + bind = ["[::]:${toString port}"]; + protocol = proto; + tls.implicit = tls; + }; + + proxyWellKnown = names: let + uris = map (n: "/.well-known/${n}") names; + in (lib.genAttrs uris (uri: { + proxyPass = "http://localhost:3080${uri}"; + recommendedProxySettings = true; + })); + + makeHttpRedirect = target: https: { return = "302 ${if https then "https" else "http"}://${target}";}; + + in { + networking.hosts = { + "127.0.0.1" = [cfg.domain]; + }; + + # Need this bc otherwise sops will complain for some reason + users = { + groups.stalwart-mail = { }; + users.stalwart-mail = { + isSystemUser = true; + group = "stalwart-mail"; + }; + }; + + + # TODO: Move this into a util function or option; + sops.secrets = + (lib.genAttrs (map toSops secrets) + (name: { + sopsFile = sopsPath; + mode = "0440"; + owner = "stalwart-mail"; + })); + + + services.nginx.virtualHosts = (lib.genAttrs cfg.wellKnownDomains ( + (wdomain: { + locations = (proxyWellKnown ["jmap" "mta-sts.txt" "mail-v1.xml" "autoconfig/mail"]) + // (lib.genAttrs ["/.well_known/caldav" "/.well_known/webdav" ] (uri: (makeHttpRedirect "${cfg.domain}${uri}") cfg.doACME )); + }))) + // + + (lib.genAttrs (map (x: "${x}${cfg.domain}") ["" "autodiscover." "autoconfig."]) (domain: { + addSSL = cfg.doACME; + enableACME = cfg.doACME; + serverName = "${domain}"; + locations."/" = { + proxyPass = "http://localhost:3080"; + recommendedProxySettings = true; + }; + })); + + + services.stalwart-mail = { + enable = true; + dataDir = cfg.dataDir; + openFirewall = false; + credentials = lib.genAttrs secrets toCredfilePath; + settings = { + server.listener = { + smtp = basicListener "smtp" 25 false; + submission = basicListener "smtp" 465 true; + imaptls = basicListener "imap" 993 true; + imap = basicListener "imap" 143 true; + webdav = basicListener "http" 3080 false; + jmap = basicListener "http" 3080 false; + http = basicListener "http" 3080 false; + }; + + store.rocksdb = { + type = "rocksdb"; + path = cfg.dataDir; + compression = "lz4"; + }; + + directory.internal = { + type = "internal"; + store = "rocksdb"; + }; + + storage = { + data = "rocksdb"; + fts = "rocksdb"; + blob = "rocksdb"; + lookup = "rocksdb"; + directory = "internal"; + }; + + authentication.fallback-admin = { + user = "admin"; + secret = toStalwartCred "user_admin_password"; + }; + + http = { + use-x-forwarded = true; + url = "protocol + \"${cfg.domain}\""; + }; + }; + }; + }); +} diff --git a/services/freshrss.nix b/services/freshrss.nix new file mode 100644 index 0000000..0b66755 --- /dev/null +++ b/services/freshrss.nix @@ -0,0 +1,81 @@ +{config, lib, pkgs, ...}: +let + cfg = config.wilkuu.services.freshrss; + hostname = config.networking.hostName; +in +{ + + options.wilkuu.services.freshrss = with lib; { + domain = mkOption { + type = types.str; + default = "rss.${hostname}.local"; + example = "rss.wilkuu.xyz"; + description = "Domain for http connections."; + }; + doACME = mkEnableOption "Enable ACME for fresh-rss here"; + enable = mkEnableOption "Enable the fresh-rss service"; + dataDir = mkOption { + type = types.path; + description = "Storage localtion for fresh-rss data"; + default = "/srv/data/freshrss"; + example = "/srv/data/freshrss"; + }; + }; + + config = lib.mkIf cfg.enable (let + sopsPath = ../secrets/${hostname}/freshrss.yaml; + secrets = ["admin_password" "db_pass"]; + toSops = (sname: "fresh-rss/${sname}"); + in { + networking.hosts = { + "127.0.0.1" = [cfg.domain]; + }; + + sops.secrets = + (lib.genAttrs (map toSops secrets) + (name: { + sopsFile = sopsPath; + mode = "0440"; + owner = config.services.freshrss.user; + })); + + services.nginx.virtualHosts."${cfg.domain}" = { + addSSL = cfg.doACME; + enableACME = cfg.doACME; + }; + + wilkuu.services.mysql = { + enable = true; + users."freshrss" = { + sopsPlaceholder = config.sops.placeholder."fresh-rss/db_pass"; + }; + databases."freshrss" = { + enable = true; + allowedUsers = ["freshrss"]; + }; + }; + + services.freshrss = { + enable = true; + # api.enable = true; + dataDir = cfg.dataDir; + baseUrl = "https://${cfg.domain}"; + extensions = with pkgs.freshrss-extensions; [ + youtube + title-wrap + auto-ttl + reading-time + ]; + passwordFile = config.sops.secrets."fresh-rss/admin_password".path; + virtualHost = cfg.domain; + database = { + passFile = config.sops.secrets."fresh-rss/db_pass".path; + host = "localhost"; + port = config.wilkuu.services.mysql.port; + name = "freshrss"; + user = "freshrss"; + type = "mysql"; + }; + }; + }); +} diff --git a/services/mysql.nix b/services/mysql.nix new file mode 100644 index 0000000..8a7b5e2 --- /dev/null +++ b/services/mysql.nix @@ -0,0 +1,125 @@ +{ + pkgs, + config, + lib, + ... +}: +let + cfg = config.wilkuu.services.mysql; + + create_users_ensure = + uname: + (lib.genAttrs (lib.map (dn: "${dn}.*") ( + lib.attrNames (lib.filterAttrs (_: dcfg: (builtins.elem uname dcfg.allowedUsers)) cfg.databases) + )) (_: "ALL PRIVILEGES")); + + priviledge_clause = + name: db: priv: + ("GRANT ${priv} ON ${db} TO ${name};"); + + add-user-clauses = + name: ucfg: + if (!isNull ucfg.sopsPlaceholder) then + ( + '' + -- Clauses for user ${name} + ALTER USER IF EXISTS '${name}'@'%' IDENTIFIED BY '${ucfg.sopsPlaceholder}'; + CREATE USER IF NOT EXISTS '${name}'@'%' IDENTIFIED BY '${ucfg.sopsPlaceholder}'; + '' + + (lib.concatMapAttrsStringSep "\n" (priviledge_clause name) (create_users_ensure name)) + ) + else + " -- Ommitted user ${name}"; + + add-unix-user-clauses = + name: '' + -- Clauses for user ${name} + ALTER USER IF EXISTS '${name}'@'localhost' IDENTIFIED BY unix_socket'; + CREATE USER IF NOT EXISTS '${name}'@'localhost' IDENTIFIED BY unix_socket'; + '' + + (lib.concatMapAttrsStringSep "\n" (priviledge_clause name) (create_users_ensure name)); + +in +{ + options.wilkuu.services.mysql = { + enable = lib.mkEnableOption "Enable database for containers"; + port = + with lib; + mkOption { + type = types.port; + default = 3306; + }; + databases = + with lib; + mkOption { + type = types.attrsOf ( + types.submodule { + options = { + enable = mkEnableOption "Enable the database"; + allowedUsers = mkOption { + type = types.listOf types.str; + }; + }; + } + ); + default = { }; + }; + users = + with lib; + mkOption { + type = types.attrsOf ( + types.submodule { + options = { + scramPassword = mkOption { + type = types.nullOr types.str; + default = null; + }; + sopsPlaceholder = mkOption { + type = types.nullOr types.str; + default = null; + }; + allowedRanges = mkOption { + type = types.listOf types.str; + }; + }; + } + ); + default = { }; + }; + unix_users = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "Users that can identify using the unix socket"; + default = []; + example = ["wakapi"]; + }; + }; + # config.sops.secrets = lib.mkIf cfg.enable { + # "database/root_pass" = { + # sopsFile = ../secrets/${config.networking.hostName}/secrets.yaml; + # }; + # }; + config.sops.templates."init-mysql" = { + owner = config.systemd.services.mysql.serviceConfig.User; + content = (lib.concatLines ((builtins.attrValues (builtins.mapAttrs add-user-clauses cfg.users)) ++ (map add-unix-user-clauses cfg.unix_users))); + }; + + config.services.mysql = { + enable = cfg.enable; + ensureDatabases = builtins.attrNames cfg.databases; + initialScript = config.sops.templates."init-mysql".path; + package = pkgs.mariadb; + settings = { + mysqld = { + log_error = "/var/log/mysql_err.log"; + log_warnings = 2; + }; + }; + }; + + # config.host-config.utilpkgs = lib.mkIf (cfg.enable) ( + # with pkgs; + # [ + # mycli + # ] + # ); +} diff --git a/services/uptimekuma.nix b/services/uptimekuma.nix new file mode 100644 index 0000000..4cdd5f2 --- /dev/null +++ b/services/uptimekuma.nix @@ -0,0 +1,78 @@ +{config, lib, pkgs, ...}: +let + cfg = config.wilkuu.services.uptimekuma; + hostname = config.networking.hostName; +in +{ + options.wilkuu.services.uptimekuma = with lib; { + domain = mkOption { + type = types.str; + default = "uptime.${hostname}.local"; + example = "uptime.wilkuu.xyz"; + description = "Domain for http connections."; + }; + doACME = mkEnableOption "Enable ACME for uptime kuma here"; + enable = mkEnableOption "Enable the uptime-kuma service"; + dataDir = mkOption { + type = types.path; + description = "Storage localtion for uptime kuma data, currently ignored, because nixpkgs sucks"; + default = "/srv/data/uptimekuma"; + example = "/srv/data/uptimekuma"; + }; + }; + + config = lib.mkIf cfg.enable (let + # sopsPath = ../secrets/${hostname}/vaultwarden.yaml; + # secrets = []; + # toSops = (sname: "uptime-kuma/${sname}"); + in { + networking.hosts = { + "127.0.0.1" = [cfg.domain]; + }; + + users.users.uptimekuma = { + isSystemUser = true; + group = "uptimekuma"; + }; + users.groups.uptimekuma = {}; + + systemd.services.uptime-kuma.serviceConfig.User = "uptimekuma"; + + # sops.secrets = + # (lib.genAttrs (map toSops secrets) + # (name: { + # sopsFile = sopsPath; + # mode = "0440"; + # owner = "uptime-kuma"; + # })); + + services.nginx.virtualHosts."${cfg.domain}" = { + addSSL = cfg.doACME; + enableACME = cfg.doACME; + locations."/" = { + proxyPass = "http://localhost:3111"; + recommendedProxySettings = true; + }; + }; + + wilkuu.services.mysql = let + user = config.systemd.services.uptime-kuma.serviceConfig.User; + in { + unix_users = [user]; + databases.uptimekuma = { + enable = true; + allowedUsers = [user]; + }; + }; + + services.uptime-kuma = { + enable = true; + settings = { + UPTIME_KUMA_PORT = "3111"; + UPTIME_KUMA_HOST="127.0.0.1"; + UPTIME_KUMA_DB_TYPE = "sqlite"; + UPTIME_KUMA_DB_SOCKET = "/var/lib/mysql/mysql.sock"; + }; + }; + }); +} diff --git a/services/vaultwarden.nix b/services/vaultwarden.nix new file mode 100644 index 0000000..f68a2fa --- /dev/null +++ b/services/vaultwarden.nix @@ -0,0 +1,76 @@ +{config, pkgs, lib, ...}: +let + cfg = config.wilkuu.services.vaultwarden; + hostname = config.networking.hostName; +in +{ + options.wilkuu.services.vaultwarden = with lib; { + domain = mkOption { + type = types.str; + default = "bitwarden.${hostname}.local"; + example = "bitwarden.wilkuu.xyz"; + description = "Domain for http connections."; + }; + doACME = mkEnableOption "Enable ACME for vaultwarden here"; + enable = mkEnableOption "Enable the vaultwarden service"; + backupDir = mkOption { + type = types.path; + description = "Storage localtion for Vaultwarden user data backup"; + default = "/srv/data/vaultwarden"; + example = "/srv/data/vaultwarden"; + }; + signupWhitelist = mkOption { + type = types.listOf types.str; + default = []; + example = ["wilkuu.xyz"]; + description = "Domains that can sign up on vaultwarden"; + }; + }; + + config = lib.mkIf cfg.enable (let + sopsPath = ../secrets/${hostname}/vaultwarden.yaml; + secrets = ["admin_token"]; + toSops = (sname: "vaultwarden/${sname}"); + toCredfilePath = (name: config.sops.secrets.${toSops name}.path); + in { + networking.hosts = { + "127.0.0.1" = [cfg.domain]; + }; + + sops.secrets = + (lib.genAttrs (map toSops secrets) + (name: { + sopsFile = sopsPath; + mode = "0440"; + owner = "vaultwarden"; + })); + + sops.templates.vaultwardenEnvFile.content = '' + ADMIN_TOKEN=${config.sops.placeholder."vaultwarden/admin_token"} + ''; + + services.nginx.virtualHosts."${cfg.domain}" = { + enableACME = cfg.doACME; + addSSL = cfg.doACME; + locations."/" = { + proxyPass = "http://localhost:3222"; + recommendedProxySettings = true; + }; + }; + + services.vaultwarden = { + enable = cfg.enable; + backupDir = "/srv/data/vaultwarden"; + config = { + DOMAIN=cfg.domain; + ROCKET_ADDRESS = "127.0.0.1"; + ROCKET_PORT ="3222"; + SIGNUPS_DOMAINS_WHITELIST=(lib.concatStringsSep "," cfg.signupWhitelist); + SIGNUPS_ALLOWED="false"; + IP_HEADER="X-Forwarded-For"; + }; + environmentFile = config.sops.templates.vaultwardenEnvFile.path; + }; + + }); +} diff --git a/services/wakapi.nix b/services/wakapi.nix new file mode 100644 index 0000000..1b1e312 --- /dev/null +++ b/services/wakapi.nix @@ -0,0 +1,110 @@ +{config, lib, pkgs, ...}: +let + cfg = config.wilkuu.services.wakapi; + hostname = config.networking.hostName; + service_user = config.systemd.services.wakapi.serviceConfig.User; +in +{ + options.wilkuu.services.wakapi = with lib; { + domain = mkOption { + type = types.str; + default = "wakapi.${hostname}.local"; + example = "wakapi.wilkuu.xyz"; + description = "Domain for http connections."; + }; + email = mkOption { + type = types.str; + default = "wakapi@${hostname}.local"; + example = "noreply@wilkuu.xyz"; + description = "Mailer address"; + }; + doACME = mkEnableOption "Enable ACME for wakapi here"; + enable = mkEnableOption "Enable the wakapi service"; + dataDir = mkOption { + type = types.path; + description = "Storage localtion for wakapi data"; + default = "/srv/data/wakapi"; + example = "/srv/data/wakapi"; + }; + }; + + config = lib.mkIf cfg.enable (let + sopsPath = ../secrets/${hostname}/wakapi.yaml; + secrets = ["password_salt"]; + toSops = (sname: "wakapi/${sname}"); + in { + networking.hosts = { + "127.0.0.1" = [cfg.domain]; + }; + + sops.secrets = + (lib.genAttrs (map toSops secrets) + (name: { + sopsFile = sopsPath; + mode = "0440"; + owner = service_user; + })); + + services.nginx.virtualHosts."${cfg.domain}" = { + enableACME = cfg.doACME; + addSSL = cfg.doACME; + locations."/" = { + proxyPass = "http://localhost:3111"; + recommendedProxySettings = true; + }; + }; + + wilkuu.services.mysql = let + in { + unix_users = [service_user]; + databases.wakapi = { + enable = true; + allowedUsers = [service_user]; + }; + }; + + services.wakapi = { + enable = true; + stateDir = cfg.dataDir; + passwordSaltFile = config.sops.secrets.wakapi/password_salt; + settings = { + server = { + port = 3111; + public_url = cfg.domain; + }; + app = { + leaderboard_enabled = false; + leaderboard_require_auth = true; + inactive_days = 7; # time of previous days within a user must have logged in to be considered active + # go time format strings to format human-readable dates + # for details, check https://pkg.go.dev/time#Time.Format + date_format= "Mon, 02 Jan 2006"; + datetime_format= "Mon, 02 Jan 2006 15:04"; + }; + db = { + socket= "/var/lib/mysql/mysql.sock"; + name = "wakapi"; + dialect = "mysql"; + charset = "utf8mb4"; + }; + security = { + insecure_cookies = false; + trust_reverse_proxy_ips= "127.0.0.1"; + }; + mail = { + # FIXME: Add email + enabled = false; + provider = "smtp"; + sender = "<Wakapi ${cfg.email}>"; + smtp = { + }; + }; + }; + database = { + dialect = "mysql"; + createLocally = false; + }; + + }; + }); +} |
