ProtectHome 대 TOTP 시드: VPN 로그인이 실패하는 이유
올바른 코드도 시드를 열 수 없는 PAM 모듈은 도울 수 없습니다. drop-in과 쓰기 가능한 상태 경로 중에서 고른 뒤 유닛을 확인하세요.
인증 앱은 새 여섯 자리 코드를 보여 주는데 VPN은 이를 거부하고, 같은 코드를 자신의 셸에서 시험하면 잘 검증됩니다. 이 간극이 전부입니다. 등록 비밀을 열 수 없는 PAM 모듈은 잘못된 코드와 똑같아 보이는 실패를 반환하며, 홈 디렉터리를 숨기는 서비스 환경이 흔한 원인입니다. 어떤 권한이든 완화하기 전에 세 가지 상황 중 어디에 있는지 결정하세요.
세 가지 실패 중 어느 것인가?
첫째는 모듈이 전혀 읽을 수 없는 비밀입니다. 둘째는 읽을 수는 있지만 갱신할 수 없는 비밀입니다. 셋째는 정말로 잘못된 코드이며, 보통 시계 드리프트나 재생된 값 때문입니다.
실패한 시도 하나에 대해 서비스 저널과 시스템 인증 로그를 읽어 이들을 구분하고, 플러그인이 어느 PAM 서비스를 로드하는지, 그 서비스가 어느 모듈 줄을 쓰는지 확인하세요. 일반적인 인증 실패 메시지로는 구분되지 않으며, 수동 테스트가 성공해도 당신의 셸은 서비스 환경이 아니므로 마찬가지입니다.
서비스만 실패할 때는 유닛의 파일 시스템 보기가 흔한 원인입니다.
systemctl show -p ProtectHome -p ProtectSystem -p ReadWritePaths [email protected]
ls -l /var/lib/vpn-otp/*/.google_authenticator 2>/dev/null | head -n 20옵션 A: 비밀을 홈 밖으로 옮기기
서비스가 /home을 숨기고 그 보호를 유지하고 싶다면, 비밀을 서비스가 볼 수 있는 곳에 저장하세요. 모듈은 secret= 경로와 파일 작업을 수행하는 user= 계정을 받아들이므로 중앙 저장소가 많은 신원에 서비스할 수 있습니다.
이것이 깔끔한 옵션이고 가장 주의가 필요한 옵션이며, 공유 저장소는 하나의 디렉터리를 모든 계정의 보안 경계로 만들기 때문입니다.
auth required pam_google_authenticator.so secret=/var/lib/vpn-otp/${USER}/.google_authenticator user=vpn-otp신뢰하기 전에 세 가지를 확인하세요: 계정이 존재하는지, 모듈 작업 프로세스가 그 계정으로 전환할 수 있는지, 파일이 제한적 모드로 그 계정 소유인지입니다. user=와 함께 ~나 홈 변수 경로를 쓰지 말고, 경로를 동작시키려고 모듈의 소유권 검사를 끄지 마세요. 비밀번호 단계와 나머지 스택은 그대로 유지하세요.
옵션 B: 파일을 두고 경로 열기
비밀이 사용자 홈에 남아야 한다면, 유닛에는 호스트의 모든 홈을 노출하지 않고 그 한 디렉터리만 포함하는 보기가 필요합니다. drop-in은 샌드박스된 홈 아래에 범위가 좁은 바인드 마운트를 추가할 수 있습니다.
이렇게 하면 사용자별 소유권을 모델로 유지하지만, 샌드박스가 바뀔 때마다 검토해야 하는 유닛 변경 비용이 따릅니다.
두 가지 한계를 놓치기 쉽습니다. 샌드박스가 숨기는 디렉터리 아래에 추가한 읽기 전용 경로는 부모가 아예 없으므로 그것을 노출하지 않습니다. 그리고 모듈이 상태를 갱신해야 한다면 마운트가 쓰기 가능해야 하며, 이것이 다음 옵션을 불러옵니다. drop-in은 /usr/lib 아래의 패키지된 유닛을 편집하는 대신 별도 파일로 작성하여 패키지 업그레이드가 조용히 되돌리지 않게 하세요.
옵션 C: 샌드박스를 유지하고 상태 쓰기 고치기
비밀 읽기는 인증의 절반일 뿐입니다. 모듈은 시간 편차 정보, 재생 방지 또는 카운터를 다시 쓸 수 있으며, 읽기 검사를 통과한 파일도 쓰기에서 실패할 수 있습니다.
그 실패는 이번 시도가 아니라 다음 시도에서 잘못된 코드처럼 보이므로 시계 드리프트로 오해됩니다.
홈 전체를 쓰기 가능하게 만드는 대신 자체 쓰기 가능한 디렉터리를 주고 비밀 경로를 그리로 향하게 하세요. 디렉터리 소유자, 파일 시스템의 여유 공간, 유닛이 다른 곳에서 쓰기를 제한하는지 확인하세요. 그런 다음 두 번째가 쓰기 경로를 증명하고 첫 번째는 읽기만 증명하므로 연속 두 번 로그인을 시험하세요.
자신이 아니라 서비스로서 검증하기
중요한 모든 검사는 유닛의 샌드박스 안에서 유닛의 신원으로 실행되어야 합니다.
root로 성공하는 명령은 파일이 존재한다는 것만 알려 줍니다. 실제 서비스를 통한 로그인은 모듈이 비밀을 읽고, 쓰고, 필요한 것을 되쓸 수 있는지 알려 줍니다.
마지막으로 요구 사항을 그대로 유지하세요. null 비밀번호 옵션을 추가하거나 읽기 전용 실패를 받아들이거나 샌드박스를 제거하면 정책을 바꿔 증상만 없앨 뿐입니다. 코드 제시를 요구받는 사용자는 비밀에 접근할 수 없을 때 여전히 거부되어야 합니다.