用例:通过 AWS 云连接的物联网设备
物联网设备位于路由器后面,路由器通过 AWS 云发送所有流量。
物联网服务器无法配置,因此不属于 AWS 云的一部分
对于配置,需要使用 UPD 数据包通知 IOT 设备到端口 xxxxx 以建立管理连接。此 UDP 数据包不能直接发送到 AWS 云
因此,我们需要一个通信服务器来路由 UDP 数据包:
IOT服务器上无法配置路由,因此需要将UDP数据包发送到zz.zz.zz.zz
通信服务器运行 Debian 10 和 Strongswan
ipsec.conf:
conn %default
mobike=no
compress=no
authby=secret
keyexchange=ike
ike=aes128-sha1-modp1024!
ikelifetime=8h
esp=aes128-sha1-modp1024!
lifetime=1h
rekeymargin=3m
keyingtries=%forever
installpolicy=yes
dpdaction=restart
type=tunnel
conn dc-aws1
leftsubnet=zz.zz.zz.zz #local subnet
right=vv.vv.vv.vv # AWS Gateway Public IP
rightsubnet=xx.xx.0.0/16 #remoye subnet
auto=start
include /var/lib/strongswan/ipsec.conf.inc
以下部分的连接工作: 标准操作就可以了。
ipsec 连接正在运行(如预期):
sudo ipsec status
Security Associations (1 up, 0 connecting):
dc-aws1[3]: ESTABLISHED 11 seconds ago, zz.zz.zz.zz[zz.zz.zz.zz]...vv.vv.vv.vv[vv.vv.vv.vv]
dc-aws1{16}: INSTALLED, TUNNEL, reqid 2, ESP in UDP SPIs: cd6dfea5_i 401dc4d5_o
dc-aws1{16}: zz.zz.zz.zz/32 = xx.xx.0.0/16
dc-aws1{17}: INSTALLED, TUNNEL, reqid 2, ESP in UDP SPIs: c2507a98_i 9d083aa4_o
dc-aws1{17}: zz.zz.zz.zz/32 = xx.xx.0.0/16
sudo ip xfrm policy show
src zz.zz.zz.zz/32 dst xx.xx.0.0/16
dir out priority 375423 ptype main
tmpl src zz.zz.zz.zz dst vv.vv.vv.vv
proto esp spi 0x9d083aa4 reqid 2 mode tunnel
src xx.xx.0.0/16 dst zz.zz.zz.zz/32
dir fwd priority 375423 ptype main
tmpl src vv.vv.vv.vv dst zz.zz.zz.zz
proto esp reqid 2 mode tunnel
src xx.xx.0.0/16 dst zz.zz.zz.zz/32
dir in priority 375423 ptype main
tmpl src vv.vv.vv.vv dst zz.zz.zz.zz
proto esp reqid 2 mode tunnel
Ping 通过 vpn 连接在路由器和通信服务器之间工作。
如果使用 icmp 数据包,Traceroute 也会起作用。
为了将更新数据包转发到物联网设备,需要使用 iptables 进行网络地址转换
iptables -t nat -I PREROUTING -p udp -s yy.yy.yy.yy --dport xxxxx -j DNAT --to xx.xx.xx.xx
如果源是 yy.yy.yy.yy,则不适用 xfrm 策略,因此也使用源网络地址转换
iptables -t nat -I POSTROUTING -p udp -s yy.yy.yy.yy --dport xxxxx -j SNAT --to-source zz.zz.zz.zz
还需要转发规则
iptables -I FORWARD -p udp -d xx.xx.xx.xx --dport xxxxx -j ACCEPT
tcpdump 显示,udp 数据包到达并被转发(其间,有 vpn 连接的活动消息):
sudo tcpdump -n -i any host vv.vv.vv.vv or port xxxxx
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
08:22:48.520734 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: NONESP-encap: isakmp: child_sa inf2[I]
08:22:48.535700 IP vv.vv.vv.vv.4500 > zz.zz.zz.zz.4500: NONESP-encap: isakmp: child_sa inf2[R]
08:22:56.717778 IP yy.yy.yy.yy.54278 > zz.zz.zz.zz.xxxxx: UDP, length 108
08:22:56.717908 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x81f1d489,seq=0x1), length 180
08:23:06.344622 IP yy.yy.yy.yy.46955 > zz.zz.zz.zz.xxxxx: UDP, length 108
08:23:06.344749 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x81f1d489,seq=0x2), length 180
08:23:10.797048 IP yy.yy.yy.yy.33667 > zz.zz.zz.zz.xxxxx: UDP, length 108
08:23:10.797247 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x81f1d489,seq=0x3), length 180
08:23:18.521104 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: NONESP-encap: isakmp: child_sa inf2[I]
08:23:18.536895 IP vv.vv.vv.vv.4500 > zz.zz.zz.zz.4500: NONESP-encap: isakmp: child_sa inf2[R]
08:23:25.423142 IP yy.yy.yy.yy.40703 > zz.zz.zz.zz.xxxxx: UDP, length 108
08:23:25.423271 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x81f1d489,seq=0x4), length 180
08:23:31.756269 IP yy.yy.yy.yy.58584 > zz.zz.zz.zz.xxxxx: UDP, length 108
08:23:31.756378 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x81f1d489,seq=0x5), length 180
^C
14 packets captured
14 packets received by filter
0 packets dropped by kernel
无效的方法:
但是 udp 数据包似乎丢失了。在 aws 的日志中,隧道中看不到任何流量。此外,没有数据包到达路由器。
使用 udp 和 tcp 数据包的 Traceroute 不起作用。
在侦听模式下在通信服务器上运行 netcat 并从路由器后面连接到它时,可以重现此问题。在 tcp 转储中,syn 数据包正在到达,似乎已发出响应,但没有来自 aws 云中的通信服务器的流量到达。来自此测试的通信服务器的 tcpdump:
11:35:06.597736 IP vv.vv.vv.vv.4500 > zz.zz.zz.zz.4500: UDP-encap: ESP(spi=0xcb99370a,seq=0x1), length 100
11:35:06.597736 IP xx.xx.xx.xx.49768 > zz.zz.zz.zz.15952: Flags [S], seq 101710370, win 64240, options [mss 1350,sackOK,TS val 2355221232 ecr 0,nop,wscale 7], length 0
11:35:06.598157 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0xb), length 100
11:35:07.534252 IP vv.vv.vv.vv.4500 > zz.zz.zz.zz.4500: UDP-encap: ESP(spi=0xcb99370a,seq=0x2), length 100
11:35:07.534252 IP xx.xx.xx.xx.49768 > zz.zz.zz.zz.15952: Flags [S], seq 101710370, win 64240, options [mss 1350,sackOK,TS val 2355222233 ecr 0,nop,wscale 7], length 0
11:35:07.534445 IP zz.zz.zz.zz.4500 > vv.vv.vsv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0xc), length 100
11:35:08.561060 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0xd), length 100
11:35:09.559712 IP vv.vv.vv.vv.4500 > zz.zz.zz.zz.4500: UDP-encap: ESP(spi=0xcb99370a,seq=0x3), length 100
11:35:09.559712 IP xx.xx.xx.xx.49768 > zz.zz.zz.zz.15952: Flags [S], seq 101710370, win 64240, options [mss 1350,sackOK,TS val 2355224249 ecr 0,nop,wscale 7], length 0
11:35:09.559908 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0xe), length 100
11:35:11.569079 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0xf), length 100
11:35:13.672232 IP vv.vv.vv.vv.4500 > zz.zz.zz.zz.4500: UDP-encap: ESP(spi=0xcb99370a,seq=0x4), length 100
11:35:13.672232 IP xx.xx.xx.xx.49768 > zz.zz.zz.zz.15952: Flags [S], seq 101710370, win 64240, options [mss 1350,sackOK,TS val 2355228377 ecr 0,nop,wscale 7], length 0
11:35:13.672319 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0x10), length 100
11:35:17.713025 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0x11), length 100
11:35:25.905124 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0x12), length 100
11:35:42.033153 IP zz.zz.zz.zz.4500 > vv.vv.vv.vv.4500: UDP-encap: ESP(spi=0x9a2c6938,seq=0x13), length
我不清楚数据包可能在哪里丢失。欢迎提供任何有关如何缩小问题范围的提示
** 更新 **
同时,我再次检查了配置,但没有成功。
然后,我切换到经过AWS测试的Openswan(2.6.51.5)。
使用 Openswan 后,数据包如预期到达云端。
我的结论是,Strongswan 与 AWS VPC 不兼容。
如果有关于如何进一步测试的想法,我很乐意进行进一步的测试。
答案1
谢谢Strongswan 用户列表中的 Doug Tucker我也能够与 Strongswan 建立连接。
我最好的猜测是,该问题似乎与 xfrm 策略有关。
以下设置(ipsec.conf)对我有用:
config setup
# strictcrlpolicy=yes
uniqueids = no
# Add connections here.
conn %default
# mobike=no
compress=no
authby=secret
keyexchange=ike
ike=aes128-sha1-modp1024!
ikelifetime=8h
esp=aes128-sha1-modp1024!
lifetime=1h
# rekeymargin=3m
keyingtries=%forever
# installpolicy=yes
type=tunnel
leftauth=psk
rightauth=psk
dpddelay=10s
dpdtimeout=30s
dpdaction=restart
conn dc-aws1
auto=start
left=zx.zx.zx.zx
leftid=zz.zz.zz.zz
leftsubnet=zx.zx.zx.zx/24
right=vv.vv.vv.vv # AWS Gateway Public IP
rightsubnet=xx.xx.0.0/16 #remoye subnet
mark=100
leftupdown="/usr/local/bin/aws-updown.sh -ln AWSTunnel1 -ll 169.254.6.2/30 -lr 169.254.6.1/30 -m 100 -r xx.xx.0.0/16"
旁注:vpn 连接已移至虚拟机。zx.zx.zx.zx 是现在运行 vpn 连接的虚拟机的 IP 地址(托管在 zz.zz.zz.zz 上)。
aws-updown.sh 也在 strongswan 用户列表中提供,但也可以在github:
#!/bin/bash
while [[ $# > 1 ]]; do
case ${1} in
-ln|--link-name)
TUNNEL_NAME="${2}"
TUNNEL_PHY_INTERFACE="${PLUTO_INTERFACE}"
shift
;;
-ll|--link-local)
TUNNEL_LOCAL_ADDRESS="${2}"
TUNNEL_LOCAL_ENDPOINT="${PLUTO_ME}"
shift
;;
-lr|--link-remote)
TUNNEL_REMOTE_ADDRESS="${2}"
TUNNEL_REMOTE_ENDPOINT="${PLUTO_PEER}"
shift
;;
-m|--mark)
TUNNEL_MARK="${2}"
shift
;;
-r|--static-route)
TUNNEL_STATIC_ROUTE="${2}"
shift
;;
*)
echo "${0}: Unknown argument \"${1}\"" >&2
;;
esac
shift
done
command_exists() {
type "$1" >&2 2>&2
}
create_interface() {
ip link add ${TUNNEL_NAME} type vti local ${TUNNEL_LOCAL_ENDPOINT} remote ${TUNNEL_REMOTE_ENDPOINT} key ${TUNNEL_MARK}
ip addr add ${TUNNEL_LOCAL_ADDRESS} remote ${TUNNEL_REMOTE_ADDRESS} dev ${TUNNEL_NAME}
ip link set ${TUNNEL_NAME} up mtu 1419
}
configure_sysctl() {
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.${TUNNEL_NAME}.rp_filter=2
sysctl -w net.ipv4.conf.${TUNNEL_NAME}.disable_policy=1
sysctl -w net.ipv4.conf.${TUNNEL_PHY_INTERFACE}.disable_xfrm=1
sysctl -w net.ipv4.conf.${TUNNEL_PHY_INTERFACE}.disable_policy=1
}
add_route() {
IFS=',' read -ra route <<< "${TUNNEL_STATIC_ROUTE}"
for i in "${route[@]}"; do
ip route add ${i} dev ${TUNNEL_NAME} metric ${TUNNEL_MARK}
done
iptables -t mangle -A FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
iptables -t mangle -A INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK}
ip route flush table 220
}
cleanup() {
IFS=',' read -ra route <<< "${TUNNEL_STATIC_ROUTE}"
for i in "${route[@]}"; do
ip route del ${i} dev ${TUNNEL_NAME} metric ${TUNNEL_MARK}
done
iptables -t mangle -D FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
iptables -t mangle -D INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK}
ip route flush cache
}
delete_interface() {
ip link set ${TUNNEL_NAME} down
ip link del ${TUNNEL_NAME}
}
# main execution starts here
command_exists ip || echo "ERROR: ip command is required to execute the script, check if you are running as root, mostly to do with path, /sbin/" >&2 2>&2
command_exists iptables || echo "ERROR: iptables command is required to execute the script, check if you are running as root, mostly to do with path, /sbin/" >&2 2>&2
command_exists sysctl || echo "ERROR: sysctl command is required to execute the script, check if you are running as root, mostly to do with path, /sbin/" >&2 2>&2
case "${PLUTO_VERB}" in
up-client)
create_interface
configure_sysctl
add_route
;;
down-client)
cleanup
delete_interface
;;
esac