TailScale 实现「出口节点」Exit Node(导向所有流量经这出口节点)

TailScale 实现「出口节点」“Exit Node”(导向所有流量经这出口节点)

Non.Exit.Node.png

Exit.Node.png

前言:

当你使用公共网络时,如在咖啡廰的WI-FI、酒店的网络、或者钓鱼WI-FI(不信任的网络)等地方访问家里的设备、银行服务或者公司的伺服器,很可能内容会给中间人截听。

然而TailScale实现「出口节点」"Exit Node"功能,在加密(基于WireGuard)的TailScale网络内导向所有流量经「出口节点」访问,从而避免敏感资料给有心人收获取。

目录内容:
  1. 部署「出口节点」Exit Node
  2. 客户端使用「出口节点」
    • Windows 客户端
    • Android 客户端
    • Linux 客户端
1. 部署「出口节点」"Exit Node"

「出口节点」Exit Node暂时只支持在Linux系统的设备,这范例中以CentOS7平台部署「出口节点」。

  • 安装仓库管理软件

TS_Nodes_Server_091936.png

sudo yum install yum-utils
  • 添加Tailscale 仓库地址

TS_Nodes_Server_091951.png

sudo yum-config-manager --add-repo https://pkgs.tailscale.com/stable/centos/7/tailscale.repo
  • 安装Tailscale

TS_Nodes_Server_092030.png

sudo yum install tailscale -y
  • 启用Tailscale 服务

TS_Nodes_Server_092058.png

sudo systemctl enable --now tailscaled
  • 验证及接入到Tailscale 网络
sudo tailscale up

TS_Nodes_Server_092146.png

TS_Nodes_Server_092152.png

此页面显示成功授权

  • 返回CentOS7终端查看分配到的IP地址

TS_Nodes_Server_092222.png

tailscale ip

  • 启用 CentOS7上的 IP 转发(IP Forwarding)

「出口节点」的设定需要用到 IP 转发(IP Forwarding)特性

TS_Nodes_Server_092518.png

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
  • 发布此装置为「出口节点Exit Node

TS_Nodes_Server_092621.png

sudo tailscale up --advertise-exit-node
  • 网页控制台管理节点

需要允许将此设备为「出口节点Exit Node

登入到网页控制台管理页面的 > Machines

TS_Nodes_Server_092633.png

图示为未允许将此设备为「出口节点Exit Node

TS_Nodes_Server_092647.png

TS_Nodes_Server_092717.png

启用Use as Exit node

TS_Nodes_Server_092730.png

图示已经允许将此设备为「出口节点Exit Node

  • 停用「密钥过期」功能

基于安全特性,Tailscale每隔6个月需要重新授权装置,作为「出口节点Exit Node,建议停用,以防止定期重新验证。

TS_Nodes_Server_092745.png

TS_Nodes_Server_092753.png

2. 客户端使用「出口节点」

使用这个功能,TailScale的软件版本不能低于1.6

Windows 客户端使用「出口节点」

TS_Nodes_Windows_092824.png

未使用「出口节点」的公网IP地址为221.126.xxx.xxx

TS_Nodes_Windows_092905.png

TS_Nodes_Windows_094319.png

使用「出口节点」的公网IP地址为146.56.xxx.xxx

TS_Nodes_Windows_094832.png

出口的网速

Android 客户端使用「出口节点」

TS_Nodes_Andorid_20211008102710 (Small).jpg

未使用「出口节点」的公网IP地址为221.126.xxx.xxx

TS_Nodes_Andorid_20211008102810 (Small).jpg

TS_Nodes_Andorid_20211008102835 (Small).jpg

TS_Nodes_Andorid_20211008102836 (Small).jpg

TS_Nodes_Andorid_20211008102904 (Small).jpg

TS_Nodes_Andorid_20211008102915 (Small).jpg

使用「出口节点」的公网IP地址为146.56.xxx.xxx

Linux 客户端使用「出口节点」

sudo tailscale up --exit-node=100.100.57.95

其中100.100.57.95 为「出口节点」的 TailScale IP 地址`

测速

TS_Nodes_Linux_095542.png

结语:

如果内网的网络限制而不能访问某404区,基于导向所有流量经出口节点特性,应该可以跨越。(前提是「出口节点」Exit Node的地理位置要适中)

参考资料:
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要获取客户端经过所有网络节点的 IP,你需要在服务器端编写代码来获取这些信息。下面是一个简单的示例: 1. 在服务器端接收客户端连接后,使用`getpeername()`函数获取客户端的IP地址和端口号。这将给出客户端通过最后一个网络节点的IP地址。 2. 如果你想获取客户端通过的所有网络节点的IP地址,你可以通过`setsockopt()`函数将`IP_PKTINFO`选项设置为套接字。这将允许你在收到数据包时获取每个数据包的来源IP地址和接收接口的索引。 3. 在接收数据包时,你可以使用`recvmsg()`函数来获取数据包的来源IP地址和接收接口的索引。这将告诉你数据包通过哪个网络节点传输。 4. 如果你想获取所有网络节点的IP地址,你可以将接收到的所有IP地址存储在一个列表中,并进行去重处理。这样,你就可以获取客户端经过的所有网络节点的IP地址。 下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <sys/types.h> #include <netdb.h> int main(int argc, char **argv) { int sockfd, n; struct sockaddr_in servaddr, cliaddr; socklen_t len; char mesg[1000]; char str[INET_ADDRSTRLEN]; struct msghdr msg; struct iovec iov[1]; char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; struct cmsghdr *cmsg; struct in_pktinfo *pktinfo; char *buf[1000]; int i, j, k; struct addrinfo hints, *res; struct sockaddr_in *sa; sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(32000); bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); memset(&msg, 0, sizeof(msg)); memset(control, 0, sizeof(control)); iov[0].iov_base = buf; iov[0].iov_len = sizeof(buf); msg.msg_name = &cliaddr; msg.msg_namelen = sizeof(cliaddr); msg.msg_control = control; msg.msg_controllen = sizeof(control); msg.msg_iov = iov; msg.msg_iovlen = 1; while (1) { len = sizeof(cliaddr); n = recvmsg(sockfd, &msg, 0); if (n < 0) { perror("recvmsg"); exit(1); } buf[n] = '\0'; // 获取数据包来源IP地址和接收接口的索引 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { pktinfo = (struct in_pktinfo *) CMSG_DATA(cmsg); printf("Received packet from %s on interface %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), pktinfo->ipi_ifindex); } } // 获取所有网络节点的IP地址 memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; getaddrinfo(inet_ntoa(cliaddr.sin_addr), NULL, &hints, &res); for (i = 0; res != NULL; res = res->ai_next) { sa = (struct sockaddr_in *) res->ai_addr; if (sa->sin_family == AF_INET) { j = 1; for (k = 0; k < i; k++) { if (strcmp(inet_ntoa(sa->sin_addr), inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr)) == 0) { j = 0; break; } } if (j) { printf("Intermediate node %d: %s\n", i + 1, inet_ntoa(sa->sin_addr)); i++; } } } } return 0; } ``` 这个示例代码使用`recvmsg()`函数来接收客户端发送的数据包,并使用`getpeername()`函数获取客户端的IP地址。它还使用了`setsockopt()`函数将`IP_PKTINFO`选项设置为套接字,以便在收到数据包时获取每个数据包的来源IP地址和接收接口的索引。然后,它使用`getaddrinfo()`函数来获取所有网络节点的IP地址。最后,它通过去重处理来获取客户端经过的所有网络节点的IP地址。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值