OpenVPN 원격 접속 VPN — 세우고, 길을 놓고, 잠그고, 고친다
첫 터널 — CA 부터 사내 위키까지
목표
재택 노트북(laptop)이 사내 게이트웨이(gateway)의 OpenVPN 으로 붙어, 사설망에만 있는 사내 서버(intranet)의 위키를 열게 만듭니다. CA·서버·클라이언트 인증서를 직접 만들고, 노트북이 연결된 뒤에도 사내 서버가 안 열리는 이유(포워딩·돌아오는 경로)까지 손으로 풉니다.
왜 중요한가
원격 접속 VPN 은 「인증서로 신원을 확인하고, 가상 NIC(tun)로 사내 주소를 하나 받고, 그 주소로 가는 길과 돌아오는 길을 놓는」 세 가지 일입니다. 대부분의 장애는 셋째에서 납니다 — 터널은 초록불인데 서버가 안 열립니다. AWS Client VPN 도 같은 OpenVPN 위에 서 있어서, 여기서 손으로 한 일이 그 콘솔의 「서버 인증서」·「클라이언트 CIDR」·「인가 규칙·경로 표」 칸에 그대로 대응합니다.
단계
- laptop 에서
curl -s -o /dev/null -m 5 -w '%{http_code}' http://10.10.0.10/의 결과와ip -o route get 10.10.0.10가 고른 장치를 재어/root/vpn/before.txt에intranet-from-laptop <코드>,route-dev <장치>두 줄로 적으세요(이 파일은 gateway 에 둡니다). 코드는000(연결 자체가 안 됨)이어야 합니다. - gateway 에서
make-cadir /root/ca로 작업 자리를 만들고 PKI 를 초기화한 뒤, CN 이corp-vpn-ca인 CA 를 만드세요(비밀번호 없이). 결과는/root/ca/pki/ca.crt와/root/ca/pki/private/ca.key입니다. - CN
vpn.corp.example인 서버용 인증서를 발급하고(비밀번호 없이),/etc/openvpn/server/ca.crt·/etc/openvpn/server/server.crt·/etc/openvpn/server/server.key(권한 600)로 놓으세요. - laptop 에서
/etc/openvpn/client/alice.key(권한 600)와 CNalice인 서명 요청서(CSR)를 만들고, gateway 의 CA 가 클라이언트용으로 서명해/etc/openvpn/client/alice.crt와/etc/openvpn/client/ca.crt를 laptop 에 돌려주세요. gateway 의/root/ca/pki/private/에 alice 의 키가 있으면 안 됩니다. /etc/openvpn/server/corp.conf에 서버 설정을 쓰세요: UDP 1194,dev tun,topology subnet,server 10.8.0.0 255.255.255.0, ca·cert·key,dh none, 사내망 경로를 밀어 주는push "route 10.10.0.0 255.255.255.0",keepalive 10 60./etc/corp-edge.nft의edge_in체인에서counter drop앞에udp dport 1194 accept를 넣고systemctl reload corp-edge, 그다음openvpn-server@corp를 켜고(enable --now)tun0에10.8.0.1/24가 붙은 것을 확인하세요.- laptop 의
/etc/openvpn/client/alice.conf에 클라이언트 설정을 쓰세요:client,dev tun,proto udp,remote 172.30.1.2 1194,nobind, 서버 인증서의 용도를 확인하는remote-cert-tls server, 서버 이름을 확인하는verify-x509-name vpn.corp.example name, ca·cert·key.openvpn-client@alice를 켜고(enable --now) laptop 의tun0에 10.8.0.x 가 붙고ping 10.8.0.1가 되는 것, 사내망 경로가tun0으로 들어온 것을 확인하세요. - gateway 의 IP 포워딩을 켜고
/etc/sysctl.d/아래 파일로 영구히 남기세요. intranet 의/etc/systemd/network/60-corp0.network에 VPN 대역(10.8.0.0/24)으로 돌아오는 경로 ([Route]Destination=10.8.0.0/24Gateway=10.10.0.1)를 더하고networkctl reload하세요. laptop 에서http://10.10.0.10/이 200 이면/root/vpn/after.txt에intranet-from-laptop 200을 적습니다. - laptop 에서
curl -s http://10.10.0.10/whoami의 출력(사내 서버가 본 출발지 주소)을/root/vpn/whoami.txt의 첫 줄에 적으세요. 그 주소가 laptop 의 tun0 주소와 같아야 합니다(라우팅). gateway 주소(10.10.0.1)가 나오면 어딘가에서 NAT 를 하고 있는 것입니다.
참고
- 이 터미널은 gateway(사내 게이트웨이, 바깥 주소 172.30.1.2)입니다.
ssh laptop(재택 노트북, 172.30.1.4)·ssh intranet(사내 서버, 사내망 10.10.0.10)으로 다른 기계에 들어갑니다. - 사내망 10.10.0.0/24 는 gateway 와 intranet 만 붙은 망입니다. 노트북은 VPN 말고는 들어갈 길이 없습니다.
- 서버 기록:
journalctl -u openvpn-server@corp -f, 클라이언트 기록:ssh laptop journalctl -u openvpn-client@alice -f. - 세션은 60분에 시작하고 화면의 +시간으로 늘립니다. 세션이 끝나면 VM 셋과 그 안의 키·인증서가 함께 사라집니다.
VPN 전 — 노트북에서 사내 서버는 안 보인다
laptop 에서 curl -s -o /dev/null -m 5 -w '%{http_code}' http://10.10.0.10/ 의 결과와 ip -o route get 10.10.0.10 가 고른 장치를 재어 /root/vpn/before.txt 에 intranet-from-laptop <코드>, route-dev <장치> 두 줄로 적으세요(이 파일은 gateway 에 둡니다). 코드는 000(연결 자체가 안 됨)이어야 합니다.
이 터미널은 gateway 입니다. 노트북 일은 ssh laptop '<명령>' 으로 합니다. 노트북에는 사내망(10.10.0.0/24)으로 가는 경로가 없어 기본 경로(enp1s0)로 나가 버립니다 — 그 길 끝에는 사내망이 없습니다. 이것이 VPN 이 풀어야 할 문제입니다.
easy-rsa 로 CA 를 만든다
gateway 에서 make-cadir /root/ca 로 작업 자리를 만들고 PKI 를 초기화한 뒤, CN 이 corp-vpn-ca 인 CA 를 만드세요(비밀번호 없이). 결과는 /root/ca/pki/ca.crt 와 /root/ca/pki/private/ca.key 입니다.
./easyrsa init-pki → ./easyrsa build-ca nopass. 물음에 답하지 않게 하려면 EASYRSA_BATCH=1 과 --req-cn=<이름> 을 줍니다. CA 키는 이 PKI 전체의 뿌리입니다 — 이 파일이 새면 누구나 「우리 회사 직원」 인증서를 찍어 낼 수 있습니다. 운영에서는 VPN 서버가 아닌 곳(오프라인 기계)에 둡니다.
서버 인증서 — 노트북이 「진짜 회사 서버」 를 알아보는 근거
CN vpn.corp.example 인 서버용 인증서를 발급하고(비밀번호 없이), /etc/openvpn/server/ca.crt·/etc/openvpn/server/server.crt·/etc/openvpn/server/server.key(권한 600)로 놓으세요.
./easyrsa build-server-full <CN> nopass. 서버용으로 찍으면 확장 키 용도(EKU)에 「TLS Web Server Authentication」 이 들어갑니다. 클라이언트가 remote-cert-tls server 로 바로 이것을 확인하므로, 클라이언트용 인증서를 서버에 꽂아 두면 남의 노트북이 서버 행세를 할 수 있습니다.
클라이언트 인증서 — 키는 노트북에서 태어나 노트북을 떠나지 않는다
laptop 에서 /etc/openvpn/client/alice.key(권한 600)와 CN alice 인 서명 요청서(CSR)를 만들고, gateway 의 CA 가 클라이언트용으로 서명해 /etc/openvpn/client/alice.crt 와 /etc/openvpn/client/ca.crt 를 laptop 에 돌려주세요. gateway 의 /root/ca/pki/private/ 에 alice 의 키가 있으면 안 됩니다.
laptop: openssl req -new -newkey rsa:2048 -nodes -keyout alice.key -out alice.req -subj /CN=alice. gateway: scp laptop:...alice.req /tmp/ → ./easyrsa import-req /tmp/alice.req alice → ./easyrsa sign-req client alice → scp 로 인증서를 돌려줍니다. build-client-full 로 gateway 에서 키까지 만들면 편하지만, 그러면 CA 를 다루는 사람이 모든 직원의 키를 한 번씩 손에 쥐게 됩니다.
tun 서버를 세우고 바깥 방화벽에 1194/udp 를 연다
/etc/openvpn/server/corp.conf 에 서버 설정을 쓰세요: UDP 1194, dev tun, topology subnet, server 10.8.0.0 255.255.255.0, ca·cert·key, dh none, 사내망 경로를 밀어 주는 push "route 10.10.0.0 255.255.255.0", keepalive 10 60. /etc/corp-edge.nft 의 edge_in 체인에서 counter drop 앞에 udp dport 1194 accept 를 넣고 systemctl reload corp-edge, 그다음 openvpn-server@corp 를 켜고(enable --now) tun0 에 10.8.0.1/24 가 붙은 것을 확인하세요.
우분투의 openvpn-server@<이름> 유닛은 /etc/openvpn/server/<이름>.conf 를 읽고, 접속 목록을 /run/openvpn-server/status-<이름>.log 에 씁니다. nft 는 위에서부터 첫 일치로 끝나므로 drop 뒤에 둔 accept 는 영원히 닿지 않습니다. dh none 은 DH 파라미터 파일 없이 타원곡선 키 교환(ECDHE)만 쓰겠다는 뜻입니다(OpenVPN 2.4 부터).
노트북을 붙인다 — 서버를 확인하고 들어간다
laptop 의 /etc/openvpn/client/alice.conf 에 클라이언트 설정을 쓰세요: client, dev tun, proto udp, remote 172.30.1.2 1194, nobind, 서버 인증서의 용도를 확인하는 remote-cert-tls server, 서버 이름을 확인하는 verify-x509-name vpn.corp.example name, ca·cert·key. openvpn-client@alice 를 켜고(enable --now) laptop 의 tun0 에 10.8.0.x 가 붙고 ping 10.8.0.1 가 되는 것, 사내망 경로가 tun0 으로 들어온 것을 확인하세요.
이 둘(remote-cert-tls·verify-x509-name)이 없으면 같은 CA 에서 나온 다른 직원의 인증서로 서버 행세를 하는 중간자 공격을 막지 못합니다. 붙고 나면 ip route 에 10.10.0.0/24 via 10.8.0.1 dev tun0 이 보입니다 — 서버의 push 로 받은 경로입니다. 서버 쪽 접속 목록은 /run/openvpn-server/status-corp.log.
터널은 섰는데 사내 서버가 안 열린다 — 포워딩과 돌아오는 경로
gateway 의 IP 포워딩을 켜고 /etc/sysctl.d/ 아래 파일로 영구히 남기세요. intranet 의 /etc/systemd/network/60-corp0.network 에 VPN 대역(10.8.0.0/24)으로 돌아오는 경로 ([Route] Destination=10.8.0.0/24 Gateway=10.10.0.1)를 더하고 networkctl reload 하세요. laptop 에서 http://10.10.0.10/ 이 200 이면 /root/vpn/after.txt 에 intranet-from-laptop 200 을 적습니다.
리눅스는 기본으로 라우터가 아닙니다 — 자기 주소가 아닌 패킷은 버립니다(net.ipv4.ip_forward). 포워딩을 켜면 요청은 사내 서버에 닿지만, 사내 서버는 10.8.0.x 로 답할 길을 모릅니다. 기본 경로(enp1s0)로 답을 보내 버리면 그 답은 노트북에 영영 오지 않습니다. 사내 서버에서 tcpdump -ni any port 80 을 걸어 두면 「들어오는데 나가는 곳이 엉뚱하다」 가 보입니다.
사내 서버가 본 출발지 — 라우팅인가 NAT 인가
laptop 에서 curl -s http://10.10.0.10/whoami 의 출력(사내 서버가 본 출발지 주소)을 /root/vpn/whoami.txt 의 첫 줄에 적으세요. 그 주소가 laptop 의 tun0 주소와 같아야 합니다(라우팅). gateway 주소(10.10.0.1)가 나오면 어딘가에서 NAT 를 하고 있는 것입니다.
라우팅으로 붙이면 사내 서버의 접근 기록에 직원마다 다른 VPN 주소가 남습니다 — 누가 언제 무엇을 열었는지 추적할 수 있습니다. NAT 로 붙이면 설정은 쉽지만(돌아오는 경로가 필요 없다) 모든 직원이 게이트웨이 한 주소로 보입니다. intranet 의 journalctl -u intranet-web 에도 같은 주소가 남습니다.