NGINX stream 컨텍스트에서 TLS 패스스루 연결 허용하기
NGINX는 TLS를 종료하지 않고 TCP TLS 릴레이를 거부할 수 있습니다. 허용 규칙은 HTTP location이 아니라 stream에 두고, 어떤 peer 주소가 도착하는지 확인하십시오.
인증서를 전혀 보유하지 않고 암호화된 바이트를 전달하는 것이 TLS 패스스루 릴레이의 일이며, 이 구성은 편리하면서도 조금 혼란스럽습니다. 아무것도 복호화되지 않으므로 필터링할 HTTP 형태의 것이 없고, 따라서 일반적인 location 블록과 그 접근 규칙은 여기서 아무 소용이 없습니다. NGINX가 내릴 수 있는 결정은 연결 자체에 관한 것입니다. 어떤 네트워크 peer가 접속했는지, 그리고 그 peer를 전달해야 하는지입니다.
결정은 HTTP가 아니라 stream에 있습니다
stream 컨텍스트에서 NGINX는 TCP 세션을 다룹니다. 연결의 peer 주소를 보고, 어떤 바이트도 backend에 도달하기 전에 순서가 있는 allow 및 deny 규칙을 적용할 수 있습니다. 이는 완전한 수락 결정이며 개인 키, 인증서, HTTP 파싱이 필요하지 않습니다.
| Mechanism | What it decides | Where it lives |
|---|---|---|
| stream allow/deny | Whether this TCP peer is relayed | stream server block |
| ssl_preread map | Which backend a named connection goes to | stream, with the preread module |
| HTTP location rule | Whether an HTTP request is served | http server block |
그 세 줄은 결코 서로를 대체하지 않습니다. 443 포트를 언급하는 어떤 파일에 HTTP 접근 지시문을 복사해도 아무것도 바뀌지 않습니다. stream 서버는 http 컨텍스트를 결코 읽지 않기 때문입니다.
전달하기 전에 연결을 수락하십시오
stream 및 stream-access 모듈을 사용할 수 있는 기존 설치가 있고, VPN 클라이언트가 소스 주소를 10.90.0.0/24에 유지하며, 192.168.50.20:443의 backend가 이미 의도한 HTTPS 아이덴티티를 제공한다고 가정합니다.
stream {
server {
listen 443;
allow 10.90.0.0/24;
deny all;
proxy_pass 192.168.50.20:443;
}
}
이것은 동작 중인 구성의 직접 교체가 아니라 새 listener의 조각으로 읽으십시오. 기존 listener와 include를 먼저 검토하십시오. 이미 사용 중인 포트에 두 번째 listener를 두면 요청 시점이 아니라 reload 시점에 실패합니다.
빠진 것을 보십시오. listen ... ssl도 없고 인증서 지시문도 없습니다. NGINX는 암호화된 스트림을 전달하고, backend가 TLS를 종료하며 클라이언트가 검증하는 인증서를 제시합니다. allowlist를 통과하지 못한 클라이언트의 TCP 세션은 거부되며 HTTP 403 페이지가 아닙니다. 상태 코드를 기대하는 도구는 쓸모없는 결과를 보고할 것입니다.
allowlist가 포함하는 것과 포함하지 않는 것
릴레이가 이미 서버 이름으로 backend를 선택한다면 모듈이 사용 가능한 경우 ssl_preread와 map이 그 작업을 계속할 수 있습니다. 이름은 여전히 클라이언트가 제공하며, 이름을 제시하는 것이 클라이언트가 VPN에 가입했음을 증명하지는 않습니다.
예제 규칙은 stream listener 전체를 포함합니다. 따라서 공개 사이트와 VPN 전용 사이트가 섞인 단일 진입점에는 부적합하며, 이름별 예외가 필요합니다. 아무도 읽을 수 없는 이름 의존 정책보다 listener를 분리하는 편이 보통 더 명확합니다.
두 가지 틈을 더 언급할 만합니다. prefix 일치는 패킷이 어느 인터페이스로 들어왔는지 말해주지 않으며, 다른 곳의 HTTP 규칙은 이 listener를 제한할 수 없습니다. 둘 다 한 줄의 allow를 전체 경계로 취급하는 대신 호스트 수준의 ingress 정책을 그림에 남겨 두어야 하는 이유입니다. 경계를 반드시 일치해야 하는 두 개의 제어로 적어 두어, 어느 한쪽을 바꾸면 검토되게 하십시오.
NGINX는 실제로 누구의 주소를 보는가?
로드 밸런서, 다른 프록시 또는 source NAT가 NGINX 앞에 중개자를 둘 수 있으므로 관찰되는 peer는 VPN 클라이언트의 내부 주소가 아닙니다. prefix를 고르기 전에 실제 경로를 점검하십시오. 잘못된 주소에 맞춰 작성된 allowlist는 변환 장치 뒤의 모두를 허용하거나 아무도 허용하지 않습니다.
PROXY 프로토콜과 실제 IP 처리 는 정당한 답이며, 호환되고 명시적으로 신뢰된 송신자에 대해서만 구성해야 합니다. 두 메커니즘 모두 송신자가 말하는 것을 믿기 때문에 송신자 목록이 짧아야 합니다. 임의의 인터넷 클라이언트가 보내는 신원 헤더를 신뢰한다고 해서 소스 정보가 다시 신뢰할 만해지지는 않습니다. 올바른 거부가 위조된 수락으로 바뀔 뿐입니다.
reload 전에 두 결과를 모두 테스트하십시오
구성을 검증하고 reload한 다음 두 번 테스트하십시오. 허용된 새 세션은 동작해야 하고, 거부된 세션은 동작하지 않아야 합니다. 허용된 테스트에서는 일반적인 backend 인증서 검증을 유지하십시오. 연결은 되지만 backend를 검증할 수 없는 릴레이는 한 문제를 해결하고 다른 문제를 만든 것입니다.
nginx -t
ss -tnp | grep ':443'
backend 경로와 응답도 따로 확인하십시오. allowlist 통과는 NGINX가 세션을 수락했다는 것만 증명합니다. 도달할 수 없는 backend가 응답하게 만들 수 없고, 시작하기 전에 없던 반환 경로를 고칠 수도 없습니다.