blog.stackframe.dev

systemd-networkd로 WireGuard 구축하기

systemd-networkd를 사용하면 WireGuard VPN을 쉽게 구축할 수 있다. 먼저 최소 요구사항으로 리눅스 5.6, systemd 237 이상의 버전이 필요하다. 리눅스 5.6부터 WireGuard가 메인라인 커널에 들어왔고 systemd 237부터 WireGuard를 systemd-networkd로 설정가능하기 때문이다. 그리고 키를 생성하기 위해 wg 명령이 필요하다. Arch Linux에서는 wireguard-tools 패키지로 제공되고 있다.

WireGuard는 기본적으로 서버란 개념이 없다. P2P처럼 IP 주소와 포트 번호, 미리 교환된 공개키를 알고 있으면 연결이 가능하다. 물론 한 피어를 고정된 IP와 포트 번호를 사용하여 마치 서버처럼 사용하는게 가능하다. 보통 하나를 서버로 사용하여 나머지 기기들을 연결하는 경우가 많으므로 아래에서는 서버와 클라이언트로 설명할 것이다.

여기서 설정할 VPN은 172.16.0.0/20 네트워크를 사용할 것이고 서버 주소는 172.16.0.1, 클라이언트 주소는 172.16.0.2로 할당할 것이다.

먼저 사용할 비밀키와 공개키를 생성해야 한다. 서버용 키 한 쌍과 클라이언트용 한 쌍을 생성한다. 클라이언트용 키는 클라이어트 쪽에서 생성하여 공개키만 서버에 넘기는게 안전하지만 여기서는 설명의 편의를 위해 한번에 생성하였다:

$ wg genkey > server.private
$ wg pubkey < server.private > server.public
$ wg genkey > client.private
$ wg pubkey < client.private > client.public

위의 명령을 실행하면 server.private, server.public, client.private, client.public 파일이 생성된다.

systemd-networkd가 WireGuard 인터페이스를 만들기 위해 netdev 설정파일을 만들어야 한다. 아래와 같이 /etc/systemd/network/ 디렉토리에 wg.netdev 파일을 생성한다:

[NetDev]
Name=wg0
Kind=wireguard

[WireGuard]
PrivateKey={server.private 파일 내용}
ListenPort={server port}

이렇게하면 wg0라는 인터페이스가 생성된다.

이번에는 클라이언트들의 정보가 저장될 /etc/systemd/network/wg.netdev.d/ 디렉토리를 만든다:

# mkdir /etc/systemd/network/wg.netdev.d/

systemd는 이렇게 설정파일 이름에 .d가 붙은 디렉토리가 있으면 그 안의 .conf 파일도 로드하니 참고하면 좋다. 앞으로 클라이언트를 추가하려면 이 디렉토리에 아래의 설정파일과 같은 형식으로 추가하면 된다.

위에서 만든 디렉토리 안에 클라이언트 정보가 들어있는 설정파일 client1.conf를 생성한다:

[WireGuardPeer]
PublicKey={client.public 파일 내용}
AllowedIPs=172.16.0.2/32

Allowed IPs에 대해서는 이전 글에 자세히 다루었으므로 참고하기 바란다. WireGuard의 Allowed IPs 설명

인터페이스를 만들었으니 이 인터페이스에 IP를 할당하기 위해 network 설정파일을 만들어야 한다. 아래와 같이 /etc/systemd/network/ 디렉토리에 wg.network 파일을 생성한다:

[Match]
Name=wg0

[Network]
Address=172.16.0.1/20
IPForward=yes

서브넷 마스크를 /20으로 설정하여 이 대역에 포함된 주소에 대한 라우팅 정보를 암시적으로 생성하고 IPForward를 설정하여 클라이언트가 다른 클라이언트에게 패킷을 보낸 경우 포워딩하여 통신이 가능하게 한다.

이제 클라이언트의 설정을 할 차례이다. 클라이언트도 크게 다르지 않다. 단지 서버의 entrypoint를 추가하여 연결할 수 있도록 할 뿐이다. 서버 때처럼 /etc/systemd/network/ 디렉토리에 wg.netdev를 생성한다:

[NetDev]
Name=wg0
Kind=wireguard

[WireGuard]
PrivateKey={client.private 파일 내용}

[WireGuardPeer]
PublicKey={server.public 파일 내용}
AllowedIPs=172.16.0.0/20
Endpoint={서버 공인 IP}:{server port}
PersistentKeepalive=60

클라이언트 쪽에서는 여러 피어를 넣을 일이 없기 때문에 wg.netdev.d/ 디렉토리를 만들지 않고 wg.netdev 파일에 바로 피어 정보를 넣었다. 그리고 서버와 달리 EndpointPersistentKeepalive라는 설정이 추가되었다. 클라이언트의 IP는 계속 바뀔 수 있지만 서버의 주소는 그렇지 않기 때문에 클라이언트만 Endpoint 설정을 넣어서 서버로 접속하도록 한다. 또한 PersistentKeepalive 설정으로 지속적으로 서버에 keepalive 패킷을 보내서 연결을 계속 유지시키는 것도 가능하다. 이 설정 값은 초 단위다. 이건 필요없다면 없애도 무방하다.

/etc/systemd/network/ 디렉토리에 wg.network를 생성한다:

[Match]
Name=wg0

[Network]
Address=172.16.0.2/20

이제 systemd-networkd를 재시작하면 인터페이스와 IP가 생성되어 있을 것이다.

# systemctl restart systemd-networkd

wg0 인터페이스가 생성되었고 172.16.0.2 IP가 할당되었다.

서버에 핑을 보내보면 응답이 오는걸 확인할 수 있다.

VPN이 제대로 작동한다.

댓글