在 istio 中我们见识过流量劫持的厉害,我们今天就盘算下相关的技术。
Use Iptables
使用 Iptables 是最常见的劫持方式,无论是常规的 KubeProxy
还是 istio
都使用了这种模式
1 iptables -t nat -A PREROUTING -s 192.168.0.10 -p tcp --dport 80 -j REDIRECT --to-port 8080
这样我们就可以将本身发往 192.168.0.10:80 的请求转到到 192.168.0.10:8080
这种技术一般被称之为 DNAT
Use epbf
使用比较新的 Linux 版本可以 sockmap 完成在 本机 上的更高级的转发方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <linux/bpf.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/tcp.h> struct bpf_map_def SEC ("maps" ) sock_map = { .type = BPF_MAP_TYPE_SOCKMAP, .max_entries = 2 , }; SEC("sockops" ) int handle_tcp (struct __sk_buff *skb) { struct ethhdr *eth = bpf_hdr_pointer(skb); struct iphdr *ip = (struct iphdr *)(eth + 1 ); struct tcphdr *tcp = (struct tcphdr *)(ip + 1 ); if (ip->protocol != IPPROTO_TCP) return 0 ; bpf_sock_ops_redirect(skb, &sock_map, BPF_F_INGRESS); return 0 ; }
Use Tun/Tap
相对复杂点
常见一个 tun 虚拟网卡,分配地址,并且激活
1 2 3 4 5 ip tuntap add mode tun dev tun0 ip addr add 10.0.0.1/24 dev tun0 ip link set dev tun0 up
配置路由
这里比如我们将 192.168.0.0/24
都转到这里
1 ip route add 192.168.0.0/24 dev tun0
编写代码读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int tun_fd = open("/dev/net/tun" , O_RDWR);if (tun_fd < 0 ) { perror("Failed to open TUN device" ); exit (1 ); } struct ifreq ifr ;memset (&ifr, 0 , sizeof (ifr));ifr.ifr_flags = IFF_TUN; if (ioctl(tun_fd, TUNSETIFF, (void *)&ifr) < 0 ) { perror("Failed to set TUN device options" ); close(tun_fd); exit (1 ); } char buffer[4096 ];ssize_t num_bytes;while (1 ) { num_bytes = read(tun_fd, buffer, sizeof (buffer)); if (num_bytes < 0 ) { perror("Error while reading from TUN device" ); close(tun_fd); exit (1 ); } }
这里有个完整的例子 tuntap-packet-dumper
当我们尝试 Ping
的时候,就可以从 Tun0 的另外一头获得数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Flags: 0x0000 Protocol: 0x86dd To: 60:00:00:00:00:08 From: 3a:ff:fe:80:00:00 IP Version 0 Priority: 0 Flow Label: 0x001077 Payload length: 26488 Next header: 0xd2 Hop limit : 68 Source IP: 9c62:ff02:: Dest IP: 2:8500:96a0:: 48: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0: 00 00 10 77 67 78 d2 44 9c 62 ff 02 00 00 00 00 16: 00 00 00 00 00 00 00 00 00 02 85 00 96 a0 00 00 32: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
USE LD_PRELOAD
这是 chatgpt
给的一种方式,原以为很离谱,真的有这样的 torsocks
1 export LD_PRELOAD=/lib/libtorsocks.so
本质上编写自定义共享库:创建一个共享库来拦截与 TCP 相关的系统调用并修改它们的行为。您可以使用 C 或 C++ 等编程语言来编写库。一些通常被拦截的函数包括 socket、connect、bind、listen、accept、send 和 recv。在这些拦截函数中,您可以根据需要实施重定向 TCP 流量的逻辑。