SSTP가 포트 443을 차지했다: Apache가 시작되지 않는 이유
실패하는 사이트가 443을 잡고 있는 것이 아니다. Windows SSTP가 HTTP 스택을 통해 포트를 예약한다: 리스너를 옮기거나, 앞에 IIS를 두거나, 사이트를 옮겨라.
Apache가 443에서 응답을 멈추는 것은 누군가 들어오는 VPN 연결을 켠 그 오후이며, 오류 로그는 그 뒤의 예약이 아니라 bind를 탓한다. Windows Routing and Remote Access가 그 순간 SSTP를 켜고, SSTP 리스너는 어떤 클라이언트도 인증되기 전에 HTTP 스택을 통해 TCP 443을 요구한다. 이 요구는 인증서가 없거나 이름이 틀렸거나 모든 handshake가 거부되어도 유지되므로, 아무도 로그인할 수 없는 VPN도 포트를 계속 차지한다. 먼저 수신 대기 표를 읽어라. 443을 가진 PID가 아래 수정 중 무엇이 당신에게 열려 있는지를 결정한다.
예약은 깨진 인증서보다 오래 살아남는다
SSTP는 PPP를 TLS 세션 안에 감싼다. 그래서 443이 필요하고, 그래서 다른 터널을 버리는 프록시를 통과한다. 리스너는 VPN 서비스가 스스로를 위해 여는 소켓이 아니다. SstpSvc가 커널 HTTP 드라이버에 등록하므로 서비스가 실행되는 동안 포트는 예약된 채로 남는다. 그 등록은 인증서를 전혀 조회하지 않으므로 만료된 이름, 맞지 않는 subject, 인증서가 아예 없는 경우에도 443은 점유된 채로 남는다. 거부된 handshake는 RasSstp 아래 System 로그에 남고, 웹 서버는 bind하지 못했다고 보고할 수 있을 뿐이다.
netstat -aon | findstr :443
tasklist /svc /fi "PID eq <pid>"
첫 줄은 소유 PID를 돌려주고, 둘째 줄은 그 뒤의 서비스를 지목하며, 답은 보통 HTTP 서비스나 RRAS 스택이다. tasklist가 공유 호스트 프로세스로 응답하면 프로세스 이름으로 추측하지 말고 서비스 열을 읽어라.
RasSstp 경고는 그 뒤에야 읽을 가치가 있다. 실패한 handshake를 설명할 뿐 충돌을 일으킨 예약에 대해서는 아무 말도 하지 않는다. 모든 클라이언트를 거부하는 VPN 엔드포인트도 포트는 여전히 소유한다. 로그를 첫 번째 질문이 아니라 두 번째 질문으로 다뤄라.
IIS는 드라이버를 공유하고, Apache는 소켓을 연다
IIS는 자신의 bindings를 SSTP가 쓰는 같은 커널 드라이버에 넘기고, 그 드라이버는 들어오는 연결을 호스트 이름과 URL 접두사로 나누므로 두 서비스 모두 443을 원하게 해도 어느 쪽도 실패하지 않는다. Apache와 nginx는 반대로 한다. 자기 소켓에서 bind를 호출하고, 주소가 이미 잡혀 있으므로 운영체제가 거부한다. 어느 설정에서도 잘못된 것은 없다. 차이는 어느 API가 포트를 소유하는가에 있다. 그래서 VPN을 켜면 한 호스트의 사이트는 무너지고 다른 호스트의 동일한 사이트는 그대로인 것이다.
이 비대칭이 수리의 형태를 정한다. IIS가 이미 정문이라면 충돌은 접두사, bindings, 인증서의 문제가 된다. Apache가 정문이라면 Apache가 443에서 물러나거나 SSTP 리스너가 물러나며, 다음 절이 두 이동을 모두 다룬다.
443을 어느 서비스가 유지할지 고른다
리스너 포트는 하나의 레지스트리 값에 산다. Sstpsvc 매개변수 키 아래의 십진 DWORD다. 값을 바꾸고 Routing and Remote Access를 다시 시작한 뒤 수신 대기 표에서 예전 포트가 비었는지 확인한다.
reg query "HKLM\SYSTEM\CurrentControlSet\Services\Sstpsvc\Parameters" /v ListenerPort
reg add "HKLM\SYSTEM\CurrentControlSet\Services\Sstpsvc\Parameters" /v ListenerPort /t REG_DWORD /d 4443 /f
Restart-Service RemoteAccess
호스트의 다른 무엇도 원하지 않는 포트를 골라 십진으로 입력한다: reg add는 DWORD를 적힌 그대로 저장하므로, 십육진으로 의도한 값은 다른 포트로 떨어진다. 쿼리가 값이 없다고 하면 둘째 줄이 그것을 만든다. Restart-Service RemoteAccess는 연결된 모두를 끊으므로 계획할 가치가 있다. 그 뒤 수신 대기 검사를 다시 실행하라. 443에 낡은 항목이 남아 있다면 서비스가 옛 등록을 유지했다는 뜻이다.
리스너를 옮기는 데는 레지스트리 편집이 보여주지 않는 대가가 있다. 보통의 SSTP 클라이언트는 443으로 걸므로 모든 클라이언트 프로필에 새 포트가 필요하고, 경로를 들여다보는 무엇에게도 흐름은 웹 요청처럼 보이지 않게 된다. HTTPS처럼 보이는 트래픽만 견디는 네트워크에서는 그것이 SSTP를 고른 이유의 대부분을 없앤다. 작고 관리되는 클라이언트 집단은 이를 흡수하지만, 공개 또는 BYOD 사용자 기반은 그렇지 않다.
다른 이동은 443을 IIS에 남기고 Apache를 프록시가 전달하는 backend로 취급한다. SSTP 기본값을 보존하고 포트의 소유자를 하나로 남기지만, 전달된 헤더, 애플리케이션이 더는 직접 보지 못하는 클라이언트 주소, 프록시가 실제로 보내는 프로토콜을 말해야 하는 backend라는 대가가 따른다.
소유자, 인증서, handshake를 순서대로 증명한다
완료된 TCP 연결은 리스너가 있다는 것만 증명하고 그 밖에는 아무것도 아니다. 그래서 터널이 여전히 실패하는데도 포트 스캔은 건강해 보일 수 있다. 네 단계는 서로 다른 방식으로 실패하고, 당신이 깨뜨리지 않은 단계를 겨냥한 수리는 보통 충돌을 더 깊게 만든다. 순서대로 처리하고 답이 갈라지는 곳에서 멈춰라.
netstat -aon | findstr :443
certutil -store My
Get-WinEvent -LogName System -ProviderName RasSstp -MaxEvents 20
첫 줄은 443을 가진 PID를 지목하고, 그것은 방금 설정한 서비스여야 한다. 둘째 줄은 머신 인증서를 subject와 함께 나열하므로 클라이언트 프로필의 주소와 맞지 않는 이름이 곧바로 눈에 띈다. 이벤트 쿼리는 최근 SSTP 레코드를 돌려주고, PPP 인증에 이르지 못하는 handshake는 인증된 뒤 거부되는 것과 다른 레코드를 남긴다. 마지막 단계는 클라이언트에서만 보이며, 거기서 항목은 연결됨을 보고하거나 PPP 오류를 지목한다.
한 호스트가 두 일을 모두 해야 한다면 443의 소유자를 의식적으로 고르고 그 선택을 적어 두어라. 그렇지 않으면 다음에 기능을 켜는 사람이 이 충돌을 다시 발견할 것이다. 역할을 나누는 것이 여전히 더 깨끗한 답이다. 공개 웹 서버와 원격 접속 엔드포인트는 패치, 인증서, 재시작 주기를 따로 가지며, 그 주기는 각자의 일정대로 만나고, 어느 쪽이든 수신 대기 검사와 레지스트리 경로, 고른 포트를 인수인계 메모에 남겨야 한다.