Apache 2.4로 Subversion 호스트를 VPN 사용자로 제한하기
ServerName은 가상 호스트를 선택할 뿐 누가 접속하는지는 제한하지 않습니다. VPN 대역과 실제 저장소 사용자를 요구한 다음 두 거부를 모두 증명하십시오.
Subversion 가상 호스트를 VPN 주소로 지정하는 것은 어떤 사이트가 요청에 응답하는지를 바꿀 뿐, 누가 그 요청을 보낼 수 있는지를 바꾸지 않습니다. 접근 검사는 나중에 URL과 일치한 섹션의 규칙에 따라 수행되며, 호스트 이름은 그 결정에 절대 들어가지 않습니다. ServerName만 바꾸었다면 저장소는 어제와 똑같이 접근 가능합니다. 구성에서는 어느 소켓이 연결을 받아들였는지, 요청이 어떤 신원을 지녔는지라는 두 가지 별개의 질문에 답해야 합니다.
ServerName이 실제로 결정하는 것
이름 기반 가상 호스팅은 Host 헤더를 ServerName 및 ServerAlias 항목과 비교하고, 하나의 호스트를 골라 그 호스트가 요청을 처리하게 합니다. 연결 자체는 호스트가 아니라 서버에 속한 Listen 소켓이 앞서 받아들였습니다. 사적인 이름처럼 보이는 것이 아무런 보호도 제공하지 않는 이유는, 소켓에 도달할 수 있는 클라이언트는 누구나 그 Host 헤더를 보낼 수 있기 때문입니다. 이름은 라우팅 키이며, 라우팅 키는 호출자가 고릅니다.
추측 없이 매핑을 볼 수 있습니다. apachectl -S은 각 호스트와 그 포트, 이름을 출력하고 주소에 대해 어느 것이 기본인지 표시합니다.
RequireAll은 두 검사를 모두 의무로 만드는 컨테이너입니다
Apache 2.4는 한 섹션의 Require 지시문을 암묵적 RequireAny로 감싸므로, 지시문 두 개를 그대로 두면 둘 중 하나만으로 충분하다는 뜻이 되지만, 컨테이너를 추가하면 의미가 뒤집힙니다.
<Location /svn/repo>
AuthType Basic
AuthName "Subversion"
AuthBasicProvider file
AuthUserFile /etc/apache2/svn.htpasswd
<RequireAll>
Require ip 10.8.0.0/24
Require valid-user
</RequireAll>
</Location>RequireAll이 요구하는 것은 터널 대역 안의 출발지 주소와 저장소가 받아들이는 사용자라는 두 조건입니다. Subversion URL에 대해 VPN 전용이란 바로 이것을 뜻해야 합니다. mod_authz_core 참조 문서 가 컨테이너 규칙을 설명합니다. 저장소의 기존 Location 안에 블록을 두고, 이 조각이 AuthType도 암호 파일도 경로 수준 권한도 대체하지 않으므로 이미 동작하는 DAV 지시문은 그대로 두십시오.
출발지 검사는 mod_authz_host에서 오므로, 다른 것을 디버깅하기 전에 이것이 로드되었는지 확인하십시오: apachectl -M | grep authz_host.
Apache가 평가하는 주소는 소켓 주소입니다
Require ip는 Apache가 연결에 대해 기록한 주소와 비교하며, 그 주소가 항상 클라이언트가 자기가 가진다고 믿는 주소는 아닙니다.
VPN이 주소를 변환 없이 라우팅하면 Apache는 터널 주소를 보고 10.8.0.0/24가 일치합니다. 게이트웨이가 변환하면 Apache는 게이트웨이를 보고 규칙은 조용히 아무것도 일치하지 않습니다. 호스트 앞에서 TLS를 종료하는 로드 밸런서 뒤에서도 같은 일이 벌어집니다. 그런 경우를 위해 밸런서용 RemoteIPTrustedProxy과 함께 RemoteIPHeader X-Forwarded-For을 쓰는 mod_remoteip이 있으며, 그러면 기록된 주소가 교체되고 그때서야 대역이 비교할 대상이 생깁니다.
임의의 전달 헤더를 신뢰하는 것은 규칙이 아예 없는 것보다 나쁩니다. 어떤 클라이언트든 10.8.0.9가 들어 있는 X-Forwarded-For를 보내 신뢰할 수 있는 홉에 고정된 적 없는 대역 검사를 통과할 수 있습니다.
리스너와 저장소 정책을 계층으로 나누십시오
바인딩은 별개의 제어이며 종종 더 단순합니다. Listen 10.8.0.1:443는 어떤 소켓이 존재하는지를 결정하고, 한 번도 열리지 않은 소켓에서는 요청이 호스트를 고를 수 없습니다. 대가는 시작 순서로, Apache가 시작될 때 터널 인터페이스가 내려가 있으면 바인딩이 실패하고 서비스가 올라오지 않습니다. 유닛 순서와 함께 쓰거나 터널과 무관하게 존재하는 주소에 바인딩하십시오.
경로 권한은 AuthzSVNAccessFile에 남습니다. 사이트에 유효한 계정은 프로젝트 경로에 대한 쓰기 권한과 같은 허가가 아니며, 네트워크 규칙은 그것을 바꾸지 않습니다.
블록이 포함된 파일에 있을 때 섹션 병합에 주의하십시오. Apache는 중첩된 섹션에 걸쳐 권한 컨테이너를 병합하며 가장 구체적으로 일치하는 섹션이 요구 사항을 추가하는 대신 대체할 수 있으므로, 더 좁은 섹션이 자체 규칙을 정의하면 /svn의 규칙이 /svn/repo/trunk에 도달하지 못할 수 있습니다.
끝났다고 말하기 전에 두 거부를 모두 증명하십시오
하나가 아니라 네 개의 요청을 시험하십시오. VPN 안에서 유효한 계정이면 저장소 목록을 받아야 합니다. VPN 안에서 잘못된 암호면 목록 대신 401을 받아야 합니다. VPN 밖에서 유효한 자격 증명이면 403을 받아야 하고, 다른 호스트 이름에 대한 같은 공개 요청도 같은 방식으로 실패해야 합니다.
경우마다 명령 하나면 충분합니다: curl -sI -u user:pass https://svn.example.com/svn/repo/, 그다음 상태 줄을 읽으십시오. 동작하는 경우는 svn info가 커밋과 업데이트가 사용할 DAV 메서드를 실제로 행사하므로 실제 클라이언트로도 반복하십시오.
네 결과 중 하나라도 의도한 정책과 어긋난다면 구성은 끝난 것이 아니며, 호스트 이름을 바꾼다고 달라지지 않습니다.