From 0adf31cccffb265737fa747b98dd0b28cc8a3ce6 Mon Sep 17 00:00:00 2001 From: Kersten Kriegbaum Date: Wed, 14 May 2025 23:09:22 +0200 Subject: [PATCH] add forgejo and forgejo runner flake and test commands --- README.md | 9 +- forgejo_instance_flake.nix | 153 ++++++++++++++++++++++ forgejo_runner_flake.nix | 258 +++++++++++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 forgejo_instance_flake.nix create mode 100644 forgejo_runner_flake.nix diff --git a/README.md b/README.md index a9ff0f3..85528b6 100644 --- a/README.md +++ b/README.md @@ -71,4 +71,11 @@ https://git.zx2c4.com/cgit/ ### Forgejo (my pick) https://codeberg.org/forgejo/forgejo - fork of Gitea - - it's basically Gitea as of today \ No newline at end of file + - it's basically Gitea as of today + +**Forgejo Runner Test commands** +```bash +sudo systemctl status 'gitea-runner-my\x2dforgejo\x2dinstance.service' +sudo journalctl -u 'gitea-runner-my\x2dforgejo\x2dinstance.service' -n 100 --no-pager +sudo ls -la /var/lib/gitea-actions-runner/my-forgejo-instance/ +``` \ No newline at end of file diff --git a/forgejo_instance_flake.nix b/forgejo_instance_flake.nix new file mode 100644 index 0000000..23c821c --- /dev/null +++ b/forgejo_instance_flake.nix @@ -0,0 +1,153 @@ +[badmomber@nixos-playground:/etc/nixos]$ cat flake.nix +# /etc/nixos/flake.nix +{ + description = "NixOS config - Forgejo (Single Flake File) - Nginx Fix v3"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + # sops-nix ... + }; + + outputs = { self, nixpkgs, ... }@inputs: { + + nixosConfigurations."nixos-playground" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + + modules = [ + # Modul 1: Hardware-Konfiguration importieren + ./hardware-configuration.nix + + # Modul 2: Kombinierte Konfiguration inline + ( { config, pkgs, lib, ... }: + let + badmomberPubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEK60zKqFTL66ltnQbWJZFR6voqDqIxzego6BXv2Wq1H kersten@MacBook-Pro-von-Kersten.local"; + systemSshPort = 2424; + forgejoSshPort = 2222; + forgejoDomain = "git.kriegbaum.io"; + acmeEmail = "contact@git.kriegbaum.io"; + forgejoHttpPort = 3000; + in + { + # --- Basiskonfiguration --- + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + networking.hostName = "nixos-playground"; + networking.useDHCP = true; + time.timeZone = "Europe/Berlin"; + i18n.defaultLocale = "de_DE.UTF-8"; + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + environment.systemPackages = with pkgs; [ + vim git wget curl htop tree fail2ban + ]; + + # --- Benutzerkonfiguration --- + users.users.badmomber = { + isNormalUser = true; + description = "admin"; + group = "users"; + extraGroups = [ "wheel" ]; + openssh.authorizedKeys.keys = [ badmomberPubKey ]; + }; + + # --- Sicherheit: SSH Server (Hauptzugang) --- + services.openssh = { + enable = true; + ports = [ systemSshPort ]; + settings = { + PermitRootLogin = "no"; + PubkeyAuthentication = true; + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + }; + }; + + # --- Sicherheit: Firewall --- + networking.firewall = { + enable = true; + allowedTCPPorts = [ systemSshPort forgejoSshPort 80 443 ]; + }; + + # --- Sicherheit: Fail2ban --- + services.fail2ban.enable = true; + + # --- Forgejo Service --- + services.forgejo = { + enable = true; + settings = { + # log.LEVEL = "Debug"; # Debug Log wieder aus + server = { + DOMAIN = forgejoDomain; + ROOT_URL = "https://${forgejoDomain}/"; + HTTP_ADDR = "127.0.0.1"; + HTTP_PORT = forgejoHttpPort; + PROTOCOL = "http"; + SSH_DOMAIN = forgejoDomain; + SSH_PORT = forgejoSshPort; # Der Port, der in URLs angezeigt wird (2222) + + # ---- HINZUGEFÜGTE ZEILEN ---- + START_SSH_SERVER = true; # Sicherstellen, dass der interne SSH-Server startet + SSH_LISTEN_PORT = forgejoSshPort; # Sage dem internen Server, auf Port 2222 zu lauschen + # SSH_LISTEN_HOST = "0.0.0.0"; # Lausche auf allen Interfaces (ist meist Standard) + # ----------------------------- + + ENABLE_REVERSE_PROXY_AUTHENTICATION = true; + ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = true; + ENABLE_REVERSE_PROXY_LIMIT = true; + }; + service.DISABLE_REGISTRATION = true; + }; + }; + + # --- Nginx Reverse Proxy --- + services.nginx = { + enable = true; + # Setze Upload Limit global im http-Block (korrekte NixOS-Option) + clientMaxBodySize = "512M"; # <-- KORREKTE STELLE + # recommendedProxySettings = true; # Bleibt weg + recommendedTlsSettings = true; + virtualHosts.${forgejoDomain} = { + forceSSL = true; + enableACME = true; + # clientMaxBodySize hier falsch platziert + + locations."/" = { + proxyPass = "http://127.0.0.1:${toString forgejoHttpPort}"; + # clientMaxBodySize hier auch falsch platziert + + extraConfig = '' + # Nötig für Websockets / Keep-Alive + proxy_http_version 1.1; + # Header für Websockets (könnten 400er verursachen!) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $http_connection; + # Standard Proxy Header + proxy_set_header Host $host; + 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 $scheme; + ''; + }; + }; + }; + + # --- ACME / Let's Encrypt --- + security.acme = { + acceptTerms = true; + defaults.email = acmeEmail; + }; + + # --- State Version --- + system.stateVersion = "24.11"; # Ggf. anpassen + + } + ) # Ende des inline Moduls + ]; # Ende der Modul-Liste + }; # Ende der nixosConfiguration + }; # Ende der outputs +} \ No newline at end of file diff --git a/forgejo_runner_flake.nix b/forgejo_runner_flake.nix new file mode 100644 index 0000000..be4de36 --- /dev/null +++ b/forgejo_runner_flake.nix @@ -0,0 +1,258 @@ +[runner@forgejo-runner:~]$ cat /etc/nixos/flake.nix +# /etc/nixos/flake.nix on the RUNNER machine +{ + # Description of the flake + description = "NixOS Forgejo Runner - Working state with ExecStartPre override and permissions fix"; + + # Define the inputs for this flake + inputs = { + # Use nixpkgs from the nixos-unstable channel + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + # Declare home-manager input (currently not used in this specific NixOS configuration) + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; # Ensure home-manager uses the same nixpkgs + }; + }; + + # Define the outputs of this flake + outputs = { self, nixpkgs, ... }@inputs: { + + # Define a NixOS configuration named "forgejo-runner" + nixosConfigurations."forgejo-runner" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; # Target system architecture + specialArgs = { inherit inputs; }; # Make flake inputs available to modules + + # List of NixOS modules to include in this configuration + modules = [ + # Import hardware-specific configuration + ./hardware-configuration.nix + + # Inline module for the main system configuration + ( { config, pkgs, lib, ... }: + let + # Configuration for the specific runner instance, accessed from the defined service + runnerInstanceCfg = config.services.gitea-actions-runner.instances."my-forgejo-instance"; + + # Define consistent paths and user/group names + actualStateDir = "/var/lib/gitea-actions-runner/my-forgejo-instance"; # Expected state/working directory + runnerUser = "gitea-actions-runner"; # Desired user for the runner service + runnerGroup = "gitea-actions-runner"; # Desired group for the runner service + + # Helper to escape characters in labels for CSV-like list (used by original module) + escapeLabel = label: builtins.replaceStrings ["\"" "\\"] ["\\\"" "\\\\"] label; + # Get labels list from config, ensure it's a list + labelsList = runnerInstanceCfg.labels or []; + # Concatenate labels into a Nix string like: "\"label1\"","\"label2 with space\"" + concatLabels = lib.concatStringsSep "," (map (l: "\"${escapeLabel l}\"") labelsList); + + # Custom script to handle runner registration, overriding the module's default ExecStartPre + correctedRegisterScript = pkgs.writeShellScript "gitea-register-runner-my-forgejo-instance-final" '' + set -ex # Exit on error and print commands + + echo "--- Debugging correctedRegisterScript (v5 - working version) ---" + echo "Running as user: $(id)" # Display current user + + # Define variables for clarity and to use values from Nix config + ACT_RUNNER_BIN="${config.services.gitea-actions-runner.package}/bin/act_runner" + INSTANCE_URL="${runnerInstanceCfg.url}" + TOKEN_FILE="${runnerInstanceCfg.tokenFile}" + RUNNER_NAME="${runnerInstanceCfg.name}" + + # Use single quotes for shell assignment to preserve the exact string from Nix + LABELS_CONCAT_FROM_NIX='${concatLabels}' + echo "Nix concatLabels raw value: $LABELS_CONCAT_FROM_NIX" + + # Labels are temporarily disabled in the act_runner call for stability + LABELS_ARG="" + echo "Labels Argument String: [TEMPORARILY DISABLED FOR STABILITY]" + + # Check if token file exists and is not empty + if [ ! -f "$TOKEN_FILE" ]; then + echo "Error: Token file $TOKEN_FILE not found!" + exit 1 + fi + TOKEN_VALUE=$(cat "$TOKEN_FILE") + if [ -z "$TOKEN_VALUE" ]; then + echo "Error: Token file $TOKEN_FILE is empty!" + exit 1 + fi + echo "Token Value (first 10 chars for logging): $(echo "$TOKEN_VALUE" | cut -c1-10)..." + + # Check if act_runner binary is executable + if [ ! -x "$ACT_RUNNER_BIN" ]; then + echo "Error: act_runner binary not found or not executable at $ACT_RUNNER_BIN" + exit 1 + fi + + # Create state directory and set permissions if running as root + # (script is prefixed with '+' in ExecStartPre, so it runs as root) + mkdir -p "${actualStateDir}" + if [ "$(id -u)" = "0" ]; then + current_owner_group=$(stat -c '%U:%G' "${actualStateDir}") + target_owner_group="${runnerUser}:${runnerGroup}" + if [ "$current_owner_group" != "$target_owner_group" ]; then + chown "$target_owner_group" "${actualStateDir}" || echo "chown on ${actualStateDir} failed, but continuing." + fi + current_perms=$(stat -c '%a' "${actualStateDir}") + if [[ "$current_perms" != "750" && "$current_perms" != "700" ]]; then # Ensure user can rwx + chmod 0750 "${actualStateDir}" || echo "chmod on ${actualStateDir} failed, but continuing." + fi + fi + + # Change to the state directory for act_runner operations + cd "${actualStateDir}" + echo "Current directory after cd: $(pwd)" + + # Remove old .runner and .labels files to ensure fresh registration + rm -f .runner + rm -f .labels + + # Register the runner if .runner file doesn't exist + if [ ! -f .runner ]; then + echo "Attempting registration..." + echo "Executing: $ACT_RUNNER_BIN register --instance \"$INSTANCE_URL\" --token \"REDACTED\" --name \"$RUNNER_NAME\" --no-interactive $LABELS_ARG" + + "$ACT_RUNNER_BIN" register \ + --instance "$INSTANCE_URL" \ + --token "$TOKEN_VALUE" \ + --name "$RUNNER_NAME" \ + --no-interactive \ + $LABELS_ARG # LABELS_ARG is currently empty + fi + + # Adjust ownership of files created by act_runner (which ran as root) + # This is crucial for the main daemon (running as runnerUser) to access them. + if [ -f .runner ]; then + echo "Adjusting ownership of .runner file to ${runnerUser}:${runnerGroup}" + chown ${runnerUser}:${runnerGroup} .runner + chmod 640 .runner # rw- for user, r-- for group + fi + if [ -f .labels ]; then + echo "Adjusting ownership of .labels file to ${runnerUser}:${runnerGroup}" + chown ${runnerUser}:${runnerGroup} .labels + chmod 640 .labels + fi + + echo "--- End Debugging correctedRegisterScript ---" + ''; + in + { + # --- Base System Configuration --- + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + networking.hostName = "forgejo-runner"; # System hostname + networking.useDHCP = true; # Use DHCP for network configuration + time.timeZone = "Europe/Berlin"; # Set system timezone + i18n.defaultLocale = "de_DE.UTF-8"; # Set default locale + nix.settings.experimental-features = [ "nix-command" "flakes" ]; # Enable Nix Flakes + environment.systemPackages = with pkgs; [ vim fail2ban git curl tree ]; # Basic system packages + + # --- Administrative User 'runner' --- + users.users.runner = { + isNormalUser = true; + description = "Admin for Runner"; + group = "users"; + extraGroups = [ "wheel" ]; # For sudo access + openssh.authorizedKeys.keys = [ # SSH public key for remote login + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO18UxR+2HGer5HIUlgrk01rHmVjUrj7XgGLsG09PiID forgejo-runner" + ]; + }; + + # --- System User and Group for the Forgejo Runner Service --- + users.users."gitea-actions-runner" = { + isSystemUser = true; + group = "gitea-actions-runner"; + extraGroups = [ "docker" ]; # Allow this user to access Docker socket + }; + users.groups."gitea-actions-runner" = {}; # Ensure the group exists + + # --- SSH Server Configuration --- + services.openssh = { + enable = true; + ports = [ 2424 ]; # Use a non-standard port for SSH + settings = { + PermitRootLogin = "no"; # Disable root login via SSH + PubkeyAuthentication = true; # Allow public key authentication + PasswordAuthentication = false; # Disable password authentication + KbdInteractiveAuthentication = false; # Disable keyboard-interactive authentication + }; + }; + + # --- Firewall Configuration --- + networking.firewall = { + enable = true; + allowedTCPPorts = [ 2424 ]; # Allow incoming SSH on the custom port + }; + + # --- Fail2ban Service --- + services.fail2ban.enable = true; # Intrusion prevention + + # --- Docker Service --- + virtualisation.docker = { + enable = true; + daemon.settings = { "fixed-cidr-v6" = "fd00::/80"; "ipv6" = true; }; # Docker daemon settings + }; + + # --- Forgejo Runner Service Definition --- + services.gitea-actions-runner = { # Using the gitea-actions-runner module name + package = pkgs.forgejo-runner; # Specify the runner package + instances."my-forgejo-instance" = { # Define a runner instance + enable = true; + name = "my-forgejo-runner-01"; # Name displayed in Forgejo UI + url = "https://git.kriegbaum.io"; # Your Forgejo instance URL + tokenFile = "/etc/forgejo-runner/token"; # Path to the registration token file + labels = [ # Labels for this runner + "node-22:docker://node:22-bookworm" + "nixos-latest:docker://${pkgs.nix}/bin/nix nixos/nix" + ]; + # User and group for this instance are set via systemd.services overrides below + }; + }; + + # --- Systemd Service Overrides for the Runner Instance --- + # These overrides are applied to the systemd unit generated by the NixOS module + # to fix issues with the default module behavior (e.g., DynamicUser, paths). + systemd.services."gitea-runner-my\\x2dforgejo\\x2dinstance" = { + # Provide necessary binaries for the correctedRegisterScript in its PATH + path = [ pkgs.bash pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.gawk pkgs.findutils ]; + serviceConfig = { + DynamicUser = lib.mkForce false; # Disable DynamicUser, use a persistent one + User = lib.mkForce runnerUser; # Force correct user for the service + Group = lib.mkForce runnerGroup; # Force correct group for the service + StateDirectory = lib.mkForce "gitea-actions-runner/my-forgejo-instance"; # Tell systemd to manage this state dir + WorkingDirectory = lib.mkForce actualStateDir; # Set correct working dir for the daemon + Environment = [ # Set HOME environment variable for the service + "HOME=${actualStateDir}" + ]; + # Override ExecStartPre with our corrected script + ExecStartPre = lib.mkForce [ + "" # Clear any existing ExecStartPre from the module + "+${correctedRegisterScript}" # Run our script as root (for initial mkdir/chown of state dir & .runner file) + ]; + }; + }; + + # --- tmpfiles.d Rules --- + # These rules ensure directories exist with correct permissions. + # The symlink rules are kept as they were part of the working configuration. + systemd.tmpfiles.rules = [ + # Ensure the main state directory for the runner exists with correct owner/perms + "d ${actualStateDir} 0750 ${runnerUser} ${runnerGroup} -" + # Ensure the base for the symlink workaround exists with correct owner/perms + "d /var/lib/gitea-runner 0755 ${runnerUser} ${runnerGroup} -" + # Create a symlink from the path the original module's script might have used + # to the actual state directory. This was part of the working setup. + "L+ /var/lib/gitea-runner/my-forgejo-instance - - - - ${actualStateDir}" + ]; + + # --- NixOS State Version --- + # Ensures configuration compatibility across NixOS upgrades. + system.stateVersion = "23.11"; + } + ) + ]; + }; + }; +} \ No newline at end of file