IPsec Site-to-Site VPN: Kết nối Linux với AWS VPC bằng strongSwan và BGP

Đồng hồ điểm 2 giờ sáng ngày thứ Bảy. Quá trình đồng bộ database giữa tủ rack bare-metal của bạn ở Frankfurt và AWS EU-Central-1 vừa bị đình trệ. Kênh cảnh báo liên tục gửi thông báo, và lệnh ping báo rớt 100% gói tin.

Một GRE tunnel tĩnh bị sập trước đợt tăng đột biến lưu lượng lên đến 800 Mbps do ai đó đã thay switch core ở upstream mà không thiết lập lại trạng thái neighbor. Khi đường truyền hybrid bị đứt, các transaction sẽ nhanh chóng bị tắc nghẽn. Bạn cần một IPsec tunnel dạng route-based tự động, có khả năng tính toán lại đường đi chỉ trong vài giây khi đường truyền vật lý gặp sự cố chập chờn (link flapping).

Một cầu nối hybrid sẵn sàng cho môi trường production giữa Linux edge router và Amazon VPC cần dựa vào hai công cụ chính: strongSwan phụ trách lớp mã hóa IKEv2/IPsec, còn FRRouting (FRR) quản lý việc trao đổi định tuyến động qua BGP. Trong môi trường benchmark của chúng tôi với lưu lượng database duy trì liên tục ở mức 450 Mbps, kiến trúc này giữ vững độ trễ ổn định 14 ms và an toàn vượt qua các đợt bảo trì Availability Zone của AWS mà không làm rớt phiên TCP nào.

Bắt đầu nhanh (5 phút)

Hãy xét mô hình thiết lập cơ bản sau: Bạn quản lý một gateway Ubuntu 22.04 với IP public 203.0.113.10 và subnet nội bộ 192.168.10.0/24. Trên AWS, bạn đã khởi tạo một Virtual Private Gateway (VGW) gắn vào VPC 10.0.0.0/16. AWS cung cấp hai tunnel endpoint. Chúng ta sẽ cấu hình Tunnel 1 sử dụng IP public từ xa 198.51.100.1, IP link-local bên trong tunnel 169.254.10.1/30, và pre-shared key MySuperSecretKey123.

Bắt đầu bằng việc cài đặt strongSwan và FRRouting trên máy chủ Linux:

sudo apt-get update
sudo apt-get install -y strongswan strongswan-pki frr

Điều chỉnh các tham số kernel để bật tính năng chuyển tiếp gói tin (packet forwarding) và vô hiệu hóa các vấn đề về reverse path filtering:

sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
sudo sysctl -w net.ipv4.conf.all.send_redirects=0
sudo sysctl -w net.ipv4.conf.default.rp_filter=2
sudo sysctl -w net.ipv4.conf.all.rp_filter=2

Lưu secret xác thực vào /etc/ipsec.secrets:

# /etc/ipsec.secrets
203.0.113.10 198.51.100.1 : PSK "MySuperSecretKey123"

Khai báo IKEv2 proposal và mark của Virtual Tunnel Interface (VTI) trong /etc/ipsec.conf:

# /etc/ipsec.conf
config setup
    charondebug="ike 1, knl 1, cfg 0"
    uniqueids=no

conn %default
    keyexchange=ikev2
    ike=aes256-sha256-modp2048!
    esp=aes256-sha256-modp2048!
    dpdaction=restart
    dpddelay=10s
    dpdtimeout=30s
    lifetime=8h
    ikelifetime=24h
    type=tunnel

conn aws-tunnel-1
    left=203.0.113.10
    leftid=203.0.113.10
    leftsubnet=0.0.0.0/0
    right=198.51.100.1
    rightid=198.51.100.1
    rightsubnet=0.0.0.0/0
    mark=42
    auto=start

Khởi tạo virtual tunnel interface gắn với mark 42:

sudo ip tunnel add vti0 mode vti local 203.0.113.10 remote 198.51.100.1 okey 42 ikey 42
sudo ip addr add 169.254.10.2/30 remote 169.254.10.1 dev vti0
sudo ip link set vti0 up mtu 1399

Khởi động strongSwan và kiểm tra quá trình bắt tay phase 1 và phase 2:

sudo ipsec restart
sudo ipsec statusall
ping -c 3 169.254.10.1

Nếu lệnh ping phản hồi với round-trip time dưới 20 ms, IPsec tunnel của bạn đã truyền nhận gói tin thô thành công. Bước tiếp theo là cấu hình quảng bá định tuyến động.

Phân tích chuyên sâu

Các kỹ sư thường gặp phải tình trạng “hố đen” định tuyến (routing black hole) khi kết nối Linux router với AWS. Nguyên nhân hầu như luôn bắt nguồn từ việc nhầm lẫn giữa IPsec dạng policy-based và route-based.

Kiến trúc Route-Based so với Policy-Based

Mô hình IPsec truyền thống phụ thuộc vào Cơ sở dữ liệu chính sách bảo mật (Security Policy Database – SPD). Linux kernel sẽ kiểm tra các gói tin đi ra (egress) dựa trên các bộ tuple địa chỉ chính xác. Nếu một gói tin khớp với 192.168.10.0/24 -> 10.0.0.0/16, kernel sẽ mã hóa nó thành gói tin ESP. Nhưng nếu bạn thêm một subnet mới như 10.1.0.0/16 phía AWS, lưu lượng sẽ âm thầm bị hủy bỏ (drop). Cả hai đầu kết nối bắt buộc phải đàm phán lại child security association trước.

Các giao thức định tuyến động càng khiến việc này phức tạp hơn. BGP trao đổi các gói hello qua các địa chỉ link-local (169.254.0.0/16). Những địa chỉ này nằm ngoài các chính sách mã hóa theo chuẩn RFC 1918 thông thường.

VTI giải quyết triệt để vấn đề này. Bằng cách khai báo leftsubnet=0.0.0.0/0 và rightsubnet=0.0.0.0/0 kết hợp với firewall mark XFRM (mark=42), strongSwan sẽ chặn và mã hóa mọi gói tin được đẩy ra khỏi interface vti0. Bảng định tuyến tiêu chuẩn của Linux giờ đây sẽ toàn quyền quyết định lưu lượng nào đi vào tunnel.

Cấu hình FRRouting cho BGP Peering

Kích hoạt BGP daemon bằng cách chỉnh sửa file /etc/frr/daemons:

bgpd=yes

Cấu hình autonomous system của bạn trong /etc/frr/frr.conf. Trong cấu hình này, ASN phía on-premises là 65000 và AWS VGW sử dụng ASN tiêu chuẩn 64512:

frr version 8.1
frr defaults traditional
hostname linux-vpn-gw
!
router bgp 65000
 bgp router-id 192.168.10.1
 neighbor 169.254.10.1 remote-as 64512
 neighbor 169.254.10.1 description AWS-VPC-Tunnel1
 neighbor 169.254.10.1 timers 10 30
 !
 address-family ipv4 unicast
  network 192.168.10.0/24
  neighbor 169.254.10.1 activate
  neighbor 169.254.10.1 soft-reconfiguration inbound
 exit-address-family
!
line vty
!

Áp dụng các quy tắc định tuyến mới:

sudo systemctl restart frr

Truy vấn bảng neighbor động thông qua control shell của FRR:

sudo vtysh -c "show ip bgp summary"
sudo vtysh -c "show ip route bgp"

Kiểm tra trạng thái neighbor xem đã đạt Established chưa. Trong vòng 10 đến 30 giây, bảng định tuyến của kernel sẽ hiển thị 10.0.0.0/16 via 169.254.10.1 dev vti0.

Cấu hình nâng cao

Xây dựng cơ chế dự phòng với Dual-Tunnel

AWS luôn tạo hai endpoint public gateway trên các Availability Zone riêng biệt cho mỗi kết nối VPN. Việc chỉ đấu nối một endpoint sẽ khiến đường truyền của bạn gặp rủi ro gián đoạn khi AWS thực hiện bảo trì hypervisor theo lịch trình. Hãy tạo thêm một interface VTI thứ hai (vti1) gắn với một mark riêng biệt (mark=43).

Sử dụng kỹ thuật BGP AS-Path prepending để AWS ưu tiên chọn Tunnel 1 trong điều kiện vận hành bình thường. Nếu Tunnel 1 gặp sự cố, AWS sẽ tự động chuyển hướng lưu lượng chiều vào (inbound) sang Tunnel 2 trong vòng 30 giây:

route-map TO_AWS_T1 permit 10
!
route-map TO_AWS_T2 permit 10
 set as-path prepend 65000 65000
!
router bgp 65000
 neighbor 169.254.10.1 remote-as 64512
 neighbor 169.254.11.1 remote-as 64512
 !
 address-family ipv4 unicast
  neighbor 169.254.10.1 activate
  neighbor 169.254.10.1 route-map TO_AWS_T1 out
  neighbor 169.254.11.1 activate
  neighbor 169.254.11.1 route-map TO_AWS_T2 out
 exit-address-family

AWS sẽ kiểm tra độ dài AS-Path và điều hướng các gói tin phản hồi qua Tunnel 1. Tunnel 2 sẽ duy trì ở chế độ standby cho đến khi đường truyền chính bị mất kết nối.

Lưu cấu hình VTI Interface tự động sau khi khởi động lại

Các interface tạm thời được tạo bằng lệnh ip tunnel thông thường sẽ biến mất sau khi hệ thống khởi động lại. Hãy lưu lại các thông số cấu hình interface bằng systemd-networkd. Khai báo thiết bị tunnel vào file /etc/systemd/network/25-vti0.netdev:

[NetDev]
Name=vti0
Kind=vti

[Tunnel]
Local=203.0.113.10
Remote=198.51.100.1
Key=42

Gán địa chỉ IP và cấu hình khung truyền trong /etc/systemd/network/25-vti0.network:

[Match]
Name=vti0

[Network]
Address=169.254.10.2/30
MTUBytes=1399

Kinh nghiệm thực tế

1. Ngăn ngừa lỗi Path MTU Black Hole bằng MSS Clamping

Khi lệnh ping ICMP thành công nhưng các kết nối HTTPS hoặc PostgreSQL lại bị treo ở bước đàm phán TLS, nguyên nhân thường do kích thước gói tin. Quá trình đóng gói sẽ làm tăng thêm khoảng 56 đến 73 byte overhead cho IPsec ESP. Nếu client gửi khung truyền tiêu chuẩn 1500 byte với cờ Don’t Fragment (DF) được bật, tunnel endpoint sẽ buộc phải loại bỏ gói tin.

Hãy thiết lập giới hạn (clamp) TCP Maximum Segment Size (MSS) trực tiếp trên firewall:

sudo iptables -t mangle -A FORWARD -o vti+ -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Quy tắc này buộc các client khi đàm phán kết nối phải giới hạn payload TCP ở mức 1359 byte, tránh tình trạng phân mảnh gói tin trên các interface có MTU 1399 byte.

2. Tinh chỉnh DPD Timer để phục hồi kết nối nhanh chóng

Theo mặc định, AWS sẽ ngắt các tunnel không hoạt động hoặc không phản hồi sau 30 đến 60 giây. Hãy thiết lập các tham số Dead Peer Detection (DPD) nhạy hơn trong /etc/ipsec.conf:

dpdaction=restart
dpddelay=10s
dpdtimeout=30s

Nếu đường truyền gặp nhiễu làm gián đoạn lưu lượng, strongSwan sẽ phát hiện thiếu tín hiệu heartbeat và kích hoạt xác thực lại trước khi AWS hủy bỏ phiên kết nối.

3. Quy trình xử lý sự cố trong 30 giây

Khi mất kết nối, hãy thực hiện ba lệnh chẩn đoán sau theo thứ tự:

  1. Kiểm tra trạng thái mã hóa (Crypto State): Chạy lệnh sudo ipsec statusall. Xác nhận cả IKE_SA và CHILD_SA đều hiển thị trạng thái ESTABLISHED. Nếu không có, hãy kiểm tra journalctl -u strongswan xem có gói tin UDP 500/4500 nào bị drop hoặc sai pre-shared key hay không.
  2. Kiểm tra bộ đếm trên Interface: Chạy lệnh ip -s tunnel show vti0. Đảm bảo các bộ đếm gói tin truyền (TX) và nhận (RX) tăng đều tương đối cân xứng. Nếu chỉ số TX rất lớn trong khi RX bằng 0, vấn đề có thể do Security Group phía remote đang chặn hoặc định tuyến bị cấu hình sai.
  3. Kiểm tra BGP Peering: Chạy lệnh sudo vtysh -c "show ip bgp summary". Trạng thái hiển thị Active hoặc Connect cho thấy FRR không thể kết nối tới 169.254.10.1. Hãy kiểm tra lại địa chỉ local trên vti0 và xác nhận quy tắc firewall đã cho phép lưu lượng qua cổng TCP 179.
Share: