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

다음은 반경 서버의 /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

그리고 체인을 따라갈 수 있도록 여기에 반경 서버의 /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

이것이 작동하는 방식은 클라이언트 서버가 로그인을 수락하기 위해 허용할 반경 서버만 필요하지만, 반경 서버는 수락하기 위해 GoogleAuthenticator가 포함된 OTP와 로컬 pam_unix 비밀번호가 모두 필요하다는 것입니다. 이것이 내가 원하는거야.

흥미롭게도 내가 반경 서버 /etc/pam.d/radiusd 파일에 주석을 달 때:

auth       include      password-auth

그런 다음 클라이언트 서버에 로그인하면 GoogleAuthenticator 토큰을 입력하라는 메시지가 표시됩니다. 내가 할 때 모든 것이 작동합니다. 내 전화에서 받은 OTP가 수락되고 요청이 반경으로 전송되고 클라이언트 서버로 다시 돌아와 나를 허용합니다.

그러나 위 줄의 주석 처리를 제거하면 GoogleAuthenticator 토큰을 입력하라는 메시지가 표시되며 입력할 때마다 실패합니다. 이상하게도 OTP 토큰을 4번 요구하고 시스템 계정 비밀번호를 묻고 실패했다고 합니다. 아무도 내가 이것을 작동하도록 도울 수 있습니까?

다음은 GoogleAuthenticator 토큰과 유닉스 비밀번호를 모두 사용하려고 할 때 "radiusd -X"의 디버그 출력입니다 .

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’
answer

나는 그것을 알아. 문제는 /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" 옵션을 제거하면 문제가 해결되고 원하는 동작이 제공됩니다.