OpenWrt 라우터간 strongwan을 이용해 site to site vpn연결하는 설정입니다. 먼저 간단하게 사전공유키(preshared key) 를 이용한 연결과 뒤이어 인증서(certificate) 생성해 연결하는 방법을 소개합니다.
참고로 strongswan 설정은 /etc/ipsec.conf 화일을 이용하는 방법과 /etc/swanctl/swanctl.conf 를 이용하는 방법이 있는데 지금 소개하는 방법은 예전방식 /etc/ipsec.conf를 이용하는 방법입니다.(둘의 차이는 설정화일 형식만 다를뿐 다른 차이는 없습니다.)
사이트정보
- routerAAA
- external IP : 57.57.57.57
- internal IP : 10.10.10.0/24
- hostname(ddns) : aaa.duckdns.org
- routerBBB
- external IP : 61.61.61.61
- internal IP : 192.168.1.0/24
- hostname(ddns) : bbb.duckdns.org
공통 준비
routerAAA, routerBBB 공통으로 설치 및 설정합니다.(기존에 roadwarrior 연결 설정으로 strongswan을 설치했다면 다음으로 건너뛰세요.)
사전준비
strongswan 패키지 및 dnsmasq-full 설치
opkg update
opkg install curl strongswan-default strongswan-pki ipset strongswan-mod-openssl strongswan-mod-curl strongswan-mod-dhcp strongswan-mod-eap-tls strongswan-mod-eap-identity strongswan-mod-kernel-libipsec kmod-tun openssl-util strongswan-mod-test-vectors strongswan-mod-farpopkg install dnsmasq-full --download-only && opkg remove dnsmasq && opkg install dnsmasq-full --cache . && rm *.ipk
network 인터페이스 정의
ipsec 인터페이스 정의를 위해 /etc/config/network에 다음을 추가합니다.
... config interface 'ipsec0' option ifname 'ipsec0' option proto 'none' option defaultroute '0' option peerdns '0'
방화벽 규칙 추가
site to site 연결이므로 별도의 ipsec0 인터페이스를 vpn1 zone에 할당하고, 각각의 subnet만 ipsec0 인터페이스를 통과하도록 설정합니다.
또한 아래와 같이 연결 허용 포트를 개방합니다.
... config zone option name 'vpn1' option input 'ACCEPT' option output 'ACCEPT' option forward 'REJECT' option mtu_fix '1' list network 'ipsec0' list subnet '10.10.10.0/24' list subnet '192.168.1.0/24' config forwarding option src 'lan' option dest 'vpn1' config forwarding option src 'vpn1' option dest 'lan'
... config rule option name 'Allow-IPSec-ESP' option src 'wan' option proto 'esp' option target 'ACCEPT' config rule option name 'Allow-ISAKMP' option src 'wan' option dest_port '500' option proto 'udp' option target 'ACCEPT' config rule option name 'IPSec-NAT-Traversal' option src 'wan' option proto 'udp' option dest_port '4500' option target 'ACCEPT' config rule option name 'IPSec-Auth-Header' option src 'wan' option proto 'ah' option target 'ACCEPT'
/etc/firewall.user 에 추가합니다.
iptables -I INPUT -m policy --dir in --pol ipsec --proto esp -j ACCEPT iptables -I FORWARD -m policy --dir in --pol ipsec --proto esp -j ACCEPT iptables -I FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT iptables -I OUTPUT -m policy --dir out --pol ipsec --proto esp -j ACCEPT iptables -t nat -I POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT
Preshared key tunnel
routerAAA 설정
routerAAA, routerBBB에 사용될 사전 공유키(preshared key)를 준비합니다. 원하는 텍스트 문자로 지정할 수 있고 아래와 같이 openssl 명령으로 랜덤으로 생성할 수 있습니다.
openssl rand -base64 32
[email protected]:~# openssl rand -base64 32 yvXQ+uxaFAN4efHqWLy5WwFvrOJEEKJ0KHUTvx0Hq5U=
생성된 preshared key를 /etc/ipsec.secret 에 넣어줍니다.
형식은 id : PSK "preshared key" 이며 id는 ip, FQDN, [email protected], %any 가 될 수 있습니다. 여기서는 routerBBB의 FQDN을 넣었습니다.
# /etc/ipsec.secrets - strongSwan IPsec secrets file bbb.duckdns.org : PSK yvXQ+uxaFAN4efHqWLy5WwFvrOJEEKJ0KHUTvx0Hq5U= include /var/ipsec/ipsec.secrets
/etc/ipsec.conf 내용입니다.
[email protected]:~# cat /etc/ipsec.conf # ipsec.conf - strongSwan IPsec configuration file # basic configuration config setup # charondebug="ike 1, knl 1, cfg 0" strictcrlpolicy=no uniqueid=no # Add connections here. conn %default compress=no type=tunnel keyexchange=ikev2 fragmentation=yes forceencaps=no mobike=yes dpdaction=clear dpddelay=300s rekey=no conn s2s-psk auto=add authby=secret left=%any [email protected] leftsubnet=10.10.10.0/24 leftfirewall=yes right=bbb.duckdns.org [email protected] rightsubnet=192.168.1.0/24
routerBBB 설정
routeAAA에서 생성한 preshared key를 /etc/ipsec.secret에 동일하게 넣어줍니다. id는 routerAAA를 지정합니다.
[email protected]:~# cat /etc/ipsec.secrets # /etc/ipsec.secrets - strongSwan IPsec secrets file aaa.duckdns.org : PSK yvXQ+uxaFAN4efHqWLy5WwFvrOJEEKJ0KHUTvx0Hq5U= include /var/ipsec/ipsec.secrets
routeBBB의 /etc/ipsec.conf 내용입니다.
[email protected]:~# cat /etc/ipsec.conf # ipsec.conf - strongSwan IPsec configuration file # basic configuration config setup # charondebug="ike 1, knl 1, cfg 0" strictcrlpolicy=no uniqueid=no # Add connections here. conn %default compress=no type=tunnel keyexchange=ikev2 fragmentation=yes forceencaps=no mobike=yes dpdaction=clear dpddelay=300s rekey=no conn s2s-psk authby=secret left=%any [email protected] leftsubnet=192.168.1.0/24 leftfirewall=yes right=aaa.duckdns.org [email protected] rightsubnet=10.10.10.0/24
서비스 재시작
routerAAA, routerBBB에서 각각 위에서 수정한것일 반연되도록 network, firewall, ipsec을 재시작 합니다.
/etc/init.d/network restart
/etc/init.d/firewall restart
/etc/init.d/ipsec restart
vpn 연결 및 확인
routerA, 또는 routerB에서 ipsec 연결 및 확인을 합니다.
ipsec up s2s-psk
ipsec status
ping 192.168.1.1
[email protected]:~# ipsec up s2s-psk initiating IKE_SA s2s-psk[3] to 61.61.61.61 generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ] sending packet: from 57.57.57.57[500] to 61.61.61.61[500] (296 bytes) received packet: from 61.61.61.61[500] to 57.57.57.57[500] (329 bytes) parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) CERTREQ N(FRAG_SUP) N(HASH_ALG) N(CHDLESS_SUP) N(MULT_AUTH) ] selected proposal: IKE:AES_GCM_16_256/PRF_HMAC_SHA2_384/ECP_384 remote host is behind NAT received 1 cert requests for an unknown ca sending cert request for "C=KR, O=qquack, CN=qquack Root CA" authentication of 'aaa.duckdns.org' (myself) with pre-shared key establishing CHILD_SA s2s-psk{1} generating IKE_AUTH request 1 [ IDi N(INIT_CONTACT) CERTREQ IDr AUTH SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_6_ADDR) N(MULT_AUTH) N(EAP_ONLY) N(MSG_ID_SYN_SUP) ] sending packet: from 57.57.57.57[4500] to 61.61.61.61[4500] (362 bytes) received packet: from 61.61.61.61[4500] to 57.57.57.57[4500] (255 bytes) parsed IKE_AUTH response 1 [ IDr AUTH SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) ] authentication of 'bbb.duckdns.org' with pre-shared key successful IKE_SA s2s-psk[3] established between 57.57.57.57[aaa.duckdns.org]...61.61.61.61[bbb.duckdns.org] selected proposal: ESP:AES_GCM_16_256/NO_EXT_SEQ CHILD_SA s2s-psk{1} established with SPIs 229889cf_i 9eb3bb18_o and TS 10.10.10.0/24 === 192.168.1.0/24 connection 's2s-psk' established successfully [email protected]:~# [email protected]:~# [email protected]:~# ping 192.168.1.1 PING 192.168.1.1 (192.168.1.1): 56 data bytes 64 bytes from 192.168.1.1: seq=0 ttl=64 time=9.895 ms 64 bytes from 192.168.1.1: seq=1 ttl=64 time=12.800 ms 64 bytes from 192.168.1.1: seq=2 ttl=64 time=11.828 ms 64 bytes from 192.168.1.1: seq=3 ttl=64 time=9.953 ms ^C --- 192.168.1.1 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 9.895/11.119/12.800 ms [email protected]:~#
Certificates tunnel
인증서 생성
인증서 생성작업은 routerAAA에서 진행하며, 인증서 생성을 위한 쉘스크립트를 /etc/ipsec.d 에 만듭니다.
(지난번에는 인증서 확장자가 pem(텍스트화일)이 아닌 der(2진화일)였는데 인증서 확장자가 pem이든 der든 상관없습니다. 2진화일이 기계한테는 빠를지 몰라도 이용자에게는 텍스트가 사용하기 편해서 pem으로 바꿨습니다.)
- mk_rsa_ca.sh : Root CA 인증서 생성 스크립트(한번만 실행하세요. 다시 실행하면 기존에 생성한 서버/사용자 인증서가 모두 무력화 됩니다.)
- mk_rsa_server.sh : 서버 인증서 생성 스크립트
- mk_rsa_user.sh : 사용자 인증서 생성 스크립트
- clean.sh : /etc/ipsec.d/ 하위 디렉토리 모든 화일을 삭제(모든 인증서 생성을 다시 하고 싶을 때 모든 인증서 삭제)
cd /etc/ipsec.d
vi mk_rsa_ca.sh
#!/bin/sh COUNTRY="KR" ORG="qquack" VALIDDAYS="3652" ipsec pki --gen --type rsa --size 4096 --outform pem > private/cakey.pem chmod 600 private/cakey.pem ipsec pki --self --ca --lifetime $VALIDDAYS --in private/cakey.pem --type rsa --dn "C=$COUNTRY, O=$ORG, CN=$ORG Root CA" --outform pem > cacerts/cacert.pem ipsec pki --print --in cacerts/cacert.pem
vi mk_rsa_server.sh
#!/bin/sh SRVNAME="aaa.duckdns.org" #Uncomment only if vpn server is behind a static IP #IPADDR=$(. /lib/functions/network.sh; network_get_ipaddr ip wan; echo $ip) #IPADDR="aaa.duckdns.org" COUNTRY="KR" ORG="qquack" LIFETIME="3652" ipsec pki --gen --type rsa --size 4096 --outform pem > private/hostkey.pem chmod 600 private/hostkey.pem ipsec pki --pub --in private/hostkey.pem --type rsa | ipsec pki --issue --lifetime $LIFETIME --cacert cacerts/cacert.pem --cakey private/cakey.pem --dn "C=$COUNTRY, O=$ORG, CN=$SRVNAME" --san $SRVNAME --flag serverAuth --flag ikeIntermediate --outform pem > certs/hostcert.pem ipsec pki --print --in certs/hostcert.pem
vi mk_rsa_user.sh
#!/bin/sh USERID="[email protected]" NAME="user1" #echo "Enter userid (no spaces or special characters):" #read USERID # #[ -z "$USERID" ] && { # echo Empty user ID. Cancelling. # exit 0 #} # #echo "Enter fullname:" #read NAME # #[ -z "$NAME" ] && { # echo Empty full name. Cancelling. # exit 0 #} COUNTRY="KR" ORG="qquack" LIFETIME="1826" mkdir -p /etc/ipsec.d/p12 2> /dev/null ipsec pki --gen --type rsa --size 4096 --outform pem > private/$USERID.pem chmod 600 private/$USERID.pem ipsec pki --pub --in private/$USERID.pem --type rsa | ipsec pki --issue --lifetime $LIFETIME --cacert cacerts/cacert.pem --cakey private/cakey.pem --dn "C=$COUNTRY, O=$ORG, CN=$USERID" --san "$USERID" --outform pem > certs/$USERID.pem ipsec pki --print --in certs/$USERID.pem openssl pkcs12 -export -inkey private/$USERID.pem -in certs/$USERID.pem -name "$NAME's VPN Certificate" -certfile cacerts/cacert.pem -caname "$ORG Root CA" -out p12/$USERID.p12
vi clean.sh
#!/bin/sh rm /etc/ipsec.d/*/*.* 2> /dev/null
각각의 스크립트에 실행 권한을 주고 ca, server, user 인증서를 생성합니다.
chmod 700 *.sh | chmod 600 clean.sh
./mk_rsa_ca.sh
./mk_rsa_server.sh
./mk_rsa_user.sh
인증서 복사
routerAAA, routeBBB에 쓰일 인증서는 모두 routerAAA에서 생성되었기 때문에 scp를 이용해 routerBBB로 전송할 예정입니다.
먼저 routerBBB에 ssh로 로그인하여 패스워드(sshkey가 아닌 /etc/passwd)로 ssh를 통한 shell 접속이 가능하도록 /etc/config/dropbear 설정을 아래와 같이 uci 명령으로 바꿉니다.
uci set [email protected][-1].RootPasswordAuth="on"
uci set [email protected][-1].PasswordAuth="on"
uci commit dropbear
/etc/init.d/dropbear restart
routerAAA에서 scp를 이용해 routerBBB로 인증서 화일을 전송합니다. scp 명령 형식은 scp -P <port> file [email protected]:path 이며 아래 예에서 routerBBB의 ssh 서비스 포트는 2222 입니다. 포트번호는 확인하세요.
scp -P 2222 cacerts/cacrt.pem [email protected]:/etc/ipsec.d/cacerts/
scp -P 2222 certs/[email protected] [email protected]:/etc/ipsec.d/certs/
scp -P 2222 private/[email protected] [email protected]:/etc/ipsec.d/private/
인증서 전송이 완료되면 routerBBB의 ssh 로그인이 패스워드를 통한 접속이 불가하도록 원상복구 합니다.
uci set [email protected][-1].RootPasswordAuth="off"
uci set [email protected][-1].PasswordAuth="off"
uci commit dropbear
/etc/init.d/dropbear restart
routerAAA 설정
/etc/ipsec.secret 화일에 routerAAA의 private 인증서 화일의 위치를 넣어줍니다. 형식은 : RSA : privatekey
vi /etc/ipsec.secret
# /etc/ipsec.secrets - strongSwan IPsec secrets file : RSA hostkey.pem bbb.duckdns.org : PSK yvXQ+uxaFAN4efHqWLy5WwFvrOJEEKJ0KHUTvx0Hq5U= include /var/ipsec/ipsec.secrets
vi /etc/ipsec.conf
... conn s2s-cert auto=add left=%any leftid=%any leftsubnet=10.10.10.0/24 leftcert=hostcert.pem leftfirewall=yes right=bbb.duckdns.org rightid="C=KR, O=qquack, [email protected]" rightsubnet=192.168.1.0/24
routerBBB 설정
vi /etc/ipsec.secret
# /etc/ipsec.secrets - strongSwan IPsec secrets file : RSA [email protected] aaa.duckdns.org : PSK yvXQ+uxaFAN4efHqWLy5WwFvrOJEEKJ0KHUTvx0Hq5U= include /var/ipsec/ipsec.secrets
vi /etc/ipsec.conf
... conn s2s-cert auto=add left=%any leftid=%any leftsubnet=192.168.1.0/24 [email protected] leftfirewall=yes right=aaa.duckdns.org rightid="C=KR, O=qquack, CN=aaa.duckdns.org" rightsubnet=10.10.10.0/24
서비스 재시작
routerAAA, routerBBB에서 각각 ipsec을 재시작 합니다.
/etc/init.d/ipsec restart
vpn 연결 및 확인
routerAAA 또는 routerBBB에서 ipsec 연결 및 확인을 합니다.
그리고 각각의 로컬 네트워크 하부의 PC에 ping을 날려보세요.(윈도우 방화벽 잠시 꺼두세요)
상대 네트워크 하부 PC에 핑이 날라가면 성공입니다.
ipsec up s2s-cert
ipsec status
ping 10.10.10.1
ping 10.10.10.191
[email protected]:/etc# uci show network.lan.ipaddr network.lan.ipaddr='192.168.1.1' [email protected]:/etc# [email protected]:/etc# ipsec up s2s-cert initiating IKE_SA s2s-cert[3] to 57.57.57.57 generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ] sending packet: from 61.61.61.61[500] to 57.57.57.57[500] (732 bytes) received packet: from 57.57.57.57[500] to 61.61.61.61[500] (305 bytes) parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) CERTREQ N(FRAG_SUP) N(HASH_ALG) N(CHDLESS_SUP) N(MULT_AUTH) ] selected proposal: IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256 remote host is behind NAT received cert request for "C=KR, O=qquack, CN=qquack Root CA" sending cert request for "C=KR, O=qquack, CN=qquack Root CA" authentication of 'C=KR, O=qquack, [email protected]' (myself) with RSA_EMSA_PKCS1_SHA2_384 successful sending end entity cert "C=KR, O=qquack, [email protected]" establishing CHILD_SA s2s-cert{3} generating IKE_AUTH request 1 [ IDi CERT N(INIT_CONTACT) CERTREQ IDr AUTH SA TSi TSr N(MULT_AUTH) N(EAP_ONLY) N(MSG_ID_SYN_SUP) ] splitting IKE message (2320 bytes) into 2 fragments generating IKE_AUTH request 1 [ EF(1/2) ] generating IKE_AUTH request 1 [ EF(2/2) ] sending packet: from 61.61.61.61[4500] to 57.57.57.57[4500] (1236 bytes) sending packet: from 61.61.61.61[4500] to 57.57.57.57[4500] (1156 bytes) received packet: from 57.57.57.57[4500] to 61.61.61.61[4500] (1236 bytes) parsed IKE_AUTH response 1 [ EF(1/2) ] received fragment #1 of 2, waiting for complete IKE message received packet: from 57.57.57.57[4500] to 61.61.61.61[4500] (980 bytes) parsed IKE_AUTH response 1 [ EF(2/2) ] received fragment #2 of 2, reassembled fragmented IKE message (2144 bytes) parsed IKE_AUTH response 1 [ IDr CERT AUTH SA TSi TSr ] received end entity cert "C=KR, O=qquack, CN=aaa.duckdns.org" using certificate "C=KR, O=qquack, CN=aaa.duckdns.org" using trusted ca certificate "C=KR, O=qquack, CN=qquack Root CA" checking certificate status of "C=KR, O=qquack, CN=aaa.duckdns.org" certificate status is not available reached self-signed root ca with a path length of 0 authentication of 'C=KR, O=qquack, CN=aaa.duckdns.org' with RSA_EMSA_PKCS1_SHA2_384 successful IKE_SA s2s-cert[3] established between 61.61.61.61[C=KR, O=qquack, [email protected]]...57.57.57.57[C=KR, O=qquack, CN=aaa.duckdns.org] selected proposal: ESP:AES_CBC_128/HMAC_SHA2_256_128/NO_EXT_SEQ CHILD_SA s2s-cert{3} established with SPIs fc87343c_i e450cb5d_o and TS 192.168.1.0/24 === 10.1.1.0/24 connection 's2s-cert' established successfully [email protected]:/etc# [email protected]:/etc# [email protected]:/etc# [email protected]:/etc# ipsec status Security Associations (1 up, 0 connecting): s2s-cert[3]: ESTABLISHED 15 seconds ago, 61.61.61.61[C=KR, O=qquack, [email protected]]...57.57.57.57[C=KR, O=qquack, CN=aaa.duckdns.org] s2s-cert{3}: INSTALLED, TUNNEL, reqid 3, ESP in UDP SPIs: fc87343c_i e450cb5d_o s2s-cert{3}: 192.168.1.0/24 === 10.1.1.0/24 [email protected]:/etc# [email protected]:/etc# [email protected]:/etc# ping 10.10.10.1 PING 10.10.10.1 (10.10.10.1): 56 data bytes 64 bytes from 10.10.10.1: seq=0 ttl=64 time=11.241 ms 64 bytes from 10.10.10.1: seq=1 ttl=64 time=9.976 ms 64 bytes from 10.10.10.1: seq=2 ttl=64 time=12.410 ms 64 bytes from 10.10.10.1: seq=3 ttl=64 time=11.503 ms 64 bytes from 10.10.10.1: seq=4 ttl=64 time=12.517 ms ^C --- 10.10.10.1 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max = 9.976/11.529/12.517 ms [email protected]:/etc# [email protected]:/etc# [email protected]:/etc# [email protected]:/etc# ping 10.10.10.191 PING 10.10.10.191 (10.10.10.191): 56 data bytes 64 bytes from 10.10.10.191: seq=0 ttl=127 time=11.824 ms 64 bytes from 10.10.10.191: seq=1 ttl=127 time=10.619 ms 64 bytes from 10.10.10.191: seq=2 ttl=127 time=10.639 ms 64 bytes from 10.10.10.191: seq=3 ttl=127 time=11.598 ms ^C --- 10.10.10.191 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 10.619/11.170/11.824 ms [email protected]:/etc# [email protected]:/etc#
vpn 연결해제
ipsec down s2s-cert
참고사이트
- https://wiki.strongswan.org/projects/strongswan/wiki/UsableExamples
- https://wiki.strongswan.org/projects/strongswan/wiki/connsection
- https://wiki.strongswan.org/projects/strongswan/wiki/SimpleCA
- https://wiki.strongswan.org/projects/strongswan/wiki/IpsecPKI
- https://console.kim.sg/strongswan-ipsec-vpn-with-pre-shared-key-and-certificates/
감사를 표합니다
테스트를 위해 라우터를 사용할 수 있도록 배려해주신 @mabaha님께 감사를 표합니다.