Profile picture

[Linux] VPN 오픈소스 소프트웨어 Wireguard로 VPN 서버 구축하기

JaehyoJJAng2024년 12월 02일

개요

이번 포스팅에서는 Wireguard의 기본 개념, VPN 피어(Peer), 내부 IP 주소의 이해

그리고 리눅스 서버 및 Windows 클라이언트에서의 구축 방법과 함께

도커 기반의 wireguard-ui를 사용하여 클라이언트 키를 쉽게 생성하는 방법에 대해서 기록해보려고 합니다!


VPN 피어(Peer)

VPN에서 피어(Peer) 란 서로 VPN 터널을 통해 통신하는 개별 노드를 의미합니다.

  • 서버 피어: VPN 네트워크의 중심이 되는 서버로, 여러 클라이언트와의 연결을 관리.
  • 클라이언트 피어: 서버에 연결하는 각 단말기로, 각 클라이언트마다 고유 키와 내부 IP가 할당됨.

VPN 내부 IP의 이해

VPN 구축 시 사용하는 내부 IP 주소 는 가상의 네트워크를 구성하는 데 사용됩니다.

예시로, 10.0.0.0/24와 같이 서브넷을 지정하여,

서버에 10.0.0.1

클라이언트에 10.0.0.2 등으로 할당해요.


해당 IP는 인터넷상의 real ip와 별개로 VPN 내에서만 사용이 되구요,

안전한 터널링 및 라우팅을 위해 사용된다고 보면 됩니다!


Wireguard 설치하기

이번 챕터에서는 Ubuntu 환경에서 Wireguard를 설치하고

서버를 구성하는 방법을 알아볼거에요!s


사전 준비

  • 운영체제: Ubuntu 20.04 이상 (다른 배포판도 가능)
  • 커널 버전: Linux 커널 5.6 이상 (이하일 경우 backport 패키지 필요)
  • 루트 권한: sudo 권한 필요

wireguard 설치

아래 명령어를 실행해 wireguard를 설치합시다.

sudo apt update
sudo apt install wireguard -y

1. Wireguard 서버 설정

1-1. 서버 키페어 생성

Wireguard는 공개키 암호 방식 을 사용합니다.

서버와 클라이언트 각각에서 개인키(Private Key)와 공개키(Public Key)를 생성해줘야 해요!


1. Private Key 생성

sudo wg genkey | sudo tee /etc/wireguard/server_private.key
sudo chmod 600 /etc/wireguard/server_private.key

server_private.key 파일에는 서버의 개인키가 저장됩니다. 절대 외부에 노출하지 마세용!!


2. Public Key 생성

sudo cat /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

server_public.key 파일에는 서버의 공개키가 저장됩니다. 이 키는 클라이언트 설정에 필요해요!


1-2. 서버 설정 파일 작성

이제 VPN 서버의 동작 방식을 정의하는 설정 파일을 생성해주겠습니다.

wg0.conf 라는 이름으로 파일을 생성해줄게요.
(Wireguard는 /etc/wireguard/wg0.conf 파일을 생성하여 VPN 인터페이스를 구성합니다!)


먼저 ip a 명령어를 실행하여 외부 트래픽용 인터페이스 이름(예: ens18)과 퍼블릭 IP를 확인할게요.

ip a

그리고 /etc/wireguard/wg0.conf 파일을 생성하고 편집하겠습니다.

[Interface]
# 서버의 VPN 내부 IP 주소
Address = 10.0.0.1/24
# 설정을 저장할지 여부
SaveConfig = true
# WireGuard 서비스가 시작될 때 실행할 명령어 (NAT 설정)
# eth0는 서버의 실제 네트워크 인터페이스 이름입니다. 'ip a' 명령어로 확인 후 다르면 수정하세요.
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# WireGuard 서비스가 중지될 때 실행할 명령어 (NAT 설정 복원)
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# WireGuard가 사용할 포트 (UDP)
ListenPort = 51820
# 2단계에서 생성한 서버의 '개인키'를 여기에 붙여넣으세요.
PrivateKey = <서버의 privatekey 파일 내용>

# --- 클라이언트 정보 ---
# 이곳부터는 연결할 클라이언트(Peer) 정보를 추가합니다.
[Peer]
# 클라이언트의 '공개키'를 여기에 붙여넣으세요. (클라이언트 설정 시 생성)
PublicKey = <클라이언트의 publickey 내용>
# 이 클라이언트에게 할당할 VPN 내부 IP 주소
AllowedIPs = 10.0.0.2/32

자 그러면, 상기 설정 파일에 작성된 설정 관련해서 조금 더 자세하게 설명해보도록 할게요!


NAT(Network Address Translation)

일반적으로 서버가 VPN 클라이언트의 트래픽을 인터넷으로 전달할 때,

VPN 내부 IP는 일반 공인 IP와 다르기 때문에 라우팅 및 응답에 문제가 발생할 수 있습니다.

그렇기 때문에 NAT 설정을 해주는 것이에요!

그럼 잠깐 옵션을 살펴볼까요?


Address

설명: Wireguard 인터페이스가 사용할 IP 주소 대역입니다.

10.0.0.1/24는 서버 자신은 10.0.0.1을 사용하고, 10.0.0.2 ~ 10.0.0.254까지 클라이언트에게 할당할 수 있다는 뜻입니다!

서브넷에 대한 개념이 잡혀있다면 쉽게 이해가 가능합니다.


PostUp (인터페이스가 올라갈 때 실행됨)

설명: VPN 클라이언트의 내부 IP가 아닌 서버의 공인 IP로 바꾸어 응답 패킷이 올바른 경로로 돌아올 수 있게 합니다.

  • iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    • %i는 현재 인터페이스(예: wg0)로 대체됩니다.
    • 설명: 해당 인터페이스를 통해 들어오는 패킷들을 허용(ACCEPT)하여, VPN 클라이언트에서 오는 트래픽이 서버를 통해 다른 네트워크로 전달될 수 있도록 합니다.
    • -t nat: NAT 테이블을 사용합니다.
    • POSTROUTING: 패킷이 라우팅 결정 후 외부로 나가기 전 처리하는 체인입니다.
    • -o eth0: 외부 인터페이스 이름(예: eth0)를 지정합니다 (실제 인터넷 연결에 사용되는 인터페이스를 뜻해요!)
    • MASQUERADE: 패킷의 출발지 IP를 서버의 외부 IP로 변경합니다.

PostDown (인터페이스가 내려갈 때 실행됨)

설명: VPN 연결 종료 시, 위에서 추가했던 iptables 규칙을 제거하여 서버의 네트워크 설정을 원래 상태로 복구합니다.

  • iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    • 위의 FORWARD 체인에 추가된 규칙을 삭제합니다.
    • NAT 테이블의 MASQUERADE 규칙을 삭제합니다.

ListenPort

설명: WireGuard가 클라이언트의 연결을 기다릴 UDP 포트를 명시하는 것입니다.


PrivateKey

설명: 서버 키페어 생성에서 생성했었던 server_private.key파일에 있는 키 값을 붙여 넣어주세요.


[Peer]

설명: 연결을 허용할 클라이언트에 대한 설정 섹션입니다.


PublicKey

설명: 클라이언트에서 생성할 공개키를 나중에 추가해줘야 해요.


AllowedIPs

설명: 해당 클라이언트에게 할당하고, 해당 클라이언트로부터 들어오는 것을 허용할 IP 주소입니다.


1-3. IP 포워딩 활성화

서버가 VPN 클라이언트로부터 받은 트래픽을 인터넷으로 전달(포워딩)할 수 있도록 커널 설정을 변경해야 합니다!


1. IP 포워딩 활성화

echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

2. 방화벽 설정

서버의 방화벽(예: ufw)에서 Wireguard가 사용하는 UDP 포트(51820)을 열어줘야 합니다.

# ufw를 사용하는 경우
sudo ufw allow 51820/udp
sudo ufw allow OpenSSH # SSH 연결이 끊기지 않도록 SSH 포트도 허용
sudo ufw enable
sudo ufw status

[주의] iptables 중복 설정

간혹, Wireguard 설정을 기록한 블로그들을 보면 iptables 설정을 중복해서 작성하는 게시글들이 종종 보이는데요!


서버 설정 파일 작성에서 wg0.confPOSTUP 지시어에 iptables 규칙을 넣었던 것 기억하시죠?


그런데 소수의 블로그 글들을 보면 wg0.confPOSTUP 지시어를 추가했음에도 불구하고, 추가적으로 서버에 아래와 같은 설정을 해주는 분들이 계세요!

sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o <IF> -j MASQUERADE

sudo iptables-save | sudo tee /etc/iptables/rules.v4

위 명령어 또한 VPN에 연결된 기기들이 인터넷을 사용할 수 있도록하고, 또한 iptables rules에 영구적으로 적용하는 명령어입니다.

이미 wg0.confPOSTUP 지시어에 iptables 규칙을 명시해줬다면 위 명령어를 또 입력할 필요는 전혀 없어요 ..


또한 wg0.confPostUp/PostDown 방식을 사용하는걸 더 권장하고 있답니다.

그 이유는 이 방식은 Wireguard 서비스와 규칙의 생명주기를 일치시키는데

이 때 sudo wg-quick up wg0 명령으로 VPN이 켜질 때 PostUp 규칙이 자동으로 추가되고,

sudo wg-quick down wg0 명령으로 VPN이 꺼지면 PostDown 규칙에 의해 설정했던 iptables 규칙이 자동으로 삭제됩니다.


그러나 iptables-save 방식으로 규칙을 설정해버리면 해당 규칙은 서버에 영구적으로 남아서 관리자가 지우기 전까지는 절대로 없어지지 않아요!


1-4. 서비스 시작 및 자동 실행

# WireGuard 서비스 시작
sudo wg-quick up wg0

# 부팅 시 자동 시작 설정
sudo systemctl enable wg-quick@wg0

# 서비스 상태 확인
sudo wg-quick down wg0 # 잠시 내렸다가
sudo wg-quick up wg0   # 다시 올려서 확인
sudo wg show

2. Wireguard 클라이언트 설정

클라이언트는 어떤 운영체제이든 상관없습니다.

이번 포스팅에서는 Linux(Ubuntu) 클라이언트 기준으로 설명하지만, 다른 OS도 설정하는 내용은 동일합니다!


1. 클라이언트 기기에 Wireguard 설치하기

각 운영체제에 맞는 Wireguard 클라이언트를 공식 홈페이지에서 설치해주세요!


2. 클라이언트용 키 생성

서버에서와 마찬가지로 클라이언트에서도 개인키와 공개키를 생성합니다.

# (클라이언트 PC에서 실행)
umask 077
wg genkey | tee client_privatekey | wg pubkey > client_publickey
  • client_privatekey: 클라이언트의 개인키 (클라이언트 설정에 사용)
  • client_publickey: 클라이언트의 공개키 (서버 설정 파일에 추가해야 함)

3. 클라이언트 설정 파일 작성 (client.conf 등)

아래 내용으로 설정 파일을 작성할게요!

[Interface]
# 클라이언트의 개인키를 여기에 붙여넣으세요.
PrivateKey = <클라이언트의 client_privatekey 내용>
# 서버에서 할당받을 VPN 내부 IP 주소
Address = 10.0.0.2/32
# DNS 설정 (선택사항이지만 권장)
DNS = 8.8.8.8

[Peer]
# 서버의 공개키를 여기에 붙여넣으세요.
PublicKey = <서버의 publickey 파일 내용>
# VPN을 통해 라우팅할 트래픽 대역. 0.0.0.0/0은 모든 트래픽을 의미합니다.
AllowedIPs = 0.0.0.0/0, ::/0
# 서버의 공인 IP 주소와 포트
Endpoint = <서버의 공인 IP 주소>:51820
# NAT 환경에서 연결 유지를 위한 설정 (25초마다 핑)
PersistentKeepalive = 25

클라이언트 설정 파일에 작성한 설정 내용들을 조금 살펴볼까요?

  • PrivateKey: 클라이언트에서 생성한 개인키입니다.
  • Address: 서버 설정 파일(wg0.conf)의 [Peer] 섹션에서 AllowedIPs에 지정한 IP와 동일해야 합니다.
  • DNS: VPN 연결 시 사용할 DNS 서버입니다. Google DNS(8.8.8.8)나 Cloudflare DNS(1.1.1.1)를 많이 사용합니다.
  • PublicKey: 서버의 공개키(sudo cat /etc/wireguard/publickey로 확인)입니다.
  • AllowedIPs: 0.0.0.0/0, ::/0으로 설정하면 클라이언트의 모든 인터넷 트래픽이 VPN 서버를 거치게 됩니다. (일반적인 VPN 사용 목적)
  • Endpoint: 접속할 서버의 실제 공인 IP 주소와 ListenPort를 입력합니다.
  • PersistentKeepalive: 클라이언트가 방화벽이나 NAT 뒤에 있을 때 연결이 끊기는 것을 방지합니다.

3. 연결 완료 및 테스트하기!

1. 서버에 클라이언트 정보 등록하기

다시 서버로 돌아와서, 2. Wireguard 클라이언트 설정에서 생성한 클라이언트의 공개키(client_publickey의 내용)를 서버 설정 파일에 추가해야 합니다.

# 서버에서 실행
sudo nano /etc/wireguard/wg0.conf
# ... (상단 생략) ...

[Peer]
# 2단계에서 생성한 클라이언트의 공개키를 여기에 붙여넣습니다.
PublicKey = PASTE_CLIENT_PUBLIC_KEY_HERE
AllowedIPs = 10.0.0.2/32

2. 설정 적용을 위해 Wireguard 재시작!

# 서버에서 실행
sudo wg-quick down wg0
sudo wg-quick up wg0

3. 클라이언트에서 VPN 연결

Windows / MacOS / 모바일 클라이언트의 경우에는 클라이언트 앱을 설치해주시고 앱을 실행해서

'터널 추가' 또는 import tunnel(s) from file 메뉴를 선택해 위에서 작성한 client.conf 파일을 불러와서 연결해주면 됩니다!

    Tag -

Loading script...