AWS Client VPN은 연결됐지만 EC2는 응답하지 않습니다
연결됨은 인증서나 SAML 로그인이 통과했다는 뜻일 뿐입니다. 권한 규칙, 엔드포인트 경로, 보안 그룹이 각각 프라이빗 호스트로 가는 길의 한 구간을 지킵니다.
노트북은 연결됨을 표시했습니다. AWS Client VPN 클라이언트는 초록 상태를 보였고, 엔드포인트는 세션을 기록했으며, 그런데도 프라이빗 서브넷의 EC2 인스턴스에서 MongoDB 포트를 열 수 없었습니다. 눈에 띄게 고장 난 것은 없었습니다. 인증서는 유효했고, 프로필은 콘솔에서 받았고, 터널은 트래픽을 실어 날랐습니다. 잘못은 제 쪽이었습니다. 연결됨을 권한 있음으로 읽고 있었기 때문입니다.
로그인이 답하는 질문은 정확히 하나입니다. 이 ID가 세션을 맺어도 되는가. 그 뒤의 모든 것은 인증서를 결코 보지 않는 별도 규칙이 정합니다. 인증을 통과한 세션과 도착한 패킷은 서로 다른 두 성공이고, 그 사이의 틈이 VPN은 올라와 있는데 호스트에 닿지 않는다는 보고가 사는 자리입니다.
아래 점검은 제가 지금 쓰는 순서입니다.
연결된 세션이 실제로 증명하는 것
핸드셰이크 동안 엔드포인트는 클라이언트 인증서를 자체 트러스트 스토어로 검증하거나, SAML이 설정된 경우 브라우저를 ID 공급자로 리다이렉트합니다. 그것이 통과하면 클라이언트는 엔드포인트의 클라이언트 CIDR에서 주소를 받고 콘솔은 활성 연결을 보여 줍니다. 그 교환의 어느 지점에서도 10.0.4.17의 27017 포트에 닿을 수 있는지는 묻지 않습니다.
목적지에 대한 허가는 엔드포인트 위의 별도 객체입니다. 모든 흐름은 권한 규칙과 대조되고, 각 규칙은 목적지 CIDR을 그 규칙이 부여되는 그룹과 짝지어 줍니다. 어떤 규칙도 목적지를 덮지 않으면 엔드포인트는 세션을 받아들이고 트래픽을 버립니다. 배너도, 재연결 루프도, 거부됨이라고 알려 주는 클라이언트 UI도 없습니다.
권한 규칙은 VPC가 아니라 리소스와 비교하세요
규칙을 나열하고 인스턴스가 실제로 가진 주소 옆에 두세요:
aws ec2 describe-client-vpn-authorization-rules \
--client-vpn-endpoint-id cvpn-endpoint-0a1b2c3d4e5f6a7b8출력은 규칙마다 목적지 CIDR과 그룹 ID를 보여 줍니다. 그 안에 두 가지 실패가 숨어 있습니다. 규칙이 10.0.0.0/16을 허용하는데 인스턴스는 10.1.0.0/16의 피어링된 VPC에 있는 경우입니다. 피어링 경로는 있지만 두 번째 대역에 대한 권한 규칙을 아무도 쓰지 않았습니다. 또는 규칙이 사용자가 디렉터리에는 속하지만 엔드포인트가 평가하는 그룹에는 속하지 않는 디렉터리 그룹을 가리키는 경우입니다.
목적지를 덮는 가장 좁은 규칙을 쓰고, 두 번째 CIDR이 정말 필요할 때만 두 번째 규칙을 더하세요. 빠른 시작에서 온 넓은 0.0.0.0/0 규칙은 동작하므로 운영까지 살아남아 모델을 가립니다.
패킷이 움직일지를 두 개의 라우팅 테이블이 정합니다
엔드포인트 경로는 목적지 CIDR을 엔드포인트가 연결된 서브넷 중 하나로 매핑합니다. 그것은 경로의 절반입니다. 서브넷 자체의 라우팅 테이블이 패킷을 어떻게 전달할지 여전히 알아야 하고, 목적지가 피어링 연결, 전송 게이트웨이, 또는 사이트 간 터널로 닿는 온프레미스 대역 뒤에 있는 순간 중요해집니다.
aws ec2 describe-client-vpn-routes \
--client-vpn-endpoint-id cvpn-endpoint-0a1b2c3d4e5f6a7b8목적지가 리소스 CIDR과 맞고 대상이 연결된 서브넷인 경로를 찾으세요. 경로가 없으면 추가하세요. 있는데도 호스트가 조용하면 틈은 VPC 쪽이므로 연결 서브넷의 라우팅 테이블과 비교하세요.
클라이언트 쪽 설정 하나가 터널을 깨지 않고 라우팅을 깨뜨립니다. route-nopull이 들어 있는 프로필입니다. 이 지시는 OpenVPN 기반 클라이언트에게 서버가 밀어 주는 경로를 무시하라고 하므로 인터페이스는 엔드포인트 자체 주소만으로 올라오고 목적지에는 경로가 아예 없습니다. Linux에서는 ip route show, macOS에서는 netstat -rn으로 tun 인터페이스를 지나는 경로가 있는지 확인하세요. 없으면 내보낸 프로필에서 route-nopull을 빼고 다시 연결해 밀려온 경로가 적용되게 하세요.
보안 그룹은 클라이언트 주소를 보지 않습니다
제가 반나절 동안 틀렸던 부분입니다. 서브넷에 연결된 IPv4 엔드포인트의 경우, 서비스가 클라이언트의 원본 주소를 그 서브넷에 있는 엔드포인트 네트워크 인터페이스의 주소로 변환합니다. 인스턴스에 도착하는 패킷은 클라이언트가 표시한 주소를 싣지 않으므로, 클라이언트 풀, 예컨대 172.16.0.0/22를 허용하는 보안 그룹 규칙은 무엇과도 맞지 않습니다.
포트는 클라이언트 CIDR이 아니라 연결의 인터페이스에 붙은 엔드포인트 보안 그룹에서 허용하세요. 그룹을 참조하는 편이 주소를 하드코딩하는 것보다 오래갑니다. 인터페이스 주소는 내가 통제할 수 있는 것이 아니기 때문입니다. 네트워크 ACL은 상태가 없으므로 사용자 지정 항목이 있는 곳에서는 인바운드와 아웃바운드 양쪽이 흐름을 허용해야 합니다.
인스턴스의 프라이빗 주소로도 연결하세요. 공인 IP는 패킷을 VPC 밖으로 내보내고 다른 규칙을 가진 다른 경로로 다시 들여보내므로, 공인 주소 시험은 프라이빗 경로에 대해 아무것도 말해 주지 않습니다.
첫 실패에서 멈추는 격리 순서
이것을 순서대로 진행하고 어디서 하나가 실패하면 거기서 멈추세요. 각 단계는 모든 계층이 아니라 한 계층을 배제합니다.
- 세션이 어느 ID를 썼고 엔드포인트가 어느 그룹을 평가하는지 확인하세요. 한 트러스트 스토어의 인증서는 디렉터리에서 온 SAML 어서션과 같은 주체가 아닙니다.
- 권한 규칙이 그 그룹에 대해 리소스 CIDR을 덮는지 확인하세요.
- 엔드포인트 경로가 있고 연결된 서브넷을 향하는지, 그다음 서브넷 라우팅 테이블이 더 나아갈 수 있는지 확인하세요.
- 보안 그룹이 클라이언트 풀이 아니라 엔드포인트 인터페이스에서 포트를 허용하는지 확인하세요.
- 애플리케이션 문제와 클라이언트 경로 문제를 가르기 위해 VPC 안에서, 같은 서브넷의 다른 인스턴스에 SSM 세션으로 시험하세요.
단계가 결정적이지 않을 때 제가 기대는 표는 이것입니다:
| Symptom | Layer to inspect |
|---|---|
| Client shows connected, host times out, no byte growth on the interface | Authorization rule, then security group |
| Ping fails but a TCP connect to the port succeeds | ICMP is blocked; not an access failure |
| Tunnel reconnects every few minutes | Two machines sharing one client certificate |
| Works on one laptop, fails on another | Local profile has route-nopull or stale pushed routes |
계정 세부 두 가지가 권한 실패처럼 보이는 증상을 만듭니다. 사용자마다 별도의 클라이언트 인증서가 필요합니다. 하나를 두 장비가 공유하면 세션이 서로를 밀어내고, 결과는 거부가 아니라 재연결 루프입니다. 새 엔드포인트를 만들면 콘솔이 DNS 이름을 보여 주는데, 일부 클라이언트 버전은 엔드포인트 페이지에 표시되는 무작위 접두사가 붙은 경우에만 받아들입니다. 다시 구성하지 말고 전체 이름을 복사하세요.
다섯 번째 단계에서 막히면 무엇을 바꾸기 전에 엔드포인트의 연결 로그를 받아 권한 항목을 읽으세요. 로그는 검사한 목적지와 일치했거나 일치하지 않은 규칙을 적어 주고, 그것이 네 계층의 질문을 하나의 답으로 접어 줍니다.