diff --git a/cgroup-tproxy.sh b/cgroup-tproxy.sh old mode 100644 new mode 100755 index d9e117d..4f9a9d0 --- a/cgroup-tproxy.sh +++ b/cgroup-tproxy.sh @@ -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 -# -############################################################################# -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 \ No newline at end of file diff --git a/config.json b/config.json index 22ca5fe..91f9b0e 100644 --- a/config.json +++ b/config.json @@ -13,6 +13,5 @@ "enable_ipv4": true, "enable_ipv6": true, "table": 10007, - "fwmark": 39283, - "mark_newin": 39271 + "fwmark": 39283 } diff --git a/readme.md b/readme.md index 7aa9401..13697e5 100644 --- a/readme.md +++ b/readme.md @@ -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 ``` diff --git a/src/cgproxy.hpp b/src/cgproxy.hpp index a67515f..cc4f58f 100644 --- a/src/cgproxy.hpp +++ b/src/cgproxy.hpp @@ -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 diff --git a/src/cgproxyd.hpp b/src/cgproxyd.hpp index 5a420b3..58e8a74 100644 --- a/src/cgproxyd.hpp +++ b/src/cgproxyd.hpp @@ -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)) {