하나의 Linux 게이트웨이에서 두 OpenVPN 클라이언트: 정책을 나누기
두 터널이 모두 올라와도 각 클라이언트는 같은 하나를 쓴다. 소스로 결정하고, 각 그룹에 자체 테이블을 주고, 선택한 장치에서 허용하고 변환하라.
두 OpenVPN 데몬이 모두 터널 성공을 보고해도 게이트웨이는 모든 클라이언트를 같은 하나로 보낸다. 클라이언트가 어느 터널을 쓰는지는 핸드셰이크에서 아무것도 결정하지 않으며, 결정하는 것은 커널이고, 패킷 하나하나마다 정책 규칙을 훑고 그다음 각 규칙이 가리키는 테이블을 훑는다. 이렇게 보면 설정은 OpenVPN 옵션의 문제가 아니라 소유자가 둘인 라우팅 행렬이 된다.
결정하는 것은 커널이지 데몬이 아니다
전달되는 패킷은 LAN 인터페이스에 도착하고 라우팅 코드는 규칙 목록을 우선순위 순으로 평가한다. 선택자가 처음 일치하는 규칙이 테이블을 가리키고 그 테이블의 경로가 출력 장치를 고른다. 테이블에 일치하는 경로가 없으면 조회는 다음 규칙으로 넘어가며, 바로 이 세부 덕분에 미완성 설정이 요란하게가 아니라 조용히 실패한다.
그 훑기에서 터널 프로세스는 할 말이 없다. 인터페이스를 만들고 경로를 설치할 수는 있지만, 특정 클라이언트의 패킷이 그 인터페이스로 들어가는지는 어느 테이블로 보내졌는지에 전적으로 달려 있다. 그래서 가져온 경로가 같은 두 터널이 경쟁하고, 마지막에 쓴 쪽이 모두를 가져간다. 설정 파일에서는 균형이 잡혀 보이는데도 모든 클라이언트를 하나의 데몬으로 보내는 이유가 여기에 있다.
각 인스턴스에 자체 자원 집합을 주라
이름에서 시작하라, 이름을 재사용하는 것이 두 인스턴스가 우연히 상태를 공유하게 되는 길이기 때문이다. 터널 장치, 설정 파일, 자격 증명, 서비스 단위를 각각 다르게 하라. 각 인스턴스의 외부 종단점과 터널 주소를 정책 행렬과 같은 테이블에 기록해 두면 나중 변경이 볼 곳이 한 군데뿐이다.
dev tun-office
route-nopull
pull-filter ignore "redirect-gateway"
dev tun-lab
route-nopull
route-nopull 는 서버가 밀어 넣는 경로를 로컬 테이블 밖에 두는데, 정책 테이블이 명시적이어야 할 때 바로 원하는 동작이다. 특정 서버가 redirect-gateway 옵션도 밀어 넣는지, 클라이언트 버전이 필터 형식을 지원하는지는 짐작하지 말고 설명서에서 확인할 가치가 있다.
행렬을 쓰라: 소스, 테이블, 장치
클라이언트 그룹마다 소스 주소로 테이블을 고르는 규칙을 추가하고, 그다음 의도한 경로를 그 테이블에 넣어라:
ip rule add from 10.8.1.0/24 lookup 100 priority 1000
ip rule add from 10.9.1.0/24 lookup 200 priority 1000
ip route add default dev tun-office table 100
ip route add default dev tun-lab table 200
테이블에는 그룹이 가져야 할 목적지 경로와 게이트웨이를 쓸 수 있게 유지하는 로컬 또는 관리 예외가 필요하다. 기본 경로만 담은 테이블은 그 그룹에 대한 전체 터널 정책이며, 그것이 정확히 옳을 수도 있지만 빠뜨림이 아니라 결정이어야 한다.
터널 자체의 전송은 클라이언트 그룹에 없다
각 데몬의 암호화된 패킷은 게이트웨이 자체에서 생겨 바깥 피어로 향한다. 클라이언트 소스 접두사에만 일치하는 규칙은 그 패킷을 그대로 두며, 그래서 소스로 범위를 좁힌 규칙이 모든 것에 일치하는 규칙보다 안전하다. 선택자가 없는 규칙은 터널 자체의 전송을 붙잡아 다시 터널로 보낼 수 있고, 그러면 연결이 공급자 문제처럼 보이는 방식으로 실패한다.
로컬 관리 세션과 하부 계층 경로는 정책 테이블 밖에 명시적으로 두고, 그 예외를 행렬 옆에 적어 두라. 설계의 일부이며, 장애 때 다시 발견해야 할 임시변통이 아니다.
경로를 고르는 것은 허가가 아니다
전달 허가는 경로 후반에 내려지는 두 번째 결정이다. 두 번째 터널로 나가는 패킷을 받아들이는 방화벽 규칙이 라우팅 코드로 하여금 그것을 고르게 만들지 않고, 올바른 테이블이 전달 허가를 주지도 않는다. 둘 다 있어야 하며, 하나만 증명하는 시험은 다른 하나에 대해 오해하게 만든다. 한 그룹이 되고 다른 그룹이 안 될 때는 테이블을 다시 쓰지 말고 이 두 단계를 따로 확인하라.
소스 변환은 세 번째 결정이며, 소스 그룹과 실제로 그것을 나르는 터널 장치로 범위를 좁혀야 한다. postrouting 체인을 비우는 대신 두 인스턴스를 위한 전용 체인을 추가하라 — 무관한 서비스가 거기에 항목을 가질 수 있고, 비우면 그것들이 조용히 사라지기 때문이다.
터널 하나가 떨어질 때 무슨 일이 일어나는가
실패 동작은 미리 정하라. 터널 테이블이 경로를 잃으면 조회는 다음 규칙으로 떨어지고, 그룹의 트래픽은 아무 오류 없이 다른 터널이나 평범한 상향 링크로 계속된다. 그것이 받아들일 수 없다면 더 낮은 우선순위 번호에 그 소스에 대해 도달 불가 결과를 돌려주는 종료 규칙을 추가해, 손실이 흡수되지 않고 보이게 하라.
소스 그룹마다 새 요청을 보내고 네 가지 사실을 기록하라 — 선택된 테이블, 출력 장치, 변환된 소스, 그리고 응답이 같은 경로로 돌아왔는지 여부다. 그다음 터널 하나를 내리고 반복하라. 인터페이스 목록에 TUN 장치가 둘이라는 것은 두 데몬이 모두 시작되었다는 증거일 뿐, 특정 클라이언트가 어느 쪽을 썼는지는 말해 주지 않는다. 터널 하나가 빠진 두 번째 실행이야말로 폴백 규칙이 의도대로 작동하는지 보여 준다.