mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-02-09 21:14:57 +08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06da477373 | ||
|
|
314ff64b8b | ||
|
|
d1bf9620bf | ||
|
|
b7ea8e441b | ||
|
|
b031aa8064 | ||
|
|
8226db37e0 | ||
|
|
5bbec4d1bf | ||
|
|
9b4ec44897 | ||
|
|
a1a1ed5bd9 | ||
|
|
34e5d81329 | ||
|
|
dfc688b5e5 | ||
|
|
ec9609cec8 | ||
|
|
cc83c1ae55 | ||
|
|
75751f4887 | ||
|
|
af78ad2012 | ||
|
|
c32457a1aa | ||
|
|
1d29828d1b | ||
|
|
4fea0d39a2 | ||
|
|
badf282842 | ||
|
|
41f856acd2 | ||
|
|
45adf0a233 | ||
|
|
1f4dd2fde2 | ||
|
|
40bd709995 | ||
|
|
221a75ae7b | ||
|
|
16a341205f | ||
|
|
076651b984 | ||
|
|
1c72a204a1 | ||
|
|
f501c7e476 | ||
|
|
0ec9caefe1 | ||
|
|
94b73b5103 | ||
|
|
c30df999b8 | ||
|
|
932f2bbc94 | ||
|
|
1bbd1ab6ec | ||
|
|
fa7d877de5 | ||
|
|
3475001ca3 | ||
|
|
0b25b5263a | ||
|
|
388ba6a4c8 | ||
|
|
5dbce18f95 | ||
|
|
792a156647 | ||
|
|
92abcb1851 | ||
|
|
a73b697cab | ||
|
|
aace8c3d31 | ||
|
|
665e02ceaa | ||
|
|
bfe3289201 | ||
|
|
2c8625c110 | ||
|
|
ba0b780adf |
@@ -2,11 +2,15 @@ cmake_minimum_required(VERSION 3.10)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
project(cgproxy VERSION 0.12)
|
project(cgproxy VERSION 0.15)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result")
|
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter)
|
||||||
|
|
||||||
set(build_tools OFF)
|
# for clangd
|
||||||
set(build_test OFF)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
|
||||||
|
option(build_tools OFF)
|
||||||
|
option(build_test OFF)
|
||||||
|
|
||||||
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
|
||||||
@@ -23,5 +27,19 @@ install(FILES cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
|||||||
install(FILES cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
install(FILES cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
|
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
|
||||||
install(FILES config.json DESTINATION /etc/cgproxy/)
|
install(FILES config.json DESTINATION /etc/cgproxy/)
|
||||||
install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/)
|
install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/ PERMISSIONS ${basic_permission})
|
||||||
install(FILES readme.md DESTINATION /usr/share/doc/cgproxy/)
|
install(FILES readme.md DESTINATION /usr/share/doc/cgproxy/)
|
||||||
|
|
||||||
|
# man pages
|
||||||
|
set(man_gz
|
||||||
|
${PROJECT_BINARY_DIR}/cgproxyd.1.gz
|
||||||
|
${PROJECT_BINARY_DIR}/cgproxy.1.gz
|
||||||
|
${PROJECT_BINARY_DIR}/cgnoproxy.1.gz
|
||||||
|
)
|
||||||
|
add_custom_target(man
|
||||||
|
COMMAND gzip -fk cgproxyd.1 cgproxy.1 cgnoproxy.1
|
||||||
|
COMMAND mv *.gz ${PROJECT_BINARY_DIR}
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/man
|
||||||
|
)
|
||||||
|
add_dependencies(main man)
|
||||||
|
install(FILES ${man_gz} DESTINATION /usr/share/man/man1/)
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
/usr/bin/cgproxy --noproxy $@
|
exec /usr/bin/cgproxy --noproxy $@
|
||||||
@@ -4,7 +4,7 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/cgproxyd
|
ExecStart=/usr/bin/cgproxyd --execsnoop
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
2
cgproxyd
2
cgproxyd
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
/usr/bin/cgproxy --daemon $@
|
exec /usr/bin/cgproxy --daemon $@
|
||||||
@@ -31,7 +31,7 @@ DOC
|
|||||||
}
|
}
|
||||||
|
|
||||||
## check root
|
## check root
|
||||||
[ ! $(id -u) -eq 0 ] && { >&2 echo "need root to modify iptables";exit -1; }
|
[ ! $(id -u) -eq 0 ] && { >&2 echo "iptables: need root to modify iptables";exit -1; }
|
||||||
|
|
||||||
## any process in this cgroup will be proxied
|
## any process in this cgroup will be proxied
|
||||||
if [ -z ${cgroup_proxy+x} ]; then
|
if [ -z ${cgroup_proxy+x} ]; then
|
||||||
@@ -73,7 +73,7 @@ cgroup_procs_file="cgroup.procs"
|
|||||||
|
|
||||||
stop(){
|
stop(){
|
||||||
iptables -t mangle -L TPROXY_PRE &> /dev/null || return
|
iptables -t mangle -L TPROXY_PRE &> /dev/null || return
|
||||||
echo "cleaning tproxy iptables"
|
echo "iptables: cleaning tproxy iptables"
|
||||||
iptables -t mangle -D PREROUTING -j TPROXY_PRE
|
iptables -t mangle -D PREROUTING -j TPROXY_PRE
|
||||||
iptables -t mangle -D OUTPUT -j TPROXY_OUT
|
iptables -t mangle -D OUTPUT -j TPROXY_OUT
|
||||||
iptables -t mangle -F TPROXY_PRE
|
iptables -t mangle -F TPROXY_PRE
|
||||||
@@ -124,7 +124,7 @@ test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_
|
|||||||
test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1;
|
test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1;
|
||||||
|
|
||||||
|
|
||||||
echo "applying tproxy iptables"
|
echo "iptables: applying tproxy iptables"
|
||||||
## use TPROXY
|
## use TPROXY
|
||||||
#ipv4#
|
#ipv4#
|
||||||
ip rule add fwmark $fwmark table $table
|
ip rule add fwmark $fwmark table $table
|
||||||
@@ -150,10 +150,10 @@ 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 LOCAL -j RETURN
|
||||||
iptables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
|
iptables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
|
||||||
for cg in ${cgroup_noproxy[@]}; do
|
for cg in ${cgroup_noproxy[@]}; do
|
||||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
|
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||||
done
|
done
|
||||||
for cg in ${cgroup_proxy[@]}; do
|
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 MARK --set-mark $fwmark || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||||
done
|
done
|
||||||
iptables -t mangle -A OUTPUT -j TPROXY_OUT
|
iptables -t mangle -A OUTPUT -j TPROXY_OUT
|
||||||
|
|
||||||
@@ -181,10 +181,10 @@ ip6tables -t mangle -A TPROXY_OUT -m connmark --mark $make_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 LOCAL -j RETURN
|
||||||
ip6tables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
|
ip6tables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
|
||||||
for cg in ${cgroup_noproxy[@]}; do
|
for cg in ${cgroup_noproxy[@]}; do
|
||||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
|
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||||
done
|
done
|
||||||
for cg in ${cgroup_proxy[@]}; do
|
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 MARK --set-mark $fwmark || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||||
done
|
done
|
||||||
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
|
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
|
||||||
|
|
||||||
@@ -220,8 +220,8 @@ ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --
|
|||||||
|
|
||||||
## message for user
|
## message for user
|
||||||
cat << DOC
|
cat << DOC
|
||||||
noproxy cgroup: ${cgroup_noproxy[@]}
|
iptables: noproxy cgroup: ${cgroup_noproxy[@]}
|
||||||
proxied cgroup: ${cgroup_proxy[@]}
|
iptables: proxied cgroup: ${cgroup_proxy[@]}
|
||||||
DOC
|
DOC
|
||||||
|
|
||||||
|
|
||||||
@@ -230,5 +230,5 @@ if $enable_gateway; then
|
|||||||
ip6tables -t nat -A POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE # only masquerade ipv6 private address
|
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.ipv4.ip_forward=1
|
||||||
sysctl -w net.ipv6.conf.all.forwarding=1
|
sysctl -w net.ipv6.conf.all.forwarding=1
|
||||||
echo "gateway enabled"
|
echo "ipatbles: gateway enabled"
|
||||||
fi
|
fi
|
||||||
|
|||||||
12
config.json
12
config.json
@@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"port": 12345,
|
||||||
|
"program_noproxy": ["v2ray", "qv2ray"],
|
||||||
|
"program_proxy": [],
|
||||||
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
||||||
"cgroup_proxy": [],
|
"cgroup_proxy": [],
|
||||||
"enable_dns": true,
|
|
||||||
"enable_gateway": false,
|
"enable_gateway": false,
|
||||||
"enable_ipv4": true,
|
"enable_dns": true,
|
||||||
"enable_ipv6": true,
|
|
||||||
"enable_tcp": true,
|
|
||||||
"enable_udp": true,
|
"enable_udp": true,
|
||||||
"port": 12345
|
"enable_tcp": true,
|
||||||
|
"enable_ipv4": true,
|
||||||
|
"enable_ipv6": true
|
||||||
}
|
}
|
||||||
|
|||||||
16
man/cgnoproxy.1
Normal file
16
man/cgnoproxy.1
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.\" Manpage for cgproxyd
|
||||||
|
.TH man 1 "19 May 2020" "1.0" "cgnoproxy man page"
|
||||||
|
.SH NAME
|
||||||
|
cgnoproxy \- Run program without proxy
|
||||||
|
.SH SYNOPSIS
|
||||||
|
cgnoproxy --help
|
||||||
|
cgnoproxy [--debug] <CMD>
|
||||||
|
cgnoproxy [--debug] --pid <PID>
|
||||||
|
.SH ALIAS
|
||||||
|
cgnoproxy = cgproxy --noproxy
|
||||||
|
.SH DESCRIPTION
|
||||||
|
cgnoproxy send current running process pid or specified pid to cgproxyd through unix socket, then pid is attached to non-proxied cgroup
|
||||||
|
.SH EXAMPLES
|
||||||
|
cgnoproxy sudo v2ray -config config_file
|
||||||
|
.SH SEE ALSO
|
||||||
|
cgproxyd(1), cgproxy(1), cgnoproxy(1)
|
||||||
14
man/cgproxy.1
Normal file
14
man/cgproxy.1
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
.\" Manpage for cgproxyd
|
||||||
|
.TH man 1 "19 May 2020" "1.0" "cgproxy man page"
|
||||||
|
.SH NAME
|
||||||
|
cgproxy \- Run program with proxy
|
||||||
|
.SH SYNOPSIS
|
||||||
|
cgproxy --help
|
||||||
|
cgproxy [--debug] <CMD>
|
||||||
|
cgproxy [--debug] --pid <PID>
|
||||||
|
.SH DESCRIPTION
|
||||||
|
cgproxy send current running process pid or specified pid to cgproxyd through unix socket, then pid is attached to proxied cgroup
|
||||||
|
.SH EXAMPLES
|
||||||
|
cgproxy curl -vI https://www.google.com
|
||||||
|
.SH SEE ALSO
|
||||||
|
cgproxyd(1), cgproxy(1), cgnoproxy(1)
|
||||||
54
man/cgproxyd.1
Normal file
54
man/cgproxyd.1
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
.\" Manpage for cgproxyd
|
||||||
|
.TH man 1 "19 May 2020" "1.0" "cgproxyd man page"
|
||||||
|
.SH NAME
|
||||||
|
cgproxyd \- Start a daemon with unix socket to accept control from cgproxy/cgnoproxy
|
||||||
|
.SH SYNOPSIS
|
||||||
|
cgproxyd [--help] [--debug] [--execsnoop]
|
||||||
|
.SH ALIAS
|
||||||
|
cgproxyd = cgproxy --daemon
|
||||||
|
.SH OPTIONS
|
||||||
|
.B --execsnoop
|
||||||
|
enable execsnoop to support program level proxy, need bcc installed to actually work
|
||||||
|
.SH CONFIGURATION
|
||||||
|
.I /etc/cgproxy/config.json
|
||||||
|
.br
|
||||||
|
.B port
|
||||||
|
tproxy listenning port
|
||||||
|
.br
|
||||||
|
program level proxy controll, need `bcc` installed to work:
|
||||||
|
.br
|
||||||
|
.RS
|
||||||
|
.B program_proxy
|
||||||
|
program need to be proxied
|
||||||
|
.br
|
||||||
|
.B program_noproxy
|
||||||
|
program that won't be proxied
|
||||||
|
.RE
|
||||||
|
.br
|
||||||
|
cgroup level proxy control:
|
||||||
|
.br
|
||||||
|
.RS
|
||||||
|
.B cgroup_noproxy
|
||||||
|
cgroup array that no need to proxy, /noproxy.slice is preserved.
|
||||||
|
.br
|
||||||
|
.B cgroup_proxy
|
||||||
|
cgroup array that need to proxy, /proxy.slice is preserved.
|
||||||
|
.RE
|
||||||
|
.br
|
||||||
|
.B enable_gateway
|
||||||
|
enable gateway proxy for local devices.
|
||||||
|
.br
|
||||||
|
.B enable_dns
|
||||||
|
enable dns to go to proxy.
|
||||||
|
.br
|
||||||
|
.B enable_tcp
|
||||||
|
.br
|
||||||
|
.B enable_udp
|
||||||
|
.br
|
||||||
|
.B enable_ipv4
|
||||||
|
.br
|
||||||
|
.B enable_ipv6
|
||||||
|
.br
|
||||||
|
.SH SEE ALSO
|
||||||
|
cgproxyd(1), cgproxy(1), cgnoproxy(1)
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything r
|
|||||||
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
|
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
|
||||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "x86_64")
|
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "x86_64")
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_SUGGESTS "libbpfcc")
|
||||||
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
||||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
||||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
|
||||||
@@ -16,6 +17,7 @@ set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/postinst;${C
|
|||||||
## rpm pack
|
## rpm pack
|
||||||
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
|
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
|
||||||
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
|
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
|
||||||
|
set(CPACK_RPM_PACKAGE_SUGGESTS "bcc")
|
||||||
set(CPACK_RPM_PACKAGE_GROUP "network")
|
set(CPACK_RPM_PACKAGE_GROUP "network")
|
||||||
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")
|
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")
|
||||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/postinst")
|
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/postinst")
|
||||||
|
|||||||
70
readme.md
70
readme.md
@@ -1,16 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
# Transparent Proxy with cgroup v2
|
# Transparent Proxy powered with cgroup v2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
cgproxy will transparent proxy anything running in specific cgroup. It resembles with *proxychains* and *tsock*, but without their disadvantages, and more powerfull.
|
cgproxy will transparent proxy anything running in specific cgroup. It resembles with *proxychains* and *tsock*s in default setting.
|
||||||
|
|
||||||
It aslo supports global transparent proxy and gateway proxy. See [Global transparent proxy](#global-transparent-proxy) and [Gateway proxy](#gateway-proxy).
|
Main feature:
|
||||||
|
|
||||||
|
- supports cgroup/program level proxy control.
|
||||||
|
- supports global transparent proxy and gateway proxy.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
<!--ts-->
|
<!--ts-->
|
||||||
|
|
||||||
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
|
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
* [Prerequest](#prerequest)
|
* [Prerequest](#prerequest)
|
||||||
@@ -40,6 +46,12 @@ It aslo supports global transparent proxy and gateway proxy. See [Global transpa
|
|||||||
|
|
||||||
A process listening on port (e.g. 12345) to accept iptables TPROXY, for example v2ray's dokodemo-door in tproxy mode.
|
A process listening on port (e.g. 12345) to accept iptables TPROXY, for example v2ray's dokodemo-door in tproxy mode.
|
||||||
|
|
||||||
|
- Iptables
|
||||||
|
|
||||||
|
Iptables version should be at least 1.6.0, run `iptables --version` to check.
|
||||||
|
|
||||||
|
ubuntu 16.04, debian 9, fedora 27 and later are desired
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -81,28 +93,51 @@ Config file: **/etc/cgproxy/config.json**
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"port": 12345,
|
||||||
|
"program_noproxy": ["v2ray", "qv2ray"],
|
||||||
|
"program_proxy": [ ],
|
||||||
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
||||||
"cgroup_proxy": [],
|
"cgroup_proxy": [ ],
|
||||||
"enable_dns": true,
|
|
||||||
"enable_gateway": false,
|
"enable_gateway": false,
|
||||||
"enable_ipv4": true,
|
"enable_dns": true,
|
||||||
"enable_ipv6": true,
|
|
||||||
"enable_tcp": true,
|
|
||||||
"enable_udp": true,
|
"enable_udp": true,
|
||||||
"port": 12345
|
"enable_tcp": true,
|
||||||
|
"enable_ipv4": true,
|
||||||
|
"enable_ipv6": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **port** tproxy listenning port
|
- **port** tproxy listenning port
|
||||||
- **cgroup_noproxy** cgroup array that no need to proxy, `/noproxy.slice` is preserved
|
|
||||||
- **cgroup_proxy** cgroup array that need to proxy, `/proxy.slice` is preserved
|
- program level proxy control, need `bcc` and `linux-headers` installed to work
|
||||||
|
|
||||||
|
- **program_proxy** program need to be proxied
|
||||||
|
- **program_noproxy** program that won't be proxied
|
||||||
|
|
||||||
|
- cgroup level proxy control:
|
||||||
|
|
||||||
|
- **cgroup_noproxy** cgroup array that no need to proxy, `/noproxy.slice` is preserved
|
||||||
|
- **cgroup_proxy** cgroup array that need to proxy, `/proxy.slice` is preserved
|
||||||
|
|
||||||
- **enable_gateway** enable gateway proxy for local devices
|
- **enable_gateway** enable gateway proxy for local devices
|
||||||
|
|
||||||
- **enable_dns** enable dns to go to proxy
|
- **enable_dns** enable dns to go to proxy
|
||||||
|
|
||||||
- **enable_tcp**
|
- **enable_tcp**
|
||||||
|
|
||||||
- **enable_udp**
|
- **enable_udp**
|
||||||
|
|
||||||
- **enable_ipv4**
|
- **enable_ipv4**
|
||||||
|
|
||||||
- **enable_ipv6**
|
- **enable_ipv6**
|
||||||
|
|
||||||
|
- options priority
|
||||||
|
|
||||||
|
```
|
||||||
|
program_noproxy > program_proxy > cgroup_noproxy > cgroup_proxy
|
||||||
|
enable_ipv6 > enable_ipv4 > enable_tcp > enable_udp > enable_dns
|
||||||
|
```
|
||||||
|
|
||||||
**Note**: cgroup in configuration need to be exist, otherwise ignored
|
**Note**: cgroup in configuration need to be exist, otherwise ignored
|
||||||
|
|
||||||
If you changed config, remember to restart service
|
If you changed config, remember to restart service
|
||||||
@@ -123,8 +158,10 @@ sudo systemctl restart cgproxy.service
|
|||||||
|
|
||||||
example: `cgnoproxy qv2ray`
|
example: `cgnoproxy qv2ray`
|
||||||
|
|
||||||
- passive way, set it's cgroup in configuration, very useful for service
|
- passive way, persistent config
|
||||||
|
|
||||||
|
example: `"program_noproxy":["v2ray" ,"qv2ray"]`
|
||||||
|
|
||||||
example: `"cgroup_noproxy":["/system.slice/v2ray.service"]`
|
example: `"cgroup_noproxy":["/system.slice/v2ray.service"]`
|
||||||
|
|
||||||
- Finally, restart cgproxy service, that's all
|
- Finally, restart cgproxy service, that's all
|
||||||
@@ -140,16 +177,21 @@ sudo systemctl restart cgproxy.service
|
|||||||
- `cgnoproxy` run program wihout proxy, very useful in global transparent proxy
|
- `cgnoproxy` run program wihout proxy, very useful in global transparent proxy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cgnoproxy [--debug] <CMD>
|
cgnoproxy [--debug] <CMD>
|
||||||
|
cgnoproxy [--debug] --pid <PID>
|
||||||
```
|
```
|
||||||
|
|
||||||
- `cgattach` attach specific process pid to specific cgroup which will create if not exist , cgroup can be only one level down exist cgroup, otherwise created fail.
|
- `cgattach` attach specific process pid to specific cgroup which will create if not exist , cgroup can be only one level down exist cgroup, otherwise created fail.
|
||||||
|
|
||||||
|
You need to set `set(build_tools ON)` in *CmakeLists.txt* to build this.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cgattch <pid> <cgroup>
|
cgattch <pid> <cgroup>
|
||||||
# example
|
# example
|
||||||
cgattch 9999 /proxy.slice
|
cgattch 9999 /proxy.slice
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- For more detail command usage, see `man cgproxyd` `man cgproxy` `man cgnoproxy`
|
||||||
|
|
||||||
## NOTES
|
## NOTES
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ find_package(nlohmann_json REQUIRED)
|
|||||||
include_directories(${PROJECT_SOURCE_DIR})
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if (with_execsnoop)
|
||||||
|
add_library(execsnoop MODULE execsnoop.cpp common.cpp)
|
||||||
|
target_link_libraries(execsnoop bcc)
|
||||||
|
install(TARGETS execsnoop DESTINATION /usr/lib/cgproxy/ PERMISSIONS ${basic_permission})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(main main.cpp
|
add_executable(main main.cpp
|
||||||
common.cpp config.cpp cgroup_attach.cpp
|
common.cpp config.cpp cgroup_attach.cpp
|
||||||
socket_client.cpp socket_server.cpp)
|
socket_client.cpp socket_server.cpp)
|
||||||
|
target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json Threads::Threads ${CMAKE_DL_LIBS})
|
||||||
target_link_libraries(main nlohmann_json::nlohmann_json Threads::Threads)
|
|
||||||
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
||||||
|
|
||||||
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
using namespace CGPROXY;
|
using namespace ::CGPROXY;
|
||||||
using namespace CGPROXY::CONFIG;
|
using namespace ::CGPROXY::CONFIG;
|
||||||
|
|
||||||
namespace CGPROXY::CGPROXY {
|
namespace CGPROXY::CGPROXY {
|
||||||
|
|
||||||
bool print_help = false, proxy = true;
|
bool print_help = false, proxy = true;
|
||||||
|
bool attach_pid = false;
|
||||||
|
string arg_pid;
|
||||||
inline void print_usage() {
|
inline void print_usage() {
|
||||||
if (proxy) {
|
if (proxy) {
|
||||||
cout << "Run program with proxy" << endl;
|
cout << "Run program with proxy" << endl;
|
||||||
@@ -22,14 +24,24 @@ inline void print_usage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void processArgs(const int argc, char *argv[], int &shift) {
|
bool processArgs(const int argc, char *argv[], int &shift) {
|
||||||
for (int i = 1; i < argc; i++) {
|
int i;
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--pid") == 0) {
|
||||||
|
attach_pid = true;
|
||||||
|
i++;
|
||||||
|
if (i == argc) return false;
|
||||||
|
arg_pid = argv[i];
|
||||||
|
if (!validPid(arg_pid)) return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (strcmp(argv[i], "--noproxy") == 0) { proxy = false; }
|
if (strcmp(argv[i], "--noproxy") == 0) { proxy = false; }
|
||||||
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
||||||
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||||
if (argv[i][0] != '-') { break; }
|
if (argv[i][0] != '-') { break; }
|
||||||
shift += 1;
|
|
||||||
}
|
}
|
||||||
|
shift = i;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_pid(const pid_t pid, bool proxy, int &status) {
|
void send_pid(const pid_t pid, bool proxy, int &status) {
|
||||||
@@ -40,27 +52,33 @@ void send_pid(const pid_t pid, bool proxy, int &status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int shift = 1;
|
int shift = -1;
|
||||||
processArgs(argc, argv, shift);
|
if (!processArgs(argc, argv, shift)) {
|
||||||
|
error("parameter error");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
if (print_help) {
|
if (print_help) {
|
||||||
print_usage();
|
print_usage();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == shift) {
|
if (!attach_pid && argc == shift) {
|
||||||
error("no program specified");
|
error("no program specified");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = -1;
|
int status = -1;
|
||||||
send_pid(getpid(), proxy, status);
|
send_pid(attach_pid ? stoi(arg_pid) : getpid(), proxy, status);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
error("attach process failed");
|
error("attach process failed");
|
||||||
|
if (status == 1) error("maybe cgproxy.service not running");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
// if just attach pid, return here
|
||||||
|
if (attach_pid) return 0;
|
||||||
|
|
||||||
string s = join2str(argc - shift, argv + shift, ' ');
|
string s = join2str(argc - shift, argv + shift, ' ');
|
||||||
return system(s.c_str());
|
return system(s.c_str());
|
||||||
}
|
}
|
||||||
} // namespace CGPROXY::CGPROXY
|
} // namespace CGPROXY::CGPROXY
|
||||||
|
|||||||
219
src/cgproxyd.hpp
219
src/cgproxyd.hpp
@@ -4,10 +4,18 @@
|
|||||||
#include "cgroup_attach.h"
|
#include "cgroup_attach.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "execsnoop.h"
|
||||||
#include "socket_server.h"
|
#include "socket_server.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <sched.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@@ -16,13 +24,42 @@ using json = nlohmann::json;
|
|||||||
using namespace ::CGPROXY::SOCKET;
|
using namespace ::CGPROXY::SOCKET;
|
||||||
using namespace ::CGPROXY::CONFIG;
|
using namespace ::CGPROXY::CONFIG;
|
||||||
using namespace ::CGPROXY::CGROUP;
|
using namespace ::CGPROXY::CGROUP;
|
||||||
|
// using namespace ::CGPROXY::EXECSNOOP;
|
||||||
|
|
||||||
|
namespace CGPROXY::EXECSNOOP {
|
||||||
|
bool loadExecsnoopLib() {
|
||||||
|
try {
|
||||||
|
info("loading %s", LIBEXECSNOOP_SO);
|
||||||
|
void *handle_dl = dlopen(LIBEXECSNOOP_SO, RTLD_NOW);
|
||||||
|
if (handle_dl == NULL) {
|
||||||
|
error("dlopen %s failed: %s", LIBEXECSNOOP_SO, dlerror());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_startThread = reinterpret_cast<startThread_t *>(dlsym(handle_dl, "startThread"));
|
||||||
|
if (_startThread == NULL) {
|
||||||
|
error("dlsym startThread func failed: %s", dlerror());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
info("dlsym startThread func success");
|
||||||
|
return true;
|
||||||
|
} catch (exception &e) {
|
||||||
|
debug("exception: %s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace CGPROXY::EXECSNOOP
|
||||||
|
|
||||||
namespace CGPROXY::CGPROXYD {
|
namespace CGPROXY::CGPROXYD {
|
||||||
|
|
||||||
|
bool print_help = false;
|
||||||
|
bool enable_socketserver = true;
|
||||||
|
bool enable_execsnoop = false;
|
||||||
|
|
||||||
class cgproxyd {
|
class cgproxyd {
|
||||||
thread_arg arg_t;
|
thread socketserver_thread;
|
||||||
|
thread execsnoop_thread;
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
pthread_t socket_thread_id = -1;
|
|
||||||
|
|
||||||
static cgproxyd *instance;
|
static cgproxyd *instance;
|
||||||
static int handle_msg_static(char *msg) {
|
static int handle_msg_static(char *msg) {
|
||||||
@@ -33,6 +70,65 @@ class cgproxyd {
|
|||||||
return instance->handle_msg(msg);
|
return instance->handle_msg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_pid_static(int pid) {
|
||||||
|
if (!instance) {
|
||||||
|
error("no cgproxyd instance assigned");
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
return instance->handle_pid(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_pid(int pid) {
|
||||||
|
unique_ptr<char[], decltype(&free)> path(
|
||||||
|
realpath(to_str("/proc/", pid, "/exe").c_str(), NULL), &free);
|
||||||
|
if (path == NULL) {
|
||||||
|
debug("execsnoop: pid %d live life too short", pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
debug("execsnoop: %d %s", pid, path.get());
|
||||||
|
|
||||||
|
vector<string> v;
|
||||||
|
|
||||||
|
v = config.program_noproxy;
|
||||||
|
if (find(v.begin(), v.end(), path.get()) != v.end()) {
|
||||||
|
string cg = getCgroup(pid);
|
||||||
|
if (cg.empty()) {
|
||||||
|
debug("execsnoop: cgroup get failed, ignore: %d %s", pid, path.get());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
|
||||||
|
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
|
||||||
|
info("execsnoop: already in preserverd cgroup, leave alone: %d %s", pid,
|
||||||
|
path.get());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!belongToCgroup(cg, config.cgroup_noproxy)) {
|
||||||
|
info("execsnoop; noproxy: %d %s", pid, path.get());
|
||||||
|
return attach(pid, config.cgroup_noproxy_preserved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v = config.program_proxy;
|
||||||
|
if (find(v.begin(), v.end(), path.get()) != v.end()) {
|
||||||
|
string cg = getCgroup(pid);
|
||||||
|
if (cg.empty()) {
|
||||||
|
debug("execsnoop: cgroup get failed, ignore: %d %s", pid, path.get());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
|
||||||
|
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
|
||||||
|
info("execsnoop: already in preserverd cgroup, leave alone: %d %s", pid,
|
||||||
|
path.get());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!belongToCgroup(cg, config.cgroup_proxy)) {
|
||||||
|
info("execsnoop: proxied: %d %s", pid, path.get());
|
||||||
|
return attach(pid, config.cgroup_proxy_preserved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void signalHandler(int signum) {
|
static void signalHandler(int signum) {
|
||||||
debug("Signal %d received.", signum);
|
debug("Signal %d received.", signum);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
@@ -40,7 +136,7 @@ class cgproxyd {
|
|||||||
} else {
|
} else {
|
||||||
instance->stop();
|
instance->stop();
|
||||||
}
|
}
|
||||||
exit(signum);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// single process instance
|
// single process instance
|
||||||
@@ -73,44 +169,114 @@ class cgproxyd {
|
|||||||
return MSG_ERROR;
|
return MSG_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int type, status;
|
int type, status, pid;
|
||||||
int pid, cgroup_target;
|
|
||||||
try {
|
try {
|
||||||
type = j.at("type").get<int>();
|
type = j.at("type").get<int>();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MSG_TYPE_CONFIG_JSON:
|
case MSG_TYPE_CONFIG_JSON:
|
||||||
status = config.loadFromJsonStr(j.at("data").dump());
|
status = config.loadFromJsonStr(j.at("data").dump());
|
||||||
if (status == SUCCESS) status = applyConfig(&config);
|
info("process received config json msg");
|
||||||
|
if (status == SUCCESS) status = applyConfig();
|
||||||
return status;
|
return status;
|
||||||
break;
|
break;
|
||||||
case MSG_TYPE_CONFIG_PATH:
|
case MSG_TYPE_CONFIG_PATH:
|
||||||
status = config.loadFromFile(j.at("data").get<string>());
|
status = config.loadFromFile(j.at("data").get<string>());
|
||||||
if (status == SUCCESS) status = applyConfig(&config);
|
info("process received config path msg");
|
||||||
|
if (status == SUCCESS) status = applyConfig();
|
||||||
return status;
|
return status;
|
||||||
break;
|
break;
|
||||||
case MSG_TYPE_PROXY_PID:
|
case MSG_TYPE_PROXY_PID:
|
||||||
pid = j.at("data").get<int>();
|
pid = j.at("data").get<int>();
|
||||||
|
info("process proxy pid msg: %d", pid);
|
||||||
status = attach(pid, config.cgroup_proxy_preserved);
|
status = attach(pid, config.cgroup_proxy_preserved);
|
||||||
return status;
|
return status;
|
||||||
break;
|
break;
|
||||||
case MSG_TYPE_NOPROXY_PID:
|
case MSG_TYPE_NOPROXY_PID:
|
||||||
pid = j.at("data").get<int>();
|
pid = j.at("data").get<int>();
|
||||||
|
info("process noproxy pid msg: %d", pid);
|
||||||
status = attach(pid, config.cgroup_noproxy_preserved);
|
status = attach(pid, config.cgroup_noproxy_preserved);
|
||||||
return status;
|
return status;
|
||||||
break;
|
break;
|
||||||
default: return MSG_ERROR; break;
|
default:
|
||||||
|
error("unknown msg");
|
||||||
|
return MSG_ERROR;
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
} catch (out_of_range &e) { return MSG_ERROR; } catch (exception &e) {
|
} catch (out_of_range &e) { return MSG_ERROR; } catch (exception &e) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_t startSocketListeningThread() {
|
void startSocketListeningThread() {
|
||||||
arg_t.handle_msg = &handle_msg_static;
|
promise<void> status;
|
||||||
pthread_t thread_id;
|
future<void> status_f = status.get_future();
|
||||||
int status = pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t);
|
thread th(SOCKET::startThread, handle_msg_static, move(status));
|
||||||
if (status != 0) error("socket thread create failed");
|
socketserver_thread = move(th);
|
||||||
return thread_id;
|
|
||||||
|
future_status fstatus = status_f.wait_for(chrono::seconds(THREAD_TIMEOUT));
|
||||||
|
if (fstatus == std::future_status::ready) {
|
||||||
|
info("socketserver thread started");
|
||||||
|
} else {
|
||||||
|
error("socketserver thread timeout, maybe failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startExecsnoopThread() {
|
||||||
|
if (!EXECSNOOP::loadExecsnoopLib() || EXECSNOOP::_startThread == NULL) {
|
||||||
|
error("execsnoop not ready to start, maybe bcc not installed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise<void> status;
|
||||||
|
future<void> status_f = status.get_future();
|
||||||
|
thread th(EXECSNOOP::_startThread, handle_pid_static, move(status));
|
||||||
|
execsnoop_thread = move(th);
|
||||||
|
|
||||||
|
future_status fstatus = status_f.wait_for(chrono::seconds(THREAD_TIMEOUT));
|
||||||
|
if (fstatus == std::future_status::ready) {
|
||||||
|
info("execsnoop thread started");
|
||||||
|
processRunningProgram();
|
||||||
|
} else {
|
||||||
|
error("execsnoop thread timeout, maybe failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processRunningProgram() {
|
||||||
|
debug("process running program");
|
||||||
|
for (auto &path : config.program_noproxy)
|
||||||
|
for (auto &pid : bash_pidof(path)) {
|
||||||
|
string cg = getCgroup(pid);
|
||||||
|
if (cg.empty()) {
|
||||||
|
debug("cgroup get failed, ignore: %d %s", pid, path.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!belongToCgroup(cg, config.cgroup_noproxy)) {
|
||||||
|
int status = attach(pid, config.cgroup_noproxy_preserved);
|
||||||
|
if (status == 0) info("noproxy running process %d %s", pid, path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &path : config.program_proxy)
|
||||||
|
for (auto &pid : bash_pidof(path)) {
|
||||||
|
string cg = getCgroup(pid);
|
||||||
|
if (cg.empty()) {
|
||||||
|
debug("cgroup get failed, ignore: %d %s", pid, path.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!belongToCgroup(cg, config.cgroup_proxy)) {
|
||||||
|
int status = attach(pid, config.cgroup_proxy_preserved);
|
||||||
|
if (status == 0) info("proxied running process %d %s", pid, path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void assignStaticInstance() { instance = this; }
|
void assignStaticInstance() { instance = this; }
|
||||||
@@ -122,17 +288,23 @@ public:
|
|||||||
signal(SIGTERM, &signalHandler);
|
signal(SIGTERM, &signalHandler);
|
||||||
signal(SIGHUP, &signalHandler);
|
signal(SIGHUP, &signalHandler);
|
||||||
|
|
||||||
config.loadFromFile(DEFAULT_CONFIG_FILE);
|
|
||||||
applyConfig(&config);
|
|
||||||
|
|
||||||
assignStaticInstance();
|
assignStaticInstance();
|
||||||
socket_thread_id = startSocketListeningThread();
|
|
||||||
pthread_join(socket_thread_id, NULL);
|
config.loadFromFile(DEFAULT_CONFIG_FILE);
|
||||||
|
applyConfig();
|
||||||
|
|
||||||
|
if (enable_socketserver) startSocketListeningThread();
|
||||||
|
if (enable_execsnoop) startExecsnoopThread();
|
||||||
|
|
||||||
|
if (socketserver_thread.joinable()) socketserver_thread.join();
|
||||||
|
if (execsnoop_thread.joinable()) execsnoop_thread.join();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int applyConfig(Config *c) {
|
int applyConfig() {
|
||||||
system(TPROXY_IPTABLS_CLEAN);
|
system(TPROXY_IPTABLS_CLEAN);
|
||||||
c->toEnv();
|
config.print_summary();
|
||||||
|
config.toEnv();
|
||||||
system(TPROXY_IPTABLS_START);
|
system(TPROXY_IPTABLS_START);
|
||||||
// no need to track running status
|
// no need to track running status
|
||||||
return 0;
|
return 0;
|
||||||
@@ -149,8 +321,6 @@ public:
|
|||||||
|
|
||||||
cgproxyd *cgproxyd::instance = NULL;
|
cgproxyd *cgproxyd::instance = NULL;
|
||||||
|
|
||||||
bool print_help = false;
|
|
||||||
|
|
||||||
void print_usage() {
|
void print_usage() {
|
||||||
cout << "Start a daemon with unix socket to accept control" << endl;
|
cout << "Start a daemon with unix socket to accept control" << endl;
|
||||||
cout << "Usage: cgproxyd [--help] [--debug]" << endl;
|
cout << "Usage: cgproxyd [--help] [--debug]" << endl;
|
||||||
@@ -161,6 +331,7 @@ void processArgs(const int argc, char *argv[]) {
|
|||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
||||||
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||||
|
if (strcmp(argv[i], "--execsnoop") == 0) { enable_execsnoop = true; }
|
||||||
if (argv[i][0] != '-') { break; }
|
if (argv[i][0] != '-') { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,4 +352,4 @@ int main(int argc, char *argv[]) {
|
|||||||
return d.start();
|
return d.start();
|
||||||
}
|
}
|
||||||
} // namespace CGPROXY::CGPROXYD
|
} // namespace CGPROXY::CGPROXYD
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,10 +13,18 @@
|
|||||||
|
|
||||||
namespace CGPROXY::CGROUP {
|
namespace CGPROXY::CGROUP {
|
||||||
|
|
||||||
bool exist(string path) {
|
string cgroup2_mount_point = get_cgroup2_mount_point();
|
||||||
struct stat st;
|
|
||||||
if (stat(path.c_str(), &st) != -1) { return S_ISDIR(st.st_mode); }
|
string get_cgroup2_mount_point() {
|
||||||
return false;
|
stringstream buffer;
|
||||||
|
unique_ptr<FILE, decltype(&pclose)> fp(popen("findmnt -t cgroup2 -n -o TARGET", "r"),
|
||||||
|
&pclose);
|
||||||
|
if (!fp) return "";
|
||||||
|
char buf[READ_SIZE_MAX];
|
||||||
|
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||||
|
string s = buffer.str();
|
||||||
|
s.pop_back(); // remove newline character
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validate(string pid, string cgroup) {
|
bool validate(string pid, string cgroup) {
|
||||||
@@ -25,56 +33,45 @@ bool validate(string pid, string cgroup) {
|
|||||||
if (pid_v && cg_v) return true;
|
if (pid_v && cg_v) return true;
|
||||||
|
|
||||||
error("attach paramater validate error");
|
error("attach paramater validate error");
|
||||||
return_error
|
return_error;
|
||||||
}
|
|
||||||
|
|
||||||
string get_cgroup2_mount_point(int &status) {
|
|
||||||
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");
|
|
||||||
status = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
status = 0;
|
|
||||||
return cgroup2_mount_point;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int attach(const string pid, const string cgroup_target) {
|
int attach(const string pid, const string cgroup_target) {
|
||||||
if (getuid() != 0) {
|
if (getuid() != 0) {
|
||||||
error("need root to attach cgroup");
|
error("need root to attach cgroup");
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
||||||
|
|
||||||
int status;
|
if (!validate(pid, cgroup_target)) return_error;
|
||||||
if (!validate(pid, cgroup_target))
|
if (cgroup2_mount_point.empty()) return_error;
|
||||||
return_error string cgroup_mount_point = get_cgroup2_mount_point(status);
|
string cgroup_target_path = cgroup2_mount_point + cgroup_target;
|
||||||
if (status != 0)
|
|
||||||
return_error string cgroup_target_path = cgroup_mount_point + cgroup_target;
|
|
||||||
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
||||||
|
|
||||||
// check if exist, we will create it if not exist
|
// check if exist, we will create it if not exist
|
||||||
if (!exist(cgroup_target_path)) {
|
if (!dirExist(cgroup_target_path)) {
|
||||||
if (mkdir(cgroup_target_path.c_str(),
|
if (mkdir(cgroup_target_path.c_str(),
|
||||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) {
|
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) {
|
||||||
debug("created cgroup %s success", cgroup_target.c_str());
|
debug("created cgroup %s success", cgroup_target.c_str());
|
||||||
} else {
|
} else {
|
||||||
error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno);
|
error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno);
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
// error("cgroup %s not exist",cgroup_target.c_str());
|
// error("cgroup %s not exist",cgroup_target.c_str());
|
||||||
// return_error
|
// return_error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getCgroup(pid) == cgroup_target) {
|
||||||
|
debug("%s already in %s", pid.c_str(), cgroup_target.c_str());
|
||||||
|
return_success;
|
||||||
|
}
|
||||||
|
|
||||||
// put pid to target cgroup
|
// put pid to target cgroup
|
||||||
ofstream procs(cgroup_target_procs, ofstream::app);
|
ofstream procs(cgroup_target_procs, ofstream::app);
|
||||||
if (!procs.is_open()) {
|
if (!procs.is_open()) {
|
||||||
error("open file %s failed", cgroup_target_procs.c_str());
|
error("open file %s failed", cgroup_target_procs.c_str());
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
procs << pid.c_str() << endl;
|
procs << pid.c_str() << endl;
|
||||||
procs.close();
|
procs.close();
|
||||||
@@ -83,13 +80,13 @@ int attach(const string pid, const string cgroup_target) {
|
|||||||
if (!procs) {
|
if (!procs) {
|
||||||
error("write %s to %s failed, maybe process %s not exist", pid.c_str(),
|
error("write %s to %s failed, maybe process %s not exist", pid.c_str(),
|
||||||
cgroup_target_procs.c_str(), pid.c_str());
|
cgroup_target_procs.c_str(), pid.c_str());
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
return_success
|
return_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
int attach(const int pid, const string cgroup_target) {
|
int attach(const int pid, const string cgroup_target) {
|
||||||
return attach(to_str(pid), cgroup_target);
|
return attach(to_str(pid), cgroup_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CGPROXY::CGROUP
|
} // namespace CGPROXY::CGROUP
|
||||||
|
|||||||
@@ -6,13 +6,12 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace CGPROXY::CGROUP {
|
namespace CGPROXY::CGROUP {
|
||||||
|
extern string cgroup2_mount_point;
|
||||||
bool exist(string path);
|
|
||||||
bool validate(string pid, string cgroup);
|
bool validate(string pid, string cgroup);
|
||||||
string get_cgroup2_mount_point(int &status);
|
string get_cgroup2_mount_point();
|
||||||
int attach(const string pid, const string cgroup_target);
|
int attach(const string pid, const string cgroup_target);
|
||||||
int attach(const int pid, const string cgroup_target);
|
int attach(const int pid, const string cgroup_target);
|
||||||
|
|
||||||
} // namespace CGPROXY::CGROUP
|
} // namespace CGPROXY::CGROUP
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <fstream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
bool enable_debug = false;
|
bool enable_debug = false;
|
||||||
|
bool enable_info = true;
|
||||||
|
|
||||||
string join2str(const vector<string> t, const char delm) {
|
string join2str(const vector<string> t, const char delm) {
|
||||||
string s;
|
string s;
|
||||||
@@ -18,6 +22,8 @@ string join2str(const int argc, char **argv, const char delm) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool startWith(string s, string prefix) { return s.rfind(prefix, 0) == 0; }
|
||||||
|
|
||||||
bool validCgroup(const string cgroup) {
|
bool validCgroup(const string cgroup) {
|
||||||
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
||||||
}
|
}
|
||||||
@@ -32,3 +38,87 @@ bool validCgroup(const vector<string> cgroup) {
|
|||||||
bool validPid(const string pid) { return regex_match(pid, regex("^[0-9]+$")); }
|
bool validPid(const string pid) { return regex_match(pid, regex("^[0-9]+$")); }
|
||||||
|
|
||||||
bool validPort(const int port) { return port > 0; }
|
bool validPort(const int port) { return port > 0; }
|
||||||
|
|
||||||
|
bool fileExist(const string &path) {
|
||||||
|
struct stat st;
|
||||||
|
return (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dirExist(const string &path) {
|
||||||
|
struct stat st;
|
||||||
|
return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<int> bash_pidof(const string &path) {
|
||||||
|
vector<int> pids;
|
||||||
|
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("pidof ", path).c_str(), "r"),
|
||||||
|
&pclose);
|
||||||
|
if (!fp) return pids;
|
||||||
|
int pid;
|
||||||
|
while (fscanf(fp.get(), "%d", &pid) != EOF) { pids.push_back(pid); }
|
||||||
|
return pids;
|
||||||
|
}
|
||||||
|
|
||||||
|
string bash_which(const string &name) {
|
||||||
|
stringstream buffer;
|
||||||
|
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("which ", name).c_str(), "r"),
|
||||||
|
&pclose);
|
||||||
|
if (!fp) return "";
|
||||||
|
char buf[READ_SIZE_MAX];
|
||||||
|
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||||
|
string s = buffer.str();
|
||||||
|
s.pop_back(); // remove newline character
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string bash_readlink(const string &path) {
|
||||||
|
stringstream buffer;
|
||||||
|
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("readlink -e ", path).c_str(), "r"),
|
||||||
|
&pclose);
|
||||||
|
if (!fp) return "";
|
||||||
|
char buf[READ_SIZE_MAX];
|
||||||
|
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||||
|
string s = buffer.str();
|
||||||
|
s.pop_back(); // remove newline character
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string getRealExistPath(const string &name) {
|
||||||
|
if (name[0] == '/' && fileExist(name)) return name;
|
||||||
|
string path;
|
||||||
|
path = bash_which(name);
|
||||||
|
if (path.empty()) return "";
|
||||||
|
path = bash_readlink(path);
|
||||||
|
if (!fileExist(path)) return "";
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool belongToCgroup(string cg1, string cg2) { return startWith(cg1 + '/', cg2 + '/'); }
|
||||||
|
|
||||||
|
bool belongToCgroup(string cg1, vector<string> cg2) {
|
||||||
|
for (const auto &s : cg2) {
|
||||||
|
if (startWith(cg1 + '/', s + '/')) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string getCgroup(const pid_t &pid) { return getCgroup(to_str(pid)); }
|
||||||
|
|
||||||
|
string getCgroup(const string &pid) {
|
||||||
|
string cgroup_f = to_str("/proc/", pid, "/cgroup");
|
||||||
|
if (!fileExist(cgroup_f)) return "";
|
||||||
|
|
||||||
|
string cgroup, line;
|
||||||
|
ifstream ifs(cgroup_f);
|
||||||
|
debug("prcessing file %s", cgroup_f.c_str());
|
||||||
|
while (ifs.good() && getline(ifs, line)) {
|
||||||
|
debug("process line: %s", line.c_str());
|
||||||
|
if (line[0] == '0') {
|
||||||
|
cgroup = line.substr(3);
|
||||||
|
debug("get cgroup of %s: %s", pid.c_str(), cgroup.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifs.close();
|
||||||
|
return cgroup;
|
||||||
|
}
|
||||||
|
|||||||
43
src/common.h
43
src/common.h
@@ -7,17 +7,21 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define TPROXY_IPTABLS_START "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
#define TPROXY_IPTABLS_START "/usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
||||||
#define TPROXY_IPTABLS_CLEAN "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
#define TPROXY_IPTABLS_CLEAN "/usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
||||||
|
|
||||||
|
#define LIBEXECSNOOP_SO "/usr/lib/cgproxy/libexecsnoop.so"
|
||||||
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
||||||
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
||||||
#define LISTEN_BACKLOG 64
|
#define LISTEN_BACKLOG 64
|
||||||
#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json"
|
#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json"
|
||||||
|
#define READ_SIZE_MAX 128
|
||||||
|
|
||||||
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
||||||
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
||||||
|
|
||||||
|
#define THREAD_TIMEOUT 5
|
||||||
|
|
||||||
#define MSG_TYPE_CONFIG_JSON 1
|
#define MSG_TYPE_CONFIG_JSON 1
|
||||||
#define MSG_TYPE_CONFIG_PATH 2
|
#define MSG_TYPE_CONFIG_PATH 2
|
||||||
#define MSG_TYPE_PROXY_PID 3
|
#define MSG_TYPE_PROXY_PID 3
|
||||||
@@ -35,23 +39,34 @@ using namespace std;
|
|||||||
#define FILE_ERROR 7
|
#define FILE_ERROR 7
|
||||||
|
|
||||||
extern bool enable_debug;
|
extern bool enable_debug;
|
||||||
|
extern bool enable_info;
|
||||||
|
|
||||||
#define error(...) \
|
#define error(...) \
|
||||||
{ \
|
{ \
|
||||||
fprintf(stderr, "error: "); \
|
fprintf(stderr, "error: "); \
|
||||||
fprintf(stderr, __VA_ARGS__); \
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
fprintf(stderr, "\n"); \
|
fprintf(stderr, "\n"); \
|
||||||
|
fflush(stderr); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define debug(...) \
|
#define debug(...) \
|
||||||
if (enable_debug) { \
|
if (enable_debug) { \
|
||||||
fprintf(stderr, "debug: "); \
|
fprintf(stdout, "debug: "); \
|
||||||
fprintf(stdout, __VA_ARGS__); \
|
fprintf(stdout, __VA_ARGS__); \
|
||||||
fprintf(stdout, "\n"); \
|
fprintf(stdout, "\n"); \
|
||||||
|
fflush(stdout); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define return_error return -1;
|
#define info(...) \
|
||||||
#define return_success return 0;
|
if (enable_info) { \
|
||||||
|
fprintf(stdout, "info: "); \
|
||||||
|
fprintf(stdout, __VA_ARGS__); \
|
||||||
|
fprintf(stdout, "\n"); \
|
||||||
|
fflush(stdout); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define return_error return -1
|
||||||
|
#define return_success return 0
|
||||||
|
|
||||||
template <typename... T> string to_str(T... args) {
|
template <typename... T> string to_str(T... args) {
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
@@ -63,10 +78,26 @@ template <typename... T> string to_str(T... args) {
|
|||||||
|
|
||||||
string join2str(const vector<string> t, const char delm = ' ');
|
string join2str(const vector<string> t, const char delm = ' ');
|
||||||
string join2str(const int argc, char **argv, const char delm = ' ');
|
string join2str(const int argc, char **argv, const char delm = ' ');
|
||||||
|
bool startWith(string prefix);
|
||||||
|
|
||||||
bool validCgroup(const string cgroup);
|
bool validCgroup(const string cgroup);
|
||||||
bool validCgroup(const vector<string> cgroup);
|
bool validCgroup(const vector<string> cgroup);
|
||||||
bool validPid(const string pid);
|
bool validPid(const string pid);
|
||||||
bool validPort(const int port);
|
bool validPort(const int port);
|
||||||
|
|
||||||
#endif
|
bool fileExist(const string &path);
|
||||||
|
bool dirExist(const string &path);
|
||||||
|
vector<int> bash_pidof(const string &path);
|
||||||
|
string bash_which(const string &name);
|
||||||
|
string bash_readlink(const string &path);
|
||||||
|
string getRealExistPath(const string &name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether cg1 belongs to cg2
|
||||||
|
*/
|
||||||
|
bool belongToCgroup(string cg1, string cg2);
|
||||||
|
bool belongToCgroup(string cg1, vector<string> cg2);
|
||||||
|
string getCgroup(const pid_t &pid);
|
||||||
|
string getCgroup(const string &pid);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
#define add2json(v) j[#v] = v;
|
#define add2json(v) j[#v] = v;
|
||||||
@@ -20,7 +21,8 @@ using json = nlohmann::json;
|
|||||||
namespace CGPROXY::CONFIG {
|
namespace CGPROXY::CONFIG {
|
||||||
|
|
||||||
void Config::toEnv() {
|
void Config::toEnv() {
|
||||||
mergeReserved();
|
setenv("program_proxy", join2str(program_proxy, ':').c_str(), 1);
|
||||||
|
setenv("program_noproxy", join2str(program_noproxy, ':').c_str(), 1);
|
||||||
setenv("cgroup_proxy", join2str(cgroup_proxy, ':').c_str(), 1);
|
setenv("cgroup_proxy", join2str(cgroup_proxy, ':').c_str(), 1);
|
||||||
setenv("cgroup_noproxy", join2str(cgroup_noproxy, ':').c_str(), 1);
|
setenv("cgroup_noproxy", join2str(cgroup_noproxy, ':').c_str(), 1);
|
||||||
setenv("enable_gateway", to_str(enable_gateway).c_str(), 1);
|
setenv("enable_gateway", to_str(enable_gateway).c_str(), 1);
|
||||||
@@ -43,6 +45,8 @@ int Config::saveToFile(const string f) {
|
|||||||
|
|
||||||
string Config::toJsonStr() {
|
string Config::toJsonStr() {
|
||||||
json j;
|
json j;
|
||||||
|
add2json(program_proxy);
|
||||||
|
add2json(program_noproxy);
|
||||||
add2json(cgroup_proxy);
|
add2json(cgroup_proxy);
|
||||||
add2json(cgroup_noproxy);
|
add2json(cgroup_noproxy);
|
||||||
add2json(enable_gateway);
|
add2json(enable_gateway);
|
||||||
@@ -74,6 +78,8 @@ int Config::loadFromJsonStr(const string js) {
|
|||||||
return PARAM_ERROR;
|
return PARAM_ERROR;
|
||||||
}
|
}
|
||||||
json j = json::parse(js);
|
json j = json::parse(js);
|
||||||
|
tryassign(program_proxy);
|
||||||
|
tryassign(program_noproxy);
|
||||||
tryassign(cgroup_proxy);
|
tryassign(cgroup_proxy);
|
||||||
tryassign(cgroup_noproxy);
|
tryassign(cgroup_noproxy);
|
||||||
tryassign(enable_gateway);
|
tryassign(enable_gateway);
|
||||||
@@ -83,6 +89,13 @@ int Config::loadFromJsonStr(const string js) {
|
|||||||
tryassign(enable_udp);
|
tryassign(enable_udp);
|
||||||
tryassign(enable_ipv4);
|
tryassign(enable_ipv4);
|
||||||
tryassign(enable_ipv6);
|
tryassign(enable_ipv6);
|
||||||
|
|
||||||
|
// e.g. v2ray -> /usr/bin/v2ray -> /usr/lib/v2ray/v2ray
|
||||||
|
toRealProgramPath(program_noproxy);
|
||||||
|
toRealProgramPath(program_proxy);
|
||||||
|
|
||||||
|
mergeReserved();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +109,7 @@ bool Config::validateJsonStr(const string js) {
|
|||||||
bool status = true;
|
bool status = true;
|
||||||
const set<string> boolset = {"enable_gateway", "enable_dns", "enable_tcp",
|
const set<string> boolset = {"enable_gateway", "enable_dns", "enable_tcp",
|
||||||
"enable_udp", "enable_ipv4", "enable_ipv6"};
|
"enable_udp", "enable_ipv4", "enable_ipv6"};
|
||||||
|
const set<string> allowset = {"program_proxy", "program_noproxy"};
|
||||||
for (auto &[key, value] : j.items()) {
|
for (auto &[key, value] : j.items()) {
|
||||||
if (key == "cgroup_proxy" || key == "cgroup_noproxy") {
|
if (key == "cgroup_proxy" || key == "cgroup_noproxy") {
|
||||||
if (value.is_string() && !validCgroup((string)value)) status = false;
|
if (value.is_string() && !validCgroup((string)value)) status = false;
|
||||||
@@ -106,6 +120,8 @@ bool Config::validateJsonStr(const string js) {
|
|||||||
if (!validPort(value)) status = false;
|
if (!validPort(value)) status = false;
|
||||||
} else if (boolset.find(key) != boolset.end()) {
|
} else if (boolset.find(key) != boolset.end()) {
|
||||||
if (!value.is_boolean()) status = false;
|
if (!value.is_boolean()) status = false;
|
||||||
|
} else if (allowset.find(key) != allowset.end()) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
error("unknown key: %s", key.c_str());
|
error("unknown key: %s", key.c_str());
|
||||||
return false;
|
return false;
|
||||||
@@ -118,4 +134,26 @@ bool Config::validateJsonStr(const string js) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::print_summary() {
|
||||||
|
info("noproxy program: %s", join2str(program_noproxy).c_str());
|
||||||
|
info("proxied program: %s", join2str(program_proxy).c_str());
|
||||||
|
info("noproxy cgroup: %s", join2str(cgroup_noproxy).c_str());
|
||||||
|
info("proxied cgroup: %s", join2str(cgroup_proxy).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::toRealProgramPath(vector<string> &v) {
|
||||||
|
vector<string> tmp;
|
||||||
|
for (auto &p : v) {
|
||||||
|
auto rpath = getRealExistPath(p);
|
||||||
|
if (!rpath.empty()) tmp.push_back(rpath);
|
||||||
|
else
|
||||||
|
error("%s not exist or broken link", p.c_str());
|
||||||
|
}
|
||||||
|
v = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef tryassign
|
||||||
|
#undef add2json
|
||||||
|
#undef merge
|
||||||
|
|
||||||
} // namespace CGPROXY::CONFIG
|
} // namespace CGPROXY::CONFIG
|
||||||
@@ -13,6 +13,8 @@ public:
|
|||||||
const string cgroup_proxy_preserved = CGROUP_PROXY_PRESVERED;
|
const string cgroup_proxy_preserved = CGROUP_PROXY_PRESVERED;
|
||||||
const string cgroup_noproxy_preserved = CGROUP_NOPROXY_PRESVERED;
|
const string cgroup_noproxy_preserved = CGROUP_NOPROXY_PRESVERED;
|
||||||
|
|
||||||
|
vector<string> program_proxy = {cgroup_proxy_preserved};
|
||||||
|
vector<string> program_noproxy = {cgroup_noproxy_preserved};
|
||||||
vector<string> cgroup_proxy;
|
vector<string> cgroup_proxy;
|
||||||
vector<string> cgroup_noproxy;
|
vector<string> cgroup_noproxy;
|
||||||
bool enable_gateway = false;
|
bool enable_gateway = false;
|
||||||
@@ -28,10 +30,12 @@ public:
|
|||||||
string toJsonStr();
|
string toJsonStr();
|
||||||
int loadFromFile(const string f);
|
int loadFromFile(const string f);
|
||||||
int loadFromJsonStr(const string js);
|
int loadFromJsonStr(const string js);
|
||||||
|
void print_summary();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void mergeReserved();
|
void mergeReserved();
|
||||||
bool validateJsonStr(const string js);
|
bool validateJsonStr(const string js);
|
||||||
|
void toRealProgramPath(vector<string> &v);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace CGPROXY::CONFIG
|
} // namespace CGPROXY::CONFIG
|
||||||
|
|||||||
104
src/execsnoop.cpp
Normal file
104
src/execsnoop.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include "execsnoop.h"
|
||||||
|
#include "bcc/BPF.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <bcc/libbpf.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::EXECSNOOP {
|
||||||
|
|
||||||
|
const string BPF_PROGRAM = R"(
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <uapi/linux/ptrace.h>
|
||||||
|
|
||||||
|
struct data_t {
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
BPF_PERF_OUTPUT(events);
|
||||||
|
|
||||||
|
int syscall_execve(struct pt_regs *ctx,
|
||||||
|
const char __user *filename,
|
||||||
|
const char __user *const __user *__argv,
|
||||||
|
const char __user *const __user *__envp)
|
||||||
|
{
|
||||||
|
struct data_t data = {};
|
||||||
|
data.pid = bpf_get_current_pid_tgid();
|
||||||
|
events.perf_submit(ctx, &data, sizeof(struct data_t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret_syscall_execve(struct pt_regs *ctx){
|
||||||
|
struct data_t data = {};
|
||||||
|
data.pid = bpf_get_current_pid_tgid();
|
||||||
|
int retval = PT_REGS_RC(ctx);
|
||||||
|
if (retval==0)
|
||||||
|
events.perf_submit(ctx, &data, sizeof(struct data_t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
struct data_t {
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
function<int(int)> callback = NULL;
|
||||||
|
promise<void> status;
|
||||||
|
|
||||||
|
void handle_events(void *cb_cookie, void *data, int data_size) {
|
||||||
|
auto event = static_cast<data_t *>(data);
|
||||||
|
int pid = event->pid;
|
||||||
|
|
||||||
|
if (callback) callback(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int execsnoop() {
|
||||||
|
debug("starting execsnoop");
|
||||||
|
ebpf::BPF bpf;
|
||||||
|
|
||||||
|
auto init_res = bpf.init(BPF_PROGRAM);
|
||||||
|
if (init_res.code() != 0) {
|
||||||
|
error("bpf init failed, maybe linux-headers not installed");
|
||||||
|
std::cerr << init_res.msg() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string execve_fnname = bpf.get_syscall_fnname("execve");
|
||||||
|
// auto attach_res = bpf.attach_kprobe(execve_fnname, "syscall_execve");
|
||||||
|
auto attach_res =
|
||||||
|
bpf.attach_kprobe(execve_fnname, "ret_syscall_execve", 0, BPF_PROBE_RETURN);
|
||||||
|
if (attach_res.code() != 0) {
|
||||||
|
std::cerr << attach_res.msg() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto open_res = bpf.open_perf_buffer("events", &handle_events);
|
||||||
|
if (open_res.code() != 0) {
|
||||||
|
std::cerr << open_res.msg() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpf.free_bcc_memory()) {
|
||||||
|
std::cerr << "Failed to free llvm/clang memory" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.set_value();
|
||||||
|
|
||||||
|
while (true) bpf.poll_perf_buffer("events");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startThread(function<int(int)> c, promise<void> _status) {
|
||||||
|
status = move(_status);
|
||||||
|
callback = c;
|
||||||
|
execsnoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CGPROXY::EXECSNOOP
|
||||||
22
src/execsnoop.h
Normal file
22
src/execsnoop.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef EXECSNOOP_HPP
|
||||||
|
#define EXECSNOOP_HPP 1
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::EXECSNOOP {
|
||||||
|
|
||||||
|
extern const string BPF_PROGRAM;
|
||||||
|
struct data_t;
|
||||||
|
extern function<int(int)> callback;
|
||||||
|
void handle_events(void *cb_cookie, void *data, int data_size);
|
||||||
|
int execsnoop();
|
||||||
|
|
||||||
|
extern "C" void startThread(function<int(int)> c, promise<void> _status);
|
||||||
|
typedef void startThread_t(function<int(int)>, promise<void>);
|
||||||
|
startThread_t *_startThread; // only for dlsym()
|
||||||
|
|
||||||
|
} // namespace CGPROXY::EXECSNOOP
|
||||||
|
#endif
|
||||||
@@ -42,11 +42,7 @@ void send(const char *msg, int &status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void send(const string msg, int &status) {
|
void send(const string msg, int &status) {
|
||||||
int msg_len = msg.length();
|
send(msg.c_str(), status);
|
||||||
char buff[msg_len];
|
|
||||||
msg.copy(buff, msg_len, 0);
|
|
||||||
buff[msg_len] = '\0';
|
|
||||||
send(buff, status);
|
|
||||||
debug("return status: %d", status);
|
debug("return status: %d", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "socket_server.h"
|
#include "socket_server.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -9,7 +10,7 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
namespace CGPROXY::SOCKET {
|
namespace CGPROXY::SOCKET {
|
||||||
|
|
||||||
void SocketServer::socketListening(function<int(char *)> callback) {
|
void SocketServer::socketListening(function<int(char *)> callback, promise<void> status) {
|
||||||
debug("starting socket listening");
|
debug("starting socket listening");
|
||||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
@@ -26,6 +27,8 @@ void SocketServer::socketListening(function<int(char *)> callback) {
|
|||||||
listen(sfd, LISTEN_BACKLOG);
|
listen(sfd, LISTEN_BACKLOG);
|
||||||
chmod(SOCKET_PATH, S_IRWXU | S_IRWXG | S_IRWXO);
|
chmod(SOCKET_PATH, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||||
|
|
||||||
|
status.set_value();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
close(cfd);
|
close(cfd);
|
||||||
cfd = accept(sfd, NULL, NULL);
|
cfd = accept(sfd, NULL, NULL);
|
||||||
@@ -37,29 +40,28 @@ void SocketServer::socketListening(function<int(char *)> callback) {
|
|||||||
flag = read(cfd, &msg_len, sizeof(int));
|
flag = read(cfd, &msg_len, sizeof(int));
|
||||||
continue_if_error(flag, "read length");
|
continue_if_error(flag, "read length");
|
||||||
// read msg
|
// read msg
|
||||||
char msg[msg_len];
|
auto msg = (char *)malloc(msg_len + 1);
|
||||||
flag = read(cfd, msg, msg_len * sizeof(char));
|
flag = read(cfd, msg, msg_len * sizeof(char));
|
||||||
continue_if_error(flag, "read msg");
|
continue_if_error(flag, "read msg");
|
||||||
msg[msg_len] = '\0';
|
msg[msg_len] = '\0';
|
||||||
// handle msg
|
// handle msg
|
||||||
int status = callback(msg);
|
int status = callback(msg);
|
||||||
|
free(msg);
|
||||||
// send back flag
|
// send back flag
|
||||||
flag = write(cfd, &status, sizeof(int));
|
flag = write(cfd, &status, sizeof(int));
|
||||||
continue_if_error(flag, "write back");
|
continue_if_error(flag, "write back");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *SocketServer::startThread(void *arg) {
|
|
||||||
thread_arg *p = (thread_arg *)arg;
|
|
||||||
SocketServer server;
|
|
||||||
server.socketListening(p->handle_msg);
|
|
||||||
return (void *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketServer::~SocketServer() {
|
SocketServer::~SocketServer() {
|
||||||
close(sfd);
|
close(sfd);
|
||||||
close(cfd);
|
close(cfd);
|
||||||
unlink(SOCKET_PATH);
|
unlink(SOCKET_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startThread(function<int(char *)> callback, promise<void> status) {
|
||||||
|
SocketServer server;
|
||||||
|
server.socketListening(callback, move(status));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CGPROXY::SOCKET
|
} // namespace CGPROXY::SOCKET
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#define SOCKET_SERVER_H
|
#define SOCKET_SERVER_H
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -14,20 +15,17 @@ namespace CGPROXY::SOCKET {
|
|||||||
continue; \
|
continue; \
|
||||||
}
|
}
|
||||||
|
|
||||||
struct thread_arg {
|
|
||||||
function<int(char *)> handle_msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SocketServer {
|
class SocketServer {
|
||||||
public:
|
public:
|
||||||
int sfd = -1, cfd = -1, flag = -1;
|
int sfd = -1, cfd = -1, flag = -1;
|
||||||
struct sockaddr_un unix_socket;
|
struct sockaddr_un unix_socket;
|
||||||
|
|
||||||
void socketListening(function<int(char *)> callback);
|
void socketListening(function<int(char *)> callback, promise<void> status);
|
||||||
~SocketServer();
|
~SocketServer();
|
||||||
static void *startThread(void *arg);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void startThread(function<int(char *)> callback, promise<void> status);
|
||||||
|
|
||||||
} // namespace CGPROXY::SOCKET
|
} // namespace CGPROXY::SOCKET
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
include_directories(${PROJECT_SOURCE_DIR})
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
|
||||||
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
||||||
|
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
|
||||||
|
if (with_execsnoop)
|
||||||
|
add_executable(execsnoop_exec execsnoop.cpp ../src/common.cpp ../src/execsnoop.cpp)
|
||||||
|
set_target_properties(execsnoop_exec PROPERTIES OUTPUT_NAME execsnoop)
|
||||||
|
target_link_libraries(execsnoop_exec bcc)
|
||||||
|
install(TARGETS execsnoop_exec DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
endif()
|
||||||
24
tools/execsnoop.cpp
Normal file
24
tools/execsnoop.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "execsnoop.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
using namespace std;
|
||||||
|
using namespace CGPROXY::EXECSNOOP;
|
||||||
|
|
||||||
|
#define PATH_MAX_LEN 128
|
||||||
|
|
||||||
|
int handle_pid(int pid) {
|
||||||
|
char path[PATH_MAX_LEN];
|
||||||
|
auto size = readlink(to_str("/proc/", pid, "/exe").c_str(), path, PATH_MAX_LEN);
|
||||||
|
if (size == -1) error("readlink: %s", to_str("/proc/", pid, "/exe").c_str());
|
||||||
|
path[size] = '\0';
|
||||||
|
info("%d %s", pid, path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
enable_debug = true;
|
||||||
|
enable_info = true;
|
||||||
|
callback = handle_pid;
|
||||||
|
execsnoop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ Wants=network-online.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=exec
|
Type=exec
|
||||||
ExecStart=/usr/lib/v2ray/v2ray -config /etc/v2ray/config.json
|
ExecStart=/usr/lib/v2ray/v2ray -config /etc/v2ray/config.json
|
||||||
User=nobody
|
DynamicUser=yes
|
||||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
NoNewPrivileges=yes
|
NoNewPrivileges=yes
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
@@ -15,4 +15,4 @@ Restart=on-failure
|
|||||||
RestartPreventExitStatus=23
|
RestartPreventExitStatus=23
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
Reference in New Issue
Block a user