{...}: { flake.nixosModules.forgejo-ssh = { lib, config, pkgs, ... }: let gitUser = config.services.forgejo.settings.server.BUILTIN_SSH_SERVER_USER; forgejo = config.services.forgejo.package; stateDir = config.services.forgejo.stateDir; forgejoKeysScript = pkgs.writeShellScript "forgejo-keys" '' FORGEJO_WORK_DIR=${stateDir} ${lib.getExe forgejo} keys -e git -u "$1" -t "$2" -k "$3" ''; forgejoKeysPath = "/run/forgejo-keys"; in { config = lib.mkIf config.services.forgejo.enable { # sshd rejects connections for users with nologin shell before # processing authorized_keys, so we need a valid shell even though # the command= wrapper in Forgejo's keys prevents actual shell access. users.users.${gitUser}.shell = pkgs.bash; users.groups.${config.services.forgejo.group}.members = [gitUser]; services.openssh.settings.AllowUsers = [gitUser]; # Copy the key lookup script to a root-owned path outside /nix/store. # sshd StrictModes requires AuthorizedKeysCommand and all parent dirs # to be owned by root with no group/world writes. /nix/store and /etc # symlinks both fail this check. system.activationScripts.forgejo-ssh-keys = lib.stringAfter ["etc"] '' install -m 0755 -o root -g root ${forgejoKeysScript} ${forgejoKeysPath} ''; services.openssh.extraConfig = '' Match User ${gitUser} AuthorizedKeysCommandUser ${gitUser} AuthorizedKeysCommand ${forgejoKeysPath} %u %t %k AuthenticationMethods publickey KbdInteractiveAuthentication no PasswordAuthentication no AllowAgentForwarding no AllowTcpForwarding no X11Forwarding no PermitTTY no ''; }; }; }