logical organize tproxy iptables

This commit is contained in:
Fancy Zhang
2020-07-20 14:49:41 +08:00
parent 032b780e07
commit 0eca327785
5 changed files with 154 additions and 145 deletions

286
cgroup-tproxy.sh Normal file → Executable file
View File

@@ -1,33 +1,35 @@
#!/bin/bash
### This script will proxy/noproxy anything running in specific cgroup
### need cgroup2 support, and iptables cgroup2 path match support
###
### script usage:
### cgroup-tproxy.sh [--help|--config|stop]
### options:
### --config=FILE load config from file
### --help show help info
### stop clean then stop. Variables may change when stopping, which should be avoid
### so always stop first in the last context before start new context
###
### available variables with default value:
### cgroup_noproxy="/noproxy.slice"
### cgroup_proxy="/proxy.slice"
### port=12345
### enable_dns=true
### enable_udp=true
### enable_tcp=true
### enable_ipv4=true
### enable_ipv6=true
### enable_gateway=false
### table=10007
### fwmark=0x9973
### cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET | head -n 1)
###
### semicolon to seperate multi cgroup:
### cgroup_noproxy="/noproxy1.slice:/noproxy2.slice"
### cgroup_proxy="/proxy1.slice:/proxy2.slice"
print_help(){
cat << 'DOC'
#############################################################################
#
# 1. This script need cgroup v2
#
# 2. Listening port is expected to accept iptables TPROXY, while REDIRECT
# will not work in this script, because REDIRECT only support tcp/ipv4
#
# 3. TPROXY need root or special capability whatever process is listening on port
# v2ray as example:
# sudo setcap "cap_net_bind_service=+ep cap_net_admin=+ep" /usr/lib/v2ray/v2ray
#
# 4. this script will proxy anything running in specific cgroup
#
# script usage:
# cgroup-tproxy.sh [--help|--config|stop]
# --config=FILE
# load config from file
# --help
# show help info
# stop
# clean then stop
#
# proxy usage:
# cgproxy <program>
#
#############################################################################
DOC
sed -rn 's/^### ?//;T;p' "$0"
}
## check root
@@ -47,68 +49,73 @@ else
IFS=':' read -r -a cgroup_noproxy <<< "$cgroup_noproxy"
fi
# allow as gateway for local network
[ -z ${enable_gateway+x} ] && enable_gateway=false
## some variables
## tproxy listening port
[ -z ${port+x} ] && port=12345
## some options
## controll options
[ -z ${enable_dns+x} ] && enable_dns=true
[ -z ${enable_tcp+x} ] && enable_tcp=true
[ -z ${enable_udp+x} ] && enable_udp=true
[ -z ${enable_tcp+x} ] && enable_tcp=true
[ -z ${enable_ipv4+x} ] && enable_ipv4=true
[ -z ${enable_ipv6+x} ] && enable_ipv6=true
##
get_available_route_table(){
table=10007
while true; do
ip route show table $table &> /dev/null && ((table++)) || { echo $table && break; }
done
}
[ -z ${enable_gateway+x} ] && enable_gateway=false
## mark/route things
[ -z ${table+x} ] && table=10007 # just a prime number
[ -z ${fwmark+x} ] && fwmark=0x9973
[ -z ${mark_newin+x} ] && mark_newin=0x9967
[ -z ${table+x} ] && table=10007
[ -z ${fwmark+x} ] && fwmark=0x9973
[ -z ${table_reroute+x} ] && table_reroute=$table
[ -z ${table_tproxy+x} ] && table_tproxy=$table
[ -z ${fwmark_reroute+x} ] && fwmark_reroute=$fwmark
[ -z ${fwmark_tproxy+x} ] && fwmark_tproxy=$fwmark
# echo "table: $table fwmark: $fwmark, mark_newin: $mark_newin"
## cgroup things
## cgroup mount point things
[ -z ${cgroup_mount_point+x} ] && cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET | head -n 1)
[ -z $cgroup_mount_point ] && { >&2 echo "iptables: no cgroup2 mount point available"; exit -1; }
[ ! -d $cgroup_mount_point ] && mkdir -p $cgroup_mount_point
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && mount -t cgroup2 none $cgroup_mount_point
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && { >&2 echo "iptables: mount $cgroup_mount_point failed"; exit -1; }
stop(){
iptables -t mangle -L TPROXY_PRE &> /dev/null || return
iptables -t mangle -L TPROXY_ENT &> /dev/null || return
echo "iptables: cleaning tproxy iptables"
iptables -t mangle -D PREROUTING -j TPROXY_PRE
iptables -t mangle -D OUTPUT -j TPROXY_OUT
iptables -t mangle -F TPROXY_PRE
iptables -t mangle -F TPROXY_OUT
iptables -t mangle -F TPROXY_ENT
iptables -t mangle -F TPROXY_OUT
iptables -t mangle -F TPROXY_MARK
iptables -t mangle -X TPROXY_PRE
iptables -t mangle -X TPROXY_OUT
iptables -t mangle -X TPROXY_ENT
iptables -t mangle -X TPROXY_OUT
iptables -t mangle -X TPROXY_MARK
ip rule delete fwmark $fwmark_tproxy lookup $table_tproxy
ip rule delete fwmark $fwmark_reroute lookup $table_reroute &> /dev/null
ip route flush table $table_tproxy
ip route flush table $table_reroute &> /dev/null
ip6tables -t mangle -D PREROUTING -j TPROXY_PRE
ip6tables -t mangle -D OUTPUT -j TPROXY_OUT
ip6tables -t mangle -F TPROXY_PRE
ip6tables -t mangle -F TPROXY_OUT
ip6tables -t mangle -F TPROXY_ENT
ip6tables -t mangle -F TPROXY_MARK
ip6tables -t mangle -X TPROXY_PRE
ip6tables -t mangle -X TPROXY_OUT
ip6tables -t mangle -X TPROXY_ENT
ip rule delete fwmark $fwmark lookup $table
ip route flush table $table
ip -6 rule delete fwmark $fwmark lookup $table
ip -6 route flush table $table
ip6tables -t mangle -X TPROXY_MARK
ip -6 rule delete fwmark $fwmark_tproxy lookup $table_tproxy
ip -6 rule delete fwmark $fwmark_reroute lookup $table_reroute &> /dev/null
ip -6 route flush table $table_tproxy
ip -6 route flush table $table_reroute &> /dev/null
## may not exist, just ignore, and tracking their existence is not reliable
iptables -t nat -D POSTROUTING -m owner ! --socket-exists -j MASQUERADE &> /dev/null
ip6tables -t nat -D POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE &> /dev/null
## unmount cgroup2
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" = "cgroup2" ] && umount $cgroup_mount_point
}
@@ -129,10 +136,20 @@ case $i in
print_help
exit 0
;;
*)
print_help
exit 0
;;
esac
done
## TODO cgroup need to exists before using in iptables since 5.6.5, maybe it's bug
## check cgroup_mount_point, create and mount if necessary
[ -z $cgroup_mount_point ] && { >&2 echo "iptables: no cgroup2 mount point available"; exit -1; }
[ ! -d $cgroup_mount_point ] && mkdir -p $cgroup_mount_point
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && mount -t cgroup2 none $cgroup_mount_point
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && { >&2 echo "iptables: mount $cgroup_mount_point failed"; exit -1; }
## only create the first one in arrary
test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_proxy || exit -1;
test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1;
@@ -152,111 +169,104 @@ done
unset cgroup_proxy && cgroup_proxy=${_cgroup_proxy[@]}
## ipv4 #########################################################################
echo "iptables: applying tproxy iptables"
## use TPROXY
#ipv4#
ip rule add fwmark $fwmark table $table
ip route add local default dev lo table $table
## mangle prerouting
ip rule add fwmark $fwmark_tproxy table $table_tproxy
ip route add local default dev lo table $table_tproxy
# core
iptables -t mangle -N TPROXY_ENT
iptables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark
iptables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark
iptables -t mangle -A TPROXY_ENT -m socket -j MARK --set-mark $fwmark_tproxy
iptables -t mangle -A TPROXY_ENT -m socket -j ACCEPT
iptables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark_tproxy
iptables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark_tproxy
# filter
iptables -t mangle -N TPROXY_PRE
iptables -t mangle -A TPROXY_PRE -m socket --transparent -j MARK --set-mark $fwmark
iptables -t mangle -A TPROXY_PRE -m socket --transparent -j RETURN
iptables -t mangle -A TPROXY_PRE -p icmp -j RETURN
iptables -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
iptables -t mangle -A TPROXY_PRE -p tcp --dport 53 -j TPROXY_ENT
iptables -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
iptables -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
iptables -t mangle -A TPROXY_PRE -j TPROXY_ENT
$enable_gateway || iptables -t mangle -A TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
$enable_dns && iptables -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
$enable_udp && iptables -t mangle -A TPROXY_PRE -p udp -j TPROXY_ENT
$enable_tcp && iptables -t mangle -A TPROXY_PRE -p tcp -j TPROXY_ENT
# hook
iptables -t mangle -A PREROUTING -j TPROXY_PRE
## mangle output
if [ $fwmark_reroute != $fwmark_tproxy ]; then
ip rule add fwmark $fwmark_reroute table $table_reroute
ip route add local default dev lo table $table_reroute
fi
# filter
iptables -t mangle -N TPROXY_MARK
iptables -t mangle -A TPROXY_MARK -m addrtype ! --dst-type UNICAST -j RETURN
$enable_dns && iptables -t mangle -A TPROXY_MARK -p udp --dport 53 -j MARK --set-mark $fwmark_reroute
$enable_udp && iptables -t mangle -A TPROXY_MARK -p udp -j MARK --set-mark $fwmark_reroute
$enable_tcp && iptables -t mangle -A TPROXY_MARK -p tcp -j MARK --set-mark $fwmark_reroute
# cgroup
iptables -t mangle -N TPROXY_OUT
iptables -t mangle -A TPROXY_OUT -p icmp -j RETURN
iptables -t mangle -A TPROXY_OUT -m connmark --mark $mark_newin -j RETURN
iptables -t mangle -A TPROXY_OUT -m addrtype --dst-type LOCAL -j RETURN
iptables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
iptables -t mangle -A TPROXY_OUT -m conntrack --ctdir REPLY -j RETURN
for cg in ${cgroup_noproxy[@]}; do
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
done
for cg in ${cgroup_proxy[@]}; do
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j TPROXY_MARK
done
iptables -t mangle -A OUTPUT -j TPROXY_OUT
# hook
$enable_ipv4 && iptables -t mangle -A OUTPUT -j TPROXY_OUT
#ipv6#
ip -6 rule add fwmark $fwmark table $table
ip -6 route add local default dev lo table $table
## ipv6 #########################################################################
## mangle prerouting
ip -6 rule add fwmark $fwmark_tproxy table $table_tproxy
ip -6 route add local default dev lo table $table_tproxy
# core
ip6tables -t mangle -N TPROXY_ENT
ip6tables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark
ip6tables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark
ip6tables -t mangle -A TPROXY_ENT -m socket -j MARK --set-mark $fwmark_tproxy
ip6tables -t mangle -A TPROXY_ENT -m socket -j ACCEPT
ip6tables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark_tproxy
ip6tables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark_tproxy
# filter
ip6tables -t mangle -N TPROXY_PRE
ip6tables -t mangle -A TPROXY_PRE -m socket --transparent -j MARK --set-mark $fwmark
ip6tables -t mangle -A TPROXY_PRE -m socket --transparent -j RETURN
ip6tables -t mangle -A TPROXY_PRE -p icmpv6 -j RETURN
ip6tables -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
ip6tables -t mangle -A TPROXY_PRE -p tcp --dport 53 -j TPROXY_ENT
ip6tables -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
ip6tables -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
ip6tables -t mangle -A TPROXY_PRE -j TPROXY_ENT
$enable_gateway || ip6tables -t mangle -A TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
$enable_dns && ip6tables -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
$enable_udp && ip6tables -t mangle -A TPROXY_PRE -p udp -j TPROXY_ENT
$enable_tcp && ip6tables -t mangle -A TPROXY_PRE -p tcp -j TPROXY_ENT
# hook
ip6tables -t mangle -A PREROUTING -j TPROXY_PRE
## mangle output
if [ $fwmark_reroute != $fwmark_tproxy ]; then
ip -6 rule add fwmark $fwmark_reroute table $table_reroute
ip -6 route add local default dev lo table $table_reroute
fi
# filter
ip6tables -t mangle -N TPROXY_MARK
ip6tables -t mangle -A TPROXY_MARK -m addrtype ! --dst-type UNICAST -j RETURN
$enable_dns && ip6tables -t mangle -A TPROXY_MARK -p udp --dport 53 -j MARK --set-mark $fwmark_reroute
$enable_udp && ip6tables -t mangle -A TPROXY_MARK -p udp -j MARK --set-mark $fwmark_reroute
$enable_tcp && ip6tables -t mangle -A TPROXY_MARK -p tcp -j MARK --set-mark $fwmark_reroute
# cgroup
ip6tables -t mangle -N TPROXY_OUT
ip6tables -t mangle -A TPROXY_OUT -p icmpv6 -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m connmark --mark $mark_newin -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m addrtype --dst-type LOCAL -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m conntrack --ctdir REPLY -j RETURN
for cg in ${cgroup_noproxy[@]}; do
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
done
for cg in ${cgroup_proxy[@]}; do
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j TPROXY_MARK
done
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
## allow to disable, order is important
$enable_dns || iptables -t mangle -I TPROXY_OUT -p udp --dport 53 -j RETURN
$enable_dns || ip6tables -t mangle -I TPROXY_OUT -p udp --dport 53 -j RETURN
$enable_udp || iptables -t mangle -I TPROXY_OUT -p udp -j RETURN
$enable_udp || ip6tables -t mangle -I TPROXY_OUT -p udp -j RETURN
$enable_tcp || iptables -t mangle -I TPROXY_OUT -p tcp -j RETURN
$enable_tcp || ip6tables -t mangle -I TPROXY_OUT -p tcp -j RETURN
$enable_ipv4 || iptables -t mangle -I TPROXY_OUT -j RETURN
$enable_ipv6 || ip6tables -t mangle -I TPROXY_OUT -j RETURN
if $enable_gateway; then
$enable_dns || iptables -t mangle -I TPROXY_PRE -p udp --dport 53 -j RETURN
$enable_dns || ip6tables -t mangle -I TPROXY_PRE -p udp --dport 53 -j RETURN
$enable_udp || iptables -t mangle -I TPROXY_PRE -p udp -j RETURN
$enable_udp || ip6tables -t mangle -I TPROXY_PRE -p udp -j RETURN
$enable_tcp || iptables -t mangle -I TPROXY_PRE -p tcp -j RETURN
$enable_tcp || ip6tables -t mangle -I TPROXY_PRE -p tcp -j RETURN
$enable_ipv4 || iptables -t mangle -I TPROXY_PRE -j RETURN
$enable_ipv6 || ip6tables -t mangle -I TPROXY_PRE -j RETURN
fi
## do not handle local device connection through tproxy if gateway is not enabled
$enable_gateway || iptables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
$enable_gateway || ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
## make sure following rules are the first in chain TPROXY_PRE to mark new incoming connection or gateway proxy connection
## so must put at last to insert first
iptables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $mark_newin
ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $mark_newin
# message for user
cat << DOC
iptables: noproxy cgroup: ${cgroup_noproxy[@]}
iptables: proxied cgroup: ${cgroup_proxy[@]}
DOC
# hook
$enable_ipv6 && ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
## forward #######################################################################
if $enable_gateway; then
iptables -t nat -A POSTROUTING -m owner ! --socket-exists -j MASQUERADE
ip6tables -t nat -A POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE # only masquerade ipv6 private address
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
echo "ipatbles: gateway enabled"
echo "iptables: gateway enabled"
fi
## message for user
cat << DOC
iptables: noproxy cgroup: ${cgroup_noproxy[@]}
iptables: proxied cgroup: ${cgroup_proxy[@]}
DOC

View File

@@ -13,6 +13,5 @@
"enable_ipv4": true,
"enable_ipv6": true,
"table": 10007,
"fwmark": 39283,
"mark_newin": 39271
"fwmark": 39283
}

View File

@@ -125,8 +125,7 @@ Config file: **/etc/cgproxy/config.json**
"enable_ipv4": true,
"enable_ipv6": true,
"table": 10007,
"fwmark": 39283,
"mark_newin": 39271
"fwmark": 39283
}
```
@@ -161,7 +160,7 @@ Config file: **/etc/cgproxy/config.json**
```
program_noproxy > program_proxy > cgroup_noproxy > cgroup_proxy
enable_ipv6 > enable_ipv4 > enable_tcp > enable_udp > enable_dns
enable_ipv6 = enable_ipv4 > enable_dns > enable_tcp = enable_udp
ommand cgproxy and cgnoproxy always have highest priority
```

View File

@@ -79,6 +79,7 @@ int main(int argc, char *argv[]) {
if (attach_pid) return 0;
string s = join2str(argc - shift, argv + shift, ' ');
debug("executing: %s", s.c_str());
return system(s.c_str());
}
} // namespace CGPROXY::CGPROXY

View File

@@ -271,7 +271,7 @@ class cgproxyd {
}
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
debug("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
info("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
continue;
}
if (!belongToCgroup(cg, config.cgroup_noproxy)) {
@@ -288,7 +288,7 @@ class cgproxyd {
}
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
debug("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
info("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
continue;
}
if (!belongToCgroup(cg, config.cgroup_proxy)) {