44 Commits
v0.01 ... v0.08

Author SHA1 Message Date
fancy
2838ffbb70 bump version 2020-05-14 14:28:08 +08:00
fancy
749fe38ca8 add local aur build 2020-05-14 14:21:40 +08:00
fancy
c0668fd8d2 use macro 2020-05-14 13:57:39 +08:00
fancy
06ae0b9fc5 update readme 2020-05-14 12:07:12 +08:00
fancy
4e04dcf84a update readme 2020-05-14 04:53:23 +08:00
fancy
c0e9ea24c1 allow array input for cgroup_proxy and cgroup_noproxy 2020-05-14 04:47:29 +08:00
fancy
f8e0abbb55 check root, and check iptables before clean 2020-05-13 23:42:05 +08:00
fancy
6c24c68831 only masquerade ipv6 private address 2020-05-08 11:47:51 +08:00
fancy
d3b2dc0465 small fix 2020-05-07 00:22:08 +08:00
fancy
4be7be2083 example: rename to keep order 2020-05-06 02:42:44 +08:00
fancy
25f94968ae install readme.md to doc 2020-05-05 20:14:26 +08:00
fancy
3b4b67df33 small change 2020-05-05 19:30:35 +08:00
fancy
31ae519193 v2ray.service without root 2020-05-05 00:32:09 +08:00
fancy
7f0ebe9d35 remove mark_noproxy, and other small change 2020-05-04 20:41:50 +08:00
fancy
236c08172b example: update readme 2020-05-03 18:29:08 +08:00
fancy
c07ae13030 example: add aliyun doh 2020-05-03 17:00:34 +08:00
fancy
d5ea832b4f change timeout, and dns no need to proxy 2020-05-03 15:05:56 +08:00
fancy
aa5ca6f204 update example, limit to localhost 2020-05-02 08:01:19 +08:00
fancy
a80187f947 optimize function get_cgroup2_mount_point 2020-05-02 04:15:54 +08:00
fancy
dca895c7cc readme update 2020-05-02 03:45:36 +08:00
fancy
08097a54d7 readme update 2020-05-02 03:42:22 +08:00
fancy
bce568d802 readme update 2020-05-02 03:40:32 +08:00
fancy
98c07a31af readme update 2020-05-02 03:37:58 +08:00
fancy
916c11d280 update config example 2020-05-01 17:51:53 +08:00
fancy
72579bc84a typo fix 2020-05-01 13:55:06 +08:00
fancy
5c16fdfb9f add v2ray config example 2020-05-01 11:55:57 +08:00
fancy
f55b09ec12 imporved gateway nat, and bypass broadcast things 2020-05-01 11:26:46 +08:00
fancy
6ae12bf5c4 optimize 2020-04-30 16:32:07 +08:00
fancy
31627dd956 update readme 2020-04-30 02:05:03 +08:00
fancy
9f8d540c78 important optimize 2020-04-30 02:02:11 +08:00
fancy
b8204126c5 maintain update 2020-04-28 20:25:44 +08:00
fancy
daa68f20ea package deb rpm 2020-04-28 19:58:40 +08:00
fancy
836a34cdc8 also handle gateway dns 2020-04-28 13:49:26 +08:00
fancy
3c21882b88 more compact 2020-04-28 12:17:25 +08:00
fancy
71d924a303 more robust clean whem stop 2020-04-28 03:28:48 +08:00
fancy
5d0b137778 more robust
do not handle local device connection through tproxy if gateway is not enabled
2020-04-28 02:37:36 +08:00
fancy
8a29e88bab update clean iptables 2020-04-27 23:00:25 +08:00
fancy
dd234b08a0 typo fix 2020-04-27 21:50:09 +08:00
fancy
168779ea90 fix #1 2020-04-27 21:29:44 +08:00
fancy
5b65ac0ffd typo fix 2020-04-27 14:49:56 +08:00
fancy
6c77233e1e little fix, and update readme 2020-04-27 14:43:40 +08:00
fancy
6be14b9935 new feature: add gateway support 2020-04-27 00:40:39 +08:00
fancy
c1cd7e6e07 clean stuff 2020-04-26 03:43:18 +08:00
fancy
0848d1ddeb small update 2020-04-26 00:28:10 +08:00
32 changed files with 623 additions and 124 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
build
.directory
.vscode
cgproxy2.sh
v2ray_config/proxy
v2ray_config/06_outbounds_proxy.json

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10)
project(cgproxy VERSION 1.0)
project(cgproxy VERSION 3.8)
add_executable(cgattach cgattach.cpp)
install(TARGETS cgattach DESTINATION /usr/bin
@@ -21,3 +21,34 @@ install(FILES cgproxy.conf
DESTINATION /etc/)
install(FILES cgroup-tproxy.sh
DESTINATION /usr/share/cgproxy/scripts/)
install(FILES readme.md
DESTINATION /share/doc/cgproxy/)
## package for deb and rpm
set(CPACK_GENERATOR "DEB;RPM")
set(CPACK_PACKAGE_NAME "cgproxy")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything running in specific cgroup.It aslo supports global transparent proxy and gateway proxy")
## deb pack
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "x86_64")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "springzfx@gmail.com")
set(CONTROL_DIR ${CMAKE_SOURCE_DIR}/control)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CONTROL_DIR}/postinst;${CONTROL_DIR}/prerm")
## rpm pack
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
set(CPACK_RPM_PACKAGE_GROUP "network")
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")
set(CONTROL_DIR ${CMAKE_SOURCE_DIR}/control)
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CONTROL_DIR}/postinst")
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CONTROL_DIR}/prerm")
include(CPack)

View File

@@ -0,0 +1,40 @@
# Maintainer: Fancy Zhang <springzfx@gmail.com>
pkgname=cgproxy-git
pkgver=v3.8.r1.gc0668fd
pkgrel=1
pkgdesc="A transparent proxy program with cgroup2, like proxychains"
arch=('x86_64')
url="https://github.com/springzfx/cgproxy"
license=('')
groups=('')
makedepends=('cmake')
depends=('systemd')
provides=('cgproxy')
conflicts=('cgproxy')
curr_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source=("${pkgname}::git+file://${curr_dir}/../.git")
# source=("${pkgname}::git+file:///home/fancy/workspace/cgproxy/.git")
md5sums=('SKIP')
pkgver() {
cd "$pkgname"
( set -o pipefail
git describe --long --tags 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' ||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
)
}
backup=('etc/cgproxy.conf')
install="cgproxy.install"
build(){
cd "$pkgname"
mkdir -p build && cd build && cmake .. && make
}
package_cgproxy-git(){
cd "$pkgname"/build
make DESTDIR=$pkgdir install
}

View File

@@ -0,0 +1,8 @@
#!/bin/sh
post_install(){
cat <<'DOC'
to start service:
systemctl enable --now cgproxy.service
DOC
}

View File

@@ -10,6 +10,10 @@
#include <unistd.h>
using namespace std;
#define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");}
#define debug(...) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");}
void print_usage() { fprintf(stdout, "usage: cgattach <pid> <cgroup>\n"); }
bool exist(string path) {
@@ -26,29 +30,32 @@ bool validate(string pid, string cgroup) {
if (pid_v && cg_v)
return true;
fprintf(stderr, "paramater validate error\n");
error("paramater validate error");
print_usage();
exit(EXIT_FAILURE);
}
string get_cgroup2_mount_point(){
char cgroup2_mount_point[100];
FILE* fp = popen("findmnt -t cgroup2 -n |cut -d' ' -f 1", "r");
fscanf(fp,"%s",&cgroup2_mount_point);
char cgroup2_mount_point[100]="";
FILE* fp = popen("findmnt -t cgroup2 -n -o TARGET", "r");
int count=fscanf(fp,"%s",&cgroup2_mount_point);
fclose(fp);
if (count=0){
error("cgroup2 not supported");
exit(EXIT_FAILURE);
}
return cgroup2_mount_point;
}
int main(int argc, char *argv[]) {
setuid(0);
setgid(0);
if (getuid() != 0 || getgid() != 0) {
fprintf(stderr, "cgattach need suid sticky bit or run with root\n");
int flag=setuid(0);
if (flag!=0) {
perror("cgattach setuid");
exit(EXIT_FAILURE);
}
if (argc != 3) {
fprintf(stderr, "only need 2 paramaters\n");
error("only need 2 paramaters");
print_usage();
exit(EXIT_FAILURE);
}
@@ -65,20 +72,19 @@ int main(int argc, char *argv[]) {
if (!exist(cgroup_target_path)) {
if (mkdir(cgroup_target_path.c_str(),
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) {
fprintf(stdout, "created cgroup %s success\n", cgroup_target.c_str());
debug("created cgroup %s success", cgroup_target.c_str());
} else {
fprintf(stderr, "created cgroup %s failed, errno %d\n",
cgroup_target.c_str(), errno);
error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno);
exit(EXIT_FAILURE);
}
// fprintf(stderr, "cgroup %s not exist\n",cgroup_target.c_str());
// error("cgroup %s not exist",cgroup_target.c_str());
// exit(EXIT_FAILURE);
}
// put pid to target cgroup
ofstream procs(cgroup_target_procs, ofstream::app);
if (!procs.is_open()) {
fprintf(stderr, "open file %s failed\n", cgroup_target_procs.c_str());
error("open file %s failed", cgroup_target_procs.c_str());
exit(EXIT_FAILURE);
}
procs << pid.c_str() << endl;
@@ -86,7 +92,7 @@ int main(int argc, char *argv[]) {
// maybe there some write error, for example process pid may not exist
if (!procs) {
fprintf(stderr, "write %s to %s failed, maybe process %s not exist\n",
error("write %s to %s failed, maybe process %s not exist",
pid.c_str(), cgroup_target_procs.c_str(), pid.c_str());
exit(EXIT_FAILURE);
}

View File

@@ -11,6 +11,6 @@ else
fi
# test attach success or not
[[ -z "$attached" ]] && echo "config error" && exit 1
[[ -z "$attached" ]] && echo "attach error" && exit 1
exec "$@"

View File

@@ -1,30 +1,48 @@
# see how to configure
# https://github.com/springzfx/cgproxy
########################################################################
## cgroup transparent proxy
## see how to configure, https://github.com/springzfx/cgproxy
###################################################################################
## any process in cgroup_proxy will be proxied, and cgroup_noproxy the opposite
## cgroup must start with slash '/'
# cgroup_proxy="/"
cgroup_proxy="/proxy.slice"
## note, cgroup must start with slash '/'
## the value can be string or bash array
## for array, only the first element will be created if not exist
## and the rest elements will not, so won't be applied
### global proxy with v2ray service
# cgroup_proxy="/"
# cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service")
### global proxy with manual `cgnoporxy qv2ray`
# cgroup_proxy="/"
# cgroup_noproxy="/noproxy.slice"
### default
cgroup_proxy="/proxy.slice"
cgroup_noproxy="/noproxy.slice"
########################################################################
###################################################################################
## allow as gateway for local network
enable_gateway=false
###################################################################################
## listening port of another proxy process, for example v2ray
port=12345
## if you set to false, it's traffic won't go through proxy, but still can go direct to internet
###################################################################################
## if you set to false, it's traffic won't go through proxy,
## but still can go direct to internet
enable_dns=true
enable_tcp=true
enable_udp=true
enable_ipv4=true
enable_ipv6=true
enable_dns=true
########################################################################
###################################################################################
## do not modify this if you don't known what you are doing
table=100
mark_proxy=0x01
mark_noproxy=0xff
mark_newin=0x02
fwmark=0x01
mark_newin=0x02

View File

@@ -11,6 +11,6 @@ else
fi
# test attach success or not
[[ -z "$attached" ]] && echo "config error" && exit 1
[[ -z "$attached" ]] && echo "attach error" && exit 1
exec "$@"

View File

@@ -30,28 +30,37 @@ cat << 'DOC'
DOC
}
check_root(){
uid=$(id -u)
[ ! $uid -eq 0 ] && { >&2 echo "permission denied, need root";exit 0; }
}
check_root
## any process in this cgroup will be proxied
cgroup_proxy="/proxy.slice"
cgroup_noproxy="/noproxy.slice"
# allow as gateway for local network
enable_gateway=false
## some variables
port=12345
## some options
enable_dns=true
enable_tcp=true
enable_udp=true
enable_ipv4=true
enable_ipv6=true
enable_dns=true
## do not modify this if you don't known what you are doing
table=100
mark_proxy=0x01
mark_noproxy=0xff
fwmark=0x01
make_newin=0x02
## cgroup things
# cgroup_mount_point=$(findmnt -t cgroup,cgroup2 -n -J|jq '.filesystems[0].target')
# cgroup_type=$(findmnt -t cgroup,cgroup2 -n -J|jq '.filesystems[0].fstype')
cgroup_mount_point=$(findmnt -t cgroup2 -n |cut -d' ' -f 1)
cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET)
cgroup_type="cgroup2"
cgroup_procs_file="cgroup.procs"
@@ -60,24 +69,35 @@ for i in "$@"
do
case $i in
stop)
iptables -t mangle -F
echo "stopping 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 -X TPROXY_PRE
iptables -t mangle -X TPROXY_OUT
ip6tables -t mangle -F
iptables -t mangle -X TPROXY_ENT
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 -X TPROXY_PRE
ip6tables -t mangle -X TPROXY_OUT
ip rule delete fwmark $mark_proxy lookup $table
ip6tables -t mangle -X TPROXY_ENT
ip rule delete fwmark $fwmark lookup $table
ip route flush table $table
ip -6 rule delete fwmark $mark_proxy lookup $table
ip -6 rule delete fwmark $fwmark lookup $table
ip -6 route flush table $table
iptables -t nat -A OUTPUT -F
ip6tables -t nat -A OUTPUT -F
## 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
exit 0
;;
--config=*)
config=${i#*=}
source $config
shift
;;
--help)
print_help
@@ -92,71 +112,108 @@ test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_
## use TPROXY
#ipv4#
ip rule add fwmark $mark_proxy table $table
ip rule add fwmark $fwmark table $table
ip route add local default dev lo table $table
iptables -t mangle -N TPROXY_ENT
iptables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
iptables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
iptables -t mangle -N TPROXY_PRE
iptables -t mangle -A TPROXY_PRE -p udp -m mark --mark $mark_proxy -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $mark_proxy
iptables -t mangle -A TPROXY_PRE -p tcp -m mark --mark $mark_proxy -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $mark_proxy
iptables -t mangle -A TPROXY_PRE -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
iptables -t mangle -A TPROXY_PRE -m conntrack --ctstate NEW -j CONNMARK --restore-mark
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
iptables -t mangle -A PREROUTING -j TPROXY_PRE
iptables -t mangle -N TPROXY_OUT
iptables -t mangle -A TPROXY_OUT -o lo -j RETURN
iptables -t mangle -A TPROXY_OUT -p icmp -j RETURN
iptables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN # return incoming connection directly, v2ray tproxy not work for this situation, see this: https://github.com/Kr328/ClashForAndroid/issues/146
iptables -t mangle -A TPROXY_OUT -m mark --mark $mark_noproxy -j RETURN
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_noproxy -j RETURN
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_proxy -j MARK --set-mark $mark_proxy
iptables -t mangle -A TPROXY_OUT -m connmark --mark $make_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
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
done
iptables -t mangle -A OUTPUT -j TPROXY_OUT
#ipv6#
ip -6 rule add fwmark $mark_proxy table $table
ip -6 rule add fwmark $fwmark table $table
ip -6 route add local default dev lo table $table
ip6tables -t mangle -N TPROXY_ENT
ip6tables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
ip6tables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
ip6tables -t mangle -N TPROXY_PRE
ip6tables -t mangle -A TPROXY_PRE -p udp -m mark --mark $mark_proxy -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $mark_proxy
ip6tables -t mangle -A TPROXY_PRE -p tcp -m mark --mark $mark_proxy -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $mark_proxy
ip6tables -t mangle -A TPROXY_PRE -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
ip6tables -t mangle -A TPROXY_PRE -m conntrack --ctstate NEW -j CONNMARK --restore-mark
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
ip6tables -t mangle -A PREROUTING -j TPROXY_PRE
ip6tables -t mangle -N TPROXY_OUT
ip6tables -t mangle -A TPROXY_OUT -o lo -j RETURN
ip6tables -t mangle -A TPROXY_OUT -p icmp -j RETURN
ip6tables -t mangle -A TPROXY_OUT -p icmpv6 -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m mark --mark $mark_noproxy -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_noproxy -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_proxy -j MARK --set-mark $mark_proxy
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
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
done
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
## use REDIRECT
# iptables -t nat -A OUTPUT -p tcp -m cgroup --path $cgroup_proxy -j DNAT --to-destination 127.0.0.1:12345
# ip6tables -t nat -A OUTPUT -p tcp -m cgroup --path $cgroup_proxy -j DNAT --to-destination [::1]:12345
## 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
$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
## create proxy prefix command for easy use
# cat << 'DOC' > /usr/bin/cgproxy
# !/usr/bin/bash
# systemd-run -q --slice proxy.slice --scope --user $@
# DOC
# chmod a+x /usr/bin/cgproxy
## 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 $make_newin
ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
## message for user
cat << DOC
proxied cgroup: $cgroup_proxy
noproxy cgroup: ${cgroup_noproxy[@]}
proxied cgroup: ${cgroup_proxy[@]}
DOC
## tproxy need Root or cap_net_admin capability
# setcap cap_net_admin+ep /usr/lib/v2ray/v2ray
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 "gateway enabled"
fi

2
control/postinst Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
systemctl enable --now cgproxy.service

2
control/prerm Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
systemctl disable --now cgproxy.service

118
readme.md
View File

@@ -1,3 +1,5 @@
# Transparent Proxy with cgroup v2
@@ -6,16 +8,17 @@
cgproxy will transparent proxy anything running in specific cgroup. It resembles with *proxychains* and *tsock*, but without their disadvantages, and more powerfull.
It aslo supports global transparent proxy. See [Global transparent proxy](#global-transparent-proxy)
It aslo supports global transparent proxy and gateway proxy. See [Global transparent proxy](#global-transparent-proxy) and [Gateway proxy](#gateway-proxy).
<!--ts-->
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
* [Introduction](#introduction)
* [Prerequest](#prerequest)
* [How to install](#how-to-install)
* [How to use](#how-to-use)
* [Global transparent proxy](#global-transparent-proxy)
* [Gateway proxy](#gateway-proxy)
* [Other useful tools provided in this project](#other-useful-tools-provided-in-this-project)
* [NOTES](#notes)
* [TIPS](#tips)
@@ -29,32 +32,32 @@ It aslo supports global transparent proxy. See [Global transparent proxy](#globa
- cgroup2
Both cgroup and cgroup2 are enable in linux by default. So you don't have to do anything about this.
Both cgroup and cgroup2 are enabled in linux by default. So you don't have to do anything about this.
- `systemd-cgls` to see the cgroup hierarchical tree.
- Why cgroup v2? Because simple, elegant and intuitive.
- TPROXY
A process listening on port (e.g. 12345) to accept iptables TPROXY, for example v2ray's dokodemo-door in tproxy mode.
- Why not REDIRECT? Because REDIRECT only supports tcp and ipv4.
A process listening on port (e.g. 12345) to accept iptables TPROXY, for example v2ray's dokodemo-door in tproxy mode.
## How to install
```bash
mkdir build && cd build && cmake .. && make && make install
mkdir build && cd build && cmake .. && make && sudo make install
```
It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/cgproxy/).
- It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/?K=cgproxy).
- DEB and RPM are packaged in [release page](https://github.com/springzfx/cgproxy/releases).
## How to use
- First enable service
- First enable and start service
```bash
sudo systemctl enable --now cgproxy.service
sudo systemctl status cgproxy.service
```
- Then prefix with cgproxy with your command, just like proxychains
```
@@ -67,38 +70,62 @@ It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/cgproxy/).
cgproxy curl -vIs https://www.google.com
```
More config in `/etc/cgproxy.conf`:
- To completely stop
```
sudo systemctl disable --now cgproxy.service
```
----
<details>
<summary>More config in <i>/etc/cgproxy.conf</i> (click to expand)</summary>
```bash
########################################################################
## cgroup transparent proxy
###################################################################################
## any process in cgroup_proxy will be proxied, and cgroup_noproxy the opposite
## cgroup must start with slash '/'
# cgroup_proxy="/"
cgroup_proxy="/proxy.slice"
## note, cgroup must start with slash '/'
## the value can be string or bash array
## for array, only the first element will be created if not exist
## and the rest elements will not, so won't be applied
### global proxy with v2ray service
# cgroup_proxy="/"
# cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service")
### global proxy with manual `cgnoporxy qv2ray`
# cgroup_proxy="/"
# cgroup_noproxy="/noproxy.slice"
### default
cgroup_proxy="/proxy.slice"
cgroup_noproxy="/noproxy.slice"
########################################################################
###################################################################################
## allow as gateway for local network
enable_gateway=false
###################################################################################
## listening port of another proxy process, for example v2ray
port=12345
## if you set to false, it's traffic won't go through proxy, but still can go direct to internet
###################################################################################
## if you set to false, it's traffic won't go through proxy,
## but still can go direct to internet
enable_dns=true
enable_tcp=true
enable_udp=true
enable_ipv4=true
enable_ipv6=true
enable_dns=true
########################################################################
###################################################################################
## do not modify this if you don't known what you are doing
table=100
mark_proxy=0x01
mark_noproxy=0xff
fwmark=0x01
mark_newin=0x02
```
</details>
If you changed config, remember to restart service
```bash
@@ -107,19 +134,30 @@ sudo systemctl restart cgproxy.service
## Global transparent proxy
- First, set **cgroup_proxy="/"** in `/etc/cgproxy.conf`, this will proxy all connection
- Set `cgroup_proxy="/"` in */etc/cgproxy.conf*, this will proxy all connection
- Then, run your proxy software in cgroup_noproxy to allow direct to internet
- And allow your proxy program (v2ray) direct to internet, two ways:
- active way
```bash
cgnoproxy <PROXY PROGRAM>
# qv2ray as example
cgnoproxy qv2ray
# v2ray as example
cgnoproxy sudo v2ray --config config_file
```
run `cgnoproxy <PROXY PROGRAM>`
example: `cgnoproxy sudo v2ray -config config_file`
example: `cgnoproxy qv2ray`
- passive way, useful if you run v2ray as service
set `cgroup_noproxy="<PROXY PROGRAM's CGROUP>"`
example: `cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service")`
- Finally, restart cgproxy service, that's all
- Finally, restart service `sudo systemctl restart cgproxy.service`, that's all
## Gateway proxy
- Set `enable_gateway=true` in */etc/cgproxy.conf*
- And allow your proxy software (v2ray) direct to internet, described above
- Other device set this host as gateway, and set public dns if necessary
## Other useful tools provided in this project
@@ -147,17 +185,25 @@ sudo systemctl restart cgproxy.service
## NOTES
- `cgattach` attach pid to specific cgroup, and has *suid* bit set by default, be careful to use on multi-user server for securiry. To avoid this situation, you can remove the *suid* bit , then it will fallback to use *sudo*, with *visudo* you can restrict permission or set NOPASSWD for youself.
- `cgattach` has *suid* bit set by default, be careful to use on multi-user server for securiry. To avoid this situation, you can remove the *suid* bit , then it will fallback to use *sudo*, with *sudoer* you can restrict permission or set NOPASSWD for youself.
- v2ray TPROXY need root or special permiassion
- v2ray TPROXY need root or special permission
```bash
sudo setcap "cap_net_bind_service=+ep cap_net_admin=+ep" /usr/lib/v2ray/v2ray
sudo setcap "cap_net_admin,cap_net_bind_service=ep" /usr/lib/v2ray/v2ray
```
- Why not outbound mark solution, because in v2ray [when `"localhost"` is used, out-going DNS traffic is not controlled by V2Ray](https://www.v2fly.org/en/configuration/dns.html), so no mark at all, that's pity.
## TIPS
- `systemd-cgls` to see the cgroup hierarchical tree.
- Check cgroup2 support `findmnt -t cgroup2`
- Offer you v2ray service and full config exmaple in [v2ray_config](https://github.com/springzfx/cgproxy/tree/master/v2ray_config)
- Offer you qv2ray config example
![Qv2ray config example](https://i.loli.net/2020/04/28/bdQBzUD37FOgfvt.png)
## Licences

5
v2ray_config/00_log.json Normal file
View File

@@ -0,0 +1,5 @@
{
"log": {
"loglevel": "none"
}
}

10
v2ray_config/01_api.json Normal file
View File

@@ -0,0 +1,10 @@
{
"api": {
"services": [
"HandlerService",
"LoggerService",
"StatsService"
],
"tag": "API"
}
}

22
v2ray_config/02_dns.json Normal file
View File

@@ -0,0 +1,22 @@
{
"dns": {
"hosts": {
"geosite:category-ads": "127.0.0.1"
},
"servers": [
"https+local://223.5.5.5/dns-query",
"https://1.1.1.1/dns-query",
{
"address": "localhost",
"port": 53,
"domains": [
"geosite:cn"
],
"expectIPs": [
"geoip:cn"
]
}
],
"tag": "dns_inbound"
}
}

View File

@@ -0,0 +1,8 @@
{
"policy": {
"system": {
"statsInboundDownlink": true,
"statsInboundUplink": true
}
}
}

View File

@@ -0,0 +1,54 @@
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "outBound_BLACKHOLE",
"type": "field"
},
{
"inboundTag": [
"inbound_API"
],
"outboundTag": "API",
"type": "field"
},
{
"outboundTag": "dns-out",
"port": "53",
"type": "field"
},
{
"domain": [
"geosite:google",
"geosite:github",
"geosite:netflix",
"geosite:steam",
"geosite:telegram",
"geosite:tumblr",
"geosite:bbc"
],
"outboundTag": "outBound_PROXY",
"type": "field"
},
{
"domain": [
"geosite:cn"
],
"outboundTag": "outBound_DIRECT",
"type": "field"
},
{
"ip": [
"geoip:cn",
"geoip:private"
],
"outboundTag": "outBound_DIRECT",
"type": "field"
}
]
}
}

View File

@@ -0,0 +1,14 @@
{
"inbounds": [
{
"listen": "127.0.0.1",
"port": 15490,
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
},
"sniffing": {},
"tag": "inbound_API"
}
]
}

View File

@@ -0,0 +1,30 @@
{
"inbounds": [
{
"listen": "127.0.0.1",
"port": 12345,
"protocol": "dokodemo-door",
"settings": {
"address": "",
"followRedirect": true,
"network": "tcp,udp",
"port": 0,
"timeout": 300,
"userLevel": 0
},
"sniffing": {
"destOverride": [
"http",
"tls"
],
"enabled": true
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy"
}
},
"tag": "tproxy_IN_ipv4lo"
}
]
}

View File

@@ -0,0 +1,30 @@
{
"inbounds": [
{
"listen": "::1",
"port": 12345,
"protocol": "dokodemo-door",
"settings": {
"address": "",
"followRedirect": true,
"network": "tcp,udp",
"port": 0,
"timeout": 300,
"userLevel": 0
},
"sniffing": {
"destOverride": [
"http",
"tls"
],
"enabled": true
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy"
}
},
"tag": "tproxy_IN_ipv6lo"
}
]
}

View File

@@ -0,0 +1,13 @@
{
"inbounds": [
{
"listen": "127.0.0.1",
"port": 8888,
"protocol": "http",
"sniffing": {
"enabled": false
},
"tag": "http_IN"
}
]
}

View File

@@ -0,0 +1,17 @@
{
"inbounds": [
{
"listen": "127.0.0.1",
"port": 1080,
"protocol": "socks",
"settings": {
"auth": "noauth",
"userLevel": 0
},
"sniffing": {
"enabled": false
},
"tag": "socks_IN"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"outbounds": [
{
"protocol": "blackhole",
"sendThrough": "0.0.0.0",
"settings": {
"response": {
"type": "none"
}
},
"streamSettings": {
"sockopt": {
"mark": 255
}
},
"tag": "outBound_BLACKHOLE"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"outbounds": [
{
"protocol": "freedom",
"sendThrough": "0.0.0.0",
"settings": {
"domainStrategy": "UseIP",
"redirect": ":0",
"userLevel": 0
},
"streamSettings": {
"sockopt": {
"mark": 255
}
},
"tag": "outBound_DIRECT"
}
]
}

View File

@@ -0,0 +1,13 @@
{
"outbounds": [
{
"protocol": "dns",
"streamSettings": {
"sockopt": {
"mark": 255
}
},
"tag": "dns-out"
}
]
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,3 @@
{
"stats": {}
}

View File

@@ -0,0 +1 @@
{}

2
v2ray_config/merge.sh Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
jq -rs 'reduce .[] as $item ({}; . + $item + {inbounds: (.inbounds + $item.inbounds)} + {outbounds: ($item.outbounds + .outbounds)})' *.json |sudo tee /etc/v2ray/config.json > /dev/null

8
v2ray_config/readme.md Normal file
View File

@@ -0,0 +1,8 @@
## Usage
- Fill `06_outbounds_myproxy.json` with your vmess proxy config with tag `outBound_PROXY`.
- Start with `sudo v2ray -confdir .`
## Reference
- [v2ray multi-file config](https://www.v2fly.org/chapter_02/multiple_config.html)

View File

@@ -0,0 +1,18 @@
[Unit]
Description=V2Ray - A unified platform for anti-censorship
Documentation=https://v2ray.com https://guide.v2fly.org
After=network.target nss-lookup.target
Wants=network-online.target
[Service]
Type=exec
ExecStart=/usr/lib/v2ray/v2ray -config /etc/v2ray/config.json
User=nobody
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
Restart=on-failure
# Don't restart in the case of configuration error
RestartPreventExitStatus=23
[Install]
WantedBy=multi-user.target