OpenVPN 클라이언트끼리 서로 보이지 않는다: 서브넷인가 net30인가
클라이언트는 서버에는 닿지만 서로에게는 닿지 않는다. ping 한 번으로 결함을 나누고, 그다음 서브넷 토폴로지, client-to-client, 브리지된 TAP 세그먼트 중 하나를 고른다.
서로 무관한 두 결함이 같은 불만을 만든다. 첫째는 주소가 같은 서브넷에 없거나 서버가 피어 간 트래픽을 전달하지 않아 한 클라이언트가 터널 주소로도 다른 클라이언트에 닿지 못하는 경우다. 둘째는 ping은 되는데 앱의 장치 목록이 비어 있는 경우로, 계층 3 터널이 실어 나를 수 없는 브로드캐스트에 검색이 의존하기 때문이다. ping 한 번이 둘을 갈라놓고, 각각의 고치는 방법은 서로 전혀 닮지 않았다.
ping 한 번이 문제를 둘로 가른다
한 클라이언트에서 이름도 LAN 주소도 아닌 다른 클라이언트의 터널 주소로 ping한다. 실패하면 전달이나 주소 지정이 문제이고 고쳐질 때까지 검색은 무관하다. 성공하면 전달은 이미 동작하고 남은 틈은 브로드캐스트나 이름 해석이다.
ip addr show tun0
넷마스크가 토폴로지의 이름을 말해 주므로 무엇보다 먼저 클라이언트 인터페이스를 읽는다. /24나 다른 서브넷 마스크는 공유 풀이고, 피어 주소가 있는 /30은 각 클라이언트가 서버라는 이웃 하나만 갖는 옛 점대점 배치다.
net30과 서브넷, 클라이언트 쪽에서 읽기
| What you observe | net30 | subnet |
|---|---|---|
| Client netmask | /30 with a peer address | The pool's own mask |
| Peers a client can address directly | One, the server | Every client in the pool |
| Addressing | One /30 block per client, with the server as the only peer | One pool mask shared by all clients |
| Effect of client-to-client | Required for any peer traffic | Required for peer traffic, still no broadcast |
net30에서는 서버가 클라이언트마다 작은 블록을 준다, 그래서 서버가 전달하지 않으면 다른 클라이언트의 터널 주소로 가는 경로가 없다. 서브넷이면 풀 전체가 하나의 네트워크이고 모든 클라이언트가 서로를 향할 수 있으며 서버가 그 사이를 전달한다. 그래서 서버 설정은 건강해 보이는데 피어 간 ping이 실패할 수 있다: 고장 난 것이 아니라 클라이언트가 같은 네트워크에 있지 않을 뿐이다. 클라이언트는 동작하는 링크와 응답하지 않는 피어 주소를 보게 되고, 이는 방화벽처럼 읽히지만 실제로는 주소 지정 배치다.
지시자를 바꾸는 것은 서버 쪽 편집과 재시작이며, 이미 열린 세션이 아니라 다음 연결에 적용된다. 변경을 판단하기 전에 클라이언트를 다시 연결한다. 장비가 웹 인터페이스에서 설정을 다시 생성한다면 지시자와 파일, 날짜를 기록하고, 재생성이 다음 저장에서 수동 편집을 덮어쓰므로 인터페이스가 있을 때 그쪽으로 편집한다.
client-to-client는 별개의 관문이다
같은 서브넷 안에서도 client-to-client가 설정되지 않으면 서버는 한 클라이언트에서 와서 다른 클라이언트로 향하는 패킷을 버린다. 이 키워드는 서버를 그저 받아들이는 종단점이 아니라 피어 트래픽의 전달자로 만든다. 토폴로지를 고친 뒤 사람들이 놓치는 두 번째 조건이 이것이다.
이것은 브로드캐스트로 확장되지 않는다. 이 설정은 클라이언트 사이의 유니캐스트를 전달할 뿐 터널을 공유 세그먼트로 만들지 않으며, 아무리 설정해도 멀티캐스트 검색 질의가 모든 피어에 도달하지는 않는다.
TUN과 TAP, 각각이 실어 나를 수 있는 것
| Property | TUN | TAP |
|---|---|---|
| Layer | Layer 3, IP packets | Layer 2, Ethernet frames |
| Broadcast and multicast | Not carried | Carried |
| Bridging to the LAN | Not applicable | Required for a shared segment |
| Client platform support | Everywhere | Missing on Android and iOS, limited on macOS |
tap 장치를 LAN에 브리지하면 원격 클라이언트가 사무실 브로드캐스트 도메인에 들어가 검색이 완전히 해결된다. 대가는 TAP를 구현하지 않는 플랫폼, 서버에 있어야 하는 브리지, 그리고 사무실 케이블의 브로드캐스트 폭풍이 모든 원격 노트북에 닿는 장애 도메인이다.
이름 해석이 필요한 워크스테이션 두세 대라면 hosts 항목이나 유니캐스트 주소가 브리지보다 훨씬 싸다. 바꿀 수 없는 프로토콜이 계층 2 검색에 의존할 때만 TAP을 꺼내고, 그 전에는 쓰지 않는다.
브리지 없는 같은 서브넷 클라이언트
LAN 범위에서 주소를 준다고 클라이언트가 케이블에 올라가지는 않는다. 서버가 터널과 이더넷 사이를 라우팅하므로 그 주소에 대한 ARP는 서버 인터페이스에서 멈추고 나머지 LAN은 그것이 어디 사는지 알지 못한다.
ip neigh add proxy 10.50.1.250 dev eth0
Proxy ARP는 서버가 클라이언트 대신 응답하게 해 그 틈을 막는다. 범위에 대해서는 parpd가 클라이언트가 드나들 때 항목을 관리하고, sysctl net.ipv4.conf.eth0.proxy_arp=1는 다른 곳으로 라우팅되는 모든 것에 응답하는데, 이는 붐비는 세그먼트에서 저마다의 놀라움을 만들 만큼 넓다.
어느 경로를 택하든 터널과 케이블 사이의 NAT를 없앤다. MASQUERADE 규칙은 답하려는 바로 그 주소 지정 질문을 가린다. 또한 다음 사람에게 클라이언트 쪽에서 헤아릴 수 없는 토폴로지를 남긴다.
무엇이든 바꾸기 전에 서버에서 tcpdump -ni tun0로 시도 하나를 잡아 피어 패킷이 애초에 도착하는지 확인한다. 도착했다 버려지면 client-to-client를 가리키고, 전혀 도착하지 않으면 토폴로지나 라우팅을 가리킨다. OpenVPN 2.6 설명서는 두 키워드와 기본 토폴로지를 문서화하며, 포럼 글에서 한 줄을 복사하기 전에 자기 버전과 맞춰 볼 가치가 있다.
- tun 인터페이스는 /30 피어 쌍이 아니라 풀 마스크를 보여 준다.
- 검색을 시험하기 전에 다른 터널 주소로의 직접 ping이 성공한다.
- 피어가 패킷을 주고받아야 할 때면 서버 설정에
client-to-client가 들어 있다. - 앱이 실패한다고 말하는 대신 시험 중인 검색 프로토콜을 이름으로 말한다.
- 그룹의 모든 클라이언트 플랫폼이 지원할 때만 TAP을 쓴다.
- 같은 서브넷 안의 도달성이 중요하면 NAT를 터널과 LAN 사이 경로에서 빼라.