strongswan ipsec/ikev2 vpn site2site2site

3개의 OpenWrt 라우터를 strongswan 을 이용해 site2site2site 연결해 봤습니다. swanctl.conf 와 ipsec.conf를 이용한 설정 및 xfrm 를 활용한 라우팅기반 vpn 과 정책기반 vpn 연결을 병행했습니다.

설정화일이 많다보니 자세한 설명은 생략하고 설정화일 위주로 보여드리겠습니다.

구성

vpn example
  • routerA, routerB간 연결은 xfrm 인터페이스를 이용한 라우팅기반 vpn 연결(xfrm은 OpenWrt V21.02 이상에서 지원하며 이전 버전에서는 라우팅기반 vpn 연결을 위해선 vti 인터페이스를 이용하면 됩니다.)
  • routerA, routerC간 연결은 정책기반 vpn 연결
  • routerB, routerC간 연결은 정책기반 vpn 연결
  • routerA, rw간 연결은 정책기반 vpn 연결
  • rw는 routerA 로 연결시 routerA 서브넷의 dhcp 할당
  • routerA, routerB의 설정은 /etc/swanctl/swanctl.conf를 이용
  • routerC는 전통적인 /etc/ipsec.conf를 이용한 설정
  • rw client 설정은 여기서 따로 설명하지 않으니 이전글 확인

사전준비

  • 독립된 도메인 혹은 ddns를 준비하세요. 만약 고정ip사용자라면 ip를 사용해도 상관없습니다.(본 예에서는 duckdns.org를 이용하며 duckdns가 아닌 어떤 ddns도 상관없습니다.)

패키지 설치

routeA, routerB

routerA, routerB는 xfrm 인터페이스를 이용한 라우팅기반 vpn 연결이므로 xfrm 인터페이스 모듈 등을 설치합니다.

opkg update
opkg install xfrm kmod-xfrm-interface

또한 routerA, routerB는 swanctl.conf를 이용한 설정을 할 것으므로 다른 필요한 패키지 외 strongswan-swanctl, strongswan-mod-vici 도 추가 설치합니다.

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 kmod-tun openssl-util strongswan-mod-test-vectors strongswan-mod-farp strongswan-swanctl strongswan-mod-vici

만약 이전에 게시한 글 OpenWrt strongswan 으로 구성한 ikev2 vpnStrongswan ipsec VPN site to site 연결 에 따라 패키지를 미리 설치했다면 추가되는 패키지 strongswan-swanctl, strongswan-mod-vici 만 설치하면 되지만 기존에 설치한 strongswan-mod-kernel-libipsec 패키지는 제거해야 합니다. (필요도 없고, 제거하지 않으면 데몬이 죽으면서 라우터가 먹통이 될 수 있습니다.) 제거하려면 아래와 같이 합니다.

opkg remove strongswan-mod-kernel-libipsec

또한 기존에 /etc/ipsec.conf 화일에 따로 vpn 설정을 했다면 ipsec 데몬이 기존 설정을 읽어 들입니다. swanctl.conf 화일로 마이그레이션 할 경우 /etc/ipsec.conf, /etc/ipsec.secret 화일을 빈 화일로 만들어 놓으면 되고 그냥 기존대로 ipsec.conf 화일로 설정을 하겠다면 strongswan.org 에 많은 자료가 있으니 설정방법을 확인하세요. 각자에게 맡깁니다.

마지막으로 dnsmasq-full 패키지를 설치합니다.

opkg install dnsmasq-full --download-only && opkg remove dnsmasq && opkg install dnsmasq-full --cache . && rm *.ipk

routerC

routerC는 routerA, routerC에 대해 정책기반 vpn연결을 하고 전통적인 ipsec.conf를 이용한 설정을 할 것으므로 strongswan-swanctl, strongswan-mod-vici를 설치하지 않습니다. 그리고 필수사항은 아니지만 이전글과 같이 ipsec0라고 명명된 네트워크 인터페이스를 만들어 이용할 예정이므로 strongswan의 다른 패키지 외 strongswan-mod-kernel-libipsec 설치합니다.

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 kmod-tun openssl-util strongswan-mod-test-vectors strongswan-mod-farp strongswan-mod-kernel-libipsec

dnsmasq-full 패키지를 설치합니다.

opkg install dnsmasq-full --download-only && opkg remove dnsmasq && opkg install dnsmasq-full --cache . && rm *.ipk

인증서 생성 및 psk키 생성

인증서 생성

본 예에서는 메인 라우터인 routerA에서 인증서를 생성해 각각의 peer에 전송해 사용할 것입니다.

만약 이전글을 참고해 인증서를 이미 만들었다면 그 인증서를 복사해서 사용해도 됩니다.(인증서 전송 위치는 아래 참고)

인증서를 생성하는 스크립트는 다음과 같습니다.

  • mk_rsa_ca.sh : Root CA 인증서 생성(한번만 실행하세요. 다시 실행하면 기존에 생성한 서버/사용자 인증서가 모두 무력화 됩니다.)
  • mk_rsa_server.sh : 서버 인증서 생성
  • mk_rsa_user.sh : 사용자 인증서 생성

/etc/swanctl/mk_rsa_ca.sh

#!/bin/sh

SRVNAME="aaa.duckdns.org"
#IPADDR="aaa.duckdns.orgm"

#Uncomment only if vpn server is behind a static IP
#IPADDR=$(. /lib/functions/network.sh; network_get_ipaddr ip wan; echo $ip)

COUNTRY="KR"
ORG="qquack"
VALIDDAYS="3652"

PREFIX="rsa4096_"

ipsec pki --gen --type rsa --size 4096 --outform pem > rsa/${PREFIX}cakey.pem
chmod 600 rsa/${PREFIX}cakey.pem

ipsec pki --self --ca --lifetime ${VALIDDAYS} --in rsa/${PREFIX}cakey.pem --type rsa --dn "C=${COUNTRY}, O=${ORG}, CN=${ORG} Root CA" --outform pem > x509ca/${PREFIX}cacert.pem
ipsec pki --print --in x509ca/${PREFIX}cacert.pem

/etc/swanctl/mk_rsa_server.sh

#!/bin/sh

SRVNAME="aaa.duckdns.org"
#IPADDR="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)

COUNTRY="KR"
ORG="qquack"
LIFETIME="3652"

PREFIX="rsa4096_"

ipsec pki --gen --type rsa --size 4096 --outform pem > rsa/${PREFIX}hostkey.pem
chmod 600 rsa/${PREFIX}hostkey.pem

ipsec pki --pub --in rsa/${PREFIX}hostkey.pem --type rsa | ipsec pki --issue --lifetime ${LIFETIME} --cacert x509ca/${PREFIX}cacert.pem --cakey rsa/${PREFIX}cakey.pem --dn "C=${COUNTRY}, O=${ORG}, CN=${SRVNAME}" --san ${SRVNAME} --flag serverAuth --flag ikeIntermediate --outform pem > x509/${PREFIX}hostcert.pem
ipsec pki --print --in x509/${PREFIX}hostcert.pem

/etc/swanctl/mk_rsa_user.sh

#!/bin/sh

NAME="user1"
USERID="${NAME}@aaa.duckdns.org"

#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"

PREFIX="rsa4096_"

#mkdir -p pkcs12 2> /dev/null

ipsec pki --gen --type rsa --size 4096 --outform pem > rsa/${PREFIX}${USERID}.pem
chmod 600 rsa/${PREFIX}${USERID}.pem

ipsec pki --pub --in rsa/${PREFIX}${USERID}.pem --type rsa | ipsec pki --issue --lifetime ${LIFETIME} --cacert x509ca/${PREFIX}cacert.pem --cakey rsa/${PREFIX}cakey.pem --dn "C=${COUNTRY}, O=${ORG}, CN=${USERID}" --san "${USERID}" --outform pem > x509/${PREFIX}${USERID}.pem
ipsec pki --print --in x509/${PREFIX}${USERID}.pem

openssl pkcs12 -export -inkey rsa/${PREFIX}${USERID}.pem -in x509/${PREFIX}${USERID}.pem -name "${NAME}'s VPN Certificate" -certfile x509ca/${PREFIX}cacert.pem -caname "${ORG} Root CA" -out pkcs12/${PREFIX}${USERID}.p12

본 예에서는 위의 스크립트를 활용해 다음과 같은 인증서를 생성했습니다.

  • mk_rsa_ca.sh : Root CA 인증서 생성
  • mk_rsa_server.sh : routerA 서버 인증서 생성
  • mk_rsa_user.sh : 아래 사용자 인증서 생성(pkcs12 포함)

인증서 전송

routerA에서 생성한 인증서를 scp를 이용해 전송을 하든 putty를 이용해 화면에서 긁어서 붙이든 routerB, routerC에 전송합니다. roadwarrior는 생성한 p12화일 및 cacert화일을 전송해서 쓰면 되겠죠

psk키 생성

routerA, routerC간 연결시 인증서를 통한 연결 외 참고로 psk를 활용한 연결을 만들어 봤습니다. 아래와 같이 openssl 명령으로 psk를 생성해 활용할 수 있습니다.

openssl rand -base64 32

router 설정

routerA, routerB, routerC 공통 설정(방화벽)

/etc/config/firewall

...
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

routerA 설정

/etc/config/network 에 xfrm 인터페이스 추가 및 routerB에 대한 라우팅 설정을 합니다.

...
config interface 'xfrm0'
        option proto 'xfrm'
        option mtu '1438'
        option zone 'vpn1'
        option tunlink 'wan'
        option ifid '357'

config interface 'xfrm0_s'
        option proto 'static'
        option device '@xfrm0'
        option ipaddr '10.1.3.1/30'

config route
        option interface 'xfrm0'
        option source '10.1.1.1'
        option target '192.168.10.0/24'

/etc/config/firewall

...
config zone
        option name 'vpn1'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option mtu_fix '1'
        list network 'xfrm0'
        list subnet '10.1.1.0/24'
        list subnet '192.168.10.0/24'

config forwarding
        option src 'lan'
        option dest 'vpn1'

config forwarding
        option src 'vpn1'
        option dest 'lan'
...

/etc/strongswan.conf 에 roadwarrior를 위한 dhcp 구성

charon {
        load_modular = yes
        dns1 = 10.1.1.1
#       nbns1 = 10.1.1.1
        plugins {
                include strongswan.d/charon/*.conf
                dhcp {
                        force_server_address = yes
                        # use_server_port = yes
                        # uncomment the line above if log shows that DHCP
                        # offer can't be accepted
                        identity_lease = yes
                        server = 10.1.1.255
                        # use LAN broadcast address here, not IP address.
                }
        }
}

include strongswan.d/*.conf

include /var/ipsec/strongswan.conf

/etc/swanctl/swanctl.conf

conn-defaults {
        version = 2
        fragmentation = yes
        mobike = no
        rekey_time = 240m
        over_time = 24m
        rand_time = 24m
        dpd_delay = 0s
        keyingtries = 0
        send_cert = always
        send_certreq = no
        proposals = aes128-sha256-x25519,aes128-sha1-sha256-modp2048
}

child-defaults {
        mode = tunnel
        ipcomp = no
        rekey_time = 60m
        life_time = 66m
        rand_time = 6m
        dpd_action = clear
        esp_proposals = aes128-sha256-x25519,aes128-sha1,null-sha256-x25519
}

connections {
        rw : conn-defaults {
                local_addrs = %any
                remote_addrs = %any
                pools = dhcp
                local {
                        auth = pubkey
                        certs = rsa4096_hostcert.pem
                        id = aaa.duckdns.org
                }
                remote {
                        auth = eap-tls
                        eap_id = %any
                }
                children {
                        rw : child-defaults {
                                local_ts = 0.0.0.0/0
                                remote_ts = 0.0.0.0/0
                        }
                }

        }

        bbb : conn-defaults {
                local_addrs = %any
                remote_addrs = bbb.duckdns.org
                local {
                        auth = pubkey
                        certs = rsa4096_hostcert.pem
                        id = "C=KR, O=qquack, CN=aaa.duckdns.org"
                }
                remote {
                        auth = pubkey
                        id = "C=KR, O=qquack, [email protected]"
                }
                children {
                        bbb : child-defaults {
                                local_ts = 0.0.0.0/0
                                remote_ts = 0.0.0.0/0
                                if_id_in = 357
                                if_id_out = 357
                                start_action = trap
                        }
                }
        }

        ccc : conn-defaults {
                local_addrs = %any
                remote_addrs = ccc.duckdns.org
                local {
                        auth = pubkey
                        certs = rsa4096_hostcert.pem
                        id = aaa.duckdns.org
                }
                remote {
                        auth = pubkey
                        id = "C=KR, O=qquack, [email protected]"
                }
                children {
                        ccc : child-defaults {
                                local_ts = 10.1.1.0/24
                                remote_ts = 192.168.1.0/24
                        }
                }
        }

        ccc-psk : conn-defaults {
                local_addrs = %any
                remote_addrs = ccc.duckdns.org
                local {
                        auth = psk
                        id = aaa.duckdns.org
                }
                remote {
                        auth = psk
                        id = ccc.duckdns.org
                }
                children {
                        ccc-psk : child-defaults {
                                local_ts = 10.1.1.0/24
                                remote_ts = 192.168.1.0/24
                        }
                }
        }
}

secrets {
        rsa-aaa {
                file = rsa4096_hostkey.pem
        }
        ike-ccc {
                id = ccc.duckdns.org
                secret = [email protected]
        }
}

pools {
}

authorities {
}

routerB 설정

/etc/config/network 에 xfrm 인터페이스 추가 및 routerA에 대한 라우팅 설정을 합니다.

...
config interface 'xfrm0'
        option proto 'xfrm'
        option mtu '1438'
        option zone 'vpn1'
        option tunlink 'wan'
        option ifid '308'

config interface 'xfrm0_s'
        option proto 'static'
        option device '@xfrm0'
        option ipaddr '10.1.3.2/30'

config route
        option interface 'xfrm0'
        option source '192.168.10.1'
        option target '10.1.1.0/24'

/etc/config/firewall

...
config zone
        option name 'vpn1'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option mtu_fix '1'
        list network 'xfrm0'
        list subnet '10.1.1.0/24'
        list subnet '192.168.10.0/24'

config forwarding
        option src 'lan'
        option dest 'vpn1'

config forwarding
        option src 'vpn1'
        option dest 'lan'
...

/etc/swanctl/swanctl.conf

conn-defaults {
        version = 2
        fragmentation = yes
        mobike = no
        rekey_time = 240m
        over_time = 24m
        rand_time = 24m
        dpd_delay = 0s
        keyingtries = 0
        send_cert = always
        send_certreq = no
        proposals = aes128-sha256-x25519
}

child-defaults {
        mode = tunnel
        ipcomp = no
        rekey_time = 60m
        life_time = 66m
        rand_time = 6m
        dpd_action = clear
        esp_proposals = aes128-sha256-x25519
}

connections {
        aaa : conn-defaults {
                local_addrs = %any
                remote_addrs = aaa.duckdns.org
                local {
                        auth = pubkey
                        certs = [email protected]
                        id = "C=KR, O=qquack, [email protected]"
                }
                remote {
                        auth = pubkey
                        id = "C=KR, O=qquack, CN=aaa.duckdns.org"
                }
                children {
                        aaa : child-defaults {
                                local_ts = 0.0.0.0/0
                                remote_ts = 0.0.0.0/0
                                if_id_in = 308
                                if_id_out = 308
                                start_action = trap
                        }
                }
        }

        ccc : conn-defaults {
                local_addrs = %any
                remote_addrs = ccc.duckdns.org
                local {
                        auth = pubkey
                        certs = [email protected]
                        id = "C=KR, O=qquack, [email protected]"
                }
                remote {
                        auth = pubkey
                        id = "C=KR, O=qquack, [email protected]"
                }
                children {
                        ccc : child-defaults {
                                local_ts = 192.168.10.0/24
                                remote_ts = 192.168.1.0/24
                        }
                }
        }

}
secrets {
        rsa-bbb {
                file = [email protected]
        }
}

pools {
}

authorities {
}

routerC 설정

/etc/config/network에 ipsec0 인터페이스를 생성합니다.

...
config interface 'ipsec0'
        option ifname 'ipsec0'      #OpenWrt V21.02 부터는 ifname 이 device로 변경
        option proto 'none'
        option defaultroute '0'
        option peerdns '0'

/etc/config/firewall

...
config zone
        option name 'vpn1'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option mtu_fix '1'
        list network 'ipsec0'
        list subnet '10.1.1.0/24'
        list subnet '192.168.1.0/24'
        list subnet '192.168.10.0/24'

config forwarding
        option src 'lan'
        option dest 'vpn1'

config forwarding
        option src 'vpn1'
        option dest 'lan'
...

/etc/ipsec.conf

config setup
        strictcrlpolicy=no
        uniqueid=no

# Add connections here.

conn %default
        auto=add
        compress=no
        type=tunnel
        keyexchange=ikev2
        fragmentation=yes
        forceencaps=no
        mobike=no

        dpdaction=clear
        dpddelay=300s
        rekey=no

        ike=aes128-sha256-x25519
        esp=aes128-sha256-x25519

conn aaa-psk
        authby=secret

        left=%any
        leftid=ccc.duckdns.org
        leftsubnet=192.168.1.0/24

        right=aaa.duckdns.org
        rightid=aaa.duckdns.org
        rightsubnet=10.1.1.0/24

conn aaa
        authby=pubkey

        left=%any
        leftid=%any
        leftsubnet=192.168.1.0/24
        [email protected]
        leftsendcert=always

        right=aaa.duckdns.org
        rightid=aaa.duckdns.org
        rightsubnet=10.1.1.0/24

conn bbb
#       auto=start
        authby=pubkey

        left=%any
        leftid="C=KR, O=qquack, [email protected]"
        leftsubnet=192.168.1.0/24
        [email protected]
        leftsendcert=always

        right=bbb.duckdns.org
        rightid="C=KR, O=qquack, [email protected]"
        rightsubnet=192.168.10.0/24

/etc/ipsec.secrets

# /etc/ipsec.secrets - strongSwan IPsec secrets file
 : RSA [email protected]

aaa.duckdns.org : PSK [email protected]

서비스 재시작

설정한 라우터들의 데몬을 재시작합니다.

/etc/init.d/network restart
/etc/init.d/firewall restart
/etc/init.d/ipsec restart
/etc/init.d/swanctl restart

연결

swanctl.conf 로 설정한 routerA, routerB의 경우 먼저 swanctl -q 옵션으로 설정을 읽어온 후 swanctl -i --child <name> 으로 연결을 합니다. swanctl -t --child <name> 명령으로 연결을 종료할 수 있으며 swanctl 명령의 자세한 설명은 swanctl --help 를 참고하세요.

swanctl -q --noprompt
swanctl -i --child bbb
swanctl -i --child ccc

ipsec.conf 를 이용한 설정을 한 routerC는 따로 설정을 불러 올 필요 없이 ipec up <name>, ipsec down <name> 으로 각각 연결 및 종료를 할 수 있습니다.

ipec up bbb

연결을 마친후 각각의 router에 ping 을 때려보세요.

[email protected]:~# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1): 56 data bytes
64 bytes from 10.1.1.1: seq=0 ttl=64 time=5.719 ms
64 bytes from 10.1.1.1: seq=1 ttl=64 time=4.320 ms
64 bytes from 10.1.1.1: seq=2 ttl=64 time=3.918 ms
^C
--- 10.1.1.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 3.918/4.652/5.719 ms
[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=10.757 ms
64 bytes from 192.168.1.1: seq=1 ttl=64 time=9.016 ms
64 bytes from 192.168.1.1: seq=2 ttl=64 time=11.692 ms
64 bytes from 192.168.1.1: seq=3 ttl=64 time=14.426 ms
^C
--- 192.168.1.1 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 9.016/11.472/14.426 ms
[email protected]:~#

참고

Comments

No comments yet. Why don’t you start the discussion?

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

  ⁄  7  =  1