두 OpenVPN 서버의 클라이언트가 서로 도달할 수 있을까?
OpenVPN 서버 두 대는 클라이언트 풀 두 개를 만들 뿐 하나의 네트워크를 만들지 않습니다. 서로 다른 서브넷, 상호 경로, 명시적인 신뢰 경계로 두 서버 사이를 라우팅하십시오.
두 번째 OpenVPN 서버를 세우면 클라이언트가 접속할 두 번째 장소가 생길 뿐 그 이상은 아닙니다. 그 클라이언트를 첫 서버의 네트워크에 올려놓지 않으며, client-to-client 이라는 옵션은 한 서버 프로세스 안의 전달만 바꿉니다. 두 풀이 하나의 네트워크가 되는 것은 각 방향에 책임자를 두고 의도적으로 그 사이를 라우팅할 때입니다. 아래 내용은 모두 사용자 공간 TUN 모드에서 그 내부 전달을 끈 상태를 전제하므로 결정은 커널이 내립니다.
오해: 계정 목록이 하나면 네트워크도 하나
인증과 도달성이 혼동되는 이유는 둘 다 서버 설정에 있기 때문입니다. 두 게이트웨이 어디든 로그인할 수 있는 사용자는 밖에서 보면 문이 둘 달린 한 시스템의 사용자처럼 보입니다. 안쪽에서 각 서버는 자기 풀에서 주소를 나눠 주고 자기가 종료하는 클라이언트에만 경로를 설치합니다. 그래서 게이트웨이 A의 클라이언트가 게이트웨이 B의 클라이언트로 패킷을 보내면 자기 서버가 한 번도 들어본 적 없는 목적지를 요구하는 셈입니다. 패킷은 A의 기본 경로로 가서 터널을 떠나고, 허용을 요청받은 적 없는 방화벽에 도착합니다.
따라서 두 번째 서버를 더해도 이중화나 탐색, 공유 브로드캐스트 도메인이 늘지 않습니다. 계획이 필요한 라우팅된 네트워크가 늘 뿐이고, 그 계획은 주소 지정에서 시작합니다.
각 게이트웨이에 자체 풀과 트랜짓 주소를 주십시오
다른 무엇과도 겹칠 수 없는 두 범위와 호스트 사이의 작은 트랜짓 링크가 주소 계획의 전부입니다. 트랜짓은 직접 관리하는 사설 세그먼트에 두고, 어느 서버든 임대를 내주기 전에 숫자를 적어 두십시오.
| Gateway | Client pool | Transit address |
|---|---|---|
| gateway-a | 10.64.10.0/24 | 172.28.44.1/30 |
| gateway-b | 10.64.20.0/24 | 172.28.44.2/30 |
프로필을 똑같이 유지하려고 두 서버에 같은 풀을 주고 싶은 유혹을 참으십시오. 커널 경로는 공유 범위 안의 임의 주소를 지금 두 호스트 중 누가 가졌는지 말할 수 없으므로, 라우팅 테이블은 한 게이트웨이를 가리키는데 클라이언트는 다른 쪽에 붙어 있을 수 있습니다. 인터페이스 이름은 로컬 관례일 뿐 공유 상태가 아닙니다. 여기서 두 호스트 모두 터널 장치를 tun0, 트랜짓 링크를 transit0 이라고 부르는데, 어떤 패킷도 그 이름을 싣지 않으므로 괜찮습니다.
양쪽에 경로를 설치하십시오
각 게이트웨이에는 트랜짓 주소를 거쳐 상대 풀로 가는 경로가 필요하고, 어느 쪽이든 중계 역할을 하려면 전달이 켜져 있어야 합니다. 먼저 현재 테이블을 확인한 다음 항목을 추가하고, 재접속 훅이 아니라 호스트의 네트워크 관리자를 통해 영구적으로 만드십시오.
sysctl net.ipv4.ip_forward
ip route add 10.64.20.0/24 via 172.28.44.2 dev transit0
ip route get 10.64.20.37
풀을 바꿔 다른 호스트에서도 같은 일을 하십시오. 무관한 스크립트에서 같은 경로를 반복해서 덧붙이면 호스트에 사본이 여러 개 남고 다음에 누군가 테이블을 읽을 때 알 수 없는 변경이 보입니다. 트랜짓 링크 자체가 터널이라면 경로는 그 터널의 토폴로지를 가리키고, 중계는 그 재접속을 견뎌야 합니다. 서른 초 동안 사라지는 트랜짓 주소는 풀 사이의 길도 함께 데려갑니다. 이는 발견하기보다 선언해 두는 편이 나은 설계 속성입니다.
소유권을 속이지 않고 클라이언트 경로를 밀어 넣으십시오
클라이언트에는 원격 풀로 가는 경로가 필요합니다. 없으면 각자의 운영 체제가 패킷을 기본 게이트웨이로 보내고 터널은 그것을 보지도 못합니다. 서버 설정이 그것을 전달할 자연스러운 자리입니다.
push "route 10.64.20.0 255.255.255.0"
미러 줄을 다른 서버의 설정에 넣고, 시험 클라이언트를 다시 연결해 그 테이블을 읽으십시오. 밀어 넣은 경로를 무시하는 클라이언트에는 그 클라이언트의 승인된 설정이 따로 필요하며, 끝내 도착하지 않는 경로는 접근 제어가 아닙니다. 클라이언트에 다른 게이트웨이의 풀 전체를 위한 iroute 을 주지 마십시오. 그 지시문은 연결된 클라이언트 하나 뒤의 서브넷을 위한 것이고, 빠진 게이트웨이 간 경로를 덮는 데 쓰면 도달할 수 없는 주소를 자기가 가졌다고 믿는 서버가 생깁니다.
한 요청은 통과시키고 다른 요청은 거절하십시오
실제 동작을 고르십시오. 예를 들어 10.64.10.21 의 클라이언트에서 10.64.20.37 의 서비스로 가는 HTTPS 를 두 호스트 모두에서 정확히 허용합니다. 각 방화벽은 들어오는 쪽에서 요청을, 돌아오는 쪽에서 추적된 응답을 허용하며, 목적지의 서비스 정책도 그것을 받아들여야 합니다.
iptables -A FORWARD -i tun0 -o transit0 -s 10.64.10.21 -d 10.64.20.37 -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -i transit0 -o tun0 -d 10.64.10.21 -s 10.64.20.37 -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT
상호 경로가 있으면 출발지 NAT 는 불필요하고 여기서는 오히려 해롭습니다. 출발지를 변환하면 어느 클라이언트가 요청했는지가 가려지고 목적지 쪽의 출발지 기반 정책이 무력해집니다. 인터넷 출구용 기존 masquerade 규칙이 이 경로까지 잡지 않는지 확인하십시오. 그다음 거절을 시험하십시오. 풀 B에 로컬로 남아야 하는 서비스는 풀 A에서 닿을 수 없어야 하고, 같은 게이트웨이 안의 클라이언트 짝도 시험해야 합니다. 방금 바꾼 전달 정책이 그들에게도 적용되기 때문입니다.
정신 모델에 한 가지 더 고칠 점이 있습니다. 트랜짓 링크는 복호화된 내부 패킷을 실어 나르므로 사적인 보장이 아니라 신뢰 경계입니다. 관리하지 않는 네트워크를 지난다면 그 구간을 승인된 암호화 터널로 보호하고 그 실제 토폴로지로 라우팅하십시오. 종단 간 TLS 는 여전히 애플리케이션 자체를 인증하므로, 내부 서비스는 그것을 계속 쓰는 편이 좋습니다.