Gitea with host ssh server

Hi,

I’m trying to setup Gitea as Nixos Module which has runnig as docker container until now.
Hower since users.users.<name>.openssh.authorizedKeys.keyFiles requires the file to be in store it can’t be used for gitea.
The second best option i’ve been trying to use is services.openssh.authorizedKeysCommand without much sucess. The following appears in my ssh log when trying to clone via ssh:

Dec 10 17:02:08 ks3 sshd[60777]: AuthorizedKeysCommand /run/wrappers/bin/gitea-keys git failed, status 127
Dec 10 17:02:08 ks3 sshd[60777]: AuthorizedKeysCommand /run/wrappers/bin/gitea-keys git failed, status 127
Dec 10 17:02:08 ks3 sshd[60777]: error: PAM: Authentication failure for git from xxxx.22.45
Dec 10 17:02:08 ks3 sshd[60777]: Connection closed by authenticating user git xxx.22.45 port 59400 [preauth]

Revevant config

 users.users.git = {
    isSystemUser = true;
    group = "git";
    extraGroups = [ "gitea" ];
  };
  users.groups.git = { };
  security.wrappers.gitea-keys = {
    owner = "root";
    group = "root";
    source = pkgs.writeShellScript "gitea-keys" ''
      if [ "$1" == "git" ]; then
        echo '#' $0
        cat ${config.services.gitea.settings.server.SSH_ROOT_PATH}/authorized_keys
      fi
    '';
  };
  services.openssh.authorizedKeysCommand = "/run/wrappers/bin/gitea-keys %u";
  services.openssh.authorizedKeysCommandUser = "gitea";

I was actually quite surprised that the gitea module didn’t include the necessary functionality to setup ssh, leading me to belive that an solution might not be so trival.

Can you describe your problem a bit better? I have a local gitea running without any problems, no SSH woes.

I created a user, added its keys via the interface, and then I was able to push and pull.

I’m trying to use the systems sshd rather than gitea’s internal ssh server for git. Here my full config btw:

{ config, lib, pkgs, inputs, ... }:
let cfg = config.services.gitea; in {
  services.gitea = rec {
    enable = true;
    rootUrl = "https://git.${domain}/";
    appName = "Gitea";
    disableRegistration = true;
    inherit (config.networking) domain;
    stateDir = "/srv/gitea";
    repositoryRoot = "${stateDir}/repositories";
    database = {
      type = "sqlite3";
      path = "${stateDir}/gitea.db";
    };
    enableUnixSocket = true;
    ssh = {
      clonePort = lib.head config.services.openssh.ports;
    };
    lfs = {
      enable = true;
      contentDir = "${stateDir}/lfs";
    };
    cookieSecure = true;
    settings = {
      server = {
        SSH_USER = "git";
        SSH_DOMAIN = "git.${domain}";
        START_SSH_SERVER = false;
        SSH_ROOT_PATH = "${stateDir}/ssh";
        SSH_TRUSTED_USER_CA_KEYS = lib.concatStringsSep "," [
          (builtins.readFile "${inputs.ssh}/ca.pub")
        ];
        OFFLINE_MODE = true;
      };

    };
    log.rootPath = "${stateDir}/log";

  };
  users.users.git = {
    isSystemUser = true;
    group = "git";
    extraGroups = [ "gitea" ];
  };
  users.groups.git = { };
  security.wrappers.gitea-keys = {
    owner = "root";
    group = "root";
    source = pkgs.writeShellScript "gitea-keys" ''
      if [ "$1" == "git" ]; then
        echo '#' $0
        cat ${config.services.gitea.settings.server.SSH_ROOT_PATH}/authorized_keys
      fi
    '';
  };
  services.openssh.authorizedKeysCommand = "/run/wrappers/bin/gitea-keys %u";
  services.openssh.authorizedKeysCommandUser = "gitea";
  services.nginx.upstreams.gitea = with config.services.gitea; lib.mkIf enable {
    servers = {
      "unix:${config.services.gitea.settings.server.HTTP_ADDR}" = { };
    };
  };
  services.nginx.virtualHosts."git.${config.services.gitea.domain}" = {
    forceSSL = true;
    useACMEHost = config.services.gitea.domain;
    locations."/".proxyPass =
      "http://gitea";
  };
}

The gitea module does not, per default hook into the systems sshd.

It does, but you’re overriding that here:

You even ask gitea to use the system ssh, so even though you get gitea to kind-of-but-not launch its own ssh process it ends up being unused:

And this is outright false:

Those keys are not used for git authentication in general, gitea instead will use whichever keys you configure it to use for your user in its UI. I think setting that setting for the gitea user might break that functionality.

If you wanted to reuse those keys, you could probably set something up with ExecStartPre to copy over the keys so gitea knows about them as well, but I wouldn’t recommend doing so without fully understanding gitea’s user management.

You’re overthinking the configuration. It should all work out of the box, if it does not, perhaps double check you don’t have some leftover state from your previous docker configuration.

Just delete anything regarding the gitea and git users, delete their accounts, wipe their home directories (to make sure you don’t have leftover state from your experimentation), remove all the ssh, auth and git related settings for services.gitea, delete the gitea configuration file for good measure if it’s not a symlink and redeploy. It should work, at least it does for me.

Note: Make sure to back up data from those users before you mess with them too much. Or try out nixos-rebuild build-vm to test it in a safe environment.

1 Like

For reference, this is the extent of my gitea configuration: tlaternet-server/gitea.nix at master - tlaternet-server - Gitea: Git with a cup of tea

I can sign into this with the system sshd.

1 Like

That’s my entire point I do not want giteas own ssh server

It’s not just try to include an out of store file and you’ll get an error (if you’re using flakes)

That’s what I don’t want, I want both my systems ssh and giteas ssh on port 22

Yep, the NixOS gitea module does that by default. Your settings configure an ssh server which is not the host ssh server that gitea uses by default, and then you ask it not to launch it.

The git user of the NixOS module defaults to gitea rather than git, because that way you don’t have to maintain a second user and handle permissions between them, by the way.

You can refer to files not in /nix/store, but you have to put them in place using systemd units, and the module you’re using has to support accessing file paths at runtime rather than evaluation time (i.e., not use builtins.readFile). I’d recommend against trying this for now, let’s just get your service running :wink:

My gitea config fits into ~10 lines:

Half of it is setting timeouts much higher than the default, to be able to mirrot nixpkgs.

And I think just the first line has been enough to get me started…

1 Like

I think my configuration might have confused you further, my system ssh runs on port 2222. I can access both gitea and my general ssh server from port 2222, with one single sshd process.

What I’m trying to say is that your configuration sets up a separate process, but fails to launch it. Just remove all of your ssh-related configuration, and gitea will function as you want, reusing the system ssh.

Ok figured it out. The trick is to run gitea under the git user and to leave the ssh dir option as it is such that .ssh exists within the home dir.

{ config, lib, pkgs, inputs, ... }:
let cfg = config.services.gitea; in {
  services.gitea = rec {
    enable = true;
    rootUrl = "https://git.${domain}/";
    user = "git";
    appName = "Gitea";
    disableRegistration = true;
    inherit (config.networking) domain;
    stateDir = "/srv/gitea";
    repositoryRoot = "${stateDir}/repositories";
    database = {
      type = "sqlite3";
      inherit user;
      path = "${stateDir}/gitea.db";
    };
    enableUnixSocket = true;
    ssh = {
      clonePort = lib.head config.services.openssh.ports;
    };
    lfs = {
      enable = true;
      contentDir = "${stateDir}/lfs";
    };
    cookieSecure = true;
    settings = {
      server = {
        SSH_USER = "git";
        SSH_DOMAIN = "git.${domain}";
        SSH_TRUSTED_USER_CA_KEYS = lib.concatStringsSep "," [
          (builtins.readFile "${inputs.ssh}/ca.pub")
        ];
         OFFLINE_MODE = true;
      };
    };
    log.rootPath = "${stateDir}/log";

  };

  users.users.git = {
    isSystemUser = true;
    useDefaultShell = true;
    group = "git";
    extraGroups = [ "gitea" ];
    home = cfg.stateDir;
  };
  users.groups.git = { };
 
  services.nginx.upstreams.gitea = with config.services.gitea; lib.mkIf enable {
    servers = {
      "unix:${config.services.gitea.settings.server.HTTP_ADDR}" = { };
    };
  };
  services.nginx.virtualHosts."git.${config.services.gitea.domain}" = {
    forceSSL = true;
    useACMEHost = config.services.gitea.domain;
    locations."/".proxyPass =
      "http://gitea";
  };
}
1 Like