Proxmox web이 외부로 노출되는 것을 막고, 안전하게 내부에서만 작업할 수 있도록 VPN을 뚫어주어야 한다.

이를 위해 WireGuard를 이용했다.

처음엔 wg-easy를 사용하려 했지만, AllowedIPs를 설정함에 있어서 편의성이 부족하여 WGDashboard로 갈아탔다.

 

기본적인 가이드라인은 다음 글을 참고했다.

https://svrforum.com/svr/43830

 

wg-easy 환경 설정

wget https://raw.githubusercontent.com/WeeJeWel/wg-easy/master/docker-compose.yml

위 yaml파일을 다운 받고, 글에 나온대로 WG_HOST, PASSWORD, ... 등을 설정하고 실행했더니 

/app/lib/Server.js:311
      throw new Error('DO NOT USE PASSWORD ENVIRONMENT VARIABLE. USE PASSWORD_HASH INSTEAD.\nSee https://github.com/wg-easy/wg-easy/blob/v14/How_to_generate_an_bcrypt_hash.md');
      ^

Error: DO NOT USE PASSWORD ENVIRONMENT VARIABLE. USE PASSWORD_HASH INSTEAD.
See https://github.com/wg-easy/wg-easy/blob/v14/How_to_generate_an_bcrypt_hash.md
    at new Server (/app/lib/Server.js:311:13)
    at Object.<anonymous> (/app/services/Server.js:5:18)
    at Module._compile (node:internal/modules/cjs/loader:1730:14)
    at Object..js (node:internal/modules/cjs/loader:1895:10)
    at Module.load (node:internal/modules/cjs/loader:1465:32)
    at Function._load (node:internal/modules/cjs/loader:1282:12)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)
    at Module.require (node:internal/modules/cjs/loader:1487:12)
    at require (node:internal/modules/helpers:135:16)

위 에러가 계속 뜨면서 서버가 올라가지 않았다.

 

에러 내용은 환경 변수중에 PASSWORD대신 PASSWORD_HASH를 사용하라는 것이었고, 이 해시된 암호는 bcrypt를 사용해야한다는 의미였다.

 

그래서 아래와 같이 바꿔줬다.

version: "3.8"
services:
  wg-easy:
    environment:
      # Required:
      # Change this to your host's public address
      - WG_HOST=VPN공인IP
      - PASSWORD_HASH=Bcrypt 처리된 암호
      - WG_PORT=51820
      - WG_DEFAULT_ADDRESS=10.255.255.x
      - WG_DEFAULT_DNS=1.1.1.1
      - WG_ALLOWED_IPS=0.0.0.0/0, ::/0

      # Optional:
      # - PASSWORD=foobar123
      # - WG_PORT=51820
      # - WG_DEFAULT_ADDRESS=10.8.0.x
      # - WG_DEFAULT_DNS=1.1.1.1
      # - WG_MTU=1420
      # - WG_ALLOWED_IPS=192.168.15.0/24, 10.0.1.0/24
      # - WG_PRE_UP=echo "Pre Up" > /etc/wireguard/pre-up.txt
      # - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt
      # - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt
      # - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt
      
    image: ghcr.io/wg-easy/wg-easy
    container_name: wg-easy
    volumes:
      - .:/etc/wireguard
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1

 

그랬더니 이제는 

ERROR: Invalid interpolation format for "environment" option in service "wg-easy": "PASSWORD_HASH=Bcrypt처리된 비밀번호"

위와 같이 유효하지 않은 포맷이라고 에러를 뱉어냈다..

 

GPT에 물어보니 \$ 표시를 환경변수 이름으로 착각하여 파싱 오류가 발생한다고 한다.

해결하기 위해 GPT가 시키는대로 큰따옴표로 감싸보고 작은따옴표로 감싸보고 그랬는데도 여전히 오류가 나서 그냥 환경변수화 시켜버렸다.

 

.env파일을 docker-compose.yml과 같은 디렉토리에 위치시키고

root@WG:~# cat .env

WG_HOST=공인IP주소
PASSWORD_HASH=bcrypt 처리된 비밀번호
WG_PORT=51820
WG_DEFAULT_ADDRESS=10.255.255.x
WG_DEFAULT_DNS=1.1.1.1
WG_ALLOWED_IPS=0.0.0.0/0, ::/0

이와 같이 작성한 후,

docker-compose.yml 내용을 아래와 같이 변경했다.

root@WG:~# cat docker-compose.yml 

version: "3.8"
services:
  wg-easy:
    environment:
      # Change this to your host's public address
      - WG_HOST=${WG_HOST}
      - PASSWORD_HASH=${PASSWORD_HASH}
      - WG_PORT=${WG_PORT}
      - WG_DEFAULT_ADDRESS=${WG_DEFAULT_ADDRESS}
      - WG_DEFAULT_DNS=${WG_DEFAULT_DNS}
      - WG_ALLOWED_IPS=${WG_ALLOWED_IPS}
      
...이하 생략

이렇게 바꾸고 다시 실행해보니 정상적으로 동작했다.

 

(뒤늦게 찾아보니 \$대신 \$\$처럼 쓰면 된다고 한다 https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1720#issuecomment-1004045902)

 

Invalid interpolation format when using docker-compose · Issue #1720 · NginxProxyManager/nginx-proxy-manager

hi there os: latest version of debian 11 mariadb: latest version 10.6 direct from mariadb apt repo docker: latest version from docker.io apt repo then i executed "mysql_secure_installation" and set...

github.com

 

그리고 VPN을 이용하면 데이터 송수신이 가능하도록 Proxmox 호스트에 포트 포워딩을 통해 51820 UDP는 192.168.0.2로 보냈다.

iptables -t nat -A PREROUTING -p udp --dport 51820 -j DNAT --to-destination 192.168.0.2:51820

 

전체 VPN 통신 흐름을 그려보면 아래와 같다.

[클라이언트] --인터넷--> [Proxmox Host]
                             │
                DNAT (51820/udp to 192.168.0.2:51820)
                             ↓
                  [LXC WG 컨테이너 (192.168.0.2)]
                             ↓
              Docker (wg-easy) → 내부 wg0: 10.255.255.1

 

테스트

아래는 http://192.168.0.2(내부망주소):51821/ 을 치고 들어가 클라이언트를 하나 추가한 모습이다.

 

테스트를 위해 핸드폰에 WireGuard 어플을 깔고 QR코드를 이용해 등록했다.

그다음에 Proxmox web이 들어가지는지 확인.

LTE 환경에서 잘 접속되는 모습이다.

 

WGDashboard

wg-easy는 내 니즈를 다 받아주지 못했다.

VPN에 접속하는 유저에 따라 내부망에서 어디까지 접근할 수 있을지 제한을 두어야 하는데, wg-easy는 직접 터미널에 들어가 일일이 AllowedIPs를 수정해주어야 한다.

그래서 이를 더 간편하게 해줄 수 있는 도구를 찾다가 최신까지 업데이트가 진행중인 WGDashboard를 발견했다.

 

https://github.com/donaldzou/WGDashboard/tree/main/docker

 

WGDashboard/docker at main · donaldzou/WGDashboard

Simple dashboard for WireGuard VPN written in Python & Vue.js - donaldzou/WGDashboard

github.com

여기에 나온 docker compose 파일을 참고했다.

version: '3.8'

services:
  wgdashboard:
    image: donaldzou/wgdashboard:latest
    container_name: wgdashboard
    restart: unless-stopped
    environment:
      - tz=Asia/Seoul
      - public_ip=공인IP
      - global_dns=1.1.1.1
    ports:
      - "10086:10086"
      - "51820:51820/udp"
    volumes:
      - ./conf:/etc/wireguard
      - ./data:/data
    cap_add:
      - NET_ADMIN

 

그런후 'docker-compose up -d' 로 실행시킨 후, http://아이피주소:10086/ 로 접속하면 된다. 해당 페이지 또한 외부로 노출되면 좋지 않으니 내부망에서만 작업하는게 좋다.

초기 아이디/비밀번호는 admin/admin이다.

대시보드에서 여러 와이어가드를 설정할 수 있고,

각 피어마다 설정을 더 자세하게 할 수 있다.

Endpoint Allowed IPs에 내부에 접근 가능한 IP들을 나열하면 된다.

 

 

후기

vpn을 구축하면서 많은 네트워크 설정을 건드려 보았다...

포트포워딩, NAT, 방화벽...

계속 안되는 부분이 있어서 한참을 해맸던 것 같다. 그 과정 속에서 많은 고민을 하고, 트래픽 흐름도 생각해보고..

 

결국 원하는 결과를 얻었으니 만족한다.

 

와이어가드 참 좋은 것 같다. 속도도 빠르고, 터널링을 함에 있어 여러 OS도 지원해준다. 굳!

 

다음은 NPM(Nginx Proxy Manager)를 이용해 Reverse proxy 컨트롤을 하려 한다.