systemd-networkd로 재연결한 뒤에도 ip rule을 살려 두기
셸에서 추가한 ip 규칙은 다음 링크 이벤트에서 사라집니다. 규칙과 그 테이블, 경로를 systemd-networkd에 선언해 정책이 다시 만들어지게 하십시오.
ip rule로 정책 규칙을 추가하면 링크가 사라질 때까지 동작하지만, 그 순간 규칙은 그냥 없어집니다. 요란하게 실패한 것은 아무것도 없고, 커널 상태가 바뀌었으며 그 규칙이 있어야 한다는 사실을 아는 설정 주인도 없었습니다. 살아남는 규칙과 그렇지 않은 규칙의 차이는 전부 여기에 있습니다. systemd-networkd 호스트에서 해법은 재연결마다 명령을 다시 실행하는 것이 아니라 규칙에 주인을 주는 것입니다.
규칙은 누군가 그것을 소유할 때만 존재한다
커널 라우팅 정책은 설정이 아니라 상태입니다. 실행 중인 시스템 안에 살고, 그것이 의존하는 인터페이스를 무너뜨리는 무엇이든 규칙을 함께 데려갈 수 있습니다. 셸 명령은 그 상태를 바꾸고 의도에 대한 기록을 남기지 않습니다.
네트워크 관리자는 반대로 동작해서 설정을 읽고 적용하며, 링크 이벤트가 이전 상태를 무효로 만들면 다시 적용합니다. 그들의 설정에 선언된 규칙은 다시 만들어지지만, 그 옆에 추가된 규칙은 주인 없는 외부 객체입니다.
이 구분에는 networkd에서 이름이 붙어 있고, 무엇이든 켜기 전에 읽어 볼 가치가 있습니다. 최근 버전에서는 외부 규칙이 기본으로 제거되므로, 수동으로 넣은 항목이 남으리라 기대한 사람은 당황합니다.
설정을 믿기 전에 버전을 읽어라
소유 동작은 networkd.conf의 설정으로 제어되며, 그 설정은 오래된 systemd 릴리스에는 없습니다. 오래된 호스트는 지금 읽고 있는 문서와 다르게 동작하므로 그것을 전제로 설계하기 전에 설치된 버전을 확인하십시오.
두 값이 중요합니다. 하나는 networkd 설정에 선언되지 않은 규칙을 제거할지 정하고, 다른 하나는 경로에 대해 같은 일을 합니다. 둘 다 정책의 내용이 아니라 소유에 관한 것이며, 둘 다 현재 릴리스에서는 기본으로 더 엄격한 동작을 택합니다. 이를 전역으로 끄면 모든 외부 규칙이 살아남지만, 그것은 의도한 VPN 정책 하나를 선언하는 것보다 훨씬 넓은 결정입니다.
systemctl --version | head -1
networkctl --version | head -1
grep -R "ManageForeign" /etc/systemd/ /usr/lib/systemd/ 2>/dev/null
그 grep은 로컬에서 무엇이 설정되었는지, 벤더 기본값이 무엇인지 알려 줍니다. 없음도 컴파일된 기본값이 적용된다는 뜻이므로 정보입니다. 기본값을 메모에 적어 두면 다음 사람이 그것을 다시 알아낼 필요가 없습니다.
규칙과 그것이 가리키는 테이블을 선언하라
.network 파일의 규칙은 테이블의 이름을 지정하며, 이름이 하는 일은 그게 전부입니다. 그 테이블 안의 경로를 만들지도 않고, 그것이 말이 되는지 확인하지도 않습니다. 경로 없이 규칙을 선언하면 트래픽을 일치시킨 뒤 버리는 정책이 됩니다.
같은 파일에 경로를 선언하고, 도달 가능한 다음 홉과 올바른 테이블을 지정하십시오. 선언한 규칙에 우선순위를 남겨 두어 정렬된 목록에서 기대하는 자리에 놓이게 하십시오. 의도보다 먼저 잘못된 테이블에 도달하는 규칙은 고치려던 서비스보다 더 많은 트래픽을 바꿉니다. 인터페이스 소유와 연결의 나머지 부분이 여전히 있어야 하므로 아래 조각은 완전한 파일이 아니라 형태로 읽으십시오.
[RoutingPolicyRule]
Family=ipv4
IPProtocol=tcp
SourcePort=443
Table=100
Priority=1000
[Route]
Destination=10.80.0.0/24
Gateway=10.80.0.1
Table=100
그다음 다시 불러오고 파일이 아니라 커널 상태를 읽으십시오. 깨끗하게 해석된 설정도 의도하지 않은 규칙을 만들 수 있으며, 두 관점은 선언이 완전했을 때만 일치합니다.
인터페이스의 주인을 하나로 정하라
한 터널에 두 관리자가 있으면 양쪽 모두에서 결함처럼 보이는 다툼이 생깁니다. 기존 wg-quick 설정을 networkd로 가져오는 것은 추가가 아니라 이전이며, 둘 다 실행하면 인터페이스와 그 규칙이 두 주인 아래 남습니다.
이전은 한 번에 끝내되, 옛 유닛을 멈추고 비활성화한 뒤 인터페이스와 경로를 networkd에 선언하고 다시 불러오십시오. 옛 유닛이 부팅 때 돌아올 수 없는지 확인하십시오. 재부팅 뒤 다시 활성화되는 남은 유닛은 방금 선언한 상태를 덮어씁니다. 새 설정이 재연결을 한 번 견딜 때까지 옛 설정 파일을 두고, 그다음 은퇴시키십시오.
마크 소유에도 같은 주의가 필요해서, 규칙이 방화벽 마크와 일치한다면 누군가는 그 마크를 설정해야 합니다. 아무도 설정하지 않는 마크를 참조하는 규칙은 의도적으로 보이는 죽은 설정입니다.
수명 주기 이벤트를 연습하라
설정은 이벤트 전반의 동작만큼만 가치가 있습니다. 링크를 내리고, 올리고, 서비스를 다시 시작하고, 주소를 갱신하십시오. 각각 뒤에 규칙 목록과 테이블 내용을 읽으십시오.
sudo networkctl reload
ip rule show
ip route show table 100
sudo systemctl restart systemd-networkd
의도한 결과는 같은 소유 정책이 다시 만들어지고 낡은 상태가 은퇴하는 것이며, 이는 재연결마다 셸 규칙을 다시 추가하는 것보다 오래갑니다. 변경 기록에 규칙을 소유한 파일과 그것을 지원하는 버전을 적은 한 줄을 남기십시오. 그러면 다음 엔지니어가 셸 기록을 뒤지지 않고 정책을 찾을 수 있습니다. 주인이 없는 규칙은 다시 잃어버릴 규칙입니다.