OpenVPN 는 로그인 스크립트의 정확히 하나의 출력, 즉 종료 상태에 따라 동작합니다. 스크립트가 하는 나머지 모든 일은 장식입니다. 훅은 올바른 검증자에 도달하고 응답을 분석하고 친절한 줄을 출력하면서도 작성자가 의도한 것과 반대 의미의 상태를 반환할 수 있습니다. 계약을 먼저 읽고 그 주위에 어댑터를 작성하면 그런 부류의 버그는 사라집니다.

계약은 작습니다. 제출된 계정을 식별하고, 승인된 검증자에게 의도한 질문을 던지고, 그 요청에 대해 그 검증자의 문서화된 긍정 결과만 받아들이면 됩니다. 나머지는 모두 배관입니다.

훅이 반환할 수 있는 세 가지 상태

auth-user-pass-verify 의 경우 OpenVPN 2.6 설명서 는 종료 코드 0을 수락, 1을 거부, 2를 지연 인증으로 정의합니다. 서버가 이해하는 답은 이 세 가지뿐이며 각각 연결에 다른 결과를 줍니다. 마지막 명령의 상태로 종료하는 스크립트는 사실상 로그 호출이나 정리 단계가 표를 던지게 한 것입니다. 검증자에 도달하지 못한 분기를 포함해 모든 분기에서 상태를 명시적으로 쓰세요.

HTTP 성공과 인증 성공은 별개의 계층이며, 이를 혼동하는 것이 이 훅이 잘못되는 가장 흔한 방식입니다. 202 응답은 요청이 처리 대상으로 접수되었다는 뜻이지 비밀번호가 검증되었다는 뜻이 아닙니다. 기대 상태, 응답 형태, 최종 판정을 받은 API 계약에서 정의하고, 그럴듯해 보인다는 이유로 필드 이름을 지어내지 마세요.

답이 정말로 나중에 도착한다면 그것이 지연 경로이며 그렇게 만들어야 합니다. 끝내 완료되지 않는 지연 시도는 시간 제한이 만료될 때까지 자원을 붙잡습니다.

자격 증명 파일을 기록된 그대로 읽으세요

문서화된 파일 경유 방식에서 서버는 사용자 이름과 비밀번호를 임시 파일의 처음 두 줄로 쓰고, 그 경로를 스크립트에 넘기며, 스크립트가 돌아오면 파일을 삭제합니다. 그 두 줄을 데이터로 읽으세요. 파일을 절대 source 하지 말고, 절대 평가하지 말고, 프로세스 테이블이 볼 수 있는 명령줄에 내용을 절대 붙여 넣지 마세요. 비밀번호를 바이트 단위로 보존하세요. 사용자 이름을 정규화하듯 정규화하면 자격 증명이 조용히 바뀝니다. 그 임시 파일의 디렉터리는 배포 결정이므로 존재하는지, 호스트의 모든 사람이 읽을 수 없는지 확인하세요.

검증자 자신의 자격 증명은 사용자의 것과 분리하세요. 게이트웨이 통합에 속하고 플랫폼의 비밀 저장소에 있으며 검증 권한만 필요하므로 특정 사람이 인증되었다는 증거가 아닙니다. 훅이 두 번째 비밀번호 검증자가 되게 하지 마세요. 해시 비교를 손으로 다시 구현해도 루프를 최적화한다고 안전해지지 않으며, 유지 관리되는 검증자는 계정 저장소의 솔트와 비용 방식을 이미 알고 있습니다.

분류할 수 없는 답이 무엇을 뜻하는지 정하세요

시간 초과, 전송 오류, 잘못된 형식의 응답, 사용할 수 없는 백엔드는 한 가지 속성을 공유합니다. 어느 것도 긍정적 검증이 아닙니다. 훅은 각각에 대해 닫힌 상태로 실패하고 운영자가 조치할 수 있는 이유를 기록해야 합니다. 그 이유는 보호된 로그에 속하며, 연결하려는 사람에게 돌려주는 메시지가 아닙니다.

검증자에 전혀 도달하지 않는 경로에도 똑같이 주의하세요. 빠진 구성 값, 읽을 수 없는 비밀, 설치되지 않은 의존성은 우연한 수락이 아니라 뚜렷한 운영 표식이 있는 거부를 만들어야 합니다. 그런 분기는 머리로 따지지 말고 실험실에서 의존성을 제거해 시험하세요. 로그 실패가 종료 상태를 바꿀 수 있는 곳에서는 로깅이 결정의 일부가 되었고 무해하게 만들어야 합니다.

서버 구성도 여기서 중요합니다. 사용자 정의 스크립트는 실행 중인 구성이 요구되는 보안 수준에서 허용할 때만 존중되며, 그 설정은 올려야 할 기본값이 아니라 의도적인 선택입니다.

판정을 검증하세요. 인쇄된 줄이 아니라

종료 상태를 직접 검사하는 테스트를 만드세요. 샘플 자격 증명 파일을 쓰고, 알려진 정상 계정과 알려진 불량 계정으로 그 파일에 대해 훅을 실행하고, 스크립트가 인쇄하는 텍스트가 아니라 셸이 보고하는 상태를 읽으세요. 그 한 번의 검사가 클라이언트가 연결되기 전에 대부분의 계약 버그를 잡습니다.

그다음 같은 사례를 실제 클라이언트로 비운영 리스너에 대해 실행하고 서버가 무엇을 기록했는지 확인하세요. 수락, 거부 또는 지연입니다. 비활성화된 계정, 잘못된 비밀번호, 잘못된 형식의 검증자 응답, 시간 초과, 도달할 수 없는 검증자를 포함하세요. 어떤 사례든 이름 붙일 수 없는 상태에 빠진다면 어댑터는 끝나지 않은 것입니다.

검증 결과에서 종료 상태로 가는 매핑을 저장소에서 스크립트 옆에 두어, 다음 엔지니어가 코드를 역설계하지 않고 계약을 읽을 수 있게 하세요.

관련 글