From e1901c2954a509f4fbd781239dab15f77d3914e8 Mon Sep 17 00:00:00 2001 From: "Gustavo \"Guz\" L. de Mello" Date: Fri, 12 Apr 2024 22:59:19 -0300 Subject: [PATCH] refactor: forgejo services https://guzsdaily.tumblr.com/post/747604360680374272/a-week-of-troubleshooting-my-own-stupidity --- hosts/homelab/secrets.nix | 10 +- hosts/homelab/services.nix | 25 +- modules/nixos/services/forgejo/default.nix | 344 ++++++------------ .../nixos/services/forgejo/user-handler.sh | 5 +- secrets/homelab-secrets.lesser.json | 6 +- secrets/homelab-secrets.yaml | 7 +- 6 files changed, 139 insertions(+), 258 deletions(-) diff --git a/hosts/homelab/secrets.nix b/hosts/homelab/secrets.nix index 674b542..c1f4354 100644 --- a/hosts/homelab/secrets.nix +++ b/hosts/homelab/secrets.nix @@ -22,7 +22,7 @@ in { default = lesser-secrets; }; }; - config = { + config = with lib; { environment.systemPackages = with pkgs; [ sops ]; @@ -34,16 +34,16 @@ in { owner = config.users.users."guz".name; }; - sops.secrets."forgejo/user1/name" = { + sops.secrets."forgejo/user1/name" = mkIf config.services.forgejo.enable { owner = config.services.forgejo.user; }; - sops.secrets."forgejo/user1/password" = { + sops.secrets."forgejo/user1/password" = mkIf config.services.forgejo.enable { owner = config.services.forgejo.user; }; - sops.secrets."forgejo/user1/email" = { + sops.secrets."forgejo/user1/email" = mkIf config.services.forgejo.enable { owner = config.services.forgejo.user; }; - sops.secrets."forgejo/git-password" = { + sops.secrets."forgejo/git-password" = mkIf config.services.forgejo.enable { owner = config.services.forgejo.user; }; diff --git a/hosts/homelab/services.nix b/hosts/homelab/services.nix index 7bcb374..44bbc97 100644 --- a/hosts/homelab/services.nix +++ b/hosts/homelab/services.nix @@ -63,39 +63,22 @@ in { secrets.services; networking.firewall.allowedTCPPorts = [80 433]; + services.openssh.enable = true; + services.forgejo = { enable = true; actions = { enable = true; token = secrets.services.forgejo.actions-token; - url = "http://${config.services.tailscale.deviceUrl}:${toString secrets.services.forgejo.port}"; - }; - users = { - user1 = { - name = /. + config.sops.secrets."forgejo/user1/name".path; - password = /. + config.sops.secrets."forgejo/user1/password".path; - email = /. + config.sops.secrets."forgejo/user1/email".path; - admin = true; - }; }; settings = { server = { - ROOT_URL = "https://${secrets.services.forgejo.domain}"; - DOMAIN = "${secrets.services.forgejo.domain}"; HTTP_PORT = secrets.services.forgejo.port; + DOMAIN = secrets.services.forgejo.domain; + ROOT_URL = "https://${secrets.services.forgejo.domain}"; }; }; }; - home-manager-helper.users."${config.services.forgejo.user}" = { - name = "${config.services.forgejo.user}"; - hashedPasswordFile = builtins.toString config.sops.secrets."forgejo/git-password".path; - isSystemUser = true; - homeDirectory = "/var/lib/forgejo"; - extraGroups = ["wheel" "networkmanager"]; - useDefaultShell = true; - }; - - services.openssh.enable = true; services.tailscale = { enable = true; diff --git a/modules/nixos/services/forgejo/default.nix b/modules/nixos/services/forgejo/default.nix index 8aac886..9579c63 100644 --- a/modules/nixos/services/forgejo/default.nix +++ b/modules/nixos/services/forgejo/default.nix @@ -1,12 +1,12 @@ -{ - config, - lib, - pkgs, - utils, - ... -}: let +{ config +, lib +, pkgs +, utils +, ... +}: +let cfg = config.services.forgejo; - yamlFormat = pkgs.formats.yaml {}; + yamlFormat = pkgs.formats.yaml { }; users = builtins.attrValues (builtins.mapAttrs (username: info: { name = @@ -19,150 +19,136 @@ }) cfg.users); initList = l: lib.strings.concatStringsSep "," l; -in { - imports = []; +in +{ + imports = [ ]; options.services.forgejo = with lib; - with lib.types; { - handleUndeclaredUsers = mkOption { - type = bool; - default = false; - }; - users = mkOption { - type = attrsOf (submodule ({ - config, - lib, - ... - }: - with lib; - with lib.types; { - options = { - name = mkOption { - type = nullOr (either str path); - default = null; + with lib.types; { + handleUndeclaredUsers = mkOption { + type = bool; + default = false; + }; + users = mkOption { + type = attrsOf (submodule ({ config + , lib + , ... + }: + with lib; + with lib.types; { + options = { + name = mkOption { + type = nullOr (either str path); + default = null; + }; + password = mkOption { + type = either str path; + }; + email = mkOption { + type = either str path; + }; + admin = mkOption { + type = bool; + default = false; + }; }; - password = mkOption { - type = either str path; - }; - email = mkOption { - type = either str path; - }; - admin = mkOption { - type = bool; - default = false; - }; - }; - })); - default = {}; - }; - actions = { - enable = mkEnableOption ""; - hostPackages = mkOption { - type = listOf package; - default = with pkgs; [ - bash - coreutils - curl - gawk - gitMinimal - gnused - nodejs - wget - ]; + })); + default = { }; }; - labels = mkOption { - type = listOf str; - default = [ - "host:host" - "shell:host://-self-hosted" - "debian-latest:docker://node:18-bullseye" - "ubuntu-latest:docker://node:18-bullseye" - "debian-slim:docker://node:18-bullseye-slim" - "ubuntu-slim:docker://node:18-bullseye-slim" - "alpine-latest:docker://alpine:latest" - ]; - }; - name = mkOption { - type = str; - default = "Forgejo ${toString cfg.settings.server.HTTP_PORT} - Actions Runner"; - }; - package = mkOption { - type = package; - default = pkgs.forgejo-actions-runner; - }; - settings = mkOption { - type = yamlFormat.type; - default = {}; - }; - token = mkOption { - type = nullOr str; - default = null; - }; - tokenFile = mkOption { - type = nullOr (either path str); - default = null; - }; - url = mkOption { - type = str; - default = cfg.settings.server.ROOT_URL; + actions = { + enable = mkOption { + type = bool; + default = cfg.enable; + }; + token = mkOption { + type = str; + }; + url = mkOption { + type = str; + default = "http://localhost:${toString cfg.settings.server.HTTP_PORT}"; + }; }; }; - }; config = with lib; mkIf cfg.enable { - # this opens the port for the internal actions be able to clone and interact with the instance - networking.firewall.allowedTCPPorts = - mkIf cfg.actions.enable [cfg.settings.server.HTTP_PORT]; + networking.firewall.allowedTCPPorts = mkIf cfg.settings.actions.ENABLED [ + cfg.settings.server.HTTP_PORT + ]; + networking.firewall.allowedUDPPorts = mkIf cfg.settings.actions.ENABLED [ + cfg.settings.server.HTTP_PORT + ]; - services.forgejo.settings = { - actions = { - ENABLED = mkDefault cfg.actions.enable; - DEFAULT_ACTIONS_URL = mkDefault cfg.settings.server.ROOT_URL; - }; - repository = { - DEFAULT_REPO_UNITS = mkDefault (initList [ - "repo.code" - "repo.issues" - "repo.pulls" - ]); - DISABLED_REPO_UNITS = mkIf (!cfg.actions.enable) (mkDefault "repo.actions"); - }; - security = { - ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET = mkDefault true; - }; - server = { - HTTP_PORT = mkDefault 3617; - DOMAIN = - mkIf config.services.tailscale.enable - (mkDefault "${config.services.tailscale.deviceUrl}"); - }; - service = { - DISABLE_REGISTRATION = mkDefault true; + users.users."${cfg.user}" = { + home = cfg.stateDir; + useDefaultShell = true; + group = cfg.group; + isSystemUser = true; + extraGroups = [ "wheel" "networkmanager" ]; + }; + users.groups."${cfg.group}" = { }; + + services.forgejo = { + user = mkDefault "git"; + group = mkDefault cfg.user; + settings = { + DEFAULT = { + APP_NAME = mkDefault "Forgejo: Beyond coding. We forge."; + }; + actions = { + ENABLED = mkDefault cfg.actions.enable; + DEFAULT_ACTIONS_URL = mkDefault "http://localhost:${toString cfg.settings.server.HTTP_PORT}"; + }; + repository = { + DEFAULT_REPO_UNITS = mkDefault (initList [ + "repo.code" + ]); + DISABLED_REPO_UNITS = mkIf (!cfg.actions.enable) (mkDefault (initList [ + "repo.actions" + ])); + }; + service = { + # DISABLE_REGISTRARION = mkDefault true; + }; }; }; - virtualisation.docker.enable = mkIf cfg.settings.actions.ENABLED (mkDefault true); - services.gitea-actions-runner.instances."generatedForgejo${toString cfg.settings.server.HTTP_PORT}" = mkIf cfg.actions.enable { - enable = mkDefault true; - hostPackages = mkDefault cfg.actions.hostPackages; - labels = mkDefault cfg.actions.labels; - name = mkDefault cfg.actions.name; - settings = mkDefault cfg.actions.settings; - url = mkDefault cfg.actions.url; - token = cfg.actions.token; - }; - systemd.services = { - "${utils.escapeSystemdPath "generatedForgejo${toString cfg.settings.server.HTTP_PORT}"}" = { - serviceConfig.User = mkIf cfg.actions.enable (mkDefault cfg.user); + virtualisation.docker.enable = mkIf cfg.actions.enable (mkDefault true); + services.gitea-actions-runner = mkIf cfg.actions.enable { + package = + if config.services.gitea.enable + then pkgs.gitea-actions-runner + else pkgs.forgejo-actions-runner; + instances."forgejo${toString cfg.settings.server.HTTP_PORT}" = { + enable = mkDefault true; + token = mkDefault cfg.actions.token; + name = mkDefault "${cfg.settings.DEFAULT.APP_NAME} - Actions"; + url = cfg.actions.url; + labels = mkDefault [ + /* + Remember to install git on these images so actions/checkout can work, + without it, the actions tries to use the /api/v3/repos/{user}/{repo}/tarball/{ref} + api endpoint, which Gitea/Forgejo doesn't has. + */ + "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest-slim" + "ubuntu-latest-full:docker://gitea/runner-images:ubuntu-latest" + ]; + settings = { + runner = { + insecure = true; + }; + }; }; }; + /* systemd.services."forgejo-users-setup" = with builtins; { script = '' function gum() { ${pkgs.gum}/bin/gum "$@"; } function forgejo() { - local config_file="${toString cfg.stateDir}/custom/conf/app.ini"; - touch $config_file - ${cfg.package}/bin/gitea --config $config_file "$@" + # local config_file="${toString cfg.stateDir}/custom/conf/app.ini"; + # touch $config_file + ${cfg.package}/bin/gitea \ + --work-path ${cfg.stateDir} \ + "$@" } function fjuser() { forgejo admin user "$@"; } function awk() { ${pkgs.gawk}/bin/awk "$@"; } @@ -204,96 +190,6 @@ in { '') users)} ''; - wantedBy = ["multi-user.target"]; - after = ["forgejo.service"]; - serviceConfig = { - Type = "oneshot"; - User = cfg.user; - Group = cfg.group; - }; - }; - /* - systemd.services."homelab-forgejo-setup" = with builtins; { - script = '' - - configFile="${toString cfg.stateDir}/custom/conf/app.ini"; - touch $configFile - - gum="${pkgs.gum}/bin/gum" - forgejo="${cfg.package}/bin/gitea --config $configFile" - user="$forgejo admin user" - awk="${pkgs.gawk}/bin/awk" - - declaredUsers=(${toString (map (user: "${ - if isPath user.name - then "$(cat ${toString user.name})" - else user.name - }") - users)}); - - $gum log --structured --time timeonly --level info "HANDLING UNDECLARED USERS" - - $user list | $awk '{print $2}' | tail -n +2 | while read username; do - if printf '%s\0' "''${declaredUsers[@]}" | grep -Fxqz -- "$username"; then - $gum log --structured --time timeonly --level warn "Declared user already exists, ignoring" username $username; - else - if [[ "$($user list | tail -n +2 | $awk '{print $2 " " $5}' | grep "$username " | $awk '{print $2}')" == "true" ]]; then - $gum log --structured --time timeonly --level warn "Undeclared user is a ADMIN, ignoring" username $username; - else - ${ - if cfg.handleUndeclaredUsers - then '' - $gum log --structured --time timeonly --level warn "DELETING undeclared user" username $username; - - $user delete -u "$username"; - '' - else '' - $gum log --structured --time timeonly --level warn "UNDECLARED user, please declare it in the config so it's reproducible" username "$username"; - '' - } - fi - fi - done - - ${toString (map (user: '' - username="${ - if isPath user.name - then "\"$(cat ${toString user.name})\"" - else user.name - }"; - email="${ - if isPath user.email - then "\"$(cat ${toString user.email})\"" - else user.email - }"; - password="${ - if isPath user.password - then "\"$(cat ${toString user.password})\"" - else user.password - }"; - - if [[ "$($user list | grep "$username" | $awk '{print $2}')" ]]; then - $gum log --structured --time timeonly --level warn "User with username already exists" username $username; - - elif [[ "$($user list | grep "$email" | $awk '{print $3}')" ]]; then - $gum log --structured --time timeonly --level warn "User with email already exists" email $email; - - else - $gum log --structured --time timeonly --level info ${ - if user.admin - then "Creating ADMIN user" - else "Creating user" - } username $username email $email password $password; - $user create --username $username --email $email --password $password ${ - if user.admin - then "--admin" - else "" - }; - - fi - '') - users)} - ''; wantedBy = [ "multi-user.target" ]; after = [ "forgejo.service" ]; serviceConfig = { diff --git a/modules/nixos/services/forgejo/user-handler.sh b/modules/nixos/services/forgejo/user-handler.sh index eb72132..45c05b0 100644 --- a/modules/nixos/services/forgejo/user-handler.sh +++ b/modules/nixos/services/forgejo/user-handler.sh @@ -72,7 +72,7 @@ function set-user() { fjuser create --username "$username" \ --email "$email" \ - --passowrd "$password" \ + --password "$password" \ --admin else gum log --structured \ @@ -84,8 +84,7 @@ function set-user() { fjuser create --username "$username" \ --email "$email" \ - --passowrd "$password" + --password "$password" fi fi } - diff --git a/secrets/homelab-secrets.lesser.json b/secrets/homelab-secrets.lesser.json index 438d3a5..d76337f 100644 --- a/secrets/homelab-secrets.lesser.json +++ b/secrets/homelab-secrets.lesser.json @@ -6,7 +6,7 @@ "forgejo": { "domain": "ENC[AES256_GCM,data:DJDExE7VVmAk4ZLhOkTfD2wBY5i1,iv:tnOgrKCpglvDyk75mnmeoiz2trmD3r3wCL2etHmALC4=,tag:rAiEK9U48cR1q+W7Zbkhvg==,type:str]", "port": "ENC[AES256_GCM,data:ydSACw==,iv:0RWRLLCU8YyYmOmTawns2Iy+ABiBFbBqgQ10+buZNt0=,tag:3QW0NzbKeUkcfYh/5my3fA==,type:float]", - "actions-token": "ENC[AES256_GCM,data:j9hpNPmbWC2z862ca8RtmB1A62P3F3L6hS1WdpCXiCEunDvHOo9qcg==,iv:DSWebuZqdwx8R9MaJgSvIVRfUOJO7OpR3ORjgp6amzw=,tag:/bPMMWsfeoKbY47JQgC38Q==,type:str]" + "actions-token": "ENC[AES256_GCM,data:eNZtfpBt0ZjgLrykGKGEL3gtKCHHE+UWaDATgi0QHBGj7ZZX7ROuKQ==,iv:J8wmqFVmi8sarGupw/F4PP20HdaGTrxC4pF8GERwZxs=,tag:wZve5TI4/NpacMpHtpBnoA==,type:str]" }, "adguard": { "domain": "ENC[AES256_GCM,data:QquWEbgpXY13UMV9BTXplQ5LhSgv,iv:0AstA5oaS8714QME3QK0/aiv9Khqk1bLCcFdCEPn+IA=,tag:XXIWjIjHQ0gZFSFBHU49Ag==,type:str]", @@ -24,8 +24,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnL3dCY1VLMmoxRFRmS0Ir\nV0ppTnI0RE5ZMjcvRGNPWkNxWFdJYTBDTG00ClRGQkh1UStGTmc0RE5aNy9nL3FI\nbHJIa3hLR0ZkTjd6WkFzOFkzeFdMNUEKLS0tIDBidk93Qy9LenFlSGZ2aEpuTUFt\nWVM2eS9UdXAvbzE4eEdKMjVEM3RLdm8KKeIhk+YOKVL9Y19lLyb6/Pxv8rbewK2e\nLm96jx+LOMOCFcQGxuFKWqQbTB4br/cPvRKSY5jFmFWqVg7pCPTAzQ==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2024-04-07T21:39:36Z", - "mac": "ENC[AES256_GCM,data:29gkzrSMR1O6PRVMyBuNxRwvpAHwvY+VZxzfkzukxRdlQ8GKaskVf/xjIzTlojsRZDLRBIP7ZGfCRsmYskwfkoqEZRfQ+/LwEXdol/Lhyd+OM9aP7h+GP9+qjf4KN7JvJSzj3HNdUVJNULDmAFdRaKAkIkRl5lCrVUGZJTgfj3s=,iv:GVGchkF1YllSUePk+cIz2op50OLaeuTpizbuOOF7x0E=,tag:/ZoZllvCtKdaikA5TzsdGA==,type:str]", + "lastmodified": "2024-04-11T18:25:11Z", + "mac": "ENC[AES256_GCM,data:68Knkm4T7GteUXCN6TZy+E5LDIgNnRuNlu/2iBbGeJBAovKFiOdP0vUkvqBQL8zkTJk4y5QQKAtu8w9V0MtL9dp61z9nhRbiCbGNkjGoFLfsedTGvifR0HfHy1r4tZKc7A10gn8706q72BEJ+2x3m+6mb2Xh95erXEPl/nyKJZ0=,iv:E96M8LjI2UHced4iNEVkRo2uZWDQR5QudFqyj0JEenw=,tag:MIrn+aJljyFqWbX8R/nckw==,type:str]", "pgp": null, "unencrypted_suffix": "_unencrypted", "version": "3.8.1" diff --git a/secrets/homelab-secrets.yaml b/secrets/homelab-secrets.yaml index 0d94787..a0db620 100644 --- a/secrets/homelab-secrets.yaml +++ b/secrets/homelab-secrets.yaml @@ -1,7 +1,10 @@ +guz: + password: ENC[AES256_GCM,data:zlO5xSFho7TXjFv62lgFir9SAgn+UE6XjdNEvIAgmQG9oDkthfgxO84wYdI0mQDwRIIs2PmSdBRfo0DPc3hji+ySCrItolPL8g==,iv:MZfhTxwfcbmXh5C6DkQhnY9NQGdE8zEwwvFOHQiUgKY=,tag:JjJN2bYcSXNN3ueGj5RNLg==,type:str] network: ip: ENC[AES256_GCM,data:AkbNOQLXRKLYjU2ywg==,iv:xqdTPCUYiT/cPe2zAbBJ7fUiEMViW9LZND4j0DdydLY=,tag:tq6nA5fGH4/mAvF6InUFgQ==,type:str] localIp: ENC[AES256_GCM,data:PK8THL9NW//2sal1,iv:9h3f255rIgedYToVaUGuQ9RzD33V8sczRWsZe+rTyC0=,tag:OoJbes6k0FqxXzGQ8ZG0aA==,type:str] forgejo: + git-password: ENC[AES256_GCM,data:SDyFBCwTxnZ1E6R/8HZCBIBj4AREYfqWrgzSEQ6SA3BDGPFsHghiVmF+Jt4omdzUQSoCCblMBsAx0NQBbBJrCbEoBWtybRM7Cg==,iv:KbtjXW1F8YJeapVpEkf8AdXhojmhOQKxG8nCZv7vW4k=,tag:odrL53KeKLVD5AoQB14veA==,type:str] user1: name: ENC[AES256_GCM,data:UL3g,iv:+ftGx57fhzN06DuLItxZTc7lXX2g4MhqrEqnDjk4Aug=,tag:ZNpwWuPYhBzDjRQBKikCDA==,type:str] password: ENC[AES256_GCM,data:KXx9Kv9f9UP3sAU=,iv:bCBv+IEieR+RGjgjXLKMLlsqoBOLLYjvT167QIxxFmA=,tag:dPNUcrBrE66xDlFJltmIKg==,type:str] @@ -26,8 +29,8 @@ sops: amRmVkVoS2RqeEs3OXZVeTlsZUVEV28K1WcbGJHT8LMah5b7NN1psiucTl1OfZYO 4T3RDSQMB3qj1TGQSdixjwRRKbMGtL3LXnvkNd+caVi5Z9OkF1O9Yg== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-02-11T21:34:36Z" - mac: ENC[AES256_GCM,data:wJvE4drJ+DTe6lpOj+bVryf6bPrLFwvIHtj0ugpf+rABEfCBC7qvdoc9LUEuizoi33f4t6T/WYWfVcJ6+2ng0v18iynU2W6SVfzmK1MbXg/nySqFpMRTo/3ErOZaluSpn8N8PBUsdfqO+XezI/Vq1ZL8mFx2/cBPw/LMrnFH3lc=,iv:RKvjcUc8HSm462LwFGVCQESen6S65XZ2cuazucKWItY=,tag:saZr9DHnKX6hcs2wJdIiOg==,type:str] + lastmodified: "2024-04-10T22:08:45Z" + mac: ENC[AES256_GCM,data:/qNQmTB1qVDzsXqfkrT60/evSLYSp3N2u5dozwTdDcYWckj09UwhxS03HJQvRspmmV+d1g7t1A8VYNEouFqL44IYw0xnbaVnPvSFCR+6Sp2hiFGR90brqRLb6TUmEfvIvIpvsjfHPvvq4pyP1+ap8mubhGsk01vMsAoHNOsEnIQ=,iv:8kmSjECxv8MGwIPAPw2lK+b521vGs3rYrdqhaj3PUpo=,tag:EmQcKMs3K1rqCXiOTNf5TA==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1