Autenticación SSH usando RADIUS + Google Authenticator

Autenticación SSH usando RADIUS + Google Authenticator

Estoy intentando configurar mis SSHD para autenticar usuarios usando FreeRadius. El servidor FreeRadius primero debe solicitar una OTP válida mediante Google Authenticator y luego verificar la contraseña de la cuenta del sistema.

Puedo hacer que todo funcione si configuro el servidor Radius ensolouso Google Authenticator, pero cuando agrego el paso adicional de solicitar la contraseña de la cuenta del sistema, el token de Google Authenticator falla cada vez. Creo que mi problema es mi configuración de PAM, pero no veo qué estoy haciendo mal con ella.

Llamaré al servidor que ejecuta SSHD el servidor "cliente" y al servidor FreeRadius el servidor "radius".

Aquí está mi cliente:/etc/pam.d/sshd

#%PAM-1.0
auth       required     pam_sepermit.so
auth       required     pam_radius_auth.so debug prompt=token
#auth       include      password-auth
auth       include      postlogin
account    required     pam_nologin.so
account    include      password-auth
account    sufficient   pam_radius_auth.so
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin

También habilité la autenticación de desafío/respuesta en el cliente:/etc/ssh/sshd_config con la línea:

ChallengeResponseAuthentication yes

Aquí está mi configuración de /etc/pam.d/radiusd en el servidor Radius:

#%PAM-1.0
auth       requisite    pam_google_authenticator.so
auth       include      password-auth
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
session    include      password-auth
#@include common-auth
#@include common-account
#@include common-password
#@include common-session

Y para que puedas seguir la cadena, aquí está el archivo /etc/pam.d/password-auth en el servidor Radius:

#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet
account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session     optional      pam_systemd.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so

La forma en que se supone que funciona esto es que el servidor cliente solo necesita que el servidor Radius acepte para aceptar el inicio de sesión, pero el servidor Radius necesita OTP con GoogleAuthenticator y la contraseña local pam_unix para poder aceptar. Esto es lo que quiero.

Curiosamente, cuando comento en el archivo /etc/pam.d/radiusd del servidor Radius aparece la línea:

auth       include      password-auth

Luego puedo iniciar sesión en el servidor del cliente y me solicitará mi token de GoogleAuthenticator. Todo funciona cuando hago esto. Se acepta la OTP que recibo de mi teléfono, la solicitud se envía a Radius, regresa al servidor del cliente y me deja entrar.

Pero si dejo de comentar la línea anterior, se me solicita mi token de GoogleAuthenticator y cada vez que lo escribo, falla. Curiosamente, me pide el token OTP 4 veces, luego me pide la contraseña de la cuenta del sistema y luego dice que falló. ¿Alguien puede ayudarme a hacer que esto funcione?

Aquí está mi resultado de depuración de "radiusd -X" cuando intento usarambosel token de GoogleAuthenticator y la contraseña de Unix:

Received Access-Request Id 112 from client:48253 to radius:1812 length 94
        User-Name = ‘bob’
        User-Password = '146963'
        NAS-IP-Address = client
        NAS-Identifier = 'sshd'
        NAS-Port = 9148
        NAS-Port-Type = Virtual
        Service-Type = Authenticate-Only
        Calling-Station-Id = ‘xxx.xxx.xxx.xxx’
(0) Received Access-Request packet from host 192.168.20.51 port 48253, id=112, length=94
(0)     User-Name = ‘bob’
(0)     User-Password = '146963'
(0)     NAS-IP-Address = xxx.xxx.xxx.xxx
(0)     NAS-Identifier = 'sshd'
(0)     NAS-Port = 9148
(0)     NAS-Port-Type = Virtual
(0)     Service-Type = Authenticate-Only
(0)     Calling-Station-Id = ‘xxx.xxx.xxx.xxx’
(0) # Executing section authorize from file /etc/raddb/sites-enabled/default
(0)   authorize {
(0)   filter_username filter_username {
(0)     if (!&User-Name) 
(0)     if (!&User-Name)  -> FALSE
(0)     if (&User-Name =~ / /) 
(0)     if (&User-Name =~ / /)  -> FALSE
(0)     if (&User-Name =~ /@.*@/ ) 
(0)     if (&User-Name =~ /@.*@/ )  -> FALSE
(0)     if (&User-Name =~ /\\.\\./ ) 
(0)     if (&User-Name =~ /\\.\\./ )  -> FALSE
(0)     if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\\.(.+)$/))  
(0)     if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\\.(.+)$/))   -> FALSE
(0)     if (&User-Name =~ /\\.$/)  
(0)     if (&User-Name =~ /\\.$/)   -> FALSE
(0)     if (&User-Name =~ /@\\./)  
(0)     if (&User-Name =~ /@\\./)   -> FALSE
(0)   } # filter_username filter_username = notfound
(0)   [preprocess] = ok
(0)   [chap] = noop
(0)   [mschap] = noop
(0)   [digest] = noop
(0)  suffix : Checking for suffix after "@"
(0)  suffix : No '@' in User-Name = “bob”, looking up realm NULL
(0)  suffix : No such realm "NULL"
(0)   [suffix] = noop
(0)  eap : No EAP-Message, not doing EAP
(0)   [eap] = noop
(0)  files : users: Matched entry DEFAULT at line 198
(0)   [files] = ok
(0)   [expiration] = noop
(0)   [logintime] = noop
(0)  WARNING: pap : No "known good" password found for the user.  Not setting Auth-Type
(0)  WARNING: pap : Authentication will fail unless a "known good" password is available
(0)   [pap] = noop
(0)  } #  authorize = ok
(0) Found Auth-Type = PAM
(0) # Executing group from file /etc/raddb/sites-enabled/default
(0)   authenticate {
pam_pass: using pamauth string <radiusd> for pam.conf lookup
pam_pass: function pam_authenticate FAILED for <bob>. Reason: Authentication failure
(0)   [pam] = reject
(0)  } #  authenticate = reject
(0) Failed to authenticate the user
(0) Using Post-Auth-Type Reject
(0) # Executing group from file /etc/raddb/sites-enabled/default
(0)  Post-Auth-Type REJECT {
(0)  attr_filter.access_reject : EXPAND %{User-Name}
(0)  attr_filter.access_reject :    --> bob
(0)  attr_filter.access_reject : Matched entry DEFAULT at line 11
(0)   [attr_filter.access_reject] = updated
(0)  eap : Request didn't contain an EAP-Message, not inserting EAP-Failure
(0)   [eap] = noop
(0)   remove_reply_message_if_eap remove_reply_message_if_eap {
(0)     if (&reply:EAP-Message && &reply:Reply-Message) 
(0)     if (&reply:EAP-Message && &reply:Reply-Message)  -> FALSE
(0)    else else {
(0)     [noop] = noop
(0)    } # else else = noop
(0)   } # remove_reply_message_if_eap remove_reply_message_if_eap = noop
(0)  } # Post-Auth-Type REJECT = updated
(0) Delaying response for 1 seconds
Waking up in 0.9 seconds.
(0) Sending delayed response
(0) Sending Access-Reject packet to host xxx.xxx.xxx.xxx port 48253, id=112, length=0
Sending Access-Reject Id 112 from xxx.xxx.xxx.xxx:1812 to xxx.xxx.xxx.xxx:48253
Waking up in 3.9 seconds.
(0) Cleaning up request packet ID 112 with timestamp +20
Ready to process requests
Received Access-Request Id 81 from xxx.xxx.xxx.xxx:45486 to xxx.xxx.xxx.xxx:1812 length 94
        User-Name = ’bob’
        User-Password = '146963'
        NAS-IP-Address = xxx.xxx.xxx.xxx
        NAS-Identifier = 'sshd'
        NAS-Port = 9149
        NAS-Port-Type = Virtual
        Service-Type = Authenticate-Only
        Calling-Station-Id = ‘xxx.xxx.xxx.xxx’

Respuesta1

Me lo imaginé. El problema estaba en la pila PAM para el archivo /etc/pam.d/password-auth. Específicamente esta línea:

auth        sufficient    pam_unix.so nullok try_first_pass

Lo que estaba sucediendo es que se aceptaba el token de google-authenticator, pero luego pam_unix.so intentaba usar ese código como contraseña del sistema debido a la opción "try_first_pass". No estoy seguro de por qué, pero esto estaba provocando que toda la cadena de autenticación comenzara de nuevo, solicitando la contraseña de autenticación de Google.

Deshacerme de la opción "try_first_pass" soluciona el problema y me da el comportamiento deseado.

información relacionada