FreeRadius를 사용하여 사용자를 인증하도록 SSHD를 구성하려고 합니다. FreeRadius 서버는 먼저 Google Authenticator를 사용하여 유효한 OTP를 요구한 다음 시스템 계정 비밀번호를 확인해야 합니다.
Radius 서버를 다음으로 설정하면 작업을 수행할 수 있습니다.오직Google Authenticator를 사용하는데 시스템 계정 비밀번호를 묻는 추가 단계를 추가하면 Google Authenticator 토큰이 매번 실패합니다. 내 문제는 내 PAM 구성이라고 생각하지만 내가 뭘 잘못하고 있는지 알 수 없습니다.
SSHD를 실행하는 서버를 "클라이언트" 서버라고 부르고 FreeRadius 서버를 "반경" 서버라고 부르겠습니다.
내 클라이언트는 다음과 같습니다:/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
또한 다음 줄을 사용하여 client:/etc/ssh/sshd_config에서 챌린지/응답 인증을 활성화했습니다.
ChallengeResponseAuthentication yes
Radius 서버의 /etc/pam.d/radiusd 구성은 다음과 같습니다.
#%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
체인을 따라갈 수 있도록 Radius 서버의 /etc/pam.d/password-auth 파일은 다음과 같습니다.
#%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
이것이 작동하는 방식은 클라이언트 서버가 로그인을 수락하려면 Radius 서버만 수락하면 되지만 Radius 서버는 수락하려면 GoogleAuthenticator가 포함된 OTP와 로컬 pam_unix 비밀번호가 모두 필요하다는 것입니다. 이것이 내가 원하는거야.
흥미롭게도 Radius 서버 /etc/pam.d/radiusd 파일에 다음 줄을 주석 처리했습니다.
auth include password-auth
그런 다음 클라이언트 서버에 로그인하면 GoogleAuthenticator 토큰을 입력하라는 메시지가 표시됩니다. 내가 이렇게 하면 모든 것이 작동합니다. 내 휴대폰에서 받은 OTP가 수락되고, 요청이 Radius로 전송되고, 클라이언트 서버로 다시 돌아와서 나를 허용합니다.
하지만 위 줄의 주석 처리를 해제하면 GoogleAuthenticator 토큰을 입력하라는 메시지가 표시되고 이를 입력할 때마다 실패합니다. 이상하게도 OTP 토큰을 4번이나 달라고 한 뒤 시스템 계정 비밀번호를 달라고 하는데 실패했다고 합니다. 누구든지 이 작업을 수행하도록 도와줄 수 있나요?
다음은 "radiusd -X"를 사용하려고 할 때의 디버그 출력입니다.둘 다GoogleAuthenticator 토큰 및 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’
답변1
나는 그것을 알아. 문제는 /etc/pam.d/password-auth 파일의 PAM 스택에 있었습니다. 특히 다음 줄은 다음과 같습니다.
auth sufficient pam_unix.so nullok try_first_pass
무슨 일이 일어나고 있는지는 google-authenticator에 대한 토큰이 허용되었지만 pam_unix.so가 "try_first_pass" 옵션 때문에 해당 코드를 시스템 비밀번호로 사용하려고 시도했다는 것입니다. 이유는 잘 모르겠지만 이로 인해 전체 인증 체인이 다시 시작되어 google-auth 비밀번호를 요청하게 되었습니다.
"try_first_pass" 옵션을 제거하면 문제가 해결되고 원하는 동작을 얻을 수 있습니다.