From 4e37bccc1aea1f3b941df90b0c7e094638f700f7 Mon Sep 17 00:00:00 2001 From: fancy Date: Wed, 13 May 2020 23:43:07 +0800 Subject: [PATCH 01/18] add basic unix socket control --- CMakeLists.txt | 20 ++++++ common.h | 34 ++++++++++ main.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++++ socket_client.cpp | 80 +++++++++++++++++++++++ socket_server.h | 90 ++++++++++++++++++++++++++ 5 files changed, 385 insertions(+) create mode 100644 common.h create mode 100644 main.cpp create mode 100644 socket_client.cpp create mode 100644 socket_server.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f239dae..38d220c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,26 @@ cmake_minimum_required(VERSION 3.10) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_BUILD_TYPE DEBUG) +add_compile_options(-Wno-return-type) project(cgproxy VERSION 3.7) + + +find_package(Threads REQUIRED) +find_package(libconfig++ REQUIRED) +find_package(nlohmann_json REQUIRED) +include_directories(${PROJECT_SOURCE_DIR}) + add_executable(cgattach cgattach.cpp) +add_executable(main main.cpp) +target_link_libraries(main Threads::Threads ${LIBCONFIG++_LIBRARIES} nlohmann_json::nlohmann_json) + +add_executable(client socket_client.cpp) +target_link_libraries(client nlohmann_json::nlohmann_json) + + install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID) install(FILES cgproxy.sh DESTINATION /usr/bin @@ -26,6 +44,8 @@ install(FILES readme.md DESTINATION /share/doc/cgproxy/) + + ## package for deb and rpm set(CPACK_GENERATOR "DEB;RPM") set(CPACK_PACKAGE_NAME "cgproxy") diff --git a/common.h b/common.h new file mode 100644 index 0000000..7089b33 --- /dev/null +++ b/common.h @@ -0,0 +1,34 @@ +#ifndef COMMON_H +#define COMMON_H + +#define SOCKET_PATH "/tmp/unix_socket" +#define LISTEN_BACKLOG 5 +#define DEFAULT_CONFIG_FILE "/etc/cgproxy.conf" + +#define MSG_TYPE_JSON 1 +#define MSG_TYPE_CONFIG_PATH 2 +#define MSG_TYPE_PROXY_PID 3 +#define MSG_TYPE_NOPROXY_PID 4 + +#define UNKNOWN_ERROR -99 +#define CONN_ERROR -1 +#define MSG_ERROR 1 +#define PARSE_ERROR 2 +#define PARAM_ERROR 3 +#define APPLY_ERROR 4 + +#include +#include +#include +using namespace std; +template string to_str(T... args) { + stringstream ss; + ss.clear(); + (ss << ... << args) << endl; + return ss.str(); +} + +#define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");} +#define debug(...) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");} + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a02846a --- /dev/null +++ b/main.cpp @@ -0,0 +1,161 @@ +#include "common.h" +#include "socket_server.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using json = nlohmann::json; + +struct Config { + string cgroup_proxy = "/proxy.slice"; + string cgroup_noproxy = "/noproxy.slice"; + bool enable_gateway = false; + int port = 12345; + bool enable_dns = true; + bool enable_tcp = true; + bool enable_udp = true; + bool enable_ipv4 = true; + bool enable_ipv6 = true; + + void toEnv() { + #define env(v) setenv(#v, to_str(v).c_str(), 1) + env(cgroup_proxy); + env(cgroup_noproxy); + env(enable_gateway); + env(port); + env(enable_dns); + env(enable_tcp); + env(enable_udp); + env(enable_ipv4); + env(enable_ipv6); + #undef env + } + + int safeLoadFromFile(const string path){ + Config tmp=*this; + int flag=tmp.loadFromFile(path); + if (flag!=0) return flag; + if (tmp.isValid()){ + loadFromFile(path); + return 0; + }else{ + return PARAM_ERROR; + } + } + + int safeLoadFromJson(const json& j){ + Config tmp=*this; + int flag=tmp.loadFromJson(j); + if (flag!=0) return flag; + if (tmp.isValid()){ + loadFromJson(j); + return 0; + }else{ + return PARAM_ERROR; + } + } + + private: + int loadFromFile(const string f) { + debug("loading config: %s", f.c_str()); + libconfig::Config config_f; + try { config_f.readFile(f.c_str()); } catch (exception &e) { return PARSE_ERROR; } + #define assign(v, t) if (config_f.exists(#v)) {v = (t)config_f.lookup(#v);} + assign(cgroup_proxy, string); + assign(cgroup_noproxy, string); + assign(enable_gateway, bool); + assign(port, int); + assign(enable_dns, bool); + assign(enable_tcp, bool); + assign(enable_udp, bool); + assign(enable_ipv4, bool); + assign(enable_ipv6, bool); + #undef assign + return 0; + } + + int loadFromJson(const json &j) { + #define get_to(v) try {j.at(#v).get_to(v); } catch (exception& e) {} + get_to(cgroup_proxy); + get_to(cgroup_noproxy); + get_to(enable_gateway); + get_to(port); + get_to(enable_dns); + get_to(enable_tcp); + get_to(enable_udp); + get_to(enable_ipv4); + get_to(enable_ipv6); + #undef get_to + return 0; + } + + bool isValid(){ + // TODO + return true; + } +}; + +SocketControl sc; +thread_arg arg_t; +Config config_tproxy; +pthread_t socket_thread_id = -1; + +int applyConfig(Config *c) { + system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"); + c->toEnv(); + system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"); + return 0; +} + +int handle_msg(char *msg) { + debug("received msg: %s", msg); + json j; + try{ j = json::parse(msg); }catch(exception& e){debug("msg paser error");return MSG_ERROR;} + int type = -1, status; + try { + type = j.at("type").get(); + if (type == MSG_TYPE_JSON) { // json data + status=config_tproxy.safeLoadFromJson(j.at("data")); + } else if (type == MSG_TYPE_CONFIG_PATH) { // config file + status=config_tproxy.safeLoadFromFile(j.at("data").get()); + } + } catch (out_of_range &e) { + return MSG_ERROR; + } + if (status==0){ + return applyConfig(&config_tproxy); + } + return status; +} + +pthread_t startSocketListeningThread() { + arg_t.sc = ≻ + arg_t.handle_msg = &handle_msg; + pthread_t thread_id; + int status = + pthread_create(&thread_id, NULL, &SocketControl::startThread, &arg_t); + if (status != 0) + error("socket thread create failed"); + return thread_id; +} + +int main() { + bool enable_socket = true; + string config_path = DEFAULT_CONFIG_FILE; + config_tproxy.safeLoadFromFile(config_path); + applyConfig(&config_tproxy); + if (enable_socket) { + socket_thread_id = startSocketListeningThread(); + pthread_join(socket_thread_id, NULL); + } + return 0; +} + +// TODO handle attch pid \ No newline at end of file diff --git a/socket_client.cpp b/socket_client.cpp new file mode 100644 index 0000000..7301ba9 --- /dev/null +++ b/socket_client.cpp @@ -0,0 +1,80 @@ +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using json = nlohmann::json; + +#define return_if_error(flag, msg) \ + if (flag == -1) { \ + perror(msg); \ + status = CONN_ERROR; \ + return; \ + } + +void send(char *msg, int &status) { + debug("send msg: %s", msg); + status = UNKNOWN_ERROR; + + int flag; + int sfd = socket(AF_UNIX, SOCK_STREAM, 0); + + struct sockaddr_un unix_socket; + memset(&unix_socket, '\0', sizeof(struct sockaddr_un)); + unix_socket.sun_family = AF_UNIX; + strncpy(unix_socket.sun_path, SOCKET_PATH, sizeof(unix_socket.sun_path) - 1); + + flag = + connect(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un)); + return_if_error(flag, "connect"); + + int msg_len = strlen(msg); + flag = write(sfd, &msg_len, sizeof(int)); + return_if_error(flag, "write length"); + flag = write(sfd, msg, msg_len * sizeof(char)); + return_if_error(flag, "write msg"); + + flag = read(sfd, &status, sizeof(int)); + return_if_error(flag, "read return value"); + + close(sfd); +} + +void send(const json &j, int &status) { + string msg = j.dump(); + int msg_len = msg.length(); + char buff[msg_len]; + msg.copy(buff, msg_len, 0); + buff[msg_len] = '\0'; + send(buff, status); + debug("return status: %d", status); +} + +int test_json() { + json j; + j["type"] = MSG_TYPE_JSON; + j["data"]["cgroup_proxy"] = "/"; + j["data"]["enable_dns"] = false; + int status; + send(j, status); +} + +void test_file() { + json j; + j["type"] = MSG_TYPE_CONFIG_PATH; + j["data"] = "/etc/cgproxy.conf"; + int status; + send(j, status); +} + +int main() { + test_file(); + test_json(); +} \ No newline at end of file diff --git a/socket_server.h b/socket_server.h new file mode 100644 index 0000000..2980251 --- /dev/null +++ b/socket_server.h @@ -0,0 +1,90 @@ +#ifndef SOCKET_SERVER_H +#define SOCKET_SERVER_H + +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#define SOCKET_PATH "/tmp/unix_socket" +#define LISTEN_BACKLOG 5 + +class SocketControl; +struct thread_arg; + +#define continue_if_error(flag, msg) \ + if (flag == -1) { \ + perror(msg); \ + continue; \ + } + +struct thread_arg { + SocketControl *sc; + function handle_msg; +}; + +class SocketControl { +public: + int sfd = -1, cfd = -1, flag = -1; + struct sockaddr_un unix_socket; + + void socketListening(function callback) { + debug("starting socket listening"); + sfd = socket(AF_UNIX, SOCK_STREAM, 0); + + unlink(SOCKET_PATH); + memset(&unix_socket, '\0', sizeof(struct sockaddr_un)); + unix_socket.sun_family = AF_UNIX; + strncpy(unix_socket.sun_path, SOCKET_PATH, + sizeof(unix_socket.sun_path) - 1); + + bind(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un)); + + listen(sfd, LISTEN_BACKLOG); + chmod(SOCKET_PATH,S_IRWXU|S_IRWXG|S_IRWXO); + + while (true) { + close(cfd); + cfd = accept(sfd, NULL, NULL); + continue_if_error(cfd, "accept"); + debug("accept connection: %d", cfd); + + // read length + int msg_len; + flag = read(cfd, &msg_len, sizeof(int)); + continue_if_error(flag, "read length"); + // read msg + char msg[msg_len]; + flag = read(cfd, msg, msg_len * sizeof(char)); + continue_if_error(flag, "read msg"); + msg[msg_len]='\0'; + // handle msg + int status = callback(msg); + // send back flag + flag = write(cfd, &status, sizeof(int)); + continue_if_error(flag, "write back"); + } + } + + ~SocketControl() { + close(sfd); + close(cfd); + unlink(SOCKET_PATH); + } + + static void *startThread(void *arg) { + thread_arg *p = (thread_arg *)arg; + p->sc->socketListening(p->handle_msg); + } +}; + +#endif \ No newline at end of file From b5701d8b49c2d445fc180e065263e5d1f5d1771a Mon Sep 17 00:00:00 2001 From: fancy Date: Thu, 14 May 2020 04:39:02 +0800 Subject: [PATCH 02/18] allow array input for cgroup_proxy and cgroup_noproxy --- CMakeLists.txt | 3 ++- cgproxy.conf | 27 ++++++++++++++++++++++++--- cgroup-tproxy.sh | 22 ++++++++++++++-------- readme.md | 4 +++- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38d220c..79abee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_BUILD_TYPE DEBUG) +# set(CMAKE_BUILD_TYPE DEBUG) +set(CMAKE_BUILD_TYPE RELEASE) add_compile_options(-Wno-return-type) project(cgproxy VERSION 3.7) diff --git a/cgproxy.conf b/cgproxy.conf index 15ecdc2..5557854 100644 --- a/cgproxy.conf +++ b/cgproxy.conf @@ -1,26 +1,47 @@ ## 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 ## note, cgroup must start with slash '/' -# cgroup_proxy="/" # for global tproxy -# cgroup_noproxy="/system.slice/v2ray.service" # for v2ray service +## 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 + +################################################################################### ## do not modify this if you don't known what you are doing table=100 fwmark=0x01 diff --git a/cgroup-tproxy.sh b/cgroup-tproxy.sh index 324e136..eaf885e 100644 --- a/cgroup-tproxy.sh +++ b/cgroup-tproxy.sh @@ -69,7 +69,6 @@ for i in "$@" do case $i in stop) - iptables -t mangle -L TPROXY_PRE &> /dev/null || exit 0 echo "stopping tproxy iptables" iptables -t mangle -D PREROUTING -j TPROXY_PRE iptables -t mangle -D OUTPUT -j TPROXY_OUT @@ -99,7 +98,6 @@ case $i in --config=*) config=${i#*=} source $config - shift ;; --help) print_help @@ -136,8 +134,12 @@ iptables -t mangle -A TPROXY_OUT -p icmp -j RETURN 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 -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 $fwmark +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# @@ -163,8 +165,12 @@ 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 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 cgroup --path $cgroup_noproxy -j RETURN -ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_proxy -j MARK --set-mark $fwmark +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 ## allow to disable, order is important @@ -199,8 +205,8 @@ ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack -- ## message for user cat << DOC -noproxy cgroup: $cgroup_noproxy -proxied cgroup: $cgroup_proxy +noproxy cgroup: ${cgroup_noproxy[@]} +proxied cgroup: ${cgroup_proxy[@]} DOC diff --git a/readme.md b/readme.md index 1fe4305..8ef3a57 100644 --- a/readme.md +++ b/readme.md @@ -128,7 +128,9 @@ sudo systemctl restart cgproxy.service example: `cgnoproxy sudo v2ray -config config_file` - - passive way + example: `cgnoproxy qv2ray` + + - passive way, useful if you run v2ray as service set `cgroup_noproxy=""` From 2b5ff745ac764448169fed225e6efdb73827fd82 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 00:07:04 +0800 Subject: [PATCH 03/18] core socket feature --- CMakeLists.txt | 15 +-- cgattach.cpp | 87 ++------------- common.h | 66 ++++++++++-- config.h | 142 +++++++++++++++++++++++++ config.json | 11 ++ main.cpp | 151 ++++++++------------------- socket_client.cpp => socket_client.h | 38 ++----- socket_server.h | 23 ++-- 8 files changed, 285 insertions(+), 248 deletions(-) create mode 100644 config.h create mode 100644 config.json rename socket_client.cpp => socket_client.h (74%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 79abee5..7b84937 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,23 +3,21 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # set(CMAKE_BUILD_TYPE DEBUG) set(CMAKE_BUILD_TYPE RELEASE) -add_compile_options(-Wno-return-type) project(cgproxy VERSION 3.7) find_package(Threads REQUIRED) -find_package(libconfig++ REQUIRED) find_package(nlohmann_json REQUIRED) include_directories(${PROJECT_SOURCE_DIR}) add_executable(cgattach cgattach.cpp) add_executable(main main.cpp) -target_link_libraries(main Threads::Threads ${LIBCONFIG++_LIBRARIES} nlohmann_json::nlohmann_json) +target_link_libraries(main Threads::Threads nlohmann_json::nlohmann_json) -add_executable(client socket_client.cpp) -target_link_libraries(client nlohmann_json::nlohmann_json) +add_executable(client_test socket_client_test.cpp) +target_link_libraries(client_test nlohmann_json::nlohmann_json) install(TARGETS cgattach DESTINATION /usr/bin @@ -30,14 +28,11 @@ install(FILES cgproxy.sh DESTINATION /usr/bin install(FILES cgnoproxy.sh DESTINATION /usr/bin RENAME cgnoproxy PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -# install(FILES run_in_cgroup.sh DESTINATION /usr/bin -# RENAME run_in_cgroup -# PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/) -install(FILES cgproxy.conf - DESTINATION /etc/) +install(FILES cgproxy.json + DESTINATION /etc/cgproxy/) install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/) diff --git a/cgattach.cpp b/cgattach.cpp index 7db6f98..ee5844e 100644 --- a/cgattach.cpp +++ b/cgattach.cpp @@ -1,98 +1,23 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "cgroup_attach.h" using namespace std; void print_usage() { fprintf(stdout, "usage: cgattach \n"); } -bool exist(string path) { - struct stat st; - if (stat(path.c_str(), &st) != -1) { - return S_ISDIR(st.st_mode); - } - return false; -} - -bool validate(string pid, string cgroup) { - bool pid_v = regex_match(pid, regex("^[0-9]+$")); - bool cg_v = regex_match(cgroup, regex("^\\/[a-zA-Z0-9\\-_./@]*$")); - if (pid_v && cg_v) - return true; - - fprintf(stderr, "paramater validate error\n"); - print_usage(); - exit(EXIT_FAILURE); -} - -string get_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){ - fprintf(stderr, "cgroup2 not supported\n"); - 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); } string pid = string(argv[1]); string cgroup_target = string(argv[2]); - validate(pid, cgroup_target); - // string cgroup_mount_point = "/sys/fs/cgroup"; - string cgroup_mount_point = get_cgroup2_mount_point(); - string cgroup_target_path = cgroup_mount_point + cgroup_target; - string cgroup_target_procs = cgroup_target_path + "/cgroup.procs"; - // check if exist, we will create it if not exist - 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()); - } else { - fprintf(stderr, "created cgroup %s failed, errno %d\n", - cgroup_target.c_str(), errno); - exit(EXIT_FAILURE); - } - // fprintf(stderr, "cgroup %s not exist\n",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()); - exit(EXIT_FAILURE); - } - procs << pid.c_str() << endl; - procs.close(); - - // 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", - pid.c_str(), cgroup_target_procs.c_str(), pid.c_str()); - exit(EXIT_FAILURE); - } - return EXIT_SUCCESS; + CGPROXY::CGROUP::attach(pid,cgroup_target); } diff --git a/common.h b/common.h index 7089b33..22a66cf 100644 --- a/common.h +++ b/common.h @@ -2,33 +2,77 @@ #define COMMON_H #define SOCKET_PATH "/tmp/unix_socket" -#define LISTEN_BACKLOG 5 +#define LISTEN_BACKLOG 64 #define DEFAULT_CONFIG_FILE "/etc/cgproxy.conf" +#define CGROUP_PROXY_PRESVERED "/proxy.slice" +#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice" + #define MSG_TYPE_JSON 1 #define MSG_TYPE_CONFIG_PATH 2 #define MSG_TYPE_PROXY_PID 3 #define MSG_TYPE_NOPROXY_PID 4 -#define UNKNOWN_ERROR -99 -#define CONN_ERROR -1 -#define MSG_ERROR 1 -#define PARSE_ERROR 2 -#define PARAM_ERROR 3 -#define APPLY_ERROR 4 +#define UNKNOWN_ERROR 99 +#define ERROR -1 +#define SUCCESS 0 +#define CONN_ERROR 1 +#define MSG_ERROR 2 +#define PARSE_ERROR 3 +#define PARAM_ERROR 4 +#define APPLY_ERROR 5 +#define CGROUP_ERROR 6 +#define FILE_ERROR 7 + #include #include -#include +#include +#include using namespace std; -template string to_str(T... args) { + +#define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");} +#define debug(...) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");} +#define return_error return -1; +#define return_success return 0; + + +template +string to_str(T... args) { stringstream ss; ss.clear(); (ss << ... << args) << endl; return ss.str(); } -#define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");} -#define debug(...) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");} +template +string join2str(const T t){ + string s; + string delm=" ", prefix="(", tail=")", wrap="\""; + for (const auto &e : t) + e!=*(t.end()-1)?s+=wrap+e+wrap+delm:s+=wrap+e+wrap; + return prefix+s+tail; +} + +bool validCgroup(const string cgroup){ + return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$")); +} + +bool validCgroup(const vector cgroup){ + for (auto &e:cgroup){ + if (!regex_match(e, regex("^/[a-zA-Z0-9\\-_./@]*$"))){ + return false; + } + } + return true; +} + +bool validPid(const string pid){ + return regex_match(pid, regex("^[0-9]+$")); +} + +bool validPort(const string port){ + return regex_match(port, regex("^[0-9]+$")); +} #endif \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..9f00a5d --- /dev/null +++ b/config.h @@ -0,0 +1,142 @@ +#ifndef CONFIG_H +#define CONFIG_H +#include "common.h" +#include "socket_server.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +using json = nlohmann::json; + +namespace CGPROXY::CONFIG{ + +struct Config { + public: + const string cgroup_proxy_preserved=CGROUP_PROXY_PRESVERED; + const string cgroup_noproxy_preserved=CGROUP_NOPROXY_PRESVERED; + private: + vector cgroup_proxy; + vector cgroup_noproxy; + bool enable_gateway = false; + int port = 12345; + bool enable_dns = true; + bool enable_tcp = true; + bool enable_udp = true; + bool enable_ipv4 = true; + bool enable_ipv6 = true; + +public: + void toEnv() { + mergeReserved(); + setenv("cgroup_proxy", join2str(cgroup_proxy).c_str(), 1); + setenv("cgroup_noproxy", join2str(cgroup_noproxy).c_str(), 1); + setenv("enable_gateway", to_str(enable_gateway).c_str(), 1); + setenv("port", to_str(port).c_str(), 1); + setenv("enable_dns", to_str(enable_dns).c_str(), 1); + setenv("enable_tcp", to_str(enable_tcp).c_str(), 1); + setenv("enable_udp", to_str(enable_udp).c_str(), 1); + setenv("enable_ipv4", to_str(enable_ipv4).c_str(), 1); + setenv("enable_ipv6", to_str(enable_ipv6).c_str(), 1); + } + + int saveToFile(const string f){ + ofstream o(f); + if (!o.is_open()) return FILE_ERROR; + json j=toJson(); + o << setw(4) << j << endl; + o.close(); + return 0; + } + + json toJson(){ + json j; + #define add2json(v) j[#v]=v; + add2json(cgroup_proxy); + add2json(cgroup_noproxy); + add2json(enable_gateway); + add2json(port); + add2json(enable_dns); + add2json(enable_tcp); + add2json(enable_udp); + add2json(enable_ipv4); + add2json(enable_ipv6); + #undef add2json + return j; + } + + int loadFromFile(const string f) { + debug("loading config: %s", f.c_str()); + ifstream ifs(f); + if (ifs.is_open()){ + json j; + try { ifs >> j; }catch (exception& e){error("parse error: %s", f.c_str());ifs.close();return PARSE_ERROR;} + ifs.close(); + return loadFromJson(j); + }else{ + error("open failed: %s",f.c_str()); + return FILE_ERROR; + } + } + + int loadFromJson(const json &j) { + if (!validateJson(j)) {error("json validate fail"); return PARAM_ERROR;} + #define tryassign(v) try{j.at(#v).get_to(v);}catch(exception &e){} + tryassign(cgroup_proxy); + tryassign(cgroup_noproxy); + tryassign(enable_gateway); + tryassign(port); + tryassign(enable_dns); + tryassign(enable_tcp); + tryassign(enable_udp); + tryassign(enable_ipv4); + tryassign(enable_ipv6); + #undef assign + return 0; + } + + void mergeReserved(){ + #define merge(v) { \ + v.erase(std::remove(v.begin(), v.end(), v ## _preserved), v.end()); \ + v.insert(v.begin(), v ## _preserved); \ + } + merge(cgroup_proxy); + merge(cgroup_noproxy); + #undef merge + + } + + bool validateJson(const json &j){ + bool status=true; + const set boolset={"enable_gateway","enable_dns","enable_tcp","enable_udp","enable_ipv4","enable_ipv6"}; + for (auto& [key, value] : j.items()) { + if (key=="cgroup_proxy"||key=="cgroup_noproxy"){ + if (value.is_string()&&!validCgroup((string)value)) status=false; + if (value.is_array()&&!validCgroup((vector)value)) status=false; + if (!value.is_string()&&!value.is_array()) status=false; + }else if (key=="port"){ + if (validPort(value)) status=false; + }else if (boolset.find(key)!=boolset.end()){ + if (value.is_boolean()) status=false; + }else{ + error("unknown key: %s", key.c_str()); + return false; + } + if (!status) { + error("invalid value for key: %s", key.c_str()); + return false; + } + } + return true; + } +}; + +} +#endif \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..383d3c1 --- /dev/null +++ b/config.json @@ -0,0 +1,11 @@ +{ + "cgroup_noproxy": [], + "cgroup_proxy": [], + "enable_dns": true, + "enable_gateway": false, + "enable_ipv4": true, + "enable_ipv6": true, + "enable_tcp": true, + "enable_udp": true, + "port": 12345 +} diff --git a/main.cpp b/main.cpp index a02846a..838874d 100644 --- a/main.cpp +++ b/main.cpp @@ -8,103 +8,19 @@ #include #include #include -#include +#include +#include "config.h" +#include "cgroup_attach.h" using namespace std; using json = nlohmann::json; +using namespace CGPROXY::SOCKET; +using namespace CGPROXY::CONFIG; +using namespace CGPROXY::CGROUP; -struct Config { - string cgroup_proxy = "/proxy.slice"; - string cgroup_noproxy = "/noproxy.slice"; - bool enable_gateway = false; - int port = 12345; - bool enable_dns = true; - bool enable_tcp = true; - bool enable_udp = true; - bool enable_ipv4 = true; - bool enable_ipv6 = true; - - void toEnv() { - #define env(v) setenv(#v, to_str(v).c_str(), 1) - env(cgroup_proxy); - env(cgroup_noproxy); - env(enable_gateway); - env(port); - env(enable_dns); - env(enable_tcp); - env(enable_udp); - env(enable_ipv4); - env(enable_ipv6); - #undef env - } - - int safeLoadFromFile(const string path){ - Config tmp=*this; - int flag=tmp.loadFromFile(path); - if (flag!=0) return flag; - if (tmp.isValid()){ - loadFromFile(path); - return 0; - }else{ - return PARAM_ERROR; - } - } - - int safeLoadFromJson(const json& j){ - Config tmp=*this; - int flag=tmp.loadFromJson(j); - if (flag!=0) return flag; - if (tmp.isValid()){ - loadFromJson(j); - return 0; - }else{ - return PARAM_ERROR; - } - } - - private: - int loadFromFile(const string f) { - debug("loading config: %s", f.c_str()); - libconfig::Config config_f; - try { config_f.readFile(f.c_str()); } catch (exception &e) { return PARSE_ERROR; } - #define assign(v, t) if (config_f.exists(#v)) {v = (t)config_f.lookup(#v);} - assign(cgroup_proxy, string); - assign(cgroup_noproxy, string); - assign(enable_gateway, bool); - assign(port, int); - assign(enable_dns, bool); - assign(enable_tcp, bool); - assign(enable_udp, bool); - assign(enable_ipv4, bool); - assign(enable_ipv6, bool); - #undef assign - return 0; - } - - int loadFromJson(const json &j) { - #define get_to(v) try {j.at(#v).get_to(v); } catch (exception& e) {} - get_to(cgroup_proxy); - get_to(cgroup_noproxy); - get_to(enable_gateway); - get_to(port); - get_to(enable_dns); - get_to(enable_tcp); - get_to(enable_udp); - get_to(enable_ipv4); - get_to(enable_ipv6); - #undef get_to - return 0; - } - - bool isValid(){ - // TODO - return true; - } -}; - -SocketControl sc; +SocketServer sc; thread_arg arg_t; -Config config_tproxy; +Config config; pthread_t socket_thread_id = -1; int applyConfig(Config *c) { @@ -118,21 +34,42 @@ int handle_msg(char *msg) { debug("received msg: %s", msg); json j; try{ j = json::parse(msg); }catch(exception& e){debug("msg paser error");return MSG_ERROR;} - int type = -1, status; + + int type, status; + string pid, cgroup_target; try { type = j.at("type").get(); - if (type == MSG_TYPE_JSON) { // json data - status=config_tproxy.safeLoadFromJson(j.at("data")); - } else if (type == MSG_TYPE_CONFIG_PATH) { // config file - status=config_tproxy.safeLoadFromFile(j.at("data").get()); - } + switch (type) + { + case MSG_TYPE_JSON: + status=config.loadFromJson(j.at("data")); + if (status==SUCCESS) status=applyConfig(&config); + return status; + break; + case MSG_TYPE_CONFIG_PATH: + status=config.loadFromFile(j.at("data").get()); + if (status==SUCCESS) status=applyConfig(&config); + return status; + break; + case MSG_TYPE_PROXY_PID: + pid=j.at("data").get(); + status=attach(pid, config.cgroup_proxy_preserved); + return status; + break; + case MSG_TYPE_NOPROXY_PID: + pid=j.at("data").get(); + status=attach(pid, config.cgroup_noproxy_preserved); + return status; + break; + default: + return MSG_ERROR; + break; + }; } catch (out_of_range &e) { return MSG_ERROR; + } catch (exception &e){ + return ERROR; } - if (status==0){ - return applyConfig(&config_tproxy); - } - return status; } pthread_t startSocketListeningThread() { @@ -140,7 +77,7 @@ pthread_t startSocketListeningThread() { arg_t.handle_msg = &handle_msg; pthread_t thread_id; int status = - pthread_create(&thread_id, NULL, &SocketControl::startThread, &arg_t); + pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t); if (status != 0) error("socket thread create failed"); return thread_id; @@ -149,13 +86,11 @@ pthread_t startSocketListeningThread() { int main() { bool enable_socket = true; string config_path = DEFAULT_CONFIG_FILE; - config_tproxy.safeLoadFromFile(config_path); - applyConfig(&config_tproxy); + config.loadFromFile(config_path); + applyConfig(&config); if (enable_socket) { socket_thread_id = startSocketListeningThread(); pthread_join(socket_thread_id, NULL); } return 0; -} - -// TODO handle attch pid \ No newline at end of file +} \ No newline at end of file diff --git a/socket_client.cpp b/socket_client.h similarity index 74% rename from socket_client.cpp rename to socket_client.h index 7301ba9..143ff55 100644 --- a/socket_client.cpp +++ b/socket_client.h @@ -1,25 +1,29 @@ -#include "common.h" +#ifndef SOCKET_CLIENT_H +#define SOCKET_CLIENT_H + #include -#include #include #include -#include +#include #include #include #include #include +#include "common.h" using namespace std; -using json = nlohmann::json; + +namespace CGPROXY::SOCKET{ #define return_if_error(flag, msg) \ if (flag == -1) { \ perror(msg); \ status = CONN_ERROR; \ + close(sfd); \ return; \ } -void send(char *msg, int &status) { +void send(const char *msg, int &status) { debug("send msg: %s", msg); status = UNKNOWN_ERROR; @@ -47,8 +51,7 @@ void send(char *msg, int &status) { close(sfd); } -void send(const json &j, int &status) { - string msg = j.dump(); +void send(const string msg, int &status) { int msg_len = msg.length(); char buff[msg_len]; msg.copy(buff, msg_len, 0); @@ -57,24 +60,5 @@ void send(const json &j, int &status) { debug("return status: %d", status); } -int test_json() { - json j; - j["type"] = MSG_TYPE_JSON; - j["data"]["cgroup_proxy"] = "/"; - j["data"]["enable_dns"] = false; - int status; - send(j, status); } - -void test_file() { - json j; - j["type"] = MSG_TYPE_CONFIG_PATH; - j["data"] = "/etc/cgproxy.conf"; - int status; - send(j, status); -} - -int main() { - test_file(); - test_json(); -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/socket_server.h b/socket_server.h index 2980251..dc08593 100644 --- a/socket_server.h +++ b/socket_server.h @@ -1,25 +1,21 @@ #ifndef SOCKET_SERVER_H #define SOCKET_SERVER_H -#include "common.h" #include #include #include #include #include -#include +#include #include #include #include #include #include +#include "common.h" using namespace std; -#define SOCKET_PATH "/tmp/unix_socket" -#define LISTEN_BACKLOG 5 - -class SocketControl; -struct thread_arg; +namespace CGPROXY::SOCKET{ #define continue_if_error(flag, msg) \ if (flag == -1) { \ @@ -27,12 +23,13 @@ struct thread_arg; continue; \ } +class SocketServer; struct thread_arg { - SocketControl *sc; + SocketServer *sc; function handle_msg; }; -class SocketControl { +class SocketServer { public: int sfd = -1, cfd = -1, flag = -1; struct sockaddr_un unix_socket; @@ -41,7 +38,8 @@ public: debug("starting socket listening"); sfd = socket(AF_UNIX, SOCK_STREAM, 0); - unlink(SOCKET_PATH); + flag=unlink(SOCKET_PATH); + if (flag==-1) {error("%s exist, and can't unlink",SOCKET_PATH); exit(EXIT_FAILURE);} memset(&unix_socket, '\0', sizeof(struct sockaddr_un)); unix_socket.sun_family = AF_UNIX; strncpy(unix_socket.sun_path, SOCKET_PATH, @@ -75,7 +73,7 @@ public: } } - ~SocketControl() { + ~SocketServer() { close(sfd); close(cfd); unlink(SOCKET_PATH); @@ -84,7 +82,10 @@ public: static void *startThread(void *arg) { thread_arg *p = (thread_arg *)arg; p->sc->socketListening(p->handle_msg); + return (void *)0; } }; +} + #endif \ No newline at end of file From ad362998d88bf987d7387353015830a6506d8eaa Mon Sep 17 00:00:00 2001 From: fancy Date: Thu, 14 May 2020 04:39:02 +0800 Subject: [PATCH 04/18] allow array input for cgroup_proxy and cgroup_noproxy --- cgproxy.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cgproxy.conf b/cgproxy.conf index 5557854..ea68ae0 100644 --- a/cgproxy.conf +++ b/cgproxy.conf @@ -9,12 +9,12 @@ ## 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") +# cgroup_proxy="/" +# cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service") ### global proxy with manual `cgnoporxy qv2ray` -#cgroup_proxy="/" -#cgroup_noproxy="/noproxy.slice" +# cgroup_proxy="/" +# cgroup_noproxy="/noproxy.slice" ### default cgroup_proxy="/proxy.slice" From 9b7d6804f7f94259733c95e8df6cee58da761752 Mon Sep 17 00:00:00 2001 From: fancy Date: Thu, 14 May 2020 04:53:23 +0800 Subject: [PATCH 05/18] update readme --- readme.md | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 8ef3a57..4625fbd 100644 --- a/readme.md +++ b/readme.md @@ -79,32 +79,47 @@ mkdir build && cd build && cmake .. && make && sudo make install More config in /etc/cgproxy.conf (click to expand) ```bash -######################################################################## -## cgroup transparent proxy +################################################################################### ## any process in cgroup_proxy will be proxied, and cgroup_noproxy the opposite -## cgroup must start with slash '/' +## 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="/system.slice/v2ray.service" +# 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 -######################################################################## + +################################################################################### ## do not modify this if you don't known what you are doing table=100 fwmark=0x01 @@ -134,7 +149,7 @@ sudo systemctl restart cgproxy.service set `cgroup_noproxy=""` - example: `cgroup_noproxy="/system.slice/v2ray.service"` + example: `cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service")` - Finally, restart cgproxy service, that's all From 696fcb6b4e5033f819c27f3570bbd50a7c57e8f9 Mon Sep 17 00:00:00 2001 From: fancy Date: Thu, 14 May 2020 12:07:12 +0800 Subject: [PATCH 06/18] update readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4625fbd..014d6c4 100644 --- a/readme.md +++ b/readme.md @@ -46,7 +46,7 @@ It aslo supports global transparent proxy and gateway proxy. See [Global transpa mkdir build && cd build && cmake .. && make && sudo make install ``` -- It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/cgproxy-git/). +- 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). From ffea0fb2b9bdb3aacb5d8dee8450c5e8c34dbd19 Mon Sep 17 00:00:00 2001 From: fancy Date: Thu, 14 May 2020 14:21:40 +0800 Subject: [PATCH 07/18] add local aur build --- aur-cgproxy-local/PKGBUILD | 40 +++++++++++++++++++++++++++++++ aur-cgproxy-local/cgproxy.install | 8 +++++++ 2 files changed, 48 insertions(+) create mode 100644 aur-cgproxy-local/PKGBUILD create mode 100644 aur-cgproxy-local/cgproxy.install diff --git a/aur-cgproxy-local/PKGBUILD b/aur-cgproxy-local/PKGBUILD new file mode 100644 index 0000000..0208cec --- /dev/null +++ b/aur-cgproxy-local/PKGBUILD @@ -0,0 +1,40 @@ +# Maintainer: Fancy Zhang +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 +} + diff --git a/aur-cgproxy-local/cgproxy.install b/aur-cgproxy-local/cgproxy.install new file mode 100644 index 0000000..e6081e8 --- /dev/null +++ b/aur-cgproxy-local/cgproxy.install @@ -0,0 +1,8 @@ +#!/bin/sh + +post_install(){ +cat <<'DOC' + to start service: + systemctl enable --now cgproxy.service +DOC +} From 138fa698be5185c9f2af4c36e2f6eef961fd19fc Mon Sep 17 00:00:00 2001 From: fancy Date: Thu, 14 May 2020 16:16:45 +0800 Subject: [PATCH 08/18] update readme --- cgproxy.conf | 2 +- cgroup-tproxy.sh | 1 + readme.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cgproxy.conf b/cgproxy.conf index ea68ae0..c224b52 100644 --- a/cgproxy.conf +++ b/cgproxy.conf @@ -6,7 +6,7 @@ ## 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 +## and the rest elements will not, so won't be applied if not exist ### global proxy with v2ray service # cgroup_proxy="/" diff --git a/cgroup-tproxy.sh b/cgroup-tproxy.sh index eaf885e..f419b89 100644 --- a/cgroup-tproxy.sh +++ b/cgroup-tproxy.sh @@ -107,6 +107,7 @@ esac done ## TODO cgroup need to exists before using in iptables since 5.6.5, maybe it's bug +## only create the first one in arrary test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_proxy || exit -1; test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1; diff --git a/readme.md b/readme.md index 014d6c4..f282d37 100644 --- a/readme.md +++ b/readme.md @@ -84,7 +84,7 @@ mkdir build && cd build && cmake .. && make && sudo make install ## 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 +## and the rest elements will not, so won't be applied if not exist ### global proxy with v2ray service # cgroup_proxy="/" From 87cd5a6d990921b1d2cdcd661a4cccd8fc816d4e Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 00:17:37 +0800 Subject: [PATCH 09/18] missing one file --- CMakeLists.txt | 2 +- cgroup_attach.h | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 cgroup_attach.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b84937..51401e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ install(FILES cgnoproxy.sh DESTINATION /usr/bin install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/) -install(FILES cgproxy.json +install(FILES config.json DESTINATION /etc/cgproxy/) install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/) diff --git a/cgroup_attach.h b/cgroup_attach.h new file mode 100644 index 0000000..b254611 --- /dev/null +++ b/cgroup_attach.h @@ -0,0 +1,96 @@ +#ifndef CGPROUP_ATTACH_H +#define CGPROUP_ATTACH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +using namespace std; + +namespace CGPROXY::CGROUP{ + +bool exist(string path) { + struct stat st; + if (stat(path.c_str(), &st) != -1) { + return S_ISDIR(st.st_mode); + } + return false; +} + +bool validate(string pid, string cgroup) { + bool pid_v = validPid(pid); + bool cg_v = validCgroup(cgroup); + if (pid_v && cg_v) + return true; + + error("paramater validate 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) { + if (getuid()!=0) { + error("need root to attach cgroup"); + return_error + } + int status; + validate(pid, cgroup_target); + string cgroup_mount_point = get_cgroup2_mount_point(status); + if (status!=0) return_error + string cgroup_target_path = cgroup_mount_point + cgroup_target; + string cgroup_target_procs = cgroup_target_path + "/cgroup.procs"; + + // check if exist, we will create it if not exist + if (!exist(cgroup_target_path)) { + if (mkdir(cgroup_target_path.c_str(), + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) { + debug("created cgroup %s success", cgroup_target.c_str()); + } else { + error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno); + return_error + } + // error("cgroup %s not exist",cgroup_target.c_str()); + // return_error + } + + // put pid to target cgroup + ofstream procs(cgroup_target_procs, ofstream::app); + if (!procs.is_open()) { + error("open file %s failed", cgroup_target_procs.c_str()); + return_error + } + procs << pid.c_str() << endl; + procs.close(); + + // maybe there some write error, for example process pid may not exist + if (!procs) { + error("write %s to %s failed, maybe process %s not exist", + pid.c_str(), cgroup_target_procs.c_str(), pid.c_str()); + return_error + } + return_success +} + +} + +#endif \ No newline at end of file From 1c16f57193b6c996cf0f8c802bfd250e79246809 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 00:45:38 +0800 Subject: [PATCH 10/18] now based on unix socket and json config --- CMakeLists.txt | 35 ++++++++++++-------------- aur-cgproxy-local/PKGBUILD | 6 ++--- cgnoproxy.cpp | 34 ++++++++++++++++++++++++++ cgproxy.cpp | 31 +++++++++++++++++++++++ cgproxy.service | 7 +++--- main.cpp => cgproxyd.cpp | 11 ++++++--- cgroup-tproxy.sh | 36 +++++++++++++++------------ cgroup_attach.h | 11 +++++++-- common.h | 50 +++++++++++++++++++++++++++++--------- config.h | 8 +++--- config.json | 2 +- socket_server.h | 8 ++++-- 12 files changed, 173 insertions(+), 66 deletions(-) create mode 100644 cgnoproxy.cpp create mode 100644 cgproxy.cpp rename main.cpp => cgproxyd.cpp (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51401e2..fa7eb45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,33 +1,32 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# set(CMAKE_BUILD_TYPE DEBUG) -set(CMAKE_BUILD_TYPE RELEASE) - -project(cgproxy VERSION 3.7) +set(CMAKE_BUILD_TYPE DEBUG) +# set(CMAKE_BUILD_TYPE RELEASE) +project(cgproxy VERSION 4.0) find_package(Threads REQUIRED) find_package(nlohmann_json REQUIRED) include_directories(${PROJECT_SOURCE_DIR}) add_executable(cgattach cgattach.cpp) +add_executable(cgproxyd cgproxyd.cpp) +add_executable(cgproxy cgproxy.cpp) +add_executable(cgnoproxy cgnoproxy.cpp) +target_link_libraries(cgproxyd Threads::Threads nlohmann_json::nlohmann_json) +target_link_libraries(cgproxy nlohmann_json::nlohmann_json) +target_link_libraries(cgnoproxy nlohmann_json::nlohmann_json) -add_executable(main main.cpp) -target_link_libraries(main Threads::Threads nlohmann_json::nlohmann_json) +# add_executable(client_test socket_client_test.cpp) +# target_link_libraries(client_test nlohmann_json::nlohmann_json) -add_executable(client_test socket_client_test.cpp) -target_link_libraries(client_test nlohmann_json::nlohmann_json) +set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - -install(TARGETS cgattach DESTINATION /usr/bin - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID) -install(FILES cgproxy.sh DESTINATION /usr/bin - RENAME cgproxy - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -install(FILES cgnoproxy.sh DESTINATION /usr/bin - RENAME cgnoproxy - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission}) +install(TARGETS cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission}) +install(TARGETS cgproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission}) +install(TARGETS cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission}) install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/) @@ -40,8 +39,6 @@ install(FILES readme.md DESTINATION /share/doc/cgproxy/) - - ## package for deb and rpm set(CPACK_GENERATOR "DEB;RPM") set(CPACK_PACKAGE_NAME "cgproxy") diff --git a/aur-cgproxy-local/PKGBUILD b/aur-cgproxy-local/PKGBUILD index 0208cec..6a86b39 100644 --- a/aur-cgproxy-local/PKGBUILD +++ b/aur-cgproxy-local/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Fancy Zhang pkgname=cgproxy-git -pkgver=v3.8.r1.gc0668fd +pkgver=v4.0.r1.g3fde647 pkgrel=1 pkgdesc="A transparent proxy program with cgroup2, like proxychains" arch=('x86_64') @@ -13,8 +13,8 @@ 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") +# source=("${pkgname}::git+file://${curr_dir}/../.git") +source=("${pkgname}::git+file:///home/fancy/workspace/cgproxy#branch=dev") md5sums=('SKIP') pkgver() { diff --git a/cgnoproxy.cpp b/cgnoproxy.cpp new file mode 100644 index 0000000..f41ee84 --- /dev/null +++ b/cgnoproxy.cpp @@ -0,0 +1,34 @@ +#include +#include "socket_client.h" +using json = nlohmann::json; +using namespace CGPROXY; + +bool attach2cgproxy(){ + pid_t pid=getpid(); + json j; + j["type"] = MSG_TYPE_NOPROXY_PID; + j["data"] = pid; + int status; + SOCKET::send(j.dump(), status); + return status==0; +} + +int main(int argc, char *argv[]){ + int shift=1; + if (argc==1){ + error("usage: cgnoproxy [--debug] \nexample: cgnoproxy curl -I https://www.google.com"); + exit(EXIT_FAILURE); + } + if (argv[1]=="--debug"){ + enable_debug=true; + shift+=1; + } + + if (!attach2cgproxy()){ + error("attach process failed"); + exit(EXIT_FAILURE); + } + + string s=join2str(argc-shift,argv+shift,' '); + return system(s.c_str()); +} \ No newline at end of file diff --git a/cgproxy.cpp b/cgproxy.cpp new file mode 100644 index 0000000..aba4ba2 --- /dev/null +++ b/cgproxy.cpp @@ -0,0 +1,31 @@ +#include +#include "socket_client.h" +using json = nlohmann::json; +using namespace CGPROXY; + +bool attach2cgproxy(){ + pid_t pid=getpid(); + json j; + j["type"] = MSG_TYPE_PROXY_PID; + j["data"] = pid; + int status; + SOCKET::send(j.dump(), status); + return status==0; +} + +int main(int argc, char *argv[]){ + int shift=1; + // if (argc==1){ + // error("usage: cgproxy [--debug] \nexample: cgroxy curl -I https://www.google.com"); + // exit(EXIT_FAILURE); + // } + processArgs(argc,argv,shift); + + if (!attach2cgproxy()){ + error("attach process failed"); + exit(EXIT_FAILURE); + } + + string s=join2str(argc-shift,argv+shift,' '); + return system(s.c_str()); +} \ No newline at end of file diff --git a/cgproxy.service b/cgproxy.service index 521e857..fb040ec 100644 --- a/cgproxy.service +++ b/cgproxy.service @@ -1,11 +1,10 @@ [Unit] -Description=proxy cgroup +Description=cgproxy service After=network.target [Service] -ExecStart=sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh --config=/etc/cgproxy.conf -ExecStop= sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop -RemainAfterExit=1 +Type=simple +ExecStart=/usr/bin/cgproxyd [Install] WantedBy=multi-user.target diff --git a/main.cpp b/cgproxyd.cpp similarity index 92% rename from main.cpp rename to cgproxyd.cpp index 838874d..8664321 100644 --- a/main.cpp +++ b/cgproxyd.cpp @@ -36,7 +36,7 @@ int handle_msg(char *msg) { try{ j = json::parse(msg); }catch(exception& e){debug("msg paser error");return MSG_ERROR;} int type, status; - string pid, cgroup_target; + int pid, cgroup_target; try { type = j.at("type").get(); switch (type) @@ -52,12 +52,12 @@ int handle_msg(char *msg) { return status; break; case MSG_TYPE_PROXY_PID: - pid=j.at("data").get(); + pid=j.at("data").get(); status=attach(pid, config.cgroup_proxy_preserved); return status; break; case MSG_TYPE_NOPROXY_PID: - pid=j.at("data").get(); + pid=j.at("data").get(); status=attach(pid, config.cgroup_noproxy_preserved); return status; break; @@ -83,7 +83,10 @@ pthread_t startSocketListeningThread() { return thread_id; } -int main() { +int main(int argc, char* argv[]) { + int shift=1; + processArgs(argc,argv,shift); + bool enable_socket = true; string config_path = DEFAULT_CONFIG_FILE; config.loadFromFile(config_path); diff --git a/cgroup-tproxy.sh b/cgroup-tproxy.sh index f419b89..4fba5d3 100644 --- a/cgroup-tproxy.sh +++ b/cgroup-tproxy.sh @@ -30,29 +30,35 @@ cat << 'DOC' DOC } -check_root(){ - uid=$(id -u) - [ ! $uid -eq 0 ] && { >&2 echo "permission denied, need root";exit 0; } -} - -check_root +## check root +[ ! $(id -u) -eq 0 ] && { >&2 echo "need root to modify iptables";exit -1; } ## any process in this cgroup will be proxied -cgroup_proxy="/proxy.slice" -cgroup_noproxy="/noproxy.slice" +if [ -z ${cgroup_proxy+x} ]; then + cgroup_proxy="/proxy.slice" +else + IFS=':' read -r -a cgroup_proxy <<< "$cgroup_proxy" +fi + +## any process in this cgroup will not be proxied +if [ -z ${cgroup_noproxy+x} ]; then + cgroup_noproxy="/noproxy.slice" +else + IFS=':' read -r -a cgroup_noproxy <<< "$cgroup_noproxy" +fi # allow as gateway for local network -enable_gateway=false +[ -z ${enable_gateway+x} ] && enable_gateway=false ## some variables -port=12345 +[ -z ${port+x} ] && port=12345 ## some options -enable_dns=true -enable_tcp=true -enable_udp=true -enable_ipv4=true -enable_ipv6=true +[ -z ${enable_dns+x} ] && enable_dns=true +[ -z ${enable_tcp+x} ] && enable_tcp=true +[ -z ${enable_udp+x} ] && enable_udp=true +[ -z ${enable_ipv4+x} ] && enable_ipv4=true +[ -z ${enable_ipv6+x} ] && enable_ipv6=true ## do not modify this if you don't known what you are doing table=100 diff --git a/cgroup_attach.h b/cgroup_attach.h index b254611..c1c94f4 100644 --- a/cgroup_attach.h +++ b/cgroup_attach.h @@ -30,7 +30,7 @@ bool validate(string pid, string cgroup) { if (pid_v && cg_v) return true; - error("paramater validate error"); + error("attach paramater validate error"); return_error } @@ -53,8 +53,11 @@ int attach(const string pid, const string cgroup_target) { error("need root to attach cgroup"); return_error } + + debug("attaching %s to %s",pid.c_str(),cgroup_target.c_str()); + int status; - validate(pid, cgroup_target); + if (!validate(pid, cgroup_target)) return_error string cgroup_mount_point = get_cgroup2_mount_point(status); if (status!=0) return_error string cgroup_target_path = cgroup_mount_point + cgroup_target; @@ -91,6 +94,10 @@ int attach(const string pid, const string cgroup_target) { return_success } +int attach(const int pid, const string cgroup_target){ + return attach(to_str(pid), cgroup_target); +} + } #endif \ No newline at end of file diff --git a/common.h b/common.h index 22a66cf..4a43ecd 100644 --- a/common.h +++ b/common.h @@ -1,9 +1,9 @@ #ifndef COMMON_H -#define COMMON_H +#define COMMON_H 1 -#define SOCKET_PATH "/tmp/unix_socket" +#define SOCKET_PATH "/tmp/cgproxy_unix_socket" #define LISTEN_BACKLOG 64 -#define DEFAULT_CONFIG_FILE "/etc/cgproxy.conf" +#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json" #define CGROUP_PROXY_PRESVERED "/proxy.slice" #define CGROUP_NOPROXY_PRESVERED "/noproxy.slice" @@ -31,27 +31,53 @@ #include using namespace std; +extern bool enable_debug=false; +extern bool print_help=false; + #define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");} -#define debug(...) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");} +#define debug(...) if (enable_debug) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");} #define return_error return -1; #define return_success return 0; +void processArgs(const int argc, char *argv[], int &shift){ + for (int i=1;i string to_str(T... args) { stringstream ss; ss.clear(); - (ss << ... << args) << endl; + ss << std::boolalpha; + (ss << ... << args); return ss.str(); } -template -string join2str(const T t){ +string join2str(const vector t, const char delm=' '){ string s; - string delm=" ", prefix="(", tail=")", wrap="\""; for (const auto &e : t) - e!=*(t.end()-1)?s+=wrap+e+wrap+delm:s+=wrap+e+wrap; - return prefix+s+tail; + e!=*(t.end()-1)?s+=e+delm:s+=e; + return s; +} + +string join2str(const int argc,char** argv, const char delm=' '){ + string s; + for (int i=0;i0; } #endif \ No newline at end of file diff --git a/config.h b/config.h index 9f00a5d..824d787 100644 --- a/config.h +++ b/config.h @@ -36,8 +36,8 @@ struct Config { public: void toEnv() { mergeReserved(); - setenv("cgroup_proxy", join2str(cgroup_proxy).c_str(), 1); - setenv("cgroup_noproxy", join2str(cgroup_noproxy).c_str(), 1); + setenv("cgroup_proxy", join2str(cgroup_proxy,':').c_str(), 1); + setenv("cgroup_noproxy", join2str(cgroup_noproxy, ':').c_str(), 1); setenv("enable_gateway", to_str(enable_gateway).c_str(), 1); setenv("port", to_str(port).c_str(), 1); setenv("enable_dns", to_str(enable_dns).c_str(), 1); @@ -122,9 +122,9 @@ public: if (value.is_array()&&!validCgroup((vector)value)) status=false; if (!value.is_string()&&!value.is_array()) status=false; }else if (key=="port"){ - if (validPort(value)) status=false; + if (!validPort(value)) status=false; }else if (boolset.find(key)!=boolset.end()){ - if (value.is_boolean()) status=false; + if (!value.is_boolean()) status=false; }else{ error("unknown key: %s", key.c_str()); return false; diff --git a/config.json b/config.json index 383d3c1..5fd161b 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "cgroup_noproxy": [], + "cgroup_noproxy": ["/system.slice/v2ray.service"], "cgroup_proxy": [], "enable_dns": true, "enable_gateway": false, diff --git a/socket_server.h b/socket_server.h index dc08593..8e91086 100644 --- a/socket_server.h +++ b/socket_server.h @@ -12,8 +12,10 @@ #include #include #include +#include #include "common.h" using namespace std; +namespace fs = std::filesystem; namespace CGPROXY::SOCKET{ @@ -38,8 +40,10 @@ public: debug("starting socket listening"); sfd = socket(AF_UNIX, SOCK_STREAM, 0); - flag=unlink(SOCKET_PATH); - if (flag==-1) {error("%s exist, and can't unlink",SOCKET_PATH); exit(EXIT_FAILURE);} + if (fs::exists(SOCKET_PATH)&&unlink(SOCKET_PATH)==-1){ + error("%s exist, and can't unlink",SOCKET_PATH); + exit(EXIT_FAILURE); + } memset(&unix_socket, '\0', sizeof(struct sockaddr_un)); unix_socket.sun_family = AF_UNIX; strncpy(unix_socket.sun_path, SOCKET_PATH, From fbcc499ba884137ae5dfada760ab90be5776234c Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 10:49:47 +0800 Subject: [PATCH 11/18] rename .h to .hpp --- cgattach.cpp | 2 +- cgnoproxy.cpp | 2 +- cgproxy.cpp | 10 +++++----- cgproxyd.cpp | 8 ++++---- cgroup_attach.h => cgroup_attach.hpp | 2 +- common.h => common.hpp | 4 ++-- config.h => config.hpp | 4 ++-- socket_client.h => socket_client.hpp | 2 +- socket_server.h => socket_server.hpp | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) rename cgroup_attach.h => cgroup_attach.hpp (99%) rename common.h => common.hpp (97%) rename config.h => config.hpp (98%) rename socket_client.h => socket_client.hpp (98%) rename socket_server.h => socket_server.hpp (99%) diff --git a/cgattach.cpp b/cgattach.cpp index ee5844e..55adad5 100644 --- a/cgattach.cpp +++ b/cgattach.cpp @@ -1,4 +1,4 @@ -#include "cgroup_attach.h" +#include "cgroup_attach.hpp" using namespace std; void print_usage() { fprintf(stdout, "usage: cgattach \n"); } diff --git a/cgnoproxy.cpp b/cgnoproxy.cpp index f41ee84..aca543d 100644 --- a/cgnoproxy.cpp +++ b/cgnoproxy.cpp @@ -1,5 +1,5 @@ #include -#include "socket_client.h" +#include "socket_client.hpp" using json = nlohmann::json; using namespace CGPROXY; diff --git a/cgproxy.cpp b/cgproxy.cpp index aba4ba2..867eee3 100644 --- a/cgproxy.cpp +++ b/cgproxy.cpp @@ -1,5 +1,5 @@ #include -#include "socket_client.h" +#include "socket_client.hpp" using json = nlohmann::json; using namespace CGPROXY; @@ -15,10 +15,10 @@ bool attach2cgproxy(){ int main(int argc, char *argv[]){ int shift=1; - // if (argc==1){ - // error("usage: cgproxy [--debug] \nexample: cgroxy curl -I https://www.google.com"); - // exit(EXIT_FAILURE); - // } + if (argc==1){ + error("usage: cgproxy [--debug] \nexample: cgroxy curl -I https://www.google.com"); + exit(EXIT_FAILURE); + } processArgs(argc,argv,shift); if (!attach2cgproxy()){ diff --git a/cgproxyd.cpp b/cgproxyd.cpp index 8664321..7c5a79e 100644 --- a/cgproxyd.cpp +++ b/cgproxyd.cpp @@ -1,5 +1,5 @@ -#include "common.h" -#include "socket_server.h" +#include "common.hpp" +#include "socket_server.hpp" #include #include #include @@ -9,8 +9,8 @@ #include #include #include -#include "config.h" -#include "cgroup_attach.h" +#include "config.hpp" +#include "cgroup_attach.hpp" using namespace std; using json = nlohmann::json; diff --git a/cgroup_attach.h b/cgroup_attach.hpp similarity index 99% rename from cgroup_attach.h rename to cgroup_attach.hpp index c1c94f4..06fdeb5 100644 --- a/cgroup_attach.h +++ b/cgroup_attach.hpp @@ -11,7 +11,7 @@ #include #include #include -#include "common.h" +#include "common.hpp" using namespace std; namespace CGPROXY::CGROUP{ diff --git a/common.h b/common.hpp similarity index 97% rename from common.h rename to common.hpp index 4a43ecd..f731194 100644 --- a/common.h +++ b/common.hpp @@ -31,8 +31,8 @@ #include using namespace std; -extern bool enable_debug=false; -extern bool print_help=false; +static bool enable_debug=false; +static bool print_help=false; #define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");} #define debug(...) if (enable_debug) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");} diff --git a/config.h b/config.hpp similarity index 98% rename from config.h rename to config.hpp index 824d787..53dbe20 100644 --- a/config.h +++ b/config.hpp @@ -1,7 +1,7 @@ #ifndef CONFIG_H #define CONFIG_H -#include "common.h" -#include "socket_server.h" +#include "common.hpp" +#include "socket_server.hpp" #include #include #include diff --git a/socket_client.h b/socket_client.hpp similarity index 98% rename from socket_client.h rename to socket_client.hpp index 143ff55..ce92128 100644 --- a/socket_client.h +++ b/socket_client.hpp @@ -9,7 +9,7 @@ #include #include #include -#include "common.h" +#include "common.hpp" using namespace std; diff --git a/socket_server.h b/socket_server.hpp similarity index 99% rename from socket_server.h rename to socket_server.hpp index 8e91086..f4e95d9 100644 --- a/socket_server.h +++ b/socket_server.hpp @@ -13,7 +13,7 @@ #include #include #include -#include "common.h" +#include "common.hpp" using namespace std; namespace fs = std::filesystem; From 2adba75b3e803e56cb6399510c385e1617cf6b50 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 11:34:35 +0800 Subject: [PATCH 12/18] cgproxyd as class --- .gitignore | 1 + aur-cgproxy-local/PKGBUILD | 40 ------- aur-cgproxy-local/cgproxy.install | 8 -- cgproxyd.cpp | 173 +++++++++++++++++------------- socket_server.hpp | 7 +- 5 files changed, 105 insertions(+), 124 deletions(-) delete mode 100644 aur-cgproxy-local/PKGBUILD delete mode 100644 aur-cgproxy-local/cgproxy.install diff --git a/.gitignore b/.gitignore index eb5df43..6acb7f5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build .vscode v2ray_config/proxy v2ray_config/06_outbounds_proxy.json +aur-* diff --git a/aur-cgproxy-local/PKGBUILD b/aur-cgproxy-local/PKGBUILD deleted file mode 100644 index 6a86b39..0000000 --- a/aur-cgproxy-local/PKGBUILD +++ /dev/null @@ -1,40 +0,0 @@ -# Maintainer: Fancy Zhang -pkgname=cgproxy-git -pkgver=v4.0.r1.g3fde647 -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#branch=dev") -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 -} - diff --git a/aur-cgproxy-local/cgproxy.install b/aur-cgproxy-local/cgproxy.install deleted file mode 100644 index e6081e8..0000000 --- a/aur-cgproxy-local/cgproxy.install +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -post_install(){ -cat <<'DOC' - to start service: - systemctl enable --now cgproxy.service -DOC -} diff --git a/cgproxyd.cpp b/cgproxyd.cpp index 7c5a79e..3210d61 100644 --- a/cgproxyd.cpp +++ b/cgproxyd.cpp @@ -18,82 +18,111 @@ using namespace CGPROXY::SOCKET; using namespace CGPROXY::CONFIG; using namespace CGPROXY::CGROUP; -SocketServer sc; -thread_arg arg_t; -Config config; -pthread_t socket_thread_id = -1; +namespace CGPROXY{ -int applyConfig(Config *c) { - system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"); - c->toEnv(); - system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"); - return 0; -} - -int handle_msg(char *msg) { - debug("received msg: %s", msg); - json j; - try{ j = json::parse(msg); }catch(exception& e){debug("msg paser error");return MSG_ERROR;} - - int type, status; - int pid, cgroup_target; - try { - type = j.at("type").get(); - switch (type) - { - case MSG_TYPE_JSON: - status=config.loadFromJson(j.at("data")); - if (status==SUCCESS) status=applyConfig(&config); - return status; - break; - case MSG_TYPE_CONFIG_PATH: - status=config.loadFromFile(j.at("data").get()); - if (status==SUCCESS) status=applyConfig(&config); - return status; - break; - case MSG_TYPE_PROXY_PID: - pid=j.at("data").get(); - status=attach(pid, config.cgroup_proxy_preserved); - return status; - break; - case MSG_TYPE_NOPROXY_PID: - pid=j.at("data").get(); - status=attach(pid, config.cgroup_noproxy_preserved); - return status; - break; - default: - return MSG_ERROR; - break; - }; - } catch (out_of_range &e) { - return MSG_ERROR; - } catch (exception &e){ - return ERROR; +class cgproxyd{ + thread_arg arg_t; + Config config; + pthread_t socket_thread_id = -1; + + static cgproxyd* instance; + static int handle_msg_static(char* msg){ + if (!instance) { + error("no cgproxyd instance assigned"); + return ERROR; + } + return instance->handle_msg(msg); } -} -pthread_t startSocketListeningThread() { - arg_t.sc = ≻ - arg_t.handle_msg = &handle_msg; - pthread_t thread_id; - int status = - pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t); - if (status != 0) - error("socket thread create failed"); - return thread_id; + int applyConfig(Config *c) { + system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"); + c->toEnv(); + system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"); + return 0; + } + + int handle_msg(char *msg) { + debug("received msg: %s", msg); + json j; + try{ j = json::parse(msg); }catch(exception& e){debug("msg paser error");return MSG_ERROR;} + + int type, status; + int pid, cgroup_target; + try { + type = j.at("type").get(); + switch (type) + { + case MSG_TYPE_JSON: + status=config.loadFromJson(j.at("data")); + if (status==SUCCESS) status=applyConfig(&config); + return status; + break; + case MSG_TYPE_CONFIG_PATH: + status=config.loadFromFile(j.at("data").get()); + if (status==SUCCESS) status=applyConfig(&config); + return status; + break; + case MSG_TYPE_PROXY_PID: + pid=j.at("data").get(); + status=attach(pid, config.cgroup_proxy_preserved); + return status; + break; + case MSG_TYPE_NOPROXY_PID: + pid=j.at("data").get(); + status=attach(pid, config.cgroup_noproxy_preserved); + return status; + break; + default: + return MSG_ERROR; + break; + }; + } catch (out_of_range &e) { + return MSG_ERROR; + } catch (exception &e){ + return ERROR; + } + } + + pthread_t startSocketListeningThread() { + arg_t.handle_msg = &handle_msg_static; + pthread_t thread_id; + int status = + pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t); + if (status != 0) + error("socket thread create failed"); + return thread_id; + } + + void assignStaticInstance(){ + instance=this; + } + + public: + int start(int argc, char* argv[]) { + int shift=1; + processArgs(argc,argv,shift); + + bool enable_socket = true; + string config_path = DEFAULT_CONFIG_FILE; + config.loadFromFile(config_path); + applyConfig(&config); + if (enable_socket) { + assignStaticInstance(); + socket_thread_id = startSocketListeningThread(); + pthread_join(socket_thread_id, NULL); + } + return 0; + } + ~cgproxyd(){ + + } +}; + +cgproxyd* cgproxyd::instance=NULL; + } int main(int argc, char* argv[]) { - int shift=1; - processArgs(argc,argv,shift); - - bool enable_socket = true; - string config_path = DEFAULT_CONFIG_FILE; - config.loadFromFile(config_path); - applyConfig(&config); - if (enable_socket) { - socket_thread_id = startSocketListeningThread(); - pthread_join(socket_thread_id, NULL); - } - return 0; + CGPROXY::cgproxyd d; + return d.start(argc,argv); } \ No newline at end of file diff --git a/socket_server.hpp b/socket_server.hpp index f4e95d9..a37a6f3 100644 --- a/socket_server.hpp +++ b/socket_server.hpp @@ -25,9 +25,7 @@ namespace CGPROXY::SOCKET{ continue; \ } -class SocketServer; struct thread_arg { - SocketServer *sc; function handle_msg; }; @@ -42,7 +40,7 @@ public: if (fs::exists(SOCKET_PATH)&&unlink(SOCKET_PATH)==-1){ error("%s exist, and can't unlink",SOCKET_PATH); - exit(EXIT_FAILURE); + return; } memset(&unix_socket, '\0', sizeof(struct sockaddr_un)); unix_socket.sun_family = AF_UNIX; @@ -85,7 +83,8 @@ public: static void *startThread(void *arg) { thread_arg *p = (thread_arg *)arg; - p->sc->socketListening(p->handle_msg); + SocketServer server; + server.socketListening(p->handle_msg); return (void *)0; } }; From a16cefbfb2cbd092c2e5ccd648b3a0c3020d61d3 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 14:01:10 +0800 Subject: [PATCH 13/18] handle signal, robust iptables clean --- cgproxyd.cpp | 44 +++++++++++++++++++++++-------------- cgroup-tproxy.sh | 56 +++++++++++++++++++++++++++--------------------- common.hpp | 3 +++ 3 files changed, 63 insertions(+), 40 deletions(-) diff --git a/cgproxyd.cpp b/cgproxyd.cpp index 3210d61..65effaf 100644 --- a/cgproxyd.cpp +++ b/cgproxyd.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "config.hpp" #include "cgroup_attach.hpp" @@ -24,7 +25,7 @@ class cgproxyd{ thread_arg arg_t; Config config; pthread_t socket_thread_id = -1; - + static cgproxyd* instance; static int handle_msg_static(char* msg){ if (!instance) { @@ -33,12 +34,11 @@ class cgproxyd{ } return instance->handle_msg(msg); } - - int applyConfig(Config *c) { - system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"); - c->toEnv(); - system("sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"); - return 0; + static void signalHandler( int signum ){ + debug("Signal %d received.", &signum); + if (!instance){ error("no cgproxyd instance assigned");} + else { instance->stop(); } + exit(signum); } int handle_msg(char *msg) { @@ -99,22 +99,34 @@ class cgproxyd{ public: int start(int argc, char* argv[]) { + signal(SIGINT, &signalHandler); + signal(SIGTERM,&signalHandler); + signal(SIGHUP,&signalHandler); + int shift=1; processArgs(argc,argv,shift); - bool enable_socket = true; - string config_path = DEFAULT_CONFIG_FILE; - config.loadFromFile(config_path); + config.loadFromFile(DEFAULT_CONFIG_FILE); applyConfig(&config); - if (enable_socket) { - assignStaticInstance(); - socket_thread_id = startSocketListeningThread(); - pthread_join(socket_thread_id, NULL); - } + + assignStaticInstance(); + socket_thread_id = startSocketListeningThread(); + pthread_join(socket_thread_id, NULL); return 0; } + int applyConfig(Config *c) { + system(TPROXY_IPTABLS_CLEAN); + c->toEnv(); + system(TPROXY_IPTABLS_START); + // no need to track running status + return 0; + } + void stop(){ + debug("stopping"); + system(TPROXY_IPTABLS_CLEAN); + } ~cgproxyd(){ - + stop(); } }; diff --git a/cgroup-tproxy.sh b/cgroup-tproxy.sh index 4fba5d3..575a756 100644 --- a/cgroup-tproxy.sh +++ b/cgroup-tproxy.sh @@ -70,35 +70,41 @@ cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET) cgroup_type="cgroup2" cgroup_procs_file="cgroup.procs" + +stop(){ + iptables -t mangle -L TPROXY_PRE &> /dev/null || return + echo "cleaning tproxy iptables" + iptables -t mangle -D PREROUTING -j TPROXY_PRE + iptables -t mangle -D OUTPUT -j TPROXY_OUT + iptables -t mangle -F TPROXY_PRE + iptables -t mangle -F TPROXY_OUT + iptables -t mangle -F TPROXY_ENT + iptables -t mangle -X TPROXY_PRE + iptables -t mangle -X TPROXY_OUT + 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 + ip6tables -t mangle -X TPROXY_ENT + ip rule delete fwmark $fwmark lookup $table + ip route flush table $table + ip -6 rule delete fwmark $fwmark lookup $table + ip -6 route flush table $table + ## 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 +} + ## parse parameter for i in "$@" do case $i in stop) - 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 - 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 - ip6tables -t mangle -X TPROXY_ENT - ip rule delete fwmark $fwmark lookup $table - ip route flush table $table - ip -6 rule delete fwmark $fwmark lookup $table - ip -6 route flush table $table - ## 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 + stop exit 0 ;; --config=*) @@ -117,6 +123,8 @@ done test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_proxy || exit -1; test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1; + +echo "applying tproxy iptables" ## use TPROXY #ipv4# ip rule add fwmark $fwmark table $table diff --git a/common.hpp b/common.hpp index f731194..07fe998 100644 --- a/common.hpp +++ b/common.hpp @@ -1,6 +1,9 @@ #ifndef COMMON_H #define COMMON_H 1 +#define TPROXY_IPTABLS_START "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh" +#define TPROXY_IPTABLS_CLEAN "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop" + #define SOCKET_PATH "/tmp/cgproxy_unix_socket" #define LISTEN_BACKLOG 64 #define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json" From 39275452dae46932237966398ac05b48940bec98 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 14:12:44 +0800 Subject: [PATCH 14/18] fix precess args --- cgnoproxy.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cgnoproxy.cpp b/cgnoproxy.cpp index aca543d..8177306 100644 --- a/cgnoproxy.cpp +++ b/cgnoproxy.cpp @@ -19,10 +19,7 @@ int main(int argc, char *argv[]){ error("usage: cgnoproxy [--debug] \nexample: cgnoproxy curl -I https://www.google.com"); exit(EXIT_FAILURE); } - if (argv[1]=="--debug"){ - enable_debug=true; - shift+=1; - } + processArgs(argc,argv,shift); if (!attach2cgproxy()){ error("attach process failed"); From 6de88897b2c0c9b1da76985902483d106811a305 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 14:22:04 +0800 Subject: [PATCH 15/18] add todo comment --- config.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/config.hpp b/config.hpp index 53dbe20..2e8fd48 100644 --- a/config.hpp +++ b/config.hpp @@ -119,6 +119,7 @@ public: for (auto& [key, value] : j.items()) { if (key=="cgroup_proxy"||key=="cgroup_noproxy"){ if (value.is_string()&&!validCgroup((string)value)) status=false; + // TODO what if vector etc. if (value.is_array()&&!validCgroup((vector)value)) status=false; if (!value.is_string()&&!value.is_array()) status=false; }else if (key=="port"){ From bc9f6d4d4e8f8cd768053fbd60e6f22716d5811e Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 14:35:59 +0800 Subject: [PATCH 16/18] clean from v3.x, add json dependency --- CMakeLists.txt | 17 ++++++---------- cgnoproxy.sh | 16 ---------------- cgproxy.conf | 48 ---------------------------------------------- cgproxy.sh | 16 ---------------- run_in_cgroup.sh | 50 ------------------------------------------------ 5 files changed, 6 insertions(+), 141 deletions(-) delete mode 100644 cgnoproxy.sh delete mode 100644 cgproxy.conf delete mode 100644 cgproxy.sh delete mode 100644 run_in_cgroup.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index fa7eb45..670a0b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,15 +28,10 @@ install(TARGETS cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission}) install(TARGETS cgproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission}) install(TARGETS cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission}) -install(FILES cgproxy.service - DESTINATION /usr/lib/systemd/system/) -install(FILES config.json - DESTINATION /etc/cgproxy/) -install(FILES cgroup-tproxy.sh - DESTINATION /usr/share/cgproxy/scripts/) - -install(FILES readme.md - DESTINATION /share/doc/cgproxy/) +install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/) +install(FILES config.json DESTINATION /etc/cgproxy/) +install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/) +install(FILES readme.md DESTINATION /share/doc/cgproxy/) ## package for deb and rpm @@ -47,7 +42,7 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything r ## 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_DEPENDS "systemd" "nlohmann-json3-dev") set(CPACK_DEBIAN_PACKAGE_SECTION "network") set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy") @@ -57,7 +52,7 @@ set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CONTROL_DIR}/postinst;${CONTROL_DIR}/p ## rpm pack set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64") -set(CPACK_RPM_PACKAGE_REQUIRES "systemd") +set(CPACK_RPM_PACKAGE_REQUIRES "systemd" "json-devel") set(CPACK_RPM_PACKAGE_GROUP "network") set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy") set(CONTROL_DIR ${CMAKE_SOURCE_DIR}/control) diff --git a/cgnoproxy.sh b/cgnoproxy.sh deleted file mode 100644 index e66a182..0000000 --- a/cgnoproxy.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -config="/etc/cgproxy.conf" -source $config - -# test suid bit -if [ -u "$(which cgattach)" ]; then - cgattach $$ $cgroup_noproxy && attached=1 -else - sudo cgattach $$ $cgroup_noproxy && attached=1 -fi - -# test attach success or not -[[ -z "$attached" ]] && echo "attach error" && exit 1 - -exec "$@" \ No newline at end of file diff --git a/cgproxy.conf b/cgproxy.conf deleted file mode 100644 index c224b52..0000000 --- a/cgproxy.conf +++ /dev/null @@ -1,48 +0,0 @@ -## 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 -## 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 if not exist - -### 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 -enable_dns=true -enable_tcp=true -enable_udp=true -enable_ipv4=true -enable_ipv6=true - - -################################################################################### -## do not modify this if you don't known what you are doing -table=100 -fwmark=0x01 -mark_newin=0x02 diff --git a/cgproxy.sh b/cgproxy.sh deleted file mode 100644 index beeeb52..0000000 --- a/cgproxy.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -config="/etc/cgproxy.conf" -source $config - -# test suid bit -if [ -u "$(which cgattach)" ]; then - cgattach $$ $cgroup_proxy && attached=1 -else - sudo cgattach $$ $cgroup_proxy && attached=1 -fi - -# test attach success or not -[[ -z "$attached" ]] && echo "attach error" && exit 1 - -exec "$@" \ No newline at end of file diff --git a/run_in_cgroup.sh b/run_in_cgroup.sh deleted file mode 100644 index f15c900..0000000 --- a/run_in_cgroup.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -print_help(){ -cat << 'DOC' -usage: - run_in_cgroup --cgroup=CGROUP - run_in_cgroup --help -note: - CGROUP must start will slash '/' , and no special character -example: - run_in_cgroup --cggroup=/mycgroup.slice ping 127.0.0.1 -DOC -} - -## parse parameter -for i in "$@" -do -case $i in - --cgroup=*) - cgroup=${i#*=} - shift - ;; - --help) - print_help - exit 0 - shift - ;; - -*) - shift - ;; - *) - break - ;; -esac -done - -[[ -z "$cgroup" ]] && print_help && exit 1 -[[ -z "$@" ]] && print_help && exit 1 - -# test suid bit -if [ -u "$(which cgattach)" ]; then - cgattach $$ $cgroup && attached=1 -else - sudo cgattach $$ $cgroup && attached=1 -fi - -# test attach success or not -[[ -z "$attached" ]] && print_help && exit 1 - -exec "$@" From e86ea01f6ef53f1df99fe37cfeca2a1a1be93225 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 15:12:39 +0800 Subject: [PATCH 17/18] updated readme --- cgattach.cpp | 4 +- readme.md | 126 +++++++++++++++++++-------------------------------- 2 files changed, 48 insertions(+), 82 deletions(-) diff --git a/cgattach.cpp b/cgattach.cpp index 55adad5..fe8b146 100644 --- a/cgattach.cpp +++ b/cgattach.cpp @@ -6,12 +6,12 @@ void print_usage() { fprintf(stdout, "usage: cgattach \n"); } int main(int argc, char *argv[]) { int flag=setuid(0); if (flag!=0) { - perror("cgattach setuid"); + perror("cgattach need root"); exit(EXIT_FAILURE); } if (argc != 3) { - error("only need 2 paramaters"); + error("need exact 2 paramaters"); print_usage(); exit(EXIT_FAILURE); } diff --git a/readme.md b/readme.md index f282d37..54d0f7d 100644 --- a/readme.md +++ b/readme.md @@ -11,12 +11,12 @@ cgproxy will transparent proxy anything running in specific cgroup. It resembles It aslo supports global transparent proxy and gateway proxy. See [Global transparent proxy](#global-transparent-proxy) and [Gateway proxy](#gateway-proxy). - * [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) + * [Default usage](#default-usage) + * [Configuration](#configuration) * [Global transparent proxy](#global-transparent-proxy) * [Gateway proxy](#gateway-proxy) * [Other useful tools provided in this project](#other-useful-tools-provided-in-this-project) @@ -24,7 +24,7 @@ It aslo supports global transparent proxy and gateway proxy. See [Global transpa * [TIPS](#tips) * [Licences](#licences) - + @@ -43,14 +43,14 @@ It aslo supports global transparent proxy and gateway proxy. See [Global transpa ## How to install ```bash -mkdir build && cd build && cmake .. && make && sudo make install +mkdir build && cd build && cmake .. && make && make install ``` - 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 +## Default usage - First enable and start service @@ -60,72 +60,51 @@ mkdir build && cd build && cmake .. && make && sudo make install - Then prefix with cgproxy with your command, just like proxychains - ``` - cgproxy + ```bash + cgproxy [--debug] ``` - For example, test proxy ```bash - cgproxy curl -vIs https://www.google.com + cgproxy curl -I https://www.google.com ``` - To completely stop ``` sudo systemctl disable --now cgproxy.service ``` ----- -
- More config in /etc/cgproxy.conf (click to expand) -```bash -################################################################################### -## any process in cgroup_proxy will be proxied, and cgroup_noproxy the opposite -## 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 if not exist +## Configuration -### global proxy with v2ray service -# cgroup_proxy="/" -# cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service") +Config file: **/etc/cgproxy/config.json** -### 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 -enable_dns=true -enable_tcp=true -enable_udp=true -enable_ipv4=true -enable_ipv6=true - - -################################################################################### -## do not modify this if you don't known what you are doing -table=100 -fwmark=0x01 -mark_newin=0x02 +```json +{ + "cgroup_noproxy": ["/system.slice/v2ray.service"], + "cgroup_proxy": [], + "enable_dns": true, + "enable_gateway": false, + "enable_ipv4": true, + "enable_ipv6": true, + "enable_tcp": true, + "enable_udp": true, + "port": 12345 +} ``` -
+ +- **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 +- **enable_gateway** enable gateway proxy for local devices +- **enable_dns** enable dns to go to proxy +- **enable_tcp** +- **enable_udp** +- **enable_ipv4** +- **enable_ipv6** + +**Note**: cgroup in configuration need to be exist, otherwise ignored + If you changed config, remember to restart service ```bash @@ -134,45 +113,34 @@ sudo systemctl restart cgproxy.service ## Global transparent proxy -- Set `cgroup_proxy="/"` in */etc/cgproxy.conf*, this will proxy all connection +- Set `"cgroup_proxy":["/"]` in configuration, this will proxy all connection -- And allow your proxy program (v2ray) direct to internet, two ways: - - active way - - run `cgnoproxy ` - +- Allow your proxy program (v2ray) direct to internet to avoid loop. Two ways: + +- active way, run command + example: `cgnoproxy sudo v2ray -config config_file` example: `cgnoproxy qv2ray` - - passive way, useful if you run v2ray as service + - passive way, set it's cgroup in configuration, very useful for service - set `cgroup_noproxy=""` - - example: `cgroup_noproxy=("/noproxy.slice" "/system.slice/v2ray.service")` + example: `"cgroup_noproxy":["/system.slice/v2ray.service"]` - Finally, 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 +- Set `"enable_gateway":true` in configuration +- And allow your proxy software (v2ray) direct to internet if necessary, described above +- Other device set this host as gateway, and set public dns if need ## Other useful tools provided in this project - `cgnoproxy` run program wihout proxy, very useful in global transparent proxy ```bash - cgnoproxy - ``` - -- `run_in_cgroup` run command in specific cgroup which will create if not exist , cgroup can be only one level down exist cgroup, otherwise created fail. - - ```bash - run_in_cgroup --cgroup=CGROUP - # example - run_in_cgroup --cgroup=/mycgroup.slice ping 127.0.0.1 + cgnoproxy [--debug] ``` - `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. @@ -185,8 +153,6 @@ sudo systemctl restart cgproxy.service ## NOTES -- `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 permission ```bash From f2210f9bda926b88fd6ba66d9a66d2eb1af4d825 Mon Sep 17 00:00:00 2001 From: fancy Date: Sat, 16 May 2020 15:17:40 +0800 Subject: [PATCH 18/18] updated readme --- readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 54d0f7d..8a1a3c4 100644 --- a/readme.md +++ b/readme.md @@ -117,12 +117,12 @@ sudo systemctl restart cgproxy.service - Allow your proxy program (v2ray) direct to internet to avoid loop. Two ways: -- active way, run command - - example: `cgnoproxy sudo v2ray -config config_file` - - example: `cgnoproxy qv2ray` - + - active way, run command + + example: `cgnoproxy sudo v2ray -config config_file` + + example: `cgnoproxy qv2ray` + - passive way, set it's cgroup in configuration, very useful for service example: `"cgroup_noproxy":["/system.slice/v2ray.service"]`