[AWS] Swanctl을 활용한 AWS Site-to-Site VPN 구성 with Amazon Linux 2023

ondacloud·2026년 10월 5일

AWS

목록 보기
5/5
post-thumbnail

Overview

AWS Site-to-Site VPN은 AWS VPC와 On-Premise 네트워크 간에 암호화된 IPsec 터널을 구성하여 서로 다른 네트워크를 안전하게 연결할 수 있도록 하는 서비스입니다. 일반적인 환경에서는 실제 IDC나 사내 네트워크의 VPN 장비를 Customer Gateway로 사용하지만, 이번 실습에서는 별도의 VPC와 EC2 인스턴스를 On-Premise 환경으로 구성하여 실제 Hybrid Cloud 네트워크와 유사한 환경을 구현합니다.

Cloud 환경에는 Public/Private Subnet과 Virtual Private Gateway를 구성하고, On-Premise 환경에는 별도의 VPC와 EC2 인스턴스를 구성합니다. On-Premise EC2는 Elastic IP를 할당받아 고정된 공인 IP를 가지며, strongSwan을 설치하여 AWS Site-to-Site VPN의 Customer Gateway 역할을 수행하도록 구성합니다.

VPN 연결에는 IKEv2와 Pre-Shared Key 기반 인증을 사용하며, strongSwan의 기존 ipsec.conf 및 ipsec 명령 기반의 Legacy 방식 대신 swanctl.conf와 VICI 인터페이스를 사용하는 swanctl 기반 방식으로 VPN 연결을 구성합니다. 이를 통해 두 개의 AWS VPN Tunnel에 대한 IKE SA와 CHILD SA를 정의하고, 각 Tunnel을 독립적인 XFRM Interface와 연결하여 Route-based 형태로 트래픽을 전달합니다.

또한 NAT Traversal(NAT-T), DPD, Rekey, Traffic Selector 등의 IPsec 설정과 Linux IP Forwarding 및 XFRM Interface Routing을 함께 구성하여 On-Premise 네트워크와 Cloud VPC 사이의 양방향 통신이 가능하도록 구성합니다.

Practice

VPC

VPC는 Cloud와 On-Premise용 VPC를 각각 생성합니다.

Cloud VPC

Public Subnet과 Private Subnet을 구성합니다. Availability Zone A에 구성합니다.

VPCSubnetRoute TableGateway
demo-cloud-vpcdemo-cloud-public-ademo-cloud-public-rtbdemo-cloud-igw
demo-cloud-private-ademo-cloud-private-a-rtbdemo-cloud-natgw-a

On-Premise VPC

Public Subnet과 Private Subnet을 구성합니다. Availability Zone A에 구성합니다.

VPCSubnetRoute TableGateway
demo-onprem-vpcdemo-onprem-public-ademo-onprem-public-rtbdemo-onprem-igw
demo-onprem-private-ademo-onprem-private-a-rtbdemo-onprem-natgw-a

Security Group

Cloud VM

Inbound

TypePort RangeSourceDescription
SSH220.0.0.0/0EC2 접근
ALL ICMP - IPv4ALL0.0.0.0/0ICMP

Outbound

TypePort RangeSourceDescription
HTTP800.0.0.0/0패키지 다운로드 및 외부 통신
HTTPS4430.0.0.0/0패키지 다운로드 및 API 통신
ALL ICMP - IPv4ALL0.0.0.0/0ICMP

On-Premise VM

Inbound

TypePort RangeSourceDescription
SSH220.0.0.0/0EC2 접근
ALL ICMP - IPv4ALL0.0.0.0/0ICMP

Outbound

TypePort RangeSourceDescription
HTTP800.0.0.0/0패키지 다운로드 및 외부 통신
HTTPS4430.0.0.0/0패키지 다운로드 및 API 통신
ALL ICMP - IPv4ALL0.0.0.0/0ICMP
Custom UDP5000.0.0.0/0IKE 통신 연결
Custom UDP45000.0.0.0/0NAT-T (NAT Traversal) NAT 감지

EC2

Cloud VM

User Data

#!/bin/bash
dnf update -y
dnf upgrade -y
dnf install --allowerasing -y jq curl wget unzip vim

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm

sed -i 's|PasswordAuthentication no|PasswordAuthentication yes|g' /etc/ssh/sshd_config
systemctl restart sshd
echo 'Skill53##' | passwd --stdin ec2-user
echo 'Skill53##' | passwd --stdin root

On-Premise VM

User Data

#!/bin/bash
dnf update -y
dnf upgrade -y
dnf install --allowerasing -y jq curl wget unzip vim

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm

sed -i 's|PasswordAuthentication no|PasswordAuthentication yes|g' /etc/ssh/sshd_config
systemctl restart sshd
echo 'Skill53##' | passwd --stdin ec2-user
echo 'Skill53##' | passwd --stdin root

Elastic IP

On-Premise VM에 Elastic IP를 할당하여 Customer Gateway에서 사용할 공인 IP를 고정합니다.


Site-to-Site VPN

Virtual Private Gateway


Customer Gateway

IP Address에 On-Premise Public IP 넣습니다.


Route Table

Cloud VPC의 Private Subnet에 Destination IP 부분에 On-Premise VPC CIDR 넣어줍니다.


Site to Site VPN Connections


On-Premise VM

Amazon Linux 2023에서 strongSwan 패키지를 설치하기 위한 SPAL Repository를 활성화합니다.

sudo dnf install -y spal-release

AWS Site-to-Site VPN과 IPsec Tunnel을 구성하기 위해 strongSwan을 설치합니다.

sudo dnf install -y strongswan

strongSwan 서비스를 시작하고 시스템 부팅 시 자동으로 실행되도록 설정합니다.

sudo systemctl enable --now strongswan

VPN Gateway 역할을 수행할 수 있도록 IP Forwarding을 활성화하고 Reverse Path Filtering을 비활성화합니다.

sudo tee /etc/sysctl.d/99-vpn.conf > /dev/null <<'EOF'
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.ens5.rp_filter = 0
net.ipv4.conf.xfrm1.rp_filter = 0
net.ipv4.conf.xfrm2.rp_filter = 0
EOF

sysctl에 설정한 네트워크 Kernel Parameter를 시스템에 적용합니다.

sudo sysctl --system

AWS의 두 VPN Tunnel을 연결할 XFRM Interface와 Cloud VPC로 향하는 Route를 구성합니다.

sudo tee /usr/local/bin/setup-xfrm.sh > /dev/null <<'EOF'
#!/bin/bash
sleep 5
WAN_IF=$(ip -4 route show default | awk 'NR==1 {print $5}')

ip link del xfrm1 2>/dev/null || true
ip link del xfrm2 2>/dev/null || true

ip link add xfrm1 type xfrm dev $WAN_IF if_id 1
ip link set xfrm1 mtu 1400 up

ip link add xfrm2 type xfrm dev $WAN_IF if_id 2
ip link set xfrm2 mtu 1400 up

ip route replace 10.0.0.0/16 dev xfrm1 metric 100
ip route append 10.0.0.0/16 dev xfrm2 metric 200
EOF

생성한 XFRM 설정 Script를 실행할 수 있도록 실행 권한을 부여합니다.

sudo chmod +x /usr/local/bin/setup-xfrm.sh

재부팅 이후에도 XFRM Interface와 Route가 자동으로 구성되도록 Systemd Service를 생성합니다.

sudo tee /etc/systemd/system/xfrm-setup.service > /dev/null <<'EOF'
[Unit]
Description=Setup XFRM Interfaces and Routes for IPsec
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup-xfrm.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

새롭게 생성한 Systemd Service 설정을 다시 불러옵니다.

sudo systemctl daemon-reload

XFRM 설정 Service를 실행하고 부팅 시 자동으로 실행되도록 설정합니다.

sudo systemctl enable --now xfrm-setup.service

첫 번째 VPN Tunnel에 사용할 xfrm1 Interface의 상태를 확인합니다.

ip -br link show xfrm1


두 번째 VPN Tunnel에 사용할 xfrm2 Interface의 상태를 확인합니다.

ip -br link show xfrm2


Cloud VPC로 향하는 트래픽이 XFRM Interface를 사용하도록 설정되었는지 확인합니다.

ip route show 10.0.0.0/16


VPN 구성에서 사용할 AWS Resource Name과 각 Tunnel의 Pre-Shared Key를 변수로 지정합니다.

CLOUD_VPC_NAME="demo-cloud-vpc"
ONPREM_VPC_NAME="demo-onprem-vpc"
ONPREM_VM_NAME="demo-onprem-vm"
S2S_VPN_NAME="demo-cloud-onprem-vpn"
TUNNEL1_PSK="demokey1"
TUNNEL2_PSK="demokey2"

AWS CLI를 이용해 VPC CIDR, EC2 IP, VPN Connection ID와 Tunnel Outside IP를 조회합니다.

CLOUD_VPC_CIDR=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLOUD_VPC_NAME" --query 'Vpcs[0].CidrBlock' --output text)
ONPREM_VPC_CIDR=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$ONPREM_VPC_NAME" --query 'Vpcs[0].CidrBlock' --output text)
ONPREM_VM_PUBLIC_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$ONPREM_VM_NAME" "Name=instance-state-name,Values=running" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)
ONPREM_VM_PRIVATE_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$ONPREM_VM_NAME" "Name=instance-state-name,Values=running" --query 'Reservations[0].Instances[0].PrivateIpAddress' --output text)
S2S_VPN_CONNECTION_ID=$(aws ec2 describe-vpn-connections --filters "Name=tag:Name,Values=$S2S_VPN_NAME" --query 'VpnConnections[0].VpnConnectionId' --output text)
VPN_TUNNEL_OUTSIDE_IP1=$(aws ec2 describe-vpn-connections --vpn-connection-ids $S2S_VPN_CONNECTION_ID --query 'VpnConnections[0].VgwTelemetry[0].OutsideIpAddress' --output text)
VPN_TUNNEL_OUTSIDE_IP2=$(aws ec2 describe-vpn-connections --vpn-connection-ids $S2S_VPN_CONNECTION_ID --query 'VpnConnections[0].VgwTelemetry[1].OutsideIpAddress' --output text)

swanctl.conf에 두 개의 AWS VPN Tunnel에 대한 IKEv2, IPsec, PSK 및 XFRM 설정을 정의합니다.

sudo tee /etc/strongswan/swanctl/swanctl.conf > /dev/null <<EOF
connections {
    aws-tunnel1 {
        version = 2
        local_addrs = ${ONPREM_VM_PRIVATE_IP}
        remote_addrs = ${VPN_TUNNEL_OUTSIDE_IP1}

        proposals = aes256-sha256-prfsha256-modp2048
        rekey_time = 28800s
        reauth_time = 0s
        dpd_delay = 30s
        encap = yes
        mobike = no

        local {
            auth = psk
            id = ${ONPREM_VM_PUBLIC_IP}
        }

        remote {
            auth = psk
            id = ${VPN_TUNNEL_OUTSIDE_IP1}
        }

        children {
            aws-tunnel1-child {
                local_ts = ${ONPREM_VPC_CIDR}
                remote_ts = ${CLOUD_VPC_CIDR}

                esp_proposals = aes256-sha256-modp2048
                rekey_time = 3600s
                rekey_bytes = 0
                rekey_packets = 0

                mode = tunnel
                if_id_in = 1
                if_id_out = 1
                start_action = start
                close_action = restart
                dpd_action = restart
            }
        }
    }

    aws-tunnel2 {
        version = 2
        local_addrs = ${ONPREM_VM_PRIVATE_IP}
        remote_addrs = ${VPN_TUNNEL_OUTSIDE_IP2}

        proposals = aes256-sha256-prfsha256-modp2048
        rekey_time = 28800s
        reauth_time = 0s
        dpd_delay = 30s
        encap = yes
        mobike = no

        local {
            auth = psk
            id = ${ONPREM_VM_PUBLIC_IP}
        }

        remote {
            auth = psk
            id = ${VPN_TUNNEL_OUTSIDE_IP2}
        }

        children {
            aws-tunnel2-child {
                local_ts = ${ONPREM_VPC_CIDR}
                remote_ts = ${CLOUD_VPC_CIDR}

                esp_proposals = aes256-sha256-modp2048
                rekey_time = 3600s
                rekey_bytes = 0
                rekey_packets = 0

                mode = tunnel
                if_id_in = 2
                if_id_out = 2
                start_action = start
                close_action = restart
                dpd_action = restart
            }
        }
    }
}

secrets {
    ike-tunnel1 {
        id-local = ${ONPREM_VM_PUBLIC_IP}
        id-remote = ${VPN_TUNNEL_OUTSIDE_IP1}
        secret = "${TUNNEL1_PSK}"
    }

    ike-tunnel2 {
        id-local = ${ONPREM_VM_PUBLIC_IP}
        id-remote = ${VPN_TUNNEL_OUTSIDE_IP2}
        secret = "${TUNNEL2_PSK}"
    }
}
EOF

Pre-Shared Key가 포함된 VPN 설정 파일을 root 사용자만 접근할 수 있도록 제한합니다.

sudo chmod 600 /etc/strongswan/swanctl/swanctl.conf

변경한 VPN 설정을 적용하기 위해 strongSwan 서비스를 재시작합니다.

sudo systemctl restart strongswan

swanctl.conf에 정의한 VPN Connection과 인증 정보를 strongSwan에 로드합니다.

sudo swanctl --load-all


현재 연결된 IKE SA와 CHILD SA의 상태를 확인하여 VPN Tunnel이 정상적으로 연결되었는지 확인합니다.

sudo swanctl --list-sas


Test

Cloud VM → On-Premise VM

Cloud VM에 접근하여 On-Premise VM으로 Ping을 요청하여 VPN을 통한 통신을 확인합니다.

ping 192.168.10.10 -c 4

On-Premise VM에서 ICMP 패킷을 캡처하여 Cloud VM에서 전달된 요청을 확인합니다.

sudo tcpdump -i any -p icmp


On-Premise VM → Cloud VM

On-Premise VM에 접근하여 Cloud VM으로 Ping을 요청하여 VPN을 통한 통신을 확인합니다.

ping 10.0.20.20 -c 4

Cloud VM에서 ICMP 패킷을 캡처하여 On-Premise VM에서 전달된 요청을 확인합니다.

sudo tcpdump -i any -p icmp


Result

이번 실습을 통해 AWS Site-to-Site VPN과 strongSwan을 활용하여 Cloud 환경과 가상의 On-Premise 환경을 연결하는 전체 과정을 확인할 수 있었습니다.

Cloud VPC에는 Virtual Private Gateway를 구성하고 On-Premise 환경의 EC2 인스턴스를 Customer Gateway로 사용함으로써, 실제 기업 환경에서 사용되는 Hybrid Cloud 네트워크와 유사한 Site-to-Site VPN 구조를 구성할 수 있었습니다. 또한 Elastic IP, Route Table, Security Group 등의 네트워크 구성 요소가 VPN 연결 과정에서 어떠한 역할을 수행하는지 확인할 수 있었습니다.

특히 strongSwan에서는 기존 ipsec.conf와 ipsec 명령을 사용하는 Legacy 방식 대신 swanctl.conf와 VICI 인터페이스를 사용하는 swanctl 기반 방식을 적용하였습니다. VPN 터널 자체는 IKEv2를 기반으로 구성하면서 swanctl을 통해 IKE SA와 CHILD SA, Pre-Shared Key, Traffic Selector, DPD 및 Rekey 등의 설정을 직접 관리함으로써 strongSwan의 현대적인 VPN 구성 방식을 이해할 수 있었습니다.

또한 AWS에서 제공하는 두 개의 VPN Tunnel을 각각 XFRM Interface와 연결하고 Linux Routing Table을 구성하면서 IPsec 암호화 처리와 실제 패킷 라우팅이 어떻게 연계되는지 확인할 수 있었습니다. NAT Traversal을 위한 UDP 4500 통신과 IKE 협상을 위한 UDP 500의 역할 역시 실습을 통해 확인할 수 있었습니다.

최종적으로 Cloud VM과 On-Premise VM 사이에서 양방향 ICMP 통신이 정상적으로 이루어지는 것을 확인하였으며, tcpdump와 swanctl --list-sas를 통해 VPN Tunnel의 연결 상태와 실제 네트워크 트래픽을 검증할 수 있었습니다.

이러한 과정은 AWS Site-to-Site VPN을 단순히 생성하는 것을 넘어 IKEv2, IPsec, XFRM, Routing, NAT-T와 같은 VPN의 내부 동작 구조를 이해하는 데 중요한 기반이 되며, 실제 Hybrid Cloud 환경의 네트워크 구성과 장애 분석 능력을 향상시키는 데 도움이 되었습니다.

profile
Devops Engineer가 목표인 학생

0개의 댓글