From 4625d62d83c7866cc5174316d9d14acbd4740e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sun, 2 Jul 2023 20:36:30 +0200 Subject: [PATCH] user and group juggling --- hosts/nougat-2/acme.nix | 68 ++++++++++--- hosts/nougat-2/caddy.nix | 63 +++++++++--- hosts/nougat-2/concourse.nix | 17 +++- hosts/nougat-2/configuration.nix | 20 +++- hosts/nougat-2/gitea.nix | 162 +++++++++++++++++++------------ hosts/nougat-2/keycloak.nix | 24 ++++- 6 files changed, 253 insertions(+), 101 deletions(-) diff --git a/hosts/nougat-2/acme.nix b/hosts/nougat-2/acme.nix index e0d7b426..e75a7bf9 100644 --- a/hosts/nougat-2/acme.nix +++ b/hosts/nougat-2/acme.nix @@ -18,26 +18,62 @@ in { age.secrets."hosting.de-api.key" = { file = "${self}/secrets/hosting.de-api.key"; mode = "440"; - owner = "acme"; + group = "acme"; }; - security.acme = { - acceptTerms = true; - defaults.email = "acme@benjaminbaedorf.eu"; + systemd.tmpfiles.rules = [ + "d '/data/acme' 0750 root acme - -" + ]; - certs."b12f.io" = hostingdeProviderConf; - certs."mail.b12f.io" = hostingdeProviderConf; - certs."transmission.b12f.io" = hostingdeProviderConf; + users.groups.acme = {}; + ids.uids.acme = 997; + ids.gids.acme = 997; - certs."${exDomain}" = hostingdeProviderConf; - certs."mail.${exDomain}" = hostingdeProviderConf; + containers.acme = { + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.101.0"; + localAddress = "192.168.106.0"; + hostAddress6 = "fc00::1"; + localAddress6 = "fc00::6"; - certs."${pubsolarDomain}" = hostingdeProviderConf; - certs."www.${pubsolarDomain}" = hostingdeProviderConf; - certs."auth.${pubsolarDomain}" = hostingdeProviderConf; - certs."git.${pubsolarDomain}" = hostingdeProviderConf; - certs."ci.${pubsolarDomain}" = hostingdeProviderConf; - certs."list.${pubsolarDomain}" = hostingdeProviderConf; - certs."obs-portal.${pubsolarDomain}" = hostingdeProviderConf; + bindMounts = { + "/var/lib/acme" = { + hostPath = "/data/acme"; + isReadOnly = false; + }; + + "${config.age.secrets."hosting.de-api.key".path}" = { + hostPath = "${config.age.secrets."hosting.de-api.key".path}"; + isReadOnly = true; + }; + }; + + config = { + networking.nameservers = ["1.1.1.1"]; + users.groups.acme = config.users.groups.acme; + + security.acme = { + acceptTerms = true; + defaults.email = "acme@benjaminbaedorf.eu"; + defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; + defaults.group = "acme"; + + certs."b12f.io" = hostingdeProviderConf; + certs."mail.b12f.io" = hostingdeProviderConf; + certs."transmission.b12f.io" = hostingdeProviderConf; + + certs."${exDomain}" = hostingdeProviderConf; + certs."mail.${exDomain}" = hostingdeProviderConf; + + certs."${pubsolarDomain}" = hostingdeProviderConf; + certs."www.${pubsolarDomain}" = hostingdeProviderConf; + certs."auth.${pubsolarDomain}" = hostingdeProviderConf; + certs."git.${pubsolarDomain}" = hostingdeProviderConf; + certs."ci.${pubsolarDomain}" = hostingdeProviderConf; + certs."list.${pubsolarDomain}" = hostingdeProviderConf; + certs."obs-portal.${pubsolarDomain}" = hostingdeProviderConf; + }; + }; }; } diff --git a/hosts/nougat-2/caddy.nix b/hosts/nougat-2/caddy.nix index 727054a1..33bd5684 100644 --- a/hosts/nougat-2/caddy.nix +++ b/hosts/nougat-2/caddy.nix @@ -6,7 +6,9 @@ ... }: let pubsolarDomain = import ./pubsolar-domain.nix; + # Machine user for CI pipelines in { + networking.firewall.allowedTCPPorts = [80 443]; networking.networkmanager.unmanaged = ["interface-name:ve-caddy"]; networking.nat = { enable = true; @@ -17,13 +19,35 @@ in { # Lazy IPv6 connectivity for the container enableIPv6 = true; }; + + systemd.tmpfiles.rules = [ + "d '/data/www' 0750 root www - -" + "d '/data/caddy' 0750 root caddy - -" + ]; + + users.groups.caddy = {}; + users.groups.www = {}; + users.users.hakkonaut.extraGroups = ["www"]; + ids.uids.www = 996; + ids.gids.www = 996; + + fileSystems."/var/lib/caddy" = { + device = "/data/caddy"; + options = ["bind"]; + }; + + fileSystems."/srv/www" = { + device = "/data/www"; + options = ["bind"]; + }; + containers.caddy = { autoStart = true; privateNetwork = true; hostAddress = "192.168.101.0"; - localAddress = "192.168.102.0"; + localAddress = "192.168.103.0"; hostAddress6 = "fc00::1"; - localAddress6 = "fc00::2"; + localAddress6 = "fc00::3"; forwardPorts = [ { @@ -40,20 +64,36 @@ in { bindMounts = { "/srv/www/" = { - hostPath = "/data/www/"; + hostPath = "/data/www"; + isReadOnly = false; + }; + + "/var/lib/caddy/" = { + hostPath = "/data/caddy"; + isReadOnly = false; + }; + + "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory" = { + hostPath = "/data/acme"; isReadOnly = false; }; }; config = { + users.groups.caddy = {}; + users.groups.www = {}; + users.groups.acme = {}; + users.users.caddy.extraGroups = ["www" "acme"]; + + networking.firewall.allowedTCPPorts = [80 443]; + environment.etc."resolv.conf".text = "nameserver 1.1.1.0"; + services.caddy = { enable = lib.mkForce true; - group = "hakkonaut"; - email = "admins@pub.solar"; - globalConfig = lib.mkForce '' - auto_https off - ''; - acmeCA = null; + + email = "acme@benjaminbaedorf.eu"; + globalConfig = lib.mkForce ""; + virtualHosts = { "dashboard.nougat-2.b12f.io" = { extraConfig = '' @@ -113,7 +153,7 @@ in { ''; extraConfig = '' redir / /realms/${pubsolarDomain}/account temporary - reverse_proxy 192.168.103.0:8080 + reverse_proxy 192.168.104.0:8080 ''; }; "git.${pubsolarDomain}" = { @@ -122,7 +162,7 @@ in { ''; extraConfig = '' redir /user/login /user/oauth2/keycloak temporary - reverse_proxy 192.168.101.0:3000 + reverse_proxy 192.168.105.0:3000 ''; }; "ci.${pubsolarDomain}" = { @@ -135,7 +175,6 @@ in { }; }; }; - networking.firewall.allowedTCPPorts = [80 443]; }; }; } diff --git a/hosts/nougat-2/concourse.nix b/hosts/nougat-2/concourse.nix index 9bc1fe91..80d6a8f1 100644 --- a/hosts/nougat-2/concourse.nix +++ b/hosts/nougat-2/concourse.nix @@ -33,22 +33,31 @@ in { description = "Concourse Service"; home = "/var/lib/concourse"; useDefaultShell = true; - uid = 10001; group = "concourse"; isSystemUser = true; }; users.groups.concourse = {}; + users.groups.postgres = {}; + ids.uids.concourse = 995; + ids.gids.concourse = 995; systemd.tmpfiles.rules = [ - "d '/var/lib/concourse' 0750 concourse concourse - -" + "d '/data/concourse/db' 0770 ${builtins.toString config.ids.uids.postgres} postgres - -" ]; + system.activationScripts.mkConcourseNet = let + docker = config.virtualisation.oci-containers.backend; + dockerBin = "${pkgs.${docker}}/bin/${docker}"; + in '' + ${dockerBin} network inspect concourse-net >/dev/null 2>&1 || ${dockerBin} network create concourse-net --subnet 172.20.0.0/24 + ''; + virtualisation.oci-containers = { containers."concourse-db" = { image = "postgres:14"; autoStart = true; - user = "994"; + user = builtins.toString config.ids.uids.postgres; volumes = [ "/data/concourse/db:/var/lib/postgresql/data" ]; @@ -63,7 +72,7 @@ in { containers."concourse" = { image = "concourse/concourse:7.9.1"; autoStart = true; - user = "10001"; + user = builtins.toString config.ids.uids.concourse; ports = [ "8080:8080" ]; diff --git a/hosts/nougat-2/configuration.nix b/hosts/nougat-2/configuration.nix index f86ac199..f163d1cc 100644 --- a/hosts/nougat-2/configuration.nix +++ b/hosts/nougat-2/configuration.nix @@ -76,7 +76,7 @@ in { interface = "enp0s31f6"; }; - networking.nameservers = ["8.8.8.8"]; + networking.nameservers = ["1.1.1.1"]; # Initial empty root password for easy login: users.users.root.initialHashedPassword = ""; @@ -85,6 +85,21 @@ in { then psCfg.user.publicKeys else []; + users.users.hakkonaut = { + home = "/home/hakkonaut"; + description = "CI and automation user"; + useDefaultShell = true; + group = "hakkonaut"; + isSystemUser = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGP5MvCwNRtCcP1pSDrn0XZTNlpOqYnjHDm9/OI4hECW hakkonaut@flora-6" + ]; + }; + + users.groups.hakkonaut = {}; + ids.uids.hakkonaut = 998; + ids.gids.hakkonaut = 998; + services.openssh.enable = true; services.openssh.settings.PermitRootLogin = "prohibit-password"; @@ -94,9 +109,6 @@ in { virtualisation = { docker = { enable = true; - extraOptions = '' - --data-root /data/docker - ''; }; oci-containers = { diff --git a/hosts/nougat-2/gitea.nix b/hosts/nougat-2/gitea.nix index 9ccf6649..6ee23f32 100644 --- a/hosts/nougat-2/gitea.nix +++ b/hosts/nougat-2/gitea.nix @@ -10,7 +10,7 @@ in { age.secrets.gitea-database-password = { file = "${self}/secrets/gitea-database-password.age"; mode = "600"; - owner = "gitea"; + group = "gitea"; }; # age.secrets.gitea-mailer-password = { @@ -19,66 +19,106 @@ in { # owner = "gitea"; # }; - services.gitea = { - enable = true; - package = pkgs.forgejo; - appName = "pub.solar git server"; - database = { - type = "postgres"; - passwordFile = config.age.secrets.gitea-database-password.path; - }; - lfs.enable = true; - # mailerPasswordFile = config.age.secrets.gitea-mailer-password.path; - settings = { - server = { - DOMAIN = "git.${pubsolarDomain}"; - HTTP_ADDR = "127.0.0.1"; - HTTP_PORT = 3000; - ROOT_URL = "https://git.${pubsolarDomain}"; - }; - mailer = { - ENABLED = false; - PROTOCOL = "smtps"; - SMTP_ADDR = "mx2.greenbaum.cloud"; - SMTP_PORT = 465; - FROM = ''"pub.solar git server" ''; - USER = "admins@pub.solar"; - }; - "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; - service.ENABLE_NOTIFY_MAIL = true; - session.COOKIE_SECURE = lib.mkForce true; - }; - }; + systemd.tmpfiles.rules = [ + "d '/data/gitea/db' 0770 root postgres - -" + "d '/data/gitea/gitea' 0770 root gitea - -" + ]; - # See: https://docs.gitea.io/en-us/signing/#installing-and-generating-a-gpg-key-for-gitea - # Required for gitea server side gpg signatures - # configured/setup manually in: - # /var/lib/gitea/data/home/.gitconfig - # /var/lib/gitea/data/home/.gnupg/ - # sudo su gitea - # export GNUPGHOME=/var/lib/gitea/data/home/.gnupg - # gpg --quick-gen-key 'pub.solar gitea ' ed25519 - # TODO: implement declarative GPG key generation and - # gitea gitconfig - 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)"; + users.groups.postgres = {}; + users.groups.gitea = {}; + ids.uids.gitea = 994; + ids.gids.gitea = 994; + + containers.gitea = { + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.101.0"; + localAddress = "192.168.105.0"; + hostAddress6 = "fc00::1"; + localAddress6 = "fc00::5"; + + bindMounts = { + "/var/lib/postgresql/14" = { + hostPath = "/data/gitea/db"; + isReadOnly = false; + }; + + "/var/lib/gitea" = { + hostPath = "/data/gitea/gitea"; + isReadOnly = false; + }; + + "${config.age.secrets.gitea-database-password.path}" = { + hostPath = "${config.age.secrets.gitea-database-password.path}"; + isReadOnly = true; + }; + }; + + config = { + networking.nameservers = ["1.1.1.1"]; + + services.gitea = { + enable = true; + package = pkgs.forgejo; + appName = "pub.solar git server"; + database = { + type = "postgres"; + passwordFile = config.age.secrets.gitea-database-password.path; + }; + lfs.enable = true; + # mailerPasswordFile = config.age.secrets.gitea-mailer-password.path; + settings = { + server = { + DOMAIN = "git.${pubsolarDomain}"; + HTTP_ADDR = "127.0.0.1"; + HTTP_PORT = 3000; + ROOT_URL = "https://git.${pubsolarDomain}"; + }; + mailer = { + ENABLED = false; + PROTOCOL = "smtps"; + SMTP_ADDR = "mx2.greenbaum.cloud"; + SMTP_PORT = 465; + FROM = ''"pub.solar git server" ''; + USER = "admins@pub.solar"; + }; + "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; + service.ENABLE_NOTIFY_MAIL = true; + session.COOKIE_SECURE = lib.mkForce true; + }; + }; + + # See: https://docs.gitea.io/en-us/signing/#installing-and-generating-a-gpg-key-for-gitea + # Required for gitea server side gpg signatures + # configured/setup manually in: + # /var/lib/gitea/data/home/.gitconfig + # /var/lib/gitea/data/home/.gnupg/ + # sudo su gitea + # export GNUPGHOME=/var/lib/gitea/data/home/.gnupg + # gpg --quick-gen-key 'pub.solar gitea ' ed25519 + # TODO: implement declarative GPG key generation and + # gitea gitconfig + 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/nougat-2/keycloak.nix b/hosts/nougat-2/keycloak.nix index a5fea075..df24ac1f 100644 --- a/hosts/nougat-2/keycloak.nix +++ b/hosts/nougat-2/keycloak.nix @@ -10,26 +10,42 @@ in { age.secrets.keycloak-database-password = { file = "${self}/secrets/keycloak-database-password.age"; - mode = "700"; - #owner = "keycloak"; + mode = "770"; + group = "keycloak"; }; + systemd.tmpfiles.rules = [ + "d '/data/keycloak/db' 0770 root postgres - -" + ]; + + users.groups.postgres = {}; + users.groups.keycloak = {}; + ids.uids.keycloak = 993; + ids.gids.keycloak = 993; + containers.keycloak = { autoStart = true; privateNetwork = true; hostAddress = "192.168.101.0"; - localAddress = "192.168.103.0"; + localAddress = "192.168.104.0"; hostAddress6 = "fc00::1"; - localAddress6 = "fc00::3"; + localAddress6 = "fc00::4"; bindMounts = { "/var/lib/postgresql/14" = { hostPath = "/data/keycloak/db"; isReadOnly = false; }; + + "${config.age.secrets.keycloak-database-password.path}" = { + hostPath = "${config.age.secrets.keycloak-database-password.path}"; + isReadOnly = true; + }; }; config = { + networking.nameservers = ["1.1.1.1"]; + services.keycloak = { enable = true; database.passwordFile = config.age.secrets.keycloak-database-password.path;