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.