From f291e88d86fc680ee171a406c4d5cfc04a3b4f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 25 Feb 2023 04:24:27 +0100 Subject: [PATCH] Initial proposal for momo infrastructure setup --- hosts/{flora-6/flora-6.nix => 0001/0001.nix} | 32 +++-- hosts/0001/caddy.nix | 55 ++++++++ hosts/{flora-6 => 0001}/default.nix | 2 +- hosts/{flora-6 => 0001}/drone.nix | 0 hosts/0001/gitea.nix | 120 ++++++++++++++++++ .../hardware-configuration.nix | 0 hosts/0001/keycloak.nix | 73 +++++++++++ hosts/{flora-6 => 0001}/mailman.nix | 0 hosts/0001/nextcloud.nix | 88 +++++++++++++ hosts/{flora-6 => 0001}/postfix/main.cf | 0 hosts/{flora-6 => 0001}/triton-vmtools.nix | 0 hosts/flora-6/caddy.nix | 99 --------------- hosts/flora-6/gitea.nix | 72 ----------- hosts/flora-6/keycloak.nix | 29 ----- modules/infra-node/default.nix | 42 ++++++ 15 files changed, 402 insertions(+), 210 deletions(-) rename hosts/{flora-6/flora-6.nix => 0001/0001.nix} (85%) create mode 100644 hosts/0001/caddy.nix rename hosts/{flora-6 => 0001}/default.nix (62%) rename hosts/{flora-6 => 0001}/drone.nix (100%) create mode 100644 hosts/0001/gitea.nix rename hosts/{flora-6 => 0001}/hardware-configuration.nix (100%) create mode 100644 hosts/0001/keycloak.nix rename hosts/{flora-6 => 0001}/mailman.nix (100%) create mode 100644 hosts/0001/nextcloud.nix rename hosts/{flora-6 => 0001}/postfix/main.cf (100%) rename hosts/{flora-6 => 0001}/triton-vmtools.nix (100%) delete mode 100644 hosts/flora-6/caddy.nix delete mode 100644 hosts/flora-6/gitea.nix delete mode 100644 hosts/flora-6/keycloak.nix create mode 100644 modules/infra-node/default.nix diff --git a/hosts/flora-6/flora-6.nix b/hosts/0001/0001.nix similarity index 85% rename from hosts/flora-6/flora-6.nix rename to hosts/0001/0001.nix index d7c59716..b2755b89 100644 --- a/hosts/flora-6/flora-6.nix +++ b/hosts/0001/0001.nix @@ -16,7 +16,6 @@ in { ./triton-vmtools.nix ./caddy.nix - ./drone.nix ./keycloak.nix ./gitea.nix ./mailman.nix @@ -27,11 +26,18 @@ in { "${latestModulesPath}/services/misc/gitea.nix" ]; + disabledModules = [ "services/misc/gitea.nix" ]; config = { + age.secrets.mailing-password = { + file = "${self}/secrets/gitea-database-password.age"; + mode = "700"; + owner = "root"; + }; + # # # # # # pub.solar options # # # @@ -41,6 +47,16 @@ in { lite = true; }; + pub-solar.infra-node = { + mailing = { + type = "smtp"; + user = "admin@momo.koeln"; + host = "mx2.greenbaum.cloud:465"; + from = ''"pub.solar git server" ''; + passwordFile = config.age.secrets.mailing-password.path; + }; + }; + # Allow sudo without a password for the barkeeper user security.sudo.extraRules = [ { @@ -55,19 +71,17 @@ in { ]; # Machine user for CI pipelines - users.users.hakkonaut = { - description = "CI and automation user"; + users.users.www-user = { + description = "user"; home = "/var/nix/iso-cache"; useDefaultShell = true; - uid = 998; - group = "hakkonaut"; + uid = 10001; + group = "www-user"; isSystemUser = true; - openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGP5MvCwNRtCcP1pSDrn0XZTNlpOqYnjHDm9/OI4hECW hakkonaut@flora-6" - ]; + openssh.authorizedKeys.keys = []; }; - users.groups.hakkonaut = {}; + users.groups.www-user = {}; # # # # # # Triton host specific options diff --git a/hosts/0001/caddy.nix b/hosts/0001/caddy.nix new file mode 100644 index 00000000..d306327f --- /dev/null +++ b/hosts/0001/caddy.nix @@ -0,0 +1,55 @@ +{ + config, + lib, + pkgs, + self, + ... +}: { + services.caddy = { + enable = lib.mkForce true; + group = "www-user"; + email = "admins@pub.solar"; + globalConfig = lib.mkForce ""; + virtualHosts = { + "momo.koeln" = { + logFormat = lib.mkForce '' + output discard + ''; + extraConfig = '' + # website + handle { + root * /srv/www/momo.koeln + try_files {path}.html {path} + file_server + } + # minimal error handling, respond with status code and text + handle_errors { + respond "{http.error.status_code} {http.error.status_text}" + } + ''; + }; + "www.momo.koeln" = { + logFormat = lib.mkForce '' + output discard + ''; + extraConfig = '' + redir https://momo.koeln{uri} + ''; + }; + "list.momo.koeln" = { + logFormat = lib.mkForce '' + output discard + ''; + extraConfig = '' + handle_path /static/* { + root * /var/lib/mailman/web + file_server + } + + reverse_proxy :8000 + ''; + }; + }; + }; + networking.firewall.allowedTCPPorts = [80 443]; +} diff --git a/hosts/flora-6/default.nix b/hosts/0001/default.nix similarity index 62% rename from hosts/flora-6/default.nix rename to hosts/0001/default.nix index 98269bfe..7865464f 100644 --- a/hosts/flora-6/default.nix +++ b/hosts/0001/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./flora-6.nix + ./0001.nix ]; } diff --git a/hosts/flora-6/drone.nix b/hosts/0001/drone.nix similarity index 100% rename from hosts/flora-6/drone.nix rename to hosts/0001/drone.nix diff --git a/hosts/0001/gitea.nix b/hosts/0001/gitea.nix new file mode 100644 index 00000000..729cd5c7 --- /dev/null +++ b/hosts/0001/gitea.nix @@ -0,0 +1,120 @@ +{ + config, + lib, + pkgs, + self, + ... +}: let + hostAddress = "10.10.42.1"; + serviceAddress = "10.10.42.3"; + + hostname = "git.momo.koeln"; + + dbUserName = "gitea"; + + hostStateDir = "/mnt/internal/gitea"; + containerStateDir = "/var/lib/gitea"; +in { + age.secrets.gitea-database-password = { + file = "${self}/secrets/gitea-database-password.age"; + mode = "600"; + owner = "gitea"; + }; + + age.secrets.gitea-mailer-password = { + file = "${self}/secrets/gitea-mailer-password.age"; + mode = "600"; + owner = "gitea"; + }; + + services.caddy.virtualHosts.${hostname} = { + logFormat = lib.mkForce '' + output discard + ''; + extraConfig = '' + redir /user/login /user/oauth2/${config.containers.keycloak.config.services.keycloak.settings.hostname} temporary + reverse_proxy ${serviceAddress}:8080 + ''; + }; + + containers."gitea" = { + privateNetwork = true; + hostAddress = hostAddress; + localAddress = serviceAddress; + + bindMounts."${containerStateDir}" = { + hostPath = hostStateDir; + isReadOnly = false; + }; + + bindMounts."${config.age.secrets.gitea-database-password.path}" = { + hostPath = config.age.secrets.gitea-database-password.path; + isReadOnly = true; + }; + + bindMounts."${config.age.secrets.gitea-mailer-password.path}" = { + hostPath = config.age.secrets.gitea-mailer-password.path; + isReadOnly = true; + }; + + config = { + config, + pkgs, + ... + }: { + # gitea + services.gitea = { + enable = true; + appName = "pub.solar git server"; + database = { + type = "postgres"; + passwordFile = config.age.secrets.gitea-database-password.path; + }; + domain = domain; + httpAddress = "0.0.0.0"; + httpPort = 3000; + lfs.enable = true; + mailerPasswordFile = config.pub-solar.infra-node.mailing.passwordFile; + rootUrl = "https://git.pub.solar"; + settings = { + mailer = mkIf config.pub-solar.infra-node.mailing.enabled { + ENABLED = true; + MAILER_TYPE = config.pub-solar.infra-node.mailing.type; + HOST = config.pub-solar.infra-node.mailing.host; + FROM = config.pub-solar.infra-node.mailing.from; + USER = config.pub-solar.infra-node.mailing.user; + }; + # currently broken, gpg core dumps + #"repository.signing" = { + # SIGNING_KEY = "default"; + # MERGES = "always"; + #}; + openid = { + ENABLE_OPENID_SIGNIN = true; + ENABLE_OPENID_SIGNUP = true; + }; + # uncomment after initial deployment, first user is admin user + # required to setup SSO (oauth openid-connect, keycloak auth provider) + service.ALLOW_ONLY_EXTERNAL_REGISTRATION = true; + session.COOKIE_SECURE = lib.mkForce true; + }; + }; + + # Required for gitea server side gpg signatures + # configured / setup manually in + # /var/lib/gitea/data/home/.gitconfig and + # /var/lib/gitea/data/home/.gnupg/ + programs.gnupg.agent = { + enable = true; + pinentryFlavor = "curses"; + }; + # Required to make gpg work without a graphical environment? + # otherwise generating a new gpg key fails with this error: + # gpg: agent_genkey failed: No pinentry + # see: https://github.com/NixOS/nixpkgs/issues/97861#issuecomment-827951675 + environment.variables = { + GPG_TTY = "$(tty)"; + }; + }; + }; +} diff --git a/hosts/flora-6/hardware-configuration.nix b/hosts/0001/hardware-configuration.nix similarity index 100% rename from hosts/flora-6/hardware-configuration.nix rename to hosts/0001/hardware-configuration.nix diff --git a/hosts/0001/keycloak.nix b/hosts/0001/keycloak.nix new file mode 100644 index 00000000..8b1a8a31 --- /dev/null +++ b/hosts/0001/keycloak.nix @@ -0,0 +1,73 @@ +{ + config, + lib, + inputs, + pkgs, + self, + ... +}: let + hostAddress = "10.10.42.1"; + serviceAddress = "10.10.42.1"; + + hostname = "auth.momo.koeln"; + + dbUserName = "keycloak"; + + hostStateDir = "/mnt/internal/keycloak"; + containerStateDir = "/var/lib/keycloak"; +in { + age.secrets.keycloak-database-password = { + file = "${self}/secrets/keycloak-database-password.age"; + mode = "700"; + #owner = "keycloak"; + }; + + services.caddy.virtualHosts.${hostname} = { + logFormat = lib.mkForce '' + output discard + ''; + extraConfig = '' + redir / /realms/momo.koeln/account temporary + reverse_proxy ${serviceAddress}:8080 + ''; + }; + + containers."keycloak" = { + privateNetwork = true; + hostAddress = hostAddress; + localAddress = serviceAddress; + + bindMounts."${containerStateDir}" = { + hostPath = hostStateDir; + isReadOnly = false; + }; + + bindMounts."${config.age.secrets.keycloak-database-password.path}" = { + hostPath = config.age.secrets.keycloak-database-password.path; + isReadOnly = true; + }; + + config = { + config, + pkgs, + ... + }: { + # keycloak + services.keycloak = { + enable = true; + database.passwordFile = config.age.secrets.keycloak-database-password.path; + + settings = { + hostname = domain; + http-host = "0.0.0.0"; + http-port = 8080; + proxy = "edge"; + }; + + # themes = { + # "momo.koeln" = inputs.keycloak-theme-pub-solar.legacyPackages.${pkgs.system}.keycloak-theme-pub-solar; + # }; + }; + }; + }; +} diff --git a/hosts/flora-6/mailman.nix b/hosts/0001/mailman.nix similarity index 100% rename from hosts/flora-6/mailman.nix rename to hosts/0001/mailman.nix diff --git a/hosts/0001/nextcloud.nix b/hosts/0001/nextcloud.nix new file mode 100644 index 00000000..1aeb906f --- /dev/null +++ b/hosts/0001/nextcloud.nix @@ -0,0 +1,88 @@ +{ + config, + lib, + inputs, + pkgs, + self, + ... +}: let + hostAddress = "10.10.42.1"; + serviceAddress = "10.10.42.2"; + + hostname = "cloud.momo.koeln"; + + dbUserName = "nextcloud"; + + hostStateDir = "/mnt/internal/nextcloud"; + containerStateDir = "/var/lib/nextcloud"; +in { + age.secrets.nextcloud-db-password = { + file = "${self}/secrets/nextcloud-db-password.age"; + mode = "700"; + owner = "nextcloud"; + }; + + age.secrets.nextcloud-admin-password = { + file = "${self}/secrets/nextcloud-admin-password"; + mode = "700"; + owner = "nextcloud"; + }; + + services.caddy.virtualHosts.${hostname} = { + logFormat = lib.mkForce '' + output discard + ''; + extraConfig = '' + reverse_proxy ${serviceAddress}:80 + ''; + }; + + containers."nextcloud" = { + privateNetwork = true; + hostAddress = hostAddress; + localAddress = serviceAddress; + + bindMounts."${containerStateDir}" = { + hostPath = hostStateDir; + isReadOnly = false; + }; + + config = { + config, + pkgs, + ... + }: { + networking.firewall.allowedTCPPorts = [80]; + + # nextcloud + services.nextcloud = { + enable = true; + hostName = hostname; + home = containerStateDir; + config = { + dbuser = dbUserName; + dbtype = "pgsql"; + dbport = 5432; + dbpassFile = config.age.secrets.nextcloud-db-password.path; + + adminUser = "admin"; + adminpassFile = config.age.secrets.nextcloud-admin-password.path; + }; + }; + + services.postgresql = { + enable = true; + ensureUsers = [ + { + name = dbUserName; + ensurePermissions = { + "DATABASE nextcloud" = "ALL PRIVILEGES"; + }; + } + ]; + + ensureDatabases = ["nextcloud"]; + }; + }; + }; +} diff --git a/hosts/flora-6/postfix/main.cf b/hosts/0001/postfix/main.cf similarity index 100% rename from hosts/flora-6/postfix/main.cf rename to hosts/0001/postfix/main.cf diff --git a/hosts/flora-6/triton-vmtools.nix b/hosts/0001/triton-vmtools.nix similarity index 100% rename from hosts/flora-6/triton-vmtools.nix rename to hosts/0001/triton-vmtools.nix diff --git a/hosts/flora-6/caddy.nix b/hosts/flora-6/caddy.nix deleted file mode 100644 index 648bfe33..00000000 --- a/hosts/flora-6/caddy.nix +++ /dev/null @@ -1,99 +0,0 @@ -{ - config, - lib, - pkgs, - self, - ... -}: { - services.caddy = { - enable = lib.mkForce true; - group = "hakkonaut"; - email = "admins@pub.solar"; - globalConfig = lib.mkForce ""; - virtualHosts = { - "pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - # PubSolarOS images - handle /os/download/* { - root * /srv/www - file_server /os/download/* browse - } - # serve base domain pub.solar for mastodon.pub.solar - # https://masto.host/mastodon-usernames-different-from-the-domain-used-for-installation/ - handle /.well-known/host-meta { - redir https://mastodon.pub.solar{uri} - } - # pub.solar website - handle { - root * /srv/www/pub.solar - try_files {path}.html {path} - file_server - } - # minimal error handling, respond with status code and text - handle_errors { - respond "{http.error.status_code} {http.error.status_text}" - } - ''; - }; - "www.pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - redir https://pub.solar{uri} - ''; - }; - "auth.pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - redir / /realms/pub.solar/account temporary - reverse_proxy :8080 - ''; - }; - "git.pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - redir /user/login /user/oauth2/keycloak temporary - reverse_proxy :3000 - ''; - }; - "ci.pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - reverse_proxy :4000 - ''; - }; - "list.pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - handle_path /static/* { - root * /var/lib/mailman/web - file_server - } - - reverse_proxy :8000 - ''; - }; - "obs-portal.pub.solar" = { - logFormat = lib.mkForce '' - output discard - ''; - extraConfig = '' - reverse_proxy obs-portal.svc.e5756d08-36fd-424b-f8bc-acdb92ca7b82.lev-1.int.greenbaum.zone:3000 - ''; - }; - }; - }; - networking.firewall.allowedTCPPorts = [80 443]; -} diff --git a/hosts/flora-6/gitea.nix b/hosts/flora-6/gitea.nix deleted file mode 100644 index 091be889..00000000 --- a/hosts/flora-6/gitea.nix +++ /dev/null @@ -1,72 +0,0 @@ -{ - config, - lib, - pkgs, - self, - ... -}: { - age.secrets.gitea-database-password = { - file = "${self}/secrets/gitea-database-password.age"; - mode = "600"; - owner = "gitea"; - }; - age.secrets.gitea-mailer-password = { - file = "${self}/secrets/gitea-mailer-password.age"; - mode = "600"; - owner = "gitea"; - }; - - # gitea - services.gitea = { - enable = true; - appName = "pub.solar git server"; - database = { - type = "postgres"; - passwordFile = config.age.secrets.gitea-database-password.path; - }; - domain = "git.pub.solar"; - httpAddress = "127.0.0.1"; - httpPort = 3000; - lfs.enable = true; - mailerPasswordFile = config.age.secrets.gitea-mailer-password.path; - rootUrl = "https://git.pub.solar"; - settings = { - mailer = { - ENABLED = true; - MAILER_TYPE = "smtp"; - HOST = "mx2.greenbaum.cloud:465"; - FROM = ''"pub.solar git server" ''; - USER = "admins@pub.solar"; - }; - # currently broken, gpg core dumps - #"repository.signing" = { - # SIGNING_KEY = "default"; - # MERGES = "always"; - #}; - openid = { - ENABLE_OPENID_SIGNIN = true; - ENABLE_OPENID_SIGNUP = true; - }; - # uncomment after initial deployment, first user is admin user - # required to setup SSO (oauth openid-connect, keycloak auth provider) - service.ALLOW_ONLY_EXTERNAL_REGISTRATION = true; - session.COOKIE_SECURE = lib.mkForce true; - }; - }; - - # Required for gitea server side gpg signatures - # configured / setup manually in - # /var/lib/gitea/data/home/.gitconfig and - # /var/lib/gitea/data/home/.gnupg/ - programs.gnupg.agent = { - enable = true; - pinentryFlavor = "curses"; - }; - # Required to make gpg work without a graphical environment? - # otherwise generating a new gpg key fails with this error: - # gpg: agent_genkey failed: No pinentry - # see: https://github.com/NixOS/nixpkgs/issues/97861#issuecomment-827951675 - environment.variables = { - GPG_TTY = "$(tty)"; - }; -} diff --git a/hosts/flora-6/keycloak.nix b/hosts/flora-6/keycloak.nix deleted file mode 100644 index 14f91bfc..00000000 --- a/hosts/flora-6/keycloak.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ - config, - lib, - inputs, - pkgs, - self, - ... -}: { - age.secrets.keycloak-database-password = { - file = "${self}/secrets/keycloak-database-password.age"; - mode = "700"; - #owner = "keycloak"; - }; - - # keycloak - services.keycloak = { - enable = true; - database.passwordFile = config.age.secrets.keycloak-database-password.path; - settings = { - hostname = "auth.pub.solar"; - http-host = "127.0.0.1"; - http-port = 8080; - proxy = "edge"; - }; - themes = { - "pub.solar" = inputs.keycloak-theme-pub-solar.legacyPackages.${pkgs.system}.keycloak-theme-pub-solar; - }; - }; -} diff --git a/modules/infra-node/default.nix b/modules/infra-node/default.nix new file mode 100644 index 00000000..d6b3b1c4 --- /dev/null +++ b/modules/infra-node/default.nix @@ -0,0 +1,42 @@ +{ + lib, + config, + pkgs, + ... +}: +with lib; let + cfg = config.pub-solar; +in { + options.pub-solar = { + infra-node = { + mailing = { + enabled = mkEnableOption "Whether to enable mailing for services on the host"; + type = mkOption { + description = "Mail server type"; + type = types.nullOr types.str; + default = "smtp"; + }; + host = mkOption { + description = "Mailing server host"; + type = types.nullOr types.str; + default = null; + }; + from = mkOption { + description = "Mailing server from"; + type = types.nullOr types.str; + default = null; + }; + user = mkOption { + description = "Mailing server user"; + type = types.listOf types.str; + default = []; + }; + passwordFile = mkOption { + description = "Mailing server passwordFile"; + type = types.nullOr types.str; + default = null; + }; + }; + }; + }; +}