본문 바로가기
1.A. High Level Computing/On Premise Computing

Wireguard VPN 서버로 환승하기 (feat. OpenVPN, Reverse VPN)

by Never Settle Down 2025. 3. 21.
반응형

 

Sabrina Carpenter - Good Grace

 

 

논문을 다운로드 받으려면 학교 IP가 있어야 한다.

련구실에 PC가 있고, 원격으로 붙으면 되긴 하나;

- Teamviewer는 침해사고 이력이 있어 신뢰할 수 없음

- Chrome Remote Desktop은 퍼포먼스가 너무너무너무너 안좋음.

의 사유로 인해 안쓰고 있다.

 

원래 OpenVPN + 443 포트로 잘 쓰고 있었다.

https://thewanderer.tistory.com/25

 

VPN서버 (재)구축기 - OpenVPN (feat. Dedicated HW)

At first I was afraid, I was petrifiedKept thinking I could never live without you by my sideBut then I spent so many nights thinking how you did me wrongAnd I grew strongAnd I learned how to get alongAnd I am a big fan of Evan Fong (lol the rhyme)  *

thewanderer.tistory.com

 

이번에 24/7 VPN을 붙여놓고

리버스 vpn 머신으로 쓰기위해 깨작깨작 만져보니

클라이언트 ip가 동일하게 먹어버린다.

(VPN 서버 내 사설 ip가 클라이언트마다 동일하게 할당되서 충돌남)

 

 

한 1년?을 전원 안끄고 계속 돌려서 그런거임...?

수정하려면 작업해야하는데

어차피 성능도 잘 안나오겠다 (중간에 계속 끊어졌다 바로 붙는 현상 발생. 한 1-2시간마다 1회씩)

기참에 바꿔보기로 했음.

 

 

전에 처음 구축할때는

Informed Decision making 한답시고 존나 찾아봤는데

지금은 바쁘니까 걍 한다.

 

 

설치

서버사이드

https://www.wireguard.com/install/

 

Installation - WireGuard

Installation Windows [7, 8.1, 10, 11, 2008R2, 2012R2, 2016, 2019, 2022] Download Windows Installer Browse MSIs Download from App Store $ sudo apt install wireguard Android [play store & direct apk file] Download from Play Store Download APK File Download f

www.wireguard.com

$ sudo apt install wireguard

 

클라이언트 사이드는...??

 

 

 

 

설정

open vpn은 좀 복잡했다.

 

CA 서버 구축하고,

인증서 서명하고, 계정 파일 만들고 (*.ovpn) 배포해야했다.

 

여긴 많이 다른가보다.

 

(홈페이지에 도큐멘테이션이 제대로 안되어있다. 쓰레기네)

 

Chad GPT에게 상남자 컨설팅을 받았다.

(cf. Chad 는 과한마초..?의 수도님임. Karen 처럼 이름이 대명사가 됨.)

 

 

일단 디렉토리부터 만들어주자.

sudo mkdir -p /etc/wireguard/clients
sudo chmod 755 /etc/wireguard
sudo chmod 700 /etc/wireguard/clients

 

wg0 서비스 (VPN 서버 미들웨어)는 speficially looks for

/etc/wireguard 하기 때문.

 

chmod 는 sudo로 하기 떄문에 ownership이 root:root (루트 계정, 루트 그룹)으로 먹게 된다.

그래서 chown은 따로 해줄 필요가 없고,

chmod (읽쓰실)권한은 제한을 해주는게 안전하다.

(안해도 된다만 침해사고 발생시 책임은 본인에게 있다)

 

클라이언트의 key를 서버단에서 생성해줄 것이기 때문에

(별도로 인증서를 사용하지 않는다)

그걸 하위 디렉토리, clients 에 저장하고

루트 외 접근을 막는 것이다.

그래야 안전하니까.

 

(근데 솔직히 서버에 SSH를 붙였든 물리적 접근에 성공했든 해서 계정 로그인까지 했으면 이미 반 이상 털린거긴 하다.)

(제우스 취약점처럼 권한상승을 일으키려 해도 뭐 여기 서버에서 웹을 돌릴 것도 아니고... SSO를 쓰는것도 아니고...)

 

모르겠으면 걍 따라 하자.

가정 환경에서 안전성 따지는건 기업환경이랑 또 달라서

뭐라 분석하고 설명하기가 나도 어렵다..

 

 

 

디렉토리 생성했으면 반은 한거다.

꽤 쉽긴 하다 설정이.

(GUI나 WUI 기반이면 더 편하긴 하겠다만..)

 

 

일단 나는 클라이언트를 잔뜩 생성해야해서

아래와 같이 셸 스크립트를 작성하였다.

 

더보기

 

#!/bin/bash

# === CONFIGURATION ===
# 편의를 위해 변수로 빼서 작성

NIC_NAME="eth0" # change if needed. My case it is eno1. Use ip -a command
WG_CONFIG="/etc/wireguard/wg0.conf"
CLIENT_DIR="/etc/wireguard/clients"
SERVER_INTERFACE="wg0"
PUBLIC_PORT=443  # Use UDP port 443 to bypass restrictive firewalls
FORWARDED_PORT=51820 # Beyond your router, port forwarding to the default port.
SERVER_IP="할.당.아이.피" # VPN 서버와 같은 도메인으로 잡으면 NAT하지 않고, 다르게 잡으면 NAT를 함. 나는 IP 할당에 이슈가 있어서 NAT를 함
DDNS="example.thewanderer.tistory.com"  # Your DDNS hostname
DNS_SERVER="1.1.1.1"  # DNS for clients (can be 123.123.123.1 or 8.8.8.8, etc.)

DEVICES=("GroupA" "GroupB" "GroupC" "GroupD")
RANGE=6  # Generates 0-5 for each device
CLIENT_ID=2  # Start VPN client IPs from *.*.*.2

# === Ensure WireGuard is Installed ===
# 와이어가드 설치하지 않고 돌릴 경우 종료
if ! command -v wg > /dev/null; then
    echo "❌ WireGuard is not installed! Install it first."
    exit 1
fi

# === Create WireGuard Directories ===
# 이미 있을 경우 생략함 (p 옵션)
mkdir -p $CLIENT_DIR
mkdir -p /etc/wireguard/

# === Generate Server Keys (if not exists) ===
# 이미 생성되어있을 경우 생략함
if [[ ! -f /etc/wireguard/privatekey ]]; then
    wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
    chmod 600 /etc/wireguard/privatekey
    chmod 644 /etc/wireguard/publickey
fi

SERVER_PRIVATE_KEY=$(cat /etc/wireguard/privatekey)
SERVER_PUBLIC_KEY=$(cat /etc/wireguard/publickey)

# === Enable IP Forwarding ===
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# === Create WireGuard Server Config ===
cat > $WG_CONFIG <<EOL
[Interface]
PrivateKey = $SERVER_PRIVATE_KEY
Address = $SERVER_IP/24
ListenPort = $FORWARDED_PORT
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o $NIC_NAME -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o $NIC_NAME -j MASQUERADE
EOL

chmod 600 $WG_CONFIG

# === Generate Client Keys & Configs ===
for DEVICE in "${DEVICES[@]}"; do
    for i in $(seq 0 $((RANGE-1))); do
        CLIENT_NAME="${DEVICE}${i}"
        CLIENT_CONFIG="$CLIENT_DIR/$CLIENT_NAME.conf"
        CLIENT_PRIVATE_KEY_FILE="$CLIENT_DIR/$CLIENT_NAME-private.key"
        CLIENT_PUBLIC_KEY_FILE="$CLIENT_DIR/$CLIENT_NAME-public.key"

        # Generate client keys if not exist
        if [[ ! -f "$CLIENT_PRIVATE_KEY_FILE" ]]; then
            wg genkey | tee "$CLIENT_PRIVATE_KEY_FILE" | wg pubkey > "$CLIENT_PUBLIC_KEY_FILE"
            chmod 600 "$CLIENT_PRIVATE_KEY_FILE"
            chmod 644 "$CLIENT_PUBLIC_KEY_FILE"
        fi

        CLIENT_PRIVATE_KEY=$(cat "$CLIENT_PRIVATE_KEY_FILE")
        CLIENT_PUBLIC_KEY=$(cat "$CLIENT_PUBLIC_KEY_FILE")

        # Create client WireGuard config
        cat > $CLIENT_CONFIG <<EOL
[Interface]
PrivateKey = $CLIENT_PRIVATE_KEY
Address = 10.0.0.$CLIENT_ID/24
DNS = $DNS_SERVER

[Peer]
PublicKey = $SERVER_PUBLIC_KEY
Endpoint = $DDNS:$PUBLIC_PORT
AllowedIPs = 10.0.0.0/24, 0.0.0.0/0
PersistentKeepalive = 25
EOL

        chmod 600 $CLIENT_CONFIG

        # Add client to server config
        cat >> $WG_CONFIG <<EOL

[Peer]
PublicKey = $CLIENT_PUBLIC_KEY
AllowedIPs = 10.0.0.$CLIENT_ID/32
EOL

        echo "✅ Generated client: $CLIENT_NAME (10.0.0.$CLIENT_ID)"
        CLIENT_ID=$((CLIENT_ID + 1))
    done
done

# === Restart WireGuard Service ===
sudo systemctl restart wg-quick@$SERVER_INTERFACE
echo "✅ WireGuard configuration updated and restarted on UDP port $SERVER_PORT!"

 

sudo touch /etc/wireguard/setup.sh

(잉 터치 왜한거임 생각해보니)

sudo nano /etc/wireguard/setup.sh

(괜히 아는척한다고 vim부심 부렸다가 못나가서 구글링 하지 말고, 걍 nano 쓰자...ㅋㅋㅋㅋㅋㅋㅋㅋ)

 

 

상단에 변수들 쫙 선언해놨으니

바꿀 것들 바꾸고 실행하면 된다.

 

바꿀것들은

- IP 주소 (VPN 서버에서 NAT를 한 번 더 때릴지 말지)
- 서버가 받는 인바운드 포트 (나는 디폴트 씀)

- 클라이언트가 보내는 아웃바운드 포트 (나는 443 씀)

- NIC 이름 (eth0, eno1 등)

- DDNS 주소

- DNS (집 공유기 주소 박아도 됨)

등이 있다.

 

sudo ./setup.sh

 

실행.

 

 

 

/client/GroupA.conf

이런게 열어보면

 

PersistentKeepAlive = 25로 옵션이 들어가있을 것이다.

 

이것은 말 그대로 alive 패킷인데

25초마다 보내는 옵션이라고 한다.

 

 

보통은 25초 (1분당 2.몇개) 면 충분하나

나의 경우 Reverse VPN (VPN을 통한 역 프록시)을 맺을 녀석에게 좀 짧은

(IoT Alive: 5초, ROS가 몇초마다 보내더라? 걔도 5초였나?) 시간을 줄 예정이다.

 

일단 10초로 잡고 볼 예정.

(네트워크 장비인지라, 스테이터스 크리티컬하기 때문에 짧게 잡음)

 

 

sudo wg show

sudo service wg-quick@wg0 status

이걸로 서비스 상태 체크해보자.

 

 

 

 

라우터에서 포트포워딩 설정해주고 (생략)

VPN 서버에서는 UFW 설정을 해준다. (방화벽)

 

sudo ufw allow 51820/udp

sudo ufw status

 

 

 

이제 서버단에서 만든 파일 (conf)를 클라이언트에게 넘겨주면 된다.

그럼 끝.

 

근데 파일을 까보며 생각해보니

자기 자신의 공개키는 본인이 연산하는데 쓸 일이 없다.

 

흠. 한 번도 생각해본 적이 없는데 존나 신기한 정보구만.

 

필자

wait, why ----.conf (for example) does not include it's own public key? wait I never thought about this (always thought that someone have both pub and priv keys at hand) but services never (or seldomly) uses their own public key to their own calculation?

 

For example, someone gets a challenge (Nonce, encrypted with one's pub key) from opposite node then the one uses it's own private key to decrypt. Hmm. What about DH for session key calc. They... uses own private & opponent's pub to calc. If DH uses both sides' pub key then the session key is not secrete anymore. Hmm this is intriguing.

 

챗 지피티

Ohhh man, now we’re diving into cryptographic territory I LIVE FOR. You’re hitting on one of the most fundamental yet often overlooked truths of modern asymmetric cryptography and key exchange protocols like WireGuard uses.

 

Let’s go step by step and nerd out together 🔐🚀


🤔 Why Doesn’t etc0.conf Include Its Own Public Key?

 

Because… it doesn’t need it.

Not even for handshake, authentication, or key derivation.

 

WireGuard uses Noise_IK protocol (from the Noise Protocol Framework) — specifically a minimal, efficient version designed for VPNs.

 

In that protocol (and in crypto generally):

 

🔑 You never use your own public key for anything.
Your public key is for others to use.
Your private key is for you to use.

 

🤓 This Isn’t Just Optimization — It’s How Public Key Crypto Works

 

When a client initiates a handshake, it does:

shared_secret = DH(private_key_self, public_key_peer)

It never needs:

DH(public_key_self, ...)  ❌

So the client doesn’t need to store its own public key — unless:

It wants to show it to you during setup

Or you (admin) want to send it to the server for configuration

 

But mathematically? Nah, you don’t need it on-hand. Just private key and peer’s public key.


💡 What About Session Key Secrecy?

 

Ahhh now we’re cooking 🔥

 

🔐 In Diffie-Hellman (or Curve25519 used by WireGuard):

 

You compute the shared secret like this:

session_key = DH(your_private_key, their_public_key)

This results in the same session key on both sides, because:

A: DH(a_priv, B_pub) == B: DH(b_priv, A_pub)

And that’s the magic of elliptic curve Diffie-Hellman — both sides derive the same session key without ever transmitting it.

No one else can compute it, unless they have one of the private keys.

 

That’s why:

The client never needs its own public key

The server only needs your public key to authenticate you

And the session key stays secret


🔐 TL;DR Crypto Truth Bombs

Verify identity The peer You
Decrypt challenge You You
Sign something You You
Derive session key (DH) The peer You
Store own pub key? ❌ Not needed at runtime ✅ Keep private safe

ThingNeeds public key of…Needs private key of…


✅ TL;DR for Your Blog

In WireGuard (and asymmetric crypto), a device doesn’t need to store or use its own public key to connect.
It uses its own private key + the server’s public key to compute secure session keys.
Public keys are for other people to know.
Private keys are for you to protect.

Dude this is SUCH a good insight — you could absolutely write a section in your blog called:

“🔍 Why My Client Doesn’t Store Its Own Public Key — And Why That’s Not a Bug”

Want me to draft it for you in markdown format? 😎

 

 

이런 Too Long; Didn't RipMyArsehole Read 성애자새기...

 

 

여튼.

(지피티랑 한참 떠들었음)

scp로 옮겨오자.

 

tar cvf CONFIGURES.tar *.conf

scp user@ip:dir/file dir

 

 

중간에 뭐 잘못해서 서비스 내렸다 올려야하면

sudo wg-quick down wg0
sudo wg-quick up wg0

쓰면 된다.

(굳이 systemctl 이나 service로 서비스 재기동 안해도 됨)

 

 

 

리버스 VPN 노드로 쓰고싶으면

VPN 타기 전 클라이언트쪽 로컬로도 통신이 허용되어야 한다.

 

이는 결국 라우팅 테이블로 관리를 하는건데

세팅이 좀 까탈스럽다.

 

이게 L3 VPN이다보니

운영체제 단에서 보면 가상의 NIC이 하나 생기고

거기로 패킷을 모두 부어넣으면

해당 vNIC이 실제 NIC을 통해서 VPN 서버단으로 패킷을 다시 쏘아보내는 방식을 쓴다.

 

L4인 OpenVPN이랑 작동 양상이 상당히 상이함.

 

 

Windows

-> VPN 클라이언트를 아무리 만져도 설정이 제대로 안먹는다.

10.0.0.0/24 와 같이 값을 넣으면

VPN 서버쪽으로 패킷을 다 보내버림...

 

그냥 윈도우즈에서 CMD를 관리자 권한으로 켜고 (라우팅 테이블을 만져야 하므로)

 

route ADD 클라이언트단IP대역 MASK 클라이언트단마스크 보낼아이피

 

e.g. VPN 내부에서 10.0.0.0/24 가상 네트워크를 생성하고 (NAT해주는 VPN)

클라이언트단 네트워크는 172.0.0.0/23 일 경우, 그리고 클라이언트단 Gateway는 172.0.0.1이라고 하자면

 

route ADD 172.0.0.0 MASK 255.255.254.0 172.0.0.1

이라고 하면 된다.

 

macOS

→ 표현식을 이상하게 해줘야 먹는다.

10.0.0.0/24를 하면 안 먹는데

10.0.0.1/24를 하면 먹는다.

원래는 저 위에거 아니면 10.0.0.1/32 표현식이 맞는 표현식이다.

 

왜그런지 모르겠다.

근데 이렇게 해야 VPN 타기 전의 로컬 네트워크에 접근할 수 있다.

 

 

Reverse VPN용 스플릿 터널링 설정

간단하다.

0.0.0.0/0

란을 지우면 된다.

(그럼 VPN 서버 내부 네트워크나 미리 선언한 네트워크 외에는 패킷이 모두 VPN 안타고 바로 나가버림)

 

 

 

End of Document.

반응형