client-connect 스크립트에서 주소를 반환하면 클라이언트의 IP를 설정할 것처럼 보이지만 그렇지 않습니다. 훅이 서버와 통신하는 유일한 통로는 임시 파일이며, 그 경로는 스크립트의 마지막 인수로 전달됩니다. 주소는 그곳에 ifconfig-push 구문으로 기록해야 합니다. 주소를 출력하거나 아무것도 쓰지 않고 영으로 종료하는 스크립트는 풀에서 할당된 주소를 가진 연결된 클라이언트를 만들 뿐이며, 서버 로그는 완벽하게 건강해 보입니다.

혼란은 당연합니다. 다른 여러 훅은 종료 코드나 환경 변수를 통해 실제로 응답합니다. 이 훅에는 자체 계약이 있으며, 그 계약은 파일 이름에서 시작합니다.

훅이 쓰는 것은 반환 값이 아니라 파일입니다

선언은 두 줄입니다. 호출을 허용하는 script-security 2, 그다음 client-connect /etc/openvpn/server/assign-address.sh입니다. 클라이언트가 인증되면 서버는 생성된 파일 경로를 마지막 인수로 추가하여 해당 스크립트를 실행합니다. 그 경로는 선언이 자체 추가 인수를 전달하지 않을 때만 $1이 됩니다. 그 클라이언트에 적용되는 모든 것은 구성 파일에 나타나는 것과 똑같이 한 줄에 하나씩 일반 지시문으로 그 파일에 기록됩니다. 자체 매개변수를 추가하는 래퍼는 위치를 밀어내며, 전형적인 증상은 --verbose처럼 보이는 경로에 쓰는 스크립트입니다.

종료 상태는 계약의 나머지 절반입니다. 영은 수락하고 파일을 읽으라는 뜻이며, 그 밖의 값은 연결을 거부합니다. 매핑되지 않은 ID가 조용히 풀로 돌아가는 대신 거부되는 방식이 바로 이것입니다. 성공을 반환하기 전에 전체 지시문을 작성하세요, 그래야 절반만 쓰인 파일이 클라이언트 구성이 되지 않습니다.

두 지시문은 다른 모든 것과 상호작용하므로 주의가 필요합니다. ifconfig-push는 주소를 고정하고, 푸시된 route는 터널을 통해 무엇에 도달할 수 있는지 클라이언트에 알려줍니다. 전자만 반환하고 후자를 잊으면 주소는 있지만 갈 곳이 없는 클라이언트가 됩니다. 파일 내부의 순서는 중요하지 않지만 완전성은 중요합니다. 어떤 피어도 도달할 수 없는 프리픽스에 대한 경로를 푸시하는 훅은 반대편에서 본 같은 실수입니다.

입력은 환경 변수로 전달됩니다. common_name, trusted_ip, ifconfig_pool_remote_ip 등입니다. 이 모든 것을 서버 외부에서 온 문자열로 취급하고, 절대 셸 조각으로 취급하지 마세요.

주소 매핑은 정책이지 산술이 아니다

인증서 이름을 구문 분석하고 그 숫자를 마지막 옥텟으로 사용하는 것은 유혹적인 지름길입니다. 그것은 할당자가 아니며 평범한 방식으로 실패합니다. 같은 접미사를 가진 두 이름이 충돌하고, 300으로 끝나는 이름은 주소가 아니며, 관리자가 이름을 바꾼 인증서는 장치를 조용히 옮깁니다. 호출할 때마다 서브넷 소속, 사용 가능한 호스트 범위, 예약된 주소, 고유성을 검증하고 매핑을 사람이 읽을 수 있는 표에 유지하세요.

이름을 제어하는 사람이 주소를 제어하므로, 이 배포에서 common_name이 실제로 무엇을 나타내는지 결정하세요. username-as-common-name이 활성화되면 ID는 인증서 주체가 아니라 로그인입니다. 그 값을 절대 셸 평가로 넘기지 말고, 클라이언트가 파일 경로에 영향을 미치게 해서도 안 됩니다.

레지스트리 기반 할당자는 동시성을 견뎌야 합니다. 주소를 원자적으로 예약하고, 절반만 구성된 클라이언트를 허용하지 않으면서 실패한 쓰기를 처리하고, 각 할당을 그것을 소유한 연결로 태그하세요. 그래야 이전 세션의 늦은 연결 해제가 새 세션이 이미 보유한 주소를 해제할 수 없습니다. 하나의 풀을 공유하는 여러 서버 인스턴스는 이 요구 사항을 선택 사항이 아니라 더 날카롭게 만듭니다.

토폴로지가 ifconfig-push의 의미를 결정한다

10.77.0.0/24에서 topology subnet을 사용하면 ifconfig-push 10.77.0.20 255.255.255.0은 주소와 그 넷마스크입니다. topology net30에서는 두 번째 인수가 넷마스크가 아니라 피어 주소이므로, 서브넷 형식을 net30 서버에 복사하면 연결은 되지만 트래픽을 통과시키지 못하는 클라이언트가 됩니다. 토폴로지는 의도적으로 변경하세요. 예제가 일관되게 보이도록 변경해서는 안 됩니다. 이미 net30을 실행 중인 서버에는 이전 주소 지정 방식의 활성 클라이언트가 있으므로, 전환은 한 줄 편집이 아니라 유지 관리 기간이 필요한 마이그레이션입니다.

정적 주소는 동적 풀 밖에 있어야 하고 다른 모든 정적 할당 밖에도 있어야 합니다. 일반 client-config-dir 파일이 제공하는 주소도 포함됩니다. 두 메커니즘이 서로 다른 클라이언트에 같은 주소를 생성할 수 있다면, 둘 중 하나는 결국 그렇게 됩니다.

신뢰하기 전에 네 가지 경우를 테스트하라

매핑된 ID, 매핑되지 않은 ID, 재연결, 시뮬레이션된 레지스트리 실패에 대해 동일한 검사를 실행하세요. openvpn --config client.ovpn --verb 4는 푸시 응답에 할당된 주소를 보여 주고, 서버에서는 grep -A2 PUSH_REPLY /var/log/openvpn/openvpn.log가 실제로 무엇이 전송되었는지 확인합니다. 매핑되지 않은 ID의 예상 결과는 풀 주소가 아니라 거부된 연결입니다.

그다음 데몬이 그 임시 파일을 쓰는 디렉터리를 조사하세요. 스크립트는 권한이 낮아진 서비스 컨텍스트에서 실행되므로, root는 쓸 수 있지만 데몬은 쓸 수 없는 경로는 모든 연결을 거부로 바꾸고 권한 오류는 로그 아래 여러 줄에 묻힙니다.

할당은 권한 부여가 아닙니다. 고정된 주소가 서버 뒤의 모든 목적지에 대한 액세스를 부여하지는 않습니다. 라우팅과 방화벽 정책을 자체 계층에 유지하고, 방금 고정한 주소에 대해 검증하세요.

관련 글