NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
16.47k stars 12.97k forks source link

Cannot enable Google Authenticator TOTP for sshd #115044

Open curbengh opened 3 years ago

curbengh commented 3 years ago

Describe the bug Enabling Google Authenticator TOTP via pam services for sshd has no effect.

Possibly related: https://github.com/NixOS/nixpkgs/issues/18503

To Reproduce

  1. Setup googleAuthenticator token

  2. users = {
    mutableUsers = false;
    
    users = {
      root = {
        hashedPassword = "*";
      };
      curben = {
        passwordFile = "xxx";
        isNormalUser = true;
        extraGroups = [ "wheel" ];
        openssh.authorizedKeys.keys = [ "ssh-ed25519 xxx" ];
      };
    };
    };
    
    security.pam.services = {
    login.googleAuthenticator.enable = true;
    sshd.googleAuthenticator.enable = true;
    sudo.googleAuthenticator.enable = true;
    };
    
    services.openssh = {
    enable = true;
    permitRootLogin = "no";
    passwordAuthentication = false;
    extraConfig =
      ''
        AuthenticationMethods publickey,keyboard-interactive
      '';
    };
$ cat /etc/pam.d/sshd
auth required pam_deny.so

$ cat /etc/pam.d/login
auth required pam_unix.so nullok  likeauth
auth required /nix/store/yg4cdf21frfkhjybzxsqjhr0i70zrslh-google-authenticator-libpam-1.09/lib/security/pam_google_authenticator.so no_increment_hotp
auth sufficient pam_unix.so nullok  likeauth try_first_pass
auth required pam_deny.so

$ cat /etc/pam.d/sudo
auth required pam_unix.so   likeauth
auth required /nix/store/yg4cdf21frfkhjybzxsqjhr0i70zrslh-google-authenticator-libpam-1.09/lib/security/pam_google_authenticator.so no_increment_hotp
auth sufficient pam_unix.so   likeauth try_first_pass
auth required pam_deny.so

Expected behavior /etc/pam.d/sshd should have pam_google_authenticator.so.

Metadata

 - system: `"x86_64-linux"`
 - host os: `Linux 5.4.100-hardened1, NixOS, 20.09.3343.8d82c865b41 (Nightingale)`
 - multi-user?: `yes`
 - sandbox: `yes`
 - version: `nix-env (Nix) 2.3.10`
 - nixpkgs: `/nix/var/nix/profiles/per-user/root/channels/nixos`
Patagonicus commented 3 years ago

As far as I can tell your problem is caused by this line in sshd.nix. Specifically it disables unixAuth if password authentication is disabled. You can override it with:

security.pam.services.sshd.unixAuth = lib.mkForce true;

(You'll need to add lib to { config, pkgs, ... }: at the beginning of your configuration.nix if it's not in there yet)

With that I get password authentication + TOTP. I'm not sure why it's still asking me for a password, it seems to be doing pubkey + password + TOTP, but this is also the first time I've used google-authenticator with PAM.

curbengh commented 3 years ago

I'm not sure why it's still asking me for a password

My guess is unixAuth = password prompt. Enabling googleAuthenticator also enables unixAuth (pam.nix).


https://github.com/NixOS/nixpkgs/blob/ba1fa0c60406a21b933f5cb1625e80ac0da84f50/nixos/modules/services/networking/ssh/sshd.nix#L473

A workaround can be,

-        unixAuth = cfg.passwordAuthentication;
+        unixAuth = cfg.passwordAuthentication || config.security.pam.services.sshd.googleAuthenticator.enable;

This may cause confusion because user expects no password prompt when ssh's passwordAuthentication is disabled, but password prompt is still there due to PAM/unixAuth.

We can clarify by adding a note to security.pam.services..googleAuthenticator.enable that enabling it on sshd doesn't disable password prompt if user's password is set.


This also affects other pam services like duoSecurity and gnupg.

Another approach is to separate those pam services from unixAuth.

https://github.com/NixOS/nixpkgs/blob/8b10d91d0c687ad0e3f781b8d3c2ab6a3ef8ba61/nixos/modules/security/pam.nix#L419-L426


https://github.com/NixOS/nixpkgs/blob/8b10d91d0c687ad0e3f781b8d3c2ab6a3ef8ba61/nixos/modules/security/pam.nix#L415-L416

Looks like the dependency cannot be removed.

curbengh commented 3 years ago

I replicated the ssh config on Ubuntu Server 20.04.2

# /etc/ssh/sshd_config
PasswordAuthentication no
Match user curben
  AuthenticationMethods publickey,keyboard-interactive

It resulted in similar behavior; it prompts for publickey + password + OTP. So, I think this behavior is expected.

I still feel googleAuthenticator should take precedence over ssh's passwordAuthentication.

  security.pam.services = {
    sshd.googleAuthenticator.enable = true;
  };

  services.openssh = {
    passwordAuthentication = false;
  }
Patagonicus commented 3 years ago

I think ideally we'd have an option in the openssh module to enable the google authenticator, which could then set the rest correctly. But I'm not too involved with nixpkgs, so I'm not sure if that's something that'd be wanted in general.

stale[bot] commented 2 years ago

I marked this as stale due to inactivity. → More info

nixos-discourse commented 6 months ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/setting-up-google-authenticator-for-ssh/36931/3

pdg137 commented 2 months ago

This is still a valid issue. If you want to enable Google Authenticator without passwords (i.e. ssh key + 2FA) it seems that you have to format the whole config file as text. For example this seems to mostly work:

    security.pam.services = {
      sshd.text = ''
        account required pam_unix.so # unix (order 10900)

        auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so nullok no_increment_hotp # google_authenticator (order 12500)
        auth sufficient pam_permit.so

        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 ${pkgs.systemd}/lib/security/pam_systemd.so # systemd (order 12000)
      '';
    };

You also need some settings on the OpenSSH module:

    services.openssh = {
      settings = {
        PasswordAuthentication = false;
        KbdInteractiveAuthentication = true;
        AuthenticationMethods = "publickey,keyboard-interactive";
        PermitRootLogin = "yes"; # disable "prohibit-password", which breaks PAM
      };
    };
tasiaiso commented 2 months ago

@pdg137 This works for me as well in my config, thanks !

For the other people that comes across this thread, this is what I believe this does:

# Check for the client's public key
account required pam_unix.so # unix (order 10900)

# Actually check for the 2FA code.
# "nullok": accept session if .google_authenticator doesn't exist
# "no_increment_hotp": make sure the counter isn't incremented for failed attempts.
auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so nullok no_increment_hotp # google_authenticator (order 12500)
# If .google_authenticator isn't present, you can still let them through
auth sufficient pam_permit.so

# Load the environment variables for the new ssh session
session required pam_env.so conffile=/etc/pam/environment readenv=0 # env (order 10100)
# Logs when a user logins or leave the system. 
session required pam_unix.so # unix (order 10200)
# Record user's login uid to the process attribute 
session required pam_loginuid.so # loginuid (order 10300)
# Register user sessions in the systemd login manager
session optional ${pkgs.systemd}/lib/security/pam_systemd.so # systemd (order 12000)

Let me know if I got anything wrong.

tasiaiso commented 5 days ago

@curbengh is this still an issue ?

I think ideally we'd have an option in the openssh module to enable the google authenticator, which could then set the rest correctly. But I'm not too involved with nixpkgs, so I'm not sure if that's something that'd be wanted in general.

We can open another issue for that but I think this one is solved.