精华内容
下载资源
问答
  • HevSocks5Tproxy HevSocks5Tproxy 是一个简单、轻量级的 Linux 透明代理。 特征 IPv4/IPv6。 (双栈) 重定向 TCP 连接。 重定向 UDP 数据包。 (基于 TCP 的 UDP) 如何建造 Linux : git clone --recursive ...
  • tproxy-example, 如何使用TPROXY来代理tcp连接的示例 tproxy示例Linux的防火墙是其中最强大的网络工具之一。 我最喜欢的特性之一是tproxy目标,它的名称意味着你可以以代理不同类型的连接。在寻找如何使用TPROXY的...
  • 如果省略, in默认为:3128默认到哪个端口,您将设置iptables规则的tproxy。 你可以用HTTP_PROXY,HTTPS_PROXY环境变量设置out上行代理。 ...
  • tproxy-开源

    2021-05-02 19:39:52
    用户空间单端口单向tcp代理,可处理带外数据以及telnet穿越防火墙隧道。
  • tproxy透明代理帮助,里面详细介绍了Tproxy的规则及socket设置及相关的用法功能,希望对你有所帮助。
  • tproxy, 简单TCP路由代理 tproxytproxy是构建在 Gevent 上的简单TCP路由代理( 图层 7 ),它允许你在 python 中配置例程逻辑。 产品受到来自代理机器的启发,但有一些独特的特性,如 borrowed worker worker mo
  • TPROXY使用介绍

    2020-12-30 10:45:36
    1、TPROXY是什么你可能听说过TPROXY,它通常配合负载均衡软件HAPrxoy或者缓存软件Squid使用。在所有"Proxy"类型的应用中都一个共同的问题,就是后端的目标服务器上看到的连接的Source IP都不再是用户原始的IP,而是...

    1、TPROXY是什么

    你可能听说过TPROXY,它通常配合负载均衡软件HAPrxoy或者缓存软件Squid使用。

    在所有"Proxy"类型的应用中都一个共同的问题,就是后端的目标服务器上看到的连接的Source IP都不再是用户原始的IP,而是前端的"Proxy"服务器的IP。

    拿HAProxy举例来说,假设你有3台后端Web服务器提供服务,前端使用HAProxy作为负载均衡设备。所有用户的HTTP访问会先到达HAProxy,HAProxy作为代理,将这些请求按照指定的负载均衡规则分发到后边的3台Web服务器上。这个操作本身没有任何问题,因为HAProxy就应该是这么工作的。但是对于某些对于用户的访问IP有限制的敏感应用,问题来了: 后端服务器上的ACL无法限制哪些IP可以访问,因为在它看来,所有连接的SOURCE IP都是HAProxy的IP。

    这就是为什么TPROXY产生的原因,最早TPROXY是作为Linux内核的一个patch,从2.6.28以后TPRXOY已经进入官方内核。TPRXOY允许你"模仿"用户的访问IP,就像负载均衡设备不存在一样,当HTTP请求到达后端的Web服务器时,在后端服务器上用netstat查看连接的时候,看到连接的SOURCE

    IP就是用户的真实IP,而不是haproxy的IP。TPROXY名字中的T表示的就是transparent(透明)。

    TPROXY主要功能如下:

    1.重定向一部分经过路由选择的流量到本地路由进程(类似NAT中的REDIRECT)

    2.使用非本地IP作为SOURCE

    IP初始化连接

    3.无需iptables参与,在非本地IP上起监听

    如果想要了解TPROXY的具体工作原理,请参考作者本人写的PPT,介绍的比较详细了:

    2、如何编译TPROXY

    在2.6.28以后的内核中,TPROXY已经是官方内核的一部分了。可以查看当前内核是否支持TPROXY

    # grep

    "TPROXY" config-`uname -r`

    CONFIG_NETFILTER_TPROXY=m

    CONFIG_NETFILTER_XT_TARGET_TPROXY=m

    如果没有支持,则需要自己重新编译内核,但不需要再对内核打patch,只需要在编译内核时选择TPROXY即可。

    Networking

    support (NET [=y])

    -> Networking

    options

    -> Network packet

    filtering framework (Netfilter) (NETFILTER [=y])

    -> Core Netfilter

    Configuration

    ->

    Transparent proxying support

    (EXPERIMENTAL)

    "TPROXY" target support

    (EXPERIMENTAL) (NEW)

    "socket" match support

    (EXPERIMENTAL) (NEW)

    然后在编译HAProxy时,选择TPROXY支持即可。

    make TARGET=linux26

    USE_LINUX_TPROXY=1

    make install target=linux26

    3、如何使用TPROXY(配合haproxy)

    首选,需要在HAProxy的GROUP配置中加入如下一行;

    source 0.0.0.0 usrsrc clientip

    这行配置告诉HAProxy使用用户的真实IP作为SOURCE

    IP访问这个GROUP。

    整个的配置看起来会像下边这样:

    listen VIP_Name

    192.168.2.87:80

    mode http

    option forwardfor

    source

    0.0.0.0 usesrc clientip

    cookie SERVERID

    insert nocache indirect

    server

    server1 10.0.0.60:80 weight 1 cookie server1 check

    server

    server2 10.0.0.61:80 weight 1 cookie server2 check

    server backup

    127.0.0.1:80 backup

    option

    redispatch

    接下来,使用netfilter

    mangle表中一个名为"socket"的match识别出发往本地socket的数据包(通过做一次socket检查)。

    然后我们需要配置iptables规则,给那些发往本地socket的数据包打上mark。

    然后新增一条路由规则,告诉内核将这些带有mark的数据包直接发送到本地回环地址进行路由处理。

    #!/bin/sh

    /sbin/iptables -t mangle -N DIVERT

    /sbin/iptables -t mangle -A PREROUTING -p tcp -m socket -j

    DIVERT

    /sbin/iptables -t mangle -A DIVERT -j MARK --set-mark 1

    /sbin/iptables -t mangle -A DIVERT -j ACCEPT

    /sbin/ip rule add fwmark 1 lookup 100

    /sbin/ip route add local 0.0.0.0/0 dev lo table 100

    4、部署TPROXY需要注意的点

    在部署TPROXY时,最常见的错误是遗忘将返回流量通过HAProxy。

    因为我们"伪装了"通过HAProxy的数据包的SOURCE

    IP,后端Web服务器看到的就是用户的IP,那么Web服务器使用自己的IP返回响应数据包,使用用户真实IP作为DESTINATION

    IP。当返回的数据包到达用户端的时候,用户看到返回的包的源IP不是自己请求的HAProxy的IP,则会认为这个包不是合法的回应而丢弃这个数据包。

    要解决这个问题,有两个办法:

    第一个办法是将HAProxy配置成网桥模式。

    第二个办法是将HAProxy和后端Web服务器放到同一子网中,后端服务器将自己的默认网关指向HAProxy,HAProxy同时需要承担NAT功能。

    在后端服务器上:

    ip route add default via haproxy's_lan_ip

    在haproxy上: /sbin/iptables -t nat -A POSTROUTING -s backend's_ip -o

    eht0 -j MASQUERADE

    5、haproxy上需要配合修改的一些内核参数

    # 允许ip转发

    echo 1 > /proc/sys/net/ipv4/conf/all/forwarding

    # 设置松散逆向路径过滤

    echo 2 >

    /proc/sys/net/ipv4/conf/default/rp_filter

    echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter

    echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter

    # 允许ICMP重定向

    echo 1 >

    /proc/sys/net/ipv4/conf/all/send_redirects

    echo 1 >

    /proc/sys/net/ipv4/conf/eth0/send_redirects

    将上边的配置持久化,写入/etc/sysctl.conf

    net.ipv4.ip_forward = 1

    net.ipv4.conf.default.rp_filter = 2

    net.ipv4.conf.all.rp_filter = 2

    net.ipv4.conf.eth0.rp_filter = 0

    net.ipv4.conf.all.send_redirects = 1net.ipv4.conf.default.send_redirects = 1

    参考文档:

    展开全文
  • d 224.0.0.0/4 -j RETURN sudo iptables -t mangle -A SSUDP -d 240.0.0.0/4 -j RETURN sudo iptables -t mangle -A SSUDP -p udp -j TPROXY –on-port 10053 –tproxy-mark 0x01/0x01 sudo iptables -t mangle -A ...

    这两天闲着没事简单研究了下关于透明代理的方面的东西,有一些感想来记录下。

    先来简单说下透明代理的大体流程:

    1.用户将流量发送的网关服务器

    2.网关通过设置iptables、ip路由策略方式将感兴趣的流量截获并重定向代理应用程序

    3.代理应用程序通过某些方式获取原始目标的IP地址和port等信息 最后根据需求来定制实现不同的协议代理(如socks5)

    ...

    一、第一步没什么好说 直接跳过 。

    二、 流量转发

    主要有两种方案来重定向流量:

    1.iptables+redirect

    2.iptables+tproxy

    因为这两种方式的原理不同,会影响第三步的代理应用的实现方式。首先方法一是采用的DNAT的方式来转发流量的,这意味着代理程序监听到的是目标连接地址是本地地址。而方案二中确实不改变目标源地址,但是其实目标端口还是变了。

    先贴一下常用的iptables的设置规则,以redsocks为例:

    #全局TCP代理规则 iptables+REDIRECT

    sudo iptables -t nat -N SSTCP

    sudo iptables -t nat -A SSTCP -d x.x.x.x -j RETURN #SS Server tcp via

    sudo iptables -t nat -A SSTCP -d 0.0.0.0/8 -j RETURN

    sudo iptables -t nat -A SSTCP -d 10.0.0.0/8 -j RETURN

    sudo iptables -t nat -A SSTCP -d 127.0.0.0/8 -j RETURN

    sudo iptables -t nat -A SSTCP -d 169.254.0.0/16 -j RETURN

    sudo iptables -t nat -A SSTCP -d 172.16.0.0/12 -j RETURN

    sudo iptables -t nat -A SSTCP -d 192.168.0.0/16 -j RETURN

    sudo iptables -t nat -A SSTCP -d 224.0.0.0/4 -j RETURN

    sudo iptables -t nat -A SSTCP -d 240.0.0.0/4 -j RETURN

    sudo iptables -t nat -A SSTCP -p tcp -j REDIRECT –to-ports 12345

    sudo iptables -t nat -A PREROUTING -p tcp -j SSTCP

    #局部UDP代理规则 手动指定IP端口 可解决DNS污染

    #sudo iptables -t nat -N SSDNS,

    #sudo iptables -t nat -A SSDNS -p udp –dport 53 -j REDIRECT –to-ports 10053

    #sudo iptables -t nat -A PREROUTING -p udp -j SSDNS

    #全局UDP代理规则 iptables+TPROXY

    sudo ip route add local 0.0.0.0/0 dev lo table 100

    sudo ip rule add fwmark 1 table 100

    sudo iptables -t mangle -N SSUDP

    sudo iptables -t mangle -A SSUDP -d 0.0.0.0/8 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 10.0.0.0/8 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 127.0.0.0/8 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 169.254.0.0/16 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 172.16.0.0/12 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 192.168.0.0/16 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 224.0.0.0/4 -j RETURN

    sudo iptables -t mangle -A SSUDP -d 240.0.0.0/4 -j RETURN

    sudo iptables -t mangle -A SSUDP -p udp -j TPROXY –on-port 10053 –tproxy-mark 0x01/0x01

    sudo iptables -t mangle -A PREROUTING -p udp -j SSUDP

    三.  应用实现(以redsocks为例)

    先说说第一种:iptables+redirect

    iptables+redirect 这种一般是用来重定向TCP数据的 所以代理程序可以通过getsockopt (s, SOL_IP, SO_ORIGINAL_DST, &dstaddr, &n)函数来获取原始目标地址和端口信息,大概的原理应该是通过查询系统ip_conntrack结构来获得原始目标地址和端口信息。但这个SO_ORIGINAL_DST参数并不适用于UDP协议,所以才有了的tproxy的用武之地。不过这种方式还是可以用在某些特定的UDP协议上如DNS,因为可以手动设置一个IP和端口作为原始目标DNS服务器地址,这样就可以解决DNS污染的问题。在redsocks的配置文件中可以这样设置:

    redudp {

    //`ip' and `port'  for redsocks to listen

    local_ip = 0.0.0.0;

    local_port = 10053;

    // `ip' and `port' of socks5 proxy server.

    ip = 127.0.0.1;

    port = 7456;

    //`ip' and `port'  of DNS server .

    dest_ip = 8.8.8.8;

    dest_port = 53;

    udp_timeout = 30;

    udp_timeout_stream = 180;

    }

    第二种:iptables+tproxy

    这种方式感觉会稍微复杂一点,但更通用,可以用来代理UDP协议。首先我们知道tproxy转发数据的时候,并不会改变原始的源地址和目标地址但却改变了目标端口 。所以这里有两个问题要解决:

    1.套接字如何监听到非本地IP地址

    2.如何获取的原始目标的端口

    第一问题比较粗暴的解决方式是创建的一个底层的套接字来抓包 ,但明显不够优雅,所以的一般的代理应用是先用setsockopt函数为套接字设置IP_TRANSPARENT标识,再去监听0.0.0.0地址这样的方式来实现监听任意IP。

    第二个问题的方式解决的也比较巧妙 先调用setsockopt (s, IPPROTO_IP, IP_RECVORIGDSTADDR, &n, sizeof(int))函数为套接字设置IP_RECVORIGDSTADDR标识,然后通过recvmsg函数从tproxy那边接受发过来的msghdr结构体信息,并循环遍历cmsghdr成员最终获取到原始目标的地址和端口,也就是说tproxy会向msghdr(附属数据结构)填入原始目标ip和端口信息,再通过sendmsg函数发送给代理应用。

    这两个问题解决了基本上后面的应用实现就随意发挥了。在使用tproxy的时候,不要忘了添加一个local路由信息将流量发到本地网卡。

    最后贴关于两个方案的测试代码:

    方案一:iptables+redirect

    //TCP.C

    #include #include #include #include #include #include #include #include #include int handle_client (int c, struct sockaddr_in *clntaddr);

    int tunnel_transparently (int c, struct sockaddr_in *clntaddr, struct sockaddr_in *dstaddr);

    int main (int argc, char **argv)

    {

    int s;

    int c;

    short int port;

    struct sockaddr_in servaddr;

    struct sockaddr_in clntaddr;

    int n;

    int ret;

    struct msghdr msg;

    char cntrlbuf[64];

    struct iovec iov[1];

    char *endptr;

    if (argc < 2)

    {

    printf ("usage: %s \n", argv[0]);

    return -1;

    }

    port = strtol (argv[1], &endptr, 0);

    if (*endptr || port <= 0)

    {

    fprintf (stderr, "invalid port number %s.\n", argv[1]);

    return -2;

    }

    if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)

    {

    fprintf (stderr, "error creating listening socket.\n");

    return -3;

    }

    n=1;

    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

    setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n));

    /* Enable TPROXY IP preservation */

    n=1;

    ret = setsockopt (s, SOL_IP, IP_TRANSPARENT, &n, sizeof(int));

    if (ret != 0)

    {

    fprintf (stderr, "error setting transparency for listening socket. err (#%d %s)\n", errno, strerror(errno));

    close (s);

    return -4;

    }

    memset (&servaddr, 0, sizeof (servaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);

    servaddr.sin_port = htons (port);

    if (bind (s, (struct sockaddr *) &servaddr, sizeof (servaddr)) < 0)

    {

    fprintf (stderr, "error calling bind()\n");

    return -6;

    }

    listen (s, 1024);

    while (1)

    {

    n=sizeof(clntaddr);

    if ((c = accept (s, (struct sockaddr *)&clntaddr, &n)) < 0)

    {

    fprintf (stderr, "error calling accept()\n");

    break;

    }

    handle_client (c, &clntaddr);

    }

    close (s);

    return 0;

    }

    int handle_client (int c, struct sockaddr_in *clntaddr)

    {

    struct sockaddr_in dstaddr={0,};

    int ret;

    int n;

    /* get original destination address */

    n=sizeof(struct sockaddr_in);

    //ret = getsockopt (c, SOL_IP, IP_ORIGDSTADDR, &dstaddr, &n); // IP_ORIGDSTADDR = 20 tproxy ??

    ret = getsockopt (c, SOL_IP, SO_ORIGINAL_DST, &dstaddr, &n); // SO_ORIGINAL_DST = 80 redir

    if (ret != 0)

    {

    fprintf (stderr, "error getting original destination address. err (#%d %s)\n", errno, strerror(errno));

    close (c);

    return -1;

    }

    dstaddr.sin_family = AF_INET;

    printf ("original destination address %X:%d\n", dstaddr.sin_addr.s_addr, dstaddr.sin_port);

    ret = tunnel_transparently (c, clntaddr, &dstaddr);

    if (ret <= 0)

    {

    close (c);

    return -2;

    }

    close (c);

    return 0;

    }

    int tunnel_transparently (int c, struct sockaddr_in *clntaddr, struct sockaddr_in *dstaddr)

    {

    int d;

    int n;

    int ret;

    if (clntaddr == NULL || dstaddr == NULL)

    {

    return -1;

    }

    d = socket (AF_INET, SOCK_STREAM, 0);

    if (d == -1)

    {

    fprintf (stderr, "error creating socket (#%d %s)\n", errno, strerror(errno));

    return -2;

    }

    n=1;

    ret = setsockopt (d, SOL_IP, IP_TRANSPARENT, &n, sizeof(int));

    if (ret != 0)

    {

    fprintf (stderr, "error setting transparency towards destination. err (#%d %s)\n", errno, strerror(errno));

    close (d);

    return -3;

    }

    ret = bind (d, (struct sockaddr *)clntaddr, sizeof (struct sockaddr_in));

    if (ret != 0)

    {

    fprintf (stderr, "error binding to client . err (#%d %s)\n", errno, strerror(errno));

    close (d);

    return -4;

    }

    ret = connect (d, (struct sockaddr *)dstaddr, sizeof (*dstaddr));

    if (ret != 0)

    {

    fprintf (stderr, "error connecting to detination. err (#%d %s)\n", errno, strerror(errno));

    close (d);

    return -5;

    }

    // TODO: send / recv

    //

    close (d);

    return 0;

    }

    方案二 :iptables+tproxy

    //UDP.C

    #include #include #include #include #include #include #include #define MAX_RECV_BUF (1000)

    int handle_msg (struct msghdr *msg);

    int send_transparently (struct msghdr *msg, struct sockaddr_in *dstaddr);

    int main (int argc, char **argv)

    {

    int s;

    short int port;

    struct sockaddr_in servaddr;

    struct sockaddr_in clntaddr;

    int n;

    int ret;

    struct msghdr msg;

    char cntrlbuf[64];

    struct iovec iov[1];

    char buffer[MAX_RECV_BUF];

    char *endptr;

    if (argc < 2)

    {

    printf ("usage: %s \n", argv[0]);

    return -1;

    }

    port = strtol (argv[1], &endptr, 0);

    if (*endptr || port <= 0)

    {

    fprintf (stderr, "invalid port number %s.\n", argv[1]);

    return -2;

    }

    if ((s = socket (AF_INET, SOCK_DGRAM, 0)) < 0)

    {

    fprintf (stderr, "error creating listening socket.\n");

    return -3;

    }

    n=1;

    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

    setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n));

    n=1;

    ret = setsockopt (s, SOL_IP, IP_TRANSPARENT, &n, sizeof(int));

    if (ret != 0)

    {

    fprintf (stderr, "error setting transparency for listening socket. err (#%d %s)\n", errno, strerror(errno));

    close (s);

    return -4;

    }

    n=1;

    ret = setsockopt (s, IPPROTO_IP, IP_RECVORIGDSTADDR, &n, sizeof(int));

    if (ret != 0)

    {

    fprintf (stderr, "error setting the listening socket to IP_TRANSPARENT. err (#%d %s)\n", errno, strerror(errno));

    close (s);

    return -5;

    }

    memset (&servaddr, 0, sizeof (servaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);

    servaddr.sin_port = htons (port);

    if (bind (s, (struct sockaddr *) &servaddr, sizeof (servaddr)) < 0)

    {

    fprintf (stderr, "error calling bind()\n");

    return -6;

    }

    while (1)

    {

    msg.msg_name = &clntaddr;

    msg.msg_namelen = sizeof(clntaddr);

    msg.msg_control = cntrlbuf;

    msg.msg_controllen = sizeof(cntrlbuf);

    iov[0].iov_base = buffer;

    iov[0].iov_len = sizeof (buffer);

    msg.msg_iov = iov;

    msg.msg_iovlen = 1;

    ret = recvmsg (s, &msg, 0);

    if (ret <= 0)

    {

    fprintf (stderr, "error calling recvmsg(). err (#%d %s)\n", errno, strerror(errno));

    break;

    }

    msg.msg_iov[0].iov_len = ret;

    handle_msg (&msg);

    }

    close (s);

    return 0;

    }

    int handle_msg (struct msghdr *msg)

    {

    struct sockaddr_in *clntaddr;

    struct sockaddr_in dstaddr={0,};

    struct cmsghdr *cmsg;

    int ret;

    int found=0;

    clntaddr = msg->msg_name;

    printf ("recvd msg from %X:%d\n", clntaddr->sin_addr.s_addr, clntaddr->sin_port);

    /* get original destination address */

    for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))

    {

    if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR)

    {

    memcpy (&dstaddr, CMSG_DATA(cmsg), sizeof (struct sockaddr_in));

    dstaddr.sin_family = AF_INET;

    printf ("original dst address %X:%d\n", dstaddr.sin_addr.s_addr, dstaddr.sin_port);

    found = 1;

    }

    }

    if (! found)

    {

    return -1;

    }

    ret = send_transparently (msg, &dstaddr);

    if (ret <= 0)

    {

    return -2;

    }

    return 0;

    }

    int send_transparently (struct msghdr *msg, struct sockaddr_in *dstaddr)

    {

    int d;

    int n;

    int ret;

    if (msg == NULL || dstaddr == NULL)

    {

    return -1;

    }

    d = socket (AF_INET, SOCK_DGRAM, 0);

    if (d == -1)

    {

    fprintf (stderr, "error creating socket (#%d %s)\n", errno, strerror(errno));

    return -2;

    }

    n=1;

    ret = setsockopt (d, SOL_IP, IP_TRANSPARENT, &n, sizeof(int));

    if (ret != 0)

    {

    fprintf (stderr, "error setting transparency towards destination. err (#%d %s)\n", errno, strerror(errno));

    close (d);

    return -3;

    }

    ret = bind (d, (struct sockaddr *)msg->msg_name, sizeof (struct sockaddr_in));

    if (ret != 0)

    {

    fprintf (stderr, "error binding to client . err (#%d %s)\n", errno, strerror(errno));

    close (d);

    return -4;

    }

    ret = sendto (d, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len, 0, (struct sockaddr *)dstaddr, sizeof (*dstaddr));

    if (ret <= 0)

    {

    fprintf (stderr, "error sending to detination. err (#%d %s)\n", errno, strerror(errno));

    close (d);

    return -5;

    }

    close (d);

    return 0;

    }

    一些参考链接:

    http://lists.netfilter.org/pipermail/netfilter-devel/2002-March/007305.html

    https://stackoverflow.com/questions/5615579/how-to-get-original-destination-port-of-redirected-udp-message

    https://ask.helplib.com/sockets/post_1007734

    http://blog.csdn.net/dog250/article/details/7518054/

    http://blog.csdn.net/dog250/article/details/13161945

    先写这么多吧。。

    展开全文
  • 1.为什么使用TPROXY才能代理UDP在进行TCP的代理时,只要在NET表上无脑进行REDIRECT就好了。例如使用ss-redir,你只要把tcp的流量redirect到ss-redir监听的端口上就OK了。但是当你使用这种方法的时候,就会不正常,...

    1.为什么使用TPROXY才能代理UDP

    在进行TCP的代理时,只要在NET表上无脑进行REDIRECT就好了。例如使用ss-redir,你只要把tcp的流量redirect到ss-redir监听的端口上就OK了。但是当你使用这种方法的时候,就会不正常,因为对于UDP进行redirect之后,原始的目的地址和端口就找不到了。

    这是为什么呢?

    ss-redir的原理很简单:使用iptables对PREROUTING与OUTPUT的TCP/UDP流量进行REDIRECT(REDIRECT是DNAT的特例),ss—redir在捕获网络流量后,通过一些技术手段获取REDIRECT之前的目的地址(dst)与端口(port),连同网络流量一起转发至远程服务器。

    针对TCP连接,的确是因为Linux Kernel连接跟踪机制的实现才使获取数据包原本的dst和port成为可能,但这种连接跟踪机制并非只存在于TCP连接中,UDP连接同样存在,conntrack -p udp便能看到UDP的连接跟踪记录。内核中有关TCP与UDP的NAT源码/net/netfilter/nf_nat_proto_tcp.c和/net/netfilter/nf_nat_proto_udp.c几乎一模一样,都是根据NAT的类型做SNAT或DNAT。

    那这究竟是怎么一回事?为什么对于UDP连接就失效了呢?

    回过头来看看ss-redir有关获取TCP原本的dst和port的源码,核心函数是getdestaddr:

    static int

    getdestaddr(int fd, struct sockaddr_storage *destaddr)

    {

    socklen_t socklen = sizeof(*destaddr);

    int error = 0;

    error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen);

    if (error) { // Didn't find a proper way to detect IP version.

    error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen);

    if (error) {

    return -1;

    }

    }

    return 0;

    }

    在内核源码中搜了下有关SO_ORIGINAL_DST的东西,看到了getorigdst:

    static int

    getorigdst(struct sock *sk, int optval, void __user *user, int *len)

    {

    const struct inet_sock *inet = inet_sk(sk);

    const struct nf_conntrack_tuple_hash *h;

    struct nf_conntrack_tuple tuple;

    memset(&tuple, 0, sizeof(tuple));

    lock_sock(sk);

    tuple.src.u3.ip = inet->inet_rcv_saddr;

    tuple.src.u.tcp.port = inet->inet_sport;

    tuple.dst.u3.ip = inet->inet_daddr;

    tuple.dst.u.tcp.port = inet->inet_dport;

    tuple.src.l3num = PF_INET;

    tuple.dst.protonum = sk->sk_protocol;

    release_sock(sk);

    /* We only do TCP and SCTP at the moment: is there a better way? */

    if (tuple.dst.protonum != IPPROTO_TCP &&

    tuple.dst.protonum != IPPROTO_SCTP) {

    pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");

    return -ENOPROTOOPT;

    }

    We only do TCP and SCTP at the moment。Oh,shit!只针对TCP与SCTP才能这么做,并非技术上不可行,只是人为地阻止罢了。

    2.TPROXY

    为了在redirect UDP后还能够获取原本的dst和port,ss-redir采用了TPROXY。Linux系统有关TPROXY的设置是以下三条命令:

    ip rule add fwmark 0x2333/0x2333 pref 100 table 100

    ip route add local default dev lo table 100

    iptables -t mangle -A PREROUTING -p udp -j TPROXY --tproxy-mark 0x2333/0x2333 --on-ip 127.0.0.1 --on-port 1080

    大意就是在mangle表的PREROUTING中为每个UDP数据包打上0x2333/0x2333标志,之后在路由选择中将具有0x2333/0x2333标志的数据包投递到本地环回设备上的1080端口;对监听0.0.0.0地址的1080端口的socket启用IP_TRANSPARENT标志,使IPv4路由能够将非本机的数据报投递到传输层,传递给监听1080端口的ss-redir。IP_RECVORIGDSTADDR与IPV6_RECVORIGDSTADDR则表示获取送达数据包的dst与port。

    可问题来了:要知道mangle表并不会修改数据包,那么TPROXY是如何做到在不修改数据包的前提下将非本机dst的数据包投递到换回设备上的1080端口呢?

    这个问题在内核中时如何实现的,还待研究,但是确定是TPROXY做了某些工作。

    TPROXY主要功能:

    重定向一部分经过路由选择的流量到本地路由进程(类似NAT中的REDIRECT)

    在非本地IP上起监听。监听后就可以转发了(神奇吧)

    TPROXY要解决的两个重要的问题

    1.套接字如何监听到非本地IP地址。

    先用setsockopt函数为套接字设置IP_TRANSPARENT标识,再去监听0.0.0.0地址这样的方式来实现监听任意IP。

    2.如何获取的原始目标的端口 。

    先调用setsockopt (s, IPPROTO_IP, IP_RECVORIGDSTADDR, &n, sizeof(int))函数为套接字设置IP_RECVORIGDSTADDR标识,然后通过recvmsg函数从tproxy那边接受发过来的msghdr结构体信息,并循环遍历cmsghdr成员最终获取到原始目标的地址和端口,也就是说tproxy会向msghdr(附属数据结构)填入原始目标ip和端口信息,再通过sendmsg函数发送给代理应用。

    展开全文
  • Linux TPROXY

    2019-07-17 13:13:00
    利用iptables TPROXY target,可以在skb进入到协议栈之前,将skb关联到一个本地监听的socket,并且设置skb的fwmark。可以利用fwmark配置高级路由功能将非本地流量送到本地lo interface,从而能进入本地协议栈的处理。...

    利用iptables TPROXY target,可以在skb进入到协议栈之前,将skb关联到一个本地监听的socket,并且设置skb的fwmark。可以利用fwmark配置高级路由功能将非本地流量送到本地lo interface,从而能进入本地协议栈的处理。skb随后通过ip_rcv进入本地协议栈处理后,可以直接利用已关联的socket进行处理,而不需像普通的处理流程那样,使用skb中的tcp 4-tuples来查找listening socket或者established socket。这样可以把去往任意目的IP和目的端口的skb,关联到本地代理程序监听的socket上。本地代理程序通过accept返回的socket中包含的local address和local port仍然为原始连接的信息。关联到哪个本地监听的socket,可以通过on-port和on-ip option来指定,并且本地监听的socket需要设置IP_TRANSPARENT option,否则TPROXY target会当做找不到指定的本地socket,将skb丢弃。

    使用TPROXY的三个步骤:

    1. 配置代理程序,确定程序监听的IP地址和端口,确保监听socket已打开IP_TRANSPARENT选项。这里程序监听的IP地址可以是

    A:0.0.0.0

    B: 127.0.0.1

    C: 任意本地接口的IP地址

    D: 其它IP地址

           选择使用哪种IP来监听,都是可以的,只要TPROXY target配置中的on-ip配置匹配就好了。

    1. 配置iptables规则,将需要透明代理的流量使用TPROXY target。使用on-port,on-ip参数,指定透明代理监听的socket;使用--tproxy-mark设置使用透明代理流量的fwmark。
    2. 配置高级路由,将使用透明代理的流量送到本地lo接口。

     

    FAQ

    Question1:已匹配iptables规则,使用TPROXY target的报文,还会继续匹配后续规则么?

    Answer1:不会。TPROXY target返回NF_DROP(报文不能匹配找到本地已设置IP_TRANSPARENT option的established或者listening socket)或者NF_ACCEPT(报文可以匹配找到本地已设置IP_TRANSPARENT option的established或者listening socket)

     

    Question2:TPROXY target对已建立连接的报文生效么?

    Answer2:生效。TPROXY target checks whether there is an established socket for the incoming skb (using the tcp 4-tuples in the skb to search the established sockets), if there is a such socket and the socket has set IP_TRANSPARENT, it sets the fwmark for the skb and associates the skb with the socket and return NF_ACCEPT.  If no established socket found, it looks for listening socket using laddr and lport. It checks the configured ip address with --on-ip option, the dev ip, and the destination ip of the skb one by one, and the first non-zero one will be used as the laddr. The port configured with --on-port option will be used as lport, if not configured, the destination port of the skb is used as lport. If a listening socket is matched, and the socket has enabled IP_TRANSPARENT, it sets the mark/mask for the skb and associates the skb with the socket and returns NF_ACCEPT. Otherwise it returns NF_DROP.

     

    Question3:既然TPROXY target对已经建立连接的报文也生效,那么Linux kernel文档https://www.kernel.org/doc/Documentation/networking/tproxy.txt中,下面这段配置是必须的么?

    # iptables -t mangle -N DIVERT

    # iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT

    # iptables -t mangle -A DIVERT -j MARK --set-mark 1

    # iptables -t mangle -A DIVERT -j ACCEPT

    Answer3:不是必须的,但加些配置性能稍微会提高些。对于可以匹配本地Established的报文,设置fwmark,配合高级路由配置确保这些报文在路由结束时可以送到本地的lo接口。这些报文不会再经过TPROXY target处理。(MARK target性能会略高于TPROXY target)

    另外这个配置在调试过程中也会更安全一些,对于报文属于某个已经建立的连接(包括到本地的连接,如ssh)将直接accept,不会走到TPROXY的规则。比如通过ssh登录到一台机器增加规则使用TPROXY target,但是配置时忘记exclude本地流量,这样包括ssh到这台机器的报文也将使用TPROXY target,而TPROXY target发现ssh的报文能匹配上本地established的socket,但是该socket未设置IP_TRANSPARENT,则会返回NF_DROP,导致ssh连接断开。

     

    转载于:https://www.cnblogs.com/gailtang/p/11200388.html

    展开全文
  • 最近尝试配置树莓派做透明代理,按说明配置完 TPROXY 后 UDP 协议的转发一直没有成功,调查后发现失败的原因是本地的 ss-redir 一直收不到内核 TPROXY 转发过来的数据包,iptables 是使用 ss-nat 进行配置的,尝试过...
  • 然后在代理服务器 B 上运行开源项目 tproxy-example ,该程序可以将主机 A 的数据转发给主机 C,同样也可以将主机 C 的数据转发到主机 A。 最后在主机 A 上运行自定义的 client 程序,该程序可以发送自定义的信息到...
  • iptables -t mangle -p tcp -A MY_TCP -j TPROXY --on-port 8081 --tproxy-mark 0x10000000/0xf0000000 #MY_TCP执行TPROXY转发为8081端口并进行标记 iptables -t mangle -A MY_TCP -j ACCEPT iptables -t mangle ...
  • iptables -t mangle -p tcp -A MY_TCP -j TPROXY --on-port 8081 --tproxy-mark 0x10000000/0xf0000000 #MY_TCP执行TPROXY转发为8081端口并进行标记 iptables -t mangle -A MY_TCP -j ACCEPT iptables -t mangle ...
  • UFW+TProxy+Docker配置

    2021-08-13 18:52:30
    Docker容器内的请求也走TProxy 步骤 安装 sudo apt install ufw sudo apt install ipset # tproxy用 # 安装docker详见官方文档 配置ufw sudo ufw default allow routed sudo ufw allow OpenSSH sudo ufw ...
  • # Script for automatic setup of an SS-TPROXY server on CentOS 7.3 Minimal. # export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" exiterr() { echo "E...
  • 图四如果OPTIONS中没有USE_LINUX_TPROXY=1,需要重新编译haproxy打开透传用户IP的代码,haproxy编译步骤如下:1.wgethttp://...
  • TPROXY之殇-NAT设备加代理的恶果

    万次阅读 2013-10-26 23:39:18
    一段往事无独有偶,过了N多年,又到了这个时候,碰到了同样的事情...xx年的万圣节,由于服务器瘫痪我们被罚了500块钱,最终发现瘫痪的原因是TPROXY造成的,当时每个大服务区有一台LVS负载均衡设备,工作在NAT模式下...
  • Tproxy //tproxy实现透明代理 声明 本文为Gleasy原创文章,转载请指明引自Gleasy团队博客 一。需求场景 具体需求如下: 4台Server,2台为Proxy Server,2台为Web Server,均为双网卡; 1个公网IP(183...
  • TPROXY与ip_conntrack

    千次阅读 2012-09-17 19:20:20
    因为写到了这里,答案显然是有的,那就是TPROXY,这个TPROXY其实也是一个Netfilter模块,它就是平添了一次查找,那就是一个流是不是本地socket相关的,和ip_conntrack没有关系,不过依然要针对每一个数据包的五元组...
  • tproxy实现透明代理_sudo_新浪博客 tproxy实现透明代理_sudo_新浪博客 posted on 2015-09-05 17:58lexus 阅...
  • udp_tproxy.c /*  * # iptables -t mangle -N DIVERT  * # iptables -t mangle -A PREROUTING -p udp -m socket -j DIVERT  * # iptables -t mangle -A DIVERT -j MARK --set-mark 1  * # iptables -t ...
  • tproxy 用例

    千次阅读 2012-02-14 09:51:34
    TPROXY - Transparent proxy - TCP program - RHEL6 tcp_tproxy.c /*  * # iptables -t mangle -N DIVERT  * # iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT  * # iptables -t ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,852
精华内容 740
关键字:

tproxy

友情链接: PV电池.zip