Setting up Google Authenticator for SSH

I’m trying to set up two factor authentication using Google Authenticator for SSH. In the NixOS options there is the security.pam.services.<name>.googleAuthenticator.enable option, which allegedly reads from ~/.google_authenticator. I’ve tried setting this up for sshd and openssh, but neither prompts 2FA during login. Enabling it for openssh links a file in /etc/static/pam.d/openssh which has

auth required /nix/store/6kk30d0jnad77520i4c08x5qmpvdraxw-google-authenticator-libpam-1.09/lib/security/pam_google_authenticator.so no_increment_hotp # google_authenticator (order 12500)

but this does not take effect, even after restarting sshd.service. Enabling it for sshd does not put the authenticator module in /etc/static/pam.d/sshd.

OpenSSH is defined in my configuration.nix as follows:

services.openssh = {
  enable = true;
  ports = [ 6229 ];
  settings = {
    PermitRootLogin = "no";
    AllowUsers = [ "tgrcdev" ];
    PasswordAuthentication = false;
  };
};

Did you check the log of the ssh server (journalctl -u sshd -e)? What does it tell? What do guides on the internet tell you to configure? Are there more steps that are missing?

These lines are printed upon SSH login (some information removed for brevity/privacy)

Dec 17 18:47:54 <host> sshd[517463]: Connection from <client ip> port 35240 on <server ip> port 6229 rdomain ""
Dec 17 18:47:56 <host> sshd[517463]: Accepted key <pubkey> found at /etc/ssh/authorized_keys.d/tgrcdev:2
Dec 17 18:47:56 <host> sshd[517463]: Postponed publickey for tgrcdev from <client ip> port 35240 ssh2 [preauth]
Dec 17 18:47:56 <host> sshd[517463]: Accepted key <pubkey> found at /etc/ssh/authorized_keys.d/tgrcdev:2
Dec 17 18:47:56 <host> sshd[517463]: Accepted publickey for tgrcdev from <client ip> port 35240 ssh2: <pubkey>
Dec 17 18:47:56 <host> sshd[517463]: pam_unix(sshd:session): session opened for user tgrcdev(uid=1000) by (uid=0)
Dec 17 18:47:56 <host> sshd[517463]: User child is on pid 517475

From what I can tell, it uses the /etc/static/pam.d/sshd config, which does not contain the google authenticator module.

The only thing I can find online is the related issue.
https://github.com/NixOS/nixpkgs/issues/115044

One workaround given is to add security.pam.services.sshd.unixAuth = lib.mkForce true. This enables the verification code, but also enables password authentication, which I do not want enabled. It also does not prompt for a code when a private key is provided.

My ideal setup is pubkey authentication, and a verification code if I’m signing in from outside of my house network, but I’m not sure how much of this is possible with Nix’s packages at this time.

I found the lines that determine what ends up in the pam.d configuration files

The specific lines for googleAuthenticator lie here:

This explains why cfg.unixAuth would enable the authenticator codes, but I don’t yet understand why cfg.googleAuthenticator.enable falls under this large if-statement.

When googleAuthenticator is enabled for both openssh and sshd, here are what the respective pam.d configs look like for openssh:

# /etc/static/pam.d/openssh

# Account management.
account required pam_unix.so # unix (order 10900)

# Authentication management.
auth optional pam_unix.so likeauth # unix-early (order 11600)
auth required /nix/store/6kk30d0jnad77520i4c08x5qmpvdraxw-google-authenticator-libpam-1.09/lib/security/pam_google_authenticator.so no_increment_hotp # google_authenticator (order 12500)
auth sufficient pam_unix.so likeauth try_first_pass # unix (order 12800)
auth required pam_deny.so # deny (order 13600)

# Password management.
password sufficient pam_unix.so nullok yescrypt # unix (order 10200)

# Session management.
session required pam_env.so conffile=/etc/pam/environment readenv=0 # env (order 10100)
session required pam_unix.so # unix (order 10200)

and sshd:

# /etc/static/pam.d/sshd

# Account management.
account required pam_unix.so # unix (order 10900)

# Authentication management.
auth required pam_deny.so # deny (order 12400)

# Password management.
password sufficient pam_unix.so nullok yescrypt # unix (order 10200)

# Session management.
session required pam_env.so conffile=/etc/pam/environment readenv=0 # env (order 10100)
session required pam_unix.so # unix (order 10200)
session required pam_loginuid.so # loginuid (order 10300)
session optional /nix/store/i0sdqs34r68if9s4sfmpixnnj36npiwj-systemd-254.6/lib/security/pam_systemd.so # systemd (order 12000)

So, for some reason, openssh has cfg.unixAuth = true, which allows it to insert the googleAuthenticator module in the pam.d file. Why this is, I have no idea, as that is not set in my configuration.nix. (EDIT: This is maybe because i have services.openssh.settings.PasswordAuthentication = false, but why would that put the module in openssh and not sshd?)

have you managed to solve this?

Sorry for not responding. I never solved this, instead I just accepted using SSH without two-factor. I don’t feel confident enough in my knowledge of PAM to submit a patch.

1 Like

I got Google Authenticator to work for SSH. I did not try with SSH key. I’ll try that later.

#configuration.nix
     packages = with pkgs; [
       google-authenticator
     ];
    services = {
        openssh = {
          enable = true;
          settings ={
            PasswordAuthentication = true;
            PermitRootLogin = "no";
            KbdInteractiveAuthentication = true;
          };
        };
    };
  security.pam = {
    services.sshd.googleAuthenticator.enable = true;
  };
2 Likes