Score:4

SSH public key authentication with google authenticator still asks for password

de flag

I'm trying to enable 2FA with ssh using libpam-google-authenticator. Not all users need authenticator enabled. Everybody uses ssh public keys, and nobody has a password. I'm running Debian buster, and I've also tried libpam-google-authenticator from bullseye.

My problem is that no matter what I put in the PAM config, users without authenticator enabled are never logged straight in, but always asked for a password.

I've install libpam-google-authenticator and configured /etc/ssh/sshd_config with:

PasswordAuthentication no
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
PasswordAuthentication no
PermitEmptyPasswords no

I haven't been able to work out the correct PAM config so that users without a .google_authenticator file are still logged in. Depending on what I use, users are either prompted for a password (they don't have one), or not allowed in at all.

In /etc/pam.d/sshd I've tried (like this Trying to get SSH with public key (no password) + google authenticator working on Ubuntu 14.04.1):

#@include common-auth
auth       required     pam_google_authenticator.so debug nullok

In this case, users without an authenticator setup get rejected with the following debug;

Aug 05 15:11:18 <host> sshd(pam_google_authenticator)[746624]: debug: start of google_authenticator for "<user>"
Aug 05 15:11:18 <host> sshd(pam_google_authenticator)[746624]: debug: end of google_authenticator for "<user>" Result: The return value should be ignored by PAM dispatch
Aug 05 15:11:18 <host> sshd[746620]: error: PAM: Permission denied for <user> from <IP>

Is pam_permit is needed to set up the fallback case?

I've also tried various combinations of auth required and auth sufficient before and after @include common-auth but they all result in users without authenticator being asked for a password and sometimes users WITH authenticator also being asked for a password.

Does anyone have a recipe to make this work?

in flag
Ok I think I am finally better understanding your problem. I did some reading, and now I have a question. Are all the users that need pubkey+totp in a specific group, or could the be in a group? Or perhaps the opposite. Are all the users that are pubkey only in a group? You can set different `AuthenticationMethod` directives in a `match` section in your sshd_config.
Hamish Moffatt avatar
de flag
I could make that work. To complicate things though, once I have this working I want to try to make the TOTP requirement dependent on where I'm connecting from using `pam_access` - if I'm connecting from a VPN IP I want to bypass TOTP. Although it looks like I could also the IP check using `match` rather than `pam_access` so maybe that would work afterall.
Score:2
de flag

Here is my working configuration. Some users have authenticator enabled and some don't, and only SSH logins with public keys are permitted, never passwords.

In /etc/ssh/sshd_config,

UsePAM yes
PasswordAuthentication no
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
PermitEmptyPasswords no

In /etc/pam.d/sshd,

# Standard Un*x authentication.
#@include common-auth

# Require authenticator, if not configured then allow
auth    required    pam_google_authenticator.so debug nullok
auth    required    pam_permit.so

@include comon-auth must be disabled because it includes pam_unix, which I don't want to use. Then you need pam_permit to make authentication successful for users without authenticator (for which pam_google_authenticator returns ignore rather than pass).

This still doesn't let root login with an ssh key; sshd logs

sshd[1244501]: fatal: Internal error: PAM auth succeeded when it should have failed

This is discussed at Google Authenticator PAM on SSH blocks root login without 2FA .

Having gotten this working as above, I think it's actually nicer to enforce 2FA for certain groups using the SSH config as @zoredache suggested. This easily allows you to whitelist certain IPs as not requiring 2FA also. In this case, sshd_config says for example

UsePAM yes
PasswordAuthentication no
ChallengeResponseAuthentication yes
#AuthenticationMethods any # default
PermitEmptyPasswords no

Match Group adm Address *,!172.16.1.0/24
    AuthenticationMethods publickey,keyboard-interactive

and /etc/pam.d/ssh says

 Standard Un*x authentication.
#@include common-auth

# Require authenticator; SSH should not allow any user in who doesn't have it
auth       sufficient   pam_google_authenticator.so debug nullok
auth       requisite    pam_deny.so
NeverEndingQueue avatar
cn flag
Thanks, none of the guide that I've found mentioned about: `pam_permit` setting in order to make 2FA optional for users.
Score:0
in flag

I don't think you need or want to comment out the @include common-auth. Or at least I did not and it seemed to work correctly. But I am still mostly been testing this.

Does anyone have a recipe to make this work?

Don't have time to translate it to a shell script for you, but this is a excerpt of an ansible playbook that seems to work for me. I suspect you should be able follow along with what this is doing even if you aren't using ansible.

- hosts: linux_systems
  tasks:

  - name: Add group 'totp'
    group:
      name: totp
      state: present
      system: yes

  - name: Create directory for totp secrets
    file:
      state: directory
      path: /var/lib/google-authenticator
      owner: "0"
      group: "0"
      mode: "0700"

  - name: install libpam-google-authenticator
    apt:
      update_cache: yes
      cache_valid_time: '{{ apt_cache_valid_time | default(7200) }}'
      state: present
      name:
      - libpam-google-authenticator

  - name: Create secret for 'example-user'
    args:
      creates: /var/lib/google-authenticator/example-user
    shell: |
      TOTP_USER=example-user; \
      google-authenticator \
        --force --quiet \
        --emergency-codes=10 \
        --time-based \
        --qr-mode=none \
        --allow-reuse \
        --window-size=3 \
        --rate-limit=4 --rate-time=30 \
        --secret=/var/lib/google-authenticator/${TOTP_USER}

  - name: update pam
    lineinfile:
      insertafter: '^@include common-password'
      path: /etc/pam.d/login
      line: 'auth required pam_google_authenticator.so nullok user=root secret=/var/lib/google-authenticator/${USER}'

  - name: update pam
    lineinfile:
      insertafter: '^@include common-password'
      path: /etc/pam.d/sshd
      line: 'auth required pam_google_authenticator.so nullok user=root secret=/var/lib/google-authenticator/${USER}'

  - name: update sshd ChallengeResponseAuthentication
    notify: Restart sshd
    lineinfile:
      path: /etc/ssh/sshd_config
      regexp: '^ChallengeResponseAuthentication .*'
      line: 'ChallengeResponseAuthentication yes'

  handlers:

  - name: Restart sshd
    service:
      name: sshd
      state: restarted
Hamish Moffatt avatar
de flag
Thanks. That looks like what I have, except that you have added the module later in the file - effectively at the end. Do you also have the AuthenticationMethods in sshd_config changed to add keyboard-interactive? I never get any prompts for the verification code without it. But with it, and the authenticator module at the end, I get asked for a password. Is your common-auth unmodified?
in flag
As far as recall, those changes are the everything I needed from a completely stock current Debian buster install.
Hamish Moffatt avatar
de flag
I don't get any attempt at TOTP verification without modifying the AuthenticationMethods in sshd_config. Other documentation I've seen eg https://blog.geoghegan.me/linux/setting-up-google-auth-2fa-on-debian also includes this step.
in flag
You are right the docs do say that for ssh. Most of my testing for this was testing local logins. Now I am not sure if I had ever tested SSH. My SSH is basically key-based only, and I wasn't worried about ssh for my test account since it didn't get any keys.
Hamish Moffatt avatar
de flag
OK thanks for the replies.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.