systemd가 VPN 서비스를 멈춘다: Type=을 클라이언트에 맞춰라
부팅 시의 VPN 유닛은 Type=이 클라이언트의 수명과 맞지 않으면 비활성 상태가 된다. 유형을 맞추고 소유권을 고치고 서비스를 터널 뒤에 배치하라.
유닛은 올바르게 보일 수 있고 systemctl start는 성공을 보고할 수 있지만, 십 초 뒤 서비스는 비활성 상태로 앉아 있다. 아니면 시작 한도가 멈출 때까지 몇십 초마다 재시작한다. 두 동작 모두 네트워크 결함이 아니다: 유닛이 서술하는 프로세스 수명이 클라이언트가 실제로 하는 일과 맞지 않는 것이다. 클라이언트는 포크하고 종료하거나, 세션을 구성하고 즉시 종료할 수 있다.
성공적인 시작 몇 초 뒤 유닛이 비활성이 되는 이유
systemd는 자기가 시작한 프로세스에서 상태를 추론하므로 Type= 는 그 프로세스에 대한 주장이지 문서가 아니다. Type=simple 에서는 명령이 도는 동안 유닛이 활성으로 계산되고 명령이 돌아오면 비활성이 된다. 연결을 도우미에게 넘기고 종료하는 클라이언트가 충돌처럼 보이는 이유가 여기에 있으며, 터널이 올라가 있어도 마찬가지다. 그러면 저널에는 고장 난 바이너리처럼 읽히는 시작과 정지의 패턴이 남는다.
네트워크 설정을 건드리기 전에 클라이언트의 행동 옆에서 유닛 유형을 읽어라.
클라이언트에 맞는 유형을 골라라
두 가지 패턴이 대부분의 벤더 CLI를 덮는다: 세션을 구성하고 종료하는 명령과, 돌아오기 전에 도우미를 포크하는 명령이다. 각각 다른 유닛 형태가 필요하다.
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/protonvpn-cli connect -f
ExecStop=/usr/bin/protonvpn-cli disconnectoneshot 와 RemainAfterExit=true 를 함께 쓰면 명령이 돌아온 뒤에도 유닛이 활성으로 남아 구성하고 종료하는 패턴에 맞고, 정지 명령은 서비스에 깔끔한 종료를 준다. 포크하는 클라이언트는 같은 형태를 유지하지만 systemd가 자식을 따라가도록 Type=forking 가 필요하다. Restart=on-failure, RestartSec=30, 그리고 유한한 StartLimitBurst 을 설정해 공급자 장애가 오류를 묻어 버리는 재시작 루프가 되지 않게 하라.
감독 아래의 클라이언트가 데몬화하도록 두지 마라
systemd 밖의 감독자도 같은 벽에 부딪힌다. 데몬화된 클라이언트를 띄우는 runit 실행 스크립트는 즉시 감독자에게 돌아오고, 감독자는 그 반환을 충돌로 읽어 또 하나의 사본을 띄운다. 모든 감독자는 전면의 프로세스를 소유하기를 기대하며, sshd -D 가 앞에 남는 것과 같다. 감독 아래의 명령에서는 데몬 플래그를 빼고 배경 작업은 감독자에게 맡겨라. 터널이 생기기 전에 돌아오는 래퍼 스크립트도 같은 혼란을 만든다.
스스로 분리되는 프로세스는 그것을 지켜보기로 되어 있던 모든 것에게 보이지 않는다.
세션이 끝나면 터널도 함께 사라진다
데스크톱 세션은 자기가 띄운 클라이언트를 소유한다. 로그인을 끝내면 세션 버스가 사라지고 터널도 함께 사라지며, 마지막 창을 닫을 때 그래픽 클라이언트의 연결이 자주 끊기는 이유가 이것이다. 서버는 스스로 올라오는 시스템 유닛이나 NetworkManager 프로필을 써야 한다. 사용자 유닛은 터널이 로그인과 함께 끝나야 할 때만 의미가 있으므로 그래픽 세션 대상에 묶고 그 사실을 설명에 적어라.
서비스 사용자는 같은 문제의 나머지 절반이다. root가 소유하고 모드가 0600인 키는 다른 계정으로 도는 유닛에게 읽히지 않으며, 셸에서 sudo가 무엇을 이루었든 마찬가지다.
systemctl cat openvpn-server@work
sudo chown -R openvpn:network /etc/openvpn/server
sudo find /etc/openvpn -type f -perm /077 -ls주소가 생긴 뒤에 종속 유닛을 배치하라
아직 만들어지지 않은 터널 주소에 데몬이 묶이면 Cannot assign requested address 로 종료한다. 해법은 재시도 루프가 아니라 명시적 순서다.
[Unit]
After=network.target [email protected]
Requires=sys-devices-virtual-net-wg0.deviceAfter= 는 작업의 순서를 정하고, 장치 유닛의 Requires= 는 종속 유닛을 인터페이스의 존재에 묶는데, 이는 네트워크 대상만 기다리는 것보다 강하다. 편집 뒤 관리자를 다시 읽고 종속 서비스를 재시작한 다음, 순서가 먹혔다고 가정하지 말고 그 저널을 읽어라. 클라이언트의 출력은 StandardOutput=journal 나 로그 옵션으로 저널에 보내라(아무도 열지 않는 파일에 쓰는 감독 아래의 클라이언트는 한 번도 시작하지 않은 것과 똑같이 보이기 때문이다).
그다음 한 번 재부팅하고 터널 장치가 나타난 뒤 유닛이 활성인지 확인하라. systemctl start 로만 검증한 수정은 고치려던 부팅 순서를 시험하지 않은 것이다.