RADIUS + Google OTP를 사용한 SSH 인증

RADIUS + Google OTP를 사용한 SSH 인증

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" 옵션을 제거하면 문제가 해결되고 원하는 동작을 얻을 수 있습니다.

관련 정보