TT Lab
블로그 시작하기
배우기 러닝패스 코스

OpenVPN 원격 접속 VPN — 세우고, 길을 놓고, 잠그고, 고친다

고장 난 VPN — 증상에서 원인으로 다섯 번

TT Lab 에서 이어서 보기

목표

「어제까지 되던」 VPN 이 오늘 아침 안 됩니다. 고장은 다섯 군데에 겹쳐 있고, 하나를 고쳐야 다음 증상이 보입니다. 증상 → 가설 → 확인 → 조치를 다섯 번 돌고 보고서를 씁니다.

왜 중요한가

VPN 장애는 「안 붙는다」 와 「붙었는데 안 된다」 두 가지로만 신고됩니다. 그 둘 뒤에 방화벽·인증서·포워딩·경로·MTU 가 숨어 있고, 층을 하나씩 내려가며 확인하지 않으면 엉뚱한 곳(인증서 다시 발급, 서버 재설치)에서 시간을 씁니다. 그리고 고치는 쪽은 언제나 우리가 책임지는 자리여야 합니다 — 클라이언트의 서버 확인을 끄거나 집 공유기를 탓하는 것은 고친 것이 아닙니다.

단계

  1. laptop 의 클라이언트 기록(journalctl -u openvpn-client@alice)의 마지막 40줄을 /root/vpn/diag/client.log(gateway)에 저장하세요. TLS key negotiation failed 줄이 있어야 합니다(클라이언트가 켜진 뒤 60초가 지나야 찍힙니다).
  2. 원인을 찾아 고치세요. 고친 뒤에는 서버 기록에 laptop(172.30.1.4)의 접속 시도가 찍혀야 합니다. 바깥 방화벽의 원본은 /etc/corp-edge.nft 입니다.
  3. 원인을 찾아 고치세요. laptop 의 alice.conf 는 바꾸지 않습니다(verify-x509-name vpn.corp.example name 그대로). 고친 뒤 laptop 의 tun0 에 10.8.0.x 가 붙고 10.8.0.1 이 응답해야 합니다.
  4. 원인을 찾아 고치세요. 지금 값과 재부팅 뒤의 값 모두 net.ipv4.ip_forward = 1 이어야 합니다. 다른 강화 항목은 남겨 두세요.
  5. 원인을 찾아 고치세요. laptop 에서 http://10.10.0.10/whoami 가 laptop 의 VPN 주소를 돌려줘야 하고, 고친 것이 재부팅 뒤에도 남아야 합니다(NAT 로 덮지 말 것).
  6. laptop 에서 curl -s -o /dev/null -m 20 -w '%{size_download}' http://10.10.0.10/big 이 4194304 바이트를 다 받게 고치세요. laptop 의 isp_cpe 표(집 공유기 흉내)는 회사가 바꿀 수 없는 바깥 망이므로 지우지 않습니다 — VPN 설정 쪽에서 풀어야 합니다.
  7. /root/vpn/diag/report.txt 에 고친 순서대로 다섯 줄을 <번호>. <증상> → <원인> → <조치> 로 적으세요. 각 줄에는 그 고장의 핵심 낱말(1: 1194 또는 방화벽, 2: CN 또는 verify-x509-name, 3: ip_forward, 4: 경로 또는 Gateway, 5: MTU 또는 mssfix)이 들어 있어야 합니다.

참고

증상부터 — 노트북의 기록을 모은다

laptop 의 클라이언트 기록(journalctl -u openvpn-client@alice)의 마지막 40줄을 /root/vpn/diag/client.log(gateway)에 저장하세요. TLS key negotiation failed 줄이 있어야 합니다(클라이언트가 켜진 뒤 60초가 지나야 찍힙니다).

「TLS key negotiation failed to occur within 60 seconds」 는 60초 동안 서버에서 아무 답도 못 받았다는 뜻입니다 — 인증서를 따지기 전 단계입니다. 다음 질문은 「서버까지 패킷이 가기는 하나」 입니다. 서버 기록에 laptop(172.30.1.4)이 한 줄이라도 있는지 보세요.

고장 1 — 서버에 패킷이 닿지 않는다

원인을 찾아 고치세요. 고친 뒤에는 서버 기록에 laptop(172.30.1.4)의 접속 시도가 찍혀야 합니다. 바깥 방화벽의 원본은 /etc/corp-edge.nft 입니다.

서버는 1194 에서 듣고 있고(ss -lun), 방화벽 파일에도 1194 허용이 「있습니다」. 그런데 nft 는 체인을 위에서부터 읽어 첫 일치에서 끝납니다. nft list chain inet corp_edge edge_in 의 순서와 drop 의 counter 가 늘어나는지를 보세요. 「규칙이 있다」 와 「규칙에 닿는다」 는 다른 사실입니다. 고친 뒤에도 한동안 아무 일이 없으면 클라이언트의 재시도 간격 때문입니다 — 실패가 쌓이면 간격이 최대 5분까지 늘어나므로 laptop 의 클라이언트를 다시 켜서 확인하세요.

고장 2 — TLS 는 되는데 노트북이 서버를 거절한다

원인을 찾아 고치세요. laptop 의 alice.conf 는 바꾸지 않습니다(verify-x509-name vpn.corp.example name 그대로). 고친 뒤 laptop 의 tun0 에 10.8.0.x 가 붙고 10.8.0.1 이 응답해야 합니다.

클라이언트 기록의 VERIFY X509NAME ERROR 가 무엇을 기대했고 무엇을 받았는지 그대로 말해 줍니다. 직원 천 명의 설정 파일을 고칠 것인가, 서버 인증서 한 장을 고칠 것인가 — 클라이언트의 이름 확인을 끄는 것은 중간자 공격의 문을 여는 일이라 답이 아닙니다. openssl x509 -in /etc/openvpn/server/server.crt -noout -subject.

고장 3 — 터널은 붙었는데 사내 쪽으로 아무것도 안 간다

원인을 찾아 고치세요. 지금 값과 재부팅 뒤의 값 모두 net.ipv4.ip_forward = 1 이어야 합니다. 다른 강화 항목은 남겨 두세요.

gateway 의 tcpdump -ni corp0 에 아무것도 안 보이면 패킷이 게이트웨이에서 멈춘 것입니다. 포워딩을 켜는 파일은 있는데 값은 0 입니다 — sysctl.d 의 파일은 이름순으로 적용되어 뒤의 것이 이깁니다. systemd-analyze cat-config sysctl.d | grep ip_forward 가 적용 순서대로 보여 줍니다.

고장 4 — 요청은 닿는데 답이 안 온다

원인을 찾아 고치세요. laptop 에서 http://10.10.0.10/whoami 가 laptop 의 VPN 주소를 돌려줘야 하고, 고친 것이 재부팅 뒤에도 남아야 합니다(NAT 로 덮지 말 것).

intranet 에서 tcpdump -ni any port 80 과 ip route get 10.8.0.2 를 보세요. 경로는 「있습니다」 — 문제는 그 경로가 어디를 가리키느냐입니다. ip neigh 에 그 주소가 FAILED 로 남아 있다면 그런 장비가 사내망에 없다는 뜻입니다.

고장 5 — 작은 것은 되는데 큰 것만 멈춘다

laptop 에서 curl -s -o /dev/null -m 20 -w '%{size_download}' http://10.10.0.10/big 이 4194304 바이트를 다 받게 고치세요. laptop 의 isp_cpe 표(집 공유기 흉내)는 회사가 바꿀 수 없는 바깥 망이므로 지우지 않습니다 — VPN 설정 쪽에서 풀어야 합니다.

/whoami 는 되고 /big 만 멈추면 크기 문제입니다. 바깥 경로(eth1)의 MTU 는 1450 인데 tun 은 1500 이라, 꽉 찬 TCP 패킷을 싸면 바깥 패킷이 1450 을 넘어 IP 조각으로 나뉩니다. 그 조각을 바깥 망이 버리면 큰 응답만 사라집니다(gateway 에서 tcpdump -ni eth1 'ip[6:2] & 0x3fff != 0'). TCP 의 MSS 를 깎아 애초에 조각이 생기지 않게 하는 OpenVPN 옵션을 찾아보세요 — 값은 바깥 경로 MTU 에 맞춥니다.

장애 보고서 — 다섯 줄

/root/vpn/diag/report.txt 에 고친 순서대로 다섯 줄을 <번호>. <증상> → <원인> → <조치> 로 적으세요. 각 줄에는 그 고장의 핵심 낱말(1: 1194 또는 방화벽, 2: CN 또는 verify-x509-name, 3: ip_forward, 4: 경로 또는 Gateway, 5: MTU 또는 mssfix)이 들어 있어야 합니다.

같은 장애가 다시 나면 이 다섯 줄이 30분을 3분으로 줄입니다. 증상(무엇이 보였나)과 원인(왜)과 조치(무엇을 바꿨나)를 섞지 말고 따로 적으세요.