mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-01-07 13:07:56 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa7d877de5 | ||
|
|
3475001ca3 | ||
|
|
0b25b5263a | ||
|
|
388ba6a4c8 | ||
|
|
5dbce18f95 | ||
|
|
792a156647 | ||
|
|
92abcb1851 | ||
|
|
a73b697cab | ||
|
|
aace8c3d31 | ||
|
|
665e02ceaa | ||
|
|
bfe3289201 | ||
|
|
2c8625c110 | ||
|
|
ba0b780adf | ||
|
|
1fa0d51e1d | ||
|
|
aedebf4e31 | ||
|
|
051742eef1 | ||
|
|
619fcaae8e |
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
project(cgproxy VERSION 4.1)
|
||||
project(cgproxy VERSION 0.13)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result")
|
||||
|
||||
set(build_tools OFF)
|
||||
@@ -19,8 +19,24 @@ if (build_test)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
install(FILES cgproxyd 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 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 execsnoop.py DESTINATION /usr/share/cgproxy/scripts/ PERMISSIONS ${basic_permission})
|
||||
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
|
||||
/usr/bin/cgproxy --noproxy $@
|
||||
exec /usr/bin/cgproxy --noproxy $@
|
||||
@@ -4,7 +4,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/cgproxyd
|
||||
ExecStart=/usr/bin/cgproxyd --execsnoop
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
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_proxy": [],
|
||||
"enable_dns": true,
|
||||
"enable_gateway": false,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true,
|
||||
"enable_tcp": true,
|
||||
"enable_dns": true,
|
||||
"enable_udp": true,
|
||||
"port": 12345
|
||||
"enable_tcp": true,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true
|
||||
}
|
||||
|
||||
135
execsnoop.py
Normal file
135
execsnoop.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/python
|
||||
# This won't catch all new processes: an application may fork() but not exec().
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, signal, shutil
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
try:
|
||||
from bcc import BPF
|
||||
from bcc.utils import ArgString, printb
|
||||
import bcc.utils as utils
|
||||
except:
|
||||
eprint("python-bcc not installed")
|
||||
exit(0)
|
||||
|
||||
# define BPF program
|
||||
bpf_text = """
|
||||
#include <uapi/linux/ptrace.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define ARGSIZE 256
|
||||
|
||||
struct data_t {
|
||||
u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
|
||||
char path[ARGSIZE];
|
||||
int retval;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
// create data here and pass to submit_arg to save stack space (#555)
|
||||
struct data_t data = {};
|
||||
struct task_struct *task;
|
||||
|
||||
data.pid = bpf_get_current_pid_tgid() >> 32;
|
||||
bpf_probe_read(data.path, sizeof(data.path), filename);
|
||||
events.perf_submit(ctx, &data, sizeof(struct data_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
def getRealPath(exec_path):
|
||||
path=exec_path.strip()
|
||||
if not path.startswith("/"):
|
||||
path=shutil.which(path)
|
||||
if path:
|
||||
path=os.path.realpath(path)
|
||||
if path and os.path.isfile(path):
|
||||
return path
|
||||
|
||||
eprint("'{0}' can not be find".format(exec_path))
|
||||
|
||||
def getParam():
|
||||
global exec_path_proxy, exec_path_noproxy
|
||||
exec_path_str=os.getenv('program_proxy')
|
||||
if exec_path_str:
|
||||
paths=exec_path_str.split(':')
|
||||
exec_path_proxy=[getRealPath(x) for x in paths]
|
||||
print("program with proxy:", end =" ")
|
||||
print(*exec_path_proxy,flush=True)
|
||||
|
||||
exec_path_str=os.getenv('program_noproxy')
|
||||
if exec_path_str:
|
||||
paths=exec_path_str.split(':')
|
||||
exec_path_noproxy=[getRealPath(x) for x in paths]
|
||||
print("program without proxy:", end =" ")
|
||||
print(*exec_path_noproxy, flush=True)
|
||||
|
||||
def exit_gracefully(signum, frame):
|
||||
eprint("execsnoop receive signal: {0}".format(signum),flush=True)
|
||||
sys.exit(0)
|
||||
|
||||
def attach(pid, path, proxy=True):
|
||||
if proxy:
|
||||
print("proxy: %-6d %s" % (pid, path),flush=True)
|
||||
os.system("/usr/bin/cgproxy --pid {0}".format(pid))
|
||||
else:
|
||||
print("noproxy: %-6d %s" % (pid, path),flush=True)
|
||||
os.system("/usr/bin/cgproxy --pid {0} --noproxy".format(pid))
|
||||
|
||||
def processAlreadyRunning():
|
||||
from subprocess import check_output
|
||||
def get_pid(name):
|
||||
try:
|
||||
return map(int,check_output(["pidof",name]).split())
|
||||
except:
|
||||
return []
|
||||
global exec_path_proxy, exec_path_noproxy
|
||||
for path in exec_path_proxy:
|
||||
for pid in get_pid(path):
|
||||
attach(pid,path,True)
|
||||
for path in exec_path_noproxy:
|
||||
for pid in get_pid(path):
|
||||
attach(pid,path,False)
|
||||
|
||||
signal.signal(signal.SIGINT, exit_gracefully)
|
||||
signal.signal(signal.SIGHUP, exit_gracefully)
|
||||
signal.signal(signal.SIGTERM, exit_gracefully)
|
||||
|
||||
show_ignore=False
|
||||
exec_path_proxy=[]
|
||||
exec_path_noproxy=[]
|
||||
getParam()
|
||||
processAlreadyRunning()
|
||||
|
||||
# initialize BPF
|
||||
b = BPF(text=bpf_text)
|
||||
execve_fnname = b.get_syscall_fnname("execve")
|
||||
b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")
|
||||
|
||||
# process event
|
||||
def print_event(cpu, data, size):
|
||||
event = b["events"].event(data)
|
||||
pid=event.pid
|
||||
exec_path=event.path.decode('utf-8')
|
||||
if (exec_path in exec_path_noproxy):
|
||||
attach(pid, exec_path, False)
|
||||
elif (exec_path in exec_path_proxy):
|
||||
attach(pid, exec_path, True)
|
||||
elif (show_ignore):
|
||||
print("ignore: %-6d %s" % (pid, exec_path),flush=True)
|
||||
|
||||
|
||||
# loop with callback to print_event
|
||||
b["events"].open_perf_buffer(print_event)
|
||||
while 1:
|
||||
b.perf_buffer_poll()
|
||||
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 python-bcc installed to actually work
|
||||
.SH CONFIGURATION
|
||||
.I /etc/cgproxy/config.json
|
||||
.br
|
||||
.B port
|
||||
tproxy listenning port
|
||||
.br
|
||||
program level proxy controll, need `python-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_ARCHITECTURE "x86_64")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
|
||||
set(CPACK_DEBIAN_PACKAGE_SUGGESTS "python-bcc")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
||||
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
|
||||
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
|
||||
set(CPACK_RPM_PACKAGE_SUGGESTS "python-bcc")
|
||||
set(CPACK_RPM_PACKAGE_GROUP "network")
|
||||
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")
|
||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/postinst")
|
||||
|
||||
56
readme.md
56
readme.md
@@ -6,7 +6,7 @@
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -40,6 +40,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.
|
||||
|
||||
- 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
|
||||
|
||||
```bash
|
||||
@@ -81,28 +87,51 @@ Config file: **/etc/cgproxy/config.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"port": 12345,
|
||||
"program_noproxy": ["v2ray", "qv2ray"],
|
||||
"program_proxy": [ ],
|
||||
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
||||
"cgroup_proxy": [],
|
||||
"enable_dns": true,
|
||||
"cgroup_proxy": [ ],
|
||||
"enable_gateway": false,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true,
|
||||
"enable_tcp": true,
|
||||
"enable_dns": true,
|
||||
"enable_udp": true,
|
||||
"port": 12345
|
||||
"enable_tcp": true,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true
|
||||
}
|
||||
```
|
||||
|
||||
- **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 controll, need `python-bcc` 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_dns** enable dns to go to proxy
|
||||
|
||||
- **enable_tcp**
|
||||
|
||||
- **enable_udp**
|
||||
|
||||
- **enable_ipv4**
|
||||
|
||||
- **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
|
||||
|
||||
If you changed config, remember to restart service
|
||||
@@ -140,16 +169,21 @@ sudo systemctl restart cgproxy.service
|
||||
- `cgnoproxy` run program wihout proxy, very useful in global transparent proxy
|
||||
|
||||
```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
|
||||
cgattch <pid> <cgroup>
|
||||
# example
|
||||
cgattch 9999 /proxy.slice
|
||||
```
|
||||
|
||||
- For more detail command usage, see `man cgproxyd` `man cgproxy` `man cgnoproxy`
|
||||
|
||||
## NOTES
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ find_package(nlohmann_json REQUIRED)
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_executable(cgproxyd cgproxyd.cpp)
|
||||
add_executable(cgproxy cgproxy.cpp)
|
||||
target_link_libraries(cgproxyd PRIVATE Threads::Threads nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(cgproxy PRIVATE nlohmann_json::nlohmann_json)
|
||||
add_executable(main main.cpp
|
||||
common.cpp config.cpp cgroup_attach.cpp
|
||||
socket_client.cpp socket_server.cpp)
|
||||
|
||||
target_link_libraries(main nlohmann_json::nlohmann_json Threads::Threads)
|
||||
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
||||
|
||||
install(TARGETS cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||
install(TARGETS cgproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "common.hpp"
|
||||
#include "socket_client.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
using namespace CGPROXY;
|
||||
|
||||
bool print_help = false, proxy = true;
|
||||
void print_usage() {
|
||||
fprintf(stdout, "Usage: cgproxy [--help] [--debug] [--noproxy] <CMD>\n");
|
||||
fprintf(stdout, "Alias: cgnoproxy = cgproxy --noproxy\n");
|
||||
}
|
||||
|
||||
void processArgs(const int argc, char *argv[], int &shift) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--noproxy") == 0) { proxy = false; }
|
||||
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
||||
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||
if (argv[i][0] != '-') { break; }
|
||||
shift += 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool attach2cgroup(pid_t pid, bool proxy) {
|
||||
json j;
|
||||
j["type"] = proxy ? MSG_TYPE_PROXY_PID : 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;
|
||||
processArgs(argc, argv, shift);
|
||||
|
||||
if (argc == shift || print_help) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pid_t pid = getpid();
|
||||
if (!attach2cgroup(pid, proxy)) {
|
||||
error("attach process failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string s = join2str(argc - shift, argv + shift, ' ');
|
||||
return system(s.c_str());
|
||||
}
|
||||
82
src/cgproxy.hpp
Normal file
82
src/cgproxy.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "socket_client.h"
|
||||
#include <cstdlib>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <unistd.h>
|
||||
using json = nlohmann::json;
|
||||
using namespace CGPROXY;
|
||||
using namespace CGPROXY::CONFIG;
|
||||
|
||||
namespace CGPROXY::CGPROXY {
|
||||
|
||||
bool print_help = false, proxy = true;
|
||||
bool attach_pid = false;
|
||||
string arg_pid;
|
||||
inline void print_usage() {
|
||||
if (proxy) {
|
||||
cout << "Run program with proxy" << endl;
|
||||
cout << "Usage: cgproxy [--help] [--debug] <CMD>" << endl;
|
||||
} else {
|
||||
cout << "Run program without proxy" << endl;
|
||||
cout << "Usage: cgpnoroxy [--help] [--debug] <CMD>" << endl;
|
||||
cout << "Alias: cgnoproxy = cgproxy --noproxy" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool processArgs(const int argc, char *argv[], int &shift) {
|
||||
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], "--debug") == 0) { enable_debug = true; }
|
||||
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||
if (argv[i][0] != '-') { break; }
|
||||
}
|
||||
shift = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
void send_pid(const pid_t pid, bool proxy, int &status) {
|
||||
json j;
|
||||
j["type"] = proxy ? MSG_TYPE_PROXY_PID : MSG_TYPE_NOPROXY_PID;
|
||||
j["data"] = pid;
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int shift = -1;
|
||||
if (!processArgs(argc, argv, shift)) {
|
||||
error("parameter error");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (print_help) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!attach_pid && argc == shift) {
|
||||
error("no program specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status = -1;
|
||||
send_pid(attach_pid ? stoi(arg_pid) : getpid(), proxy, status);
|
||||
if (status != 0) {
|
||||
error("attach process failed");
|
||||
if (status == 1) error("maybe cgproxy.service not running");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string s = join2str(argc - shift, argv + shift, ' ');
|
||||
return system(s.c_str());
|
||||
}
|
||||
} // namespace CGPROXY::CGPROXY
|
||||
@@ -1,31 +1,33 @@
|
||||
#include "cgroup_attach.hpp"
|
||||
#include "common.hpp"
|
||||
#include "config.hpp"
|
||||
#include "socket_server.hpp"
|
||||
#ifndef CGPROXYD_HPP
|
||||
#define CGPROXYD_HPP
|
||||
|
||||
#include "cgroup_attach.h"
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "socket_server.h"
|
||||
#include <csignal>
|
||||
#include <errno.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <pthread.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sched.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
using json = nlohmann::json;
|
||||
using namespace CGPROXY::SOCKET;
|
||||
using namespace CGPROXY::CONFIG;
|
||||
using namespace CGPROXY::CGROUP;
|
||||
using namespace ::CGPROXY::SOCKET;
|
||||
using namespace ::CGPROXY::CONFIG;
|
||||
using namespace ::CGPROXY::CGROUP;
|
||||
|
||||
namespace CGPROXY {
|
||||
namespace CGPROXY::CGPROXYD {
|
||||
|
||||
bool print_help = false;
|
||||
bool enable_execsnoop = false;
|
||||
|
||||
class cgproxyd {
|
||||
thread_arg arg_t;
|
||||
Config config;
|
||||
pthread_t socket_thread_id = -1;
|
||||
pid_t exec_snoop_pid = -1;
|
||||
|
||||
static cgproxyd *instance;
|
||||
static int handle_msg_static(char *msg) {
|
||||
@@ -43,7 +45,7 @@ class cgproxyd {
|
||||
} else {
|
||||
instance->stop();
|
||||
}
|
||||
exit(signum);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// single process instance
|
||||
@@ -81,8 +83,8 @@ class cgproxyd {
|
||||
try {
|
||||
type = j.at("type").get<int>();
|
||||
switch (type) {
|
||||
case MSG_TYPE_JSON:
|
||||
status = config.loadFromJson(j.at("data"));
|
||||
case MSG_TYPE_CONFIG_JSON:
|
||||
status = config.loadFromJsonStr(j.at("data").dump());
|
||||
if (status == SUCCESS) status = applyConfig(&config);
|
||||
return status;
|
||||
break;
|
||||
@@ -116,6 +118,22 @@ class cgproxyd {
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
void startExecSnoopProc() {
|
||||
if (exec_snoop_pid != -1){
|
||||
kill(exec_snoop_pid, SIGINT);
|
||||
exec_snoop_pid=-1;
|
||||
}
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
execl(BPF_EXEC_SNOOP_START, (char *) NULL);
|
||||
exit(0);
|
||||
} else if (pid<0){
|
||||
error("fork precess failed");
|
||||
}else {
|
||||
exec_snoop_pid = pid;
|
||||
}
|
||||
}
|
||||
|
||||
void assignStaticInstance() { instance = this; }
|
||||
|
||||
public:
|
||||
@@ -137,6 +155,7 @@ public:
|
||||
system(TPROXY_IPTABLS_CLEAN);
|
||||
c->toEnv();
|
||||
system(TPROXY_IPTABLS_START);
|
||||
if (enable_execsnoop) startExecSnoopProc();
|
||||
// no need to track running status
|
||||
return 0;
|
||||
}
|
||||
@@ -144,6 +163,7 @@ public:
|
||||
void stop() {
|
||||
debug("stopping");
|
||||
system(TPROXY_IPTABLS_CLEAN);
|
||||
// if (exec_snoop_pid != -1) kill(exec_snoop_pid, SIGINT);
|
||||
unlock();
|
||||
}
|
||||
|
||||
@@ -152,16 +172,17 @@ public:
|
||||
|
||||
cgproxyd *cgproxyd::instance = NULL;
|
||||
|
||||
} // namespace CGPROXY
|
||||
|
||||
bool print_help = false;
|
||||
|
||||
void print_usage() { printf("cgproxyd [--help] [--debug]\n"); }
|
||||
void print_usage() {
|
||||
cout << "Start a daemon with unix socket to accept control" << endl;
|
||||
cout << "Usage: cgproxyd [--help] [--debug]" << endl;
|
||||
cout << "Alias: cgproxyd = cgproxy --daemon" << endl;
|
||||
}
|
||||
|
||||
void processArgs(const int argc, char *argv[]) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
||||
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||
if (strcmp(argv[i], "--execsnoop") == 0) { enable_execsnoop = true; }
|
||||
if (argv[i][0] != '-') { break; }
|
||||
}
|
||||
}
|
||||
@@ -175,10 +196,11 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (getuid() != 0) {
|
||||
error("permission denied, need root");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
CGPROXY::cgproxyd d;
|
||||
cgproxyd d;
|
||||
return d.start();
|
||||
}
|
||||
}
|
||||
} // namespace CGPROXY::CGPROXYD
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#ifndef CGPROUP_ATTACH_H
|
||||
#define CGPROUP_ATTACH_H
|
||||
|
||||
#include "common.hpp"
|
||||
#include "cgroup_attach.h"
|
||||
#include "common.h"
|
||||
#include <errno.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -12,7 +10,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::CGROUP {
|
||||
|
||||
@@ -95,6 +92,4 @@ int attach(const int pid, const string cgroup_target) {
|
||||
return attach(to_str(pid), cgroup_target);
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::CGROUP
|
||||
|
||||
#endif
|
||||
} // namespace CGPROXY::CGROUP
|
||||
18
src/cgroup_attach.h
Normal file
18
src/cgroup_attach.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef CGPROUP_ATTACH_H
|
||||
#define CGPROUP_ATTACH_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::CGROUP {
|
||||
|
||||
bool exist(string path);
|
||||
bool validate(string pid, string cgroup);
|
||||
string get_cgroup2_mount_point(int &status);
|
||||
int attach(const string pid, const string cgroup_target);
|
||||
int attach(const int pid, const string cgroup_target);
|
||||
|
||||
} // namespace CGPROXY::CGROUP
|
||||
|
||||
#endif
|
||||
34
src/common.cpp
Normal file
34
src/common.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "common.h"
|
||||
#include <regex>
|
||||
|
||||
bool enable_debug = false;
|
||||
|
||||
string join2str(const vector<string> t, const char delm) {
|
||||
string s;
|
||||
for (const auto &e : t) 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; i < argc; i++) {
|
||||
s += argv[i];
|
||||
if (i != argc - 1) s += delm;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool validCgroup(const string cgroup) {
|
||||
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
||||
}
|
||||
|
||||
bool validCgroup(const vector<string> 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 int port) { return port > 0; }
|
||||
@@ -1,8 +1,15 @@
|
||||
#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"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#define TPROXY_IPTABLS_START "/usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
||||
#define TPROXY_IPTABLS_CLEAN "/usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
||||
#define BPF_EXEC_SNOOP_START "/usr/share/cgproxy/scripts/execsnoop.py"
|
||||
|
||||
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
||||
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
||||
@@ -12,7 +19,7 @@
|
||||
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
||||
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
||||
|
||||
#define MSG_TYPE_JSON 1
|
||||
#define MSG_TYPE_CONFIG_JSON 1
|
||||
#define MSG_TYPE_CONFIG_PATH 2
|
||||
#define MSG_TYPE_PROXY_PID 3
|
||||
#define MSG_TYPE_NOPROXY_PID 4
|
||||
@@ -28,24 +35,22 @@
|
||||
#define CGROUP_ERROR 6
|
||||
#define FILE_ERROR 7
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
static bool enable_debug = false;
|
||||
extern bool enable_debug;
|
||||
|
||||
#define error(...) \
|
||||
{ \
|
||||
fprintf(stderr, "error: "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
}
|
||||
|
||||
#define debug(...) \
|
||||
if (enable_debug) { \
|
||||
fprintf(stderr, "debug: "); \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
fprintf(stdout, "\n"); \
|
||||
}
|
||||
|
||||
#define return_error return -1;
|
||||
#define return_success return 0;
|
||||
|
||||
@@ -57,34 +62,12 @@ template <typename... T> string to_str(T... args) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
string join2str(const vector<string> t, const char delm = ' ') {
|
||||
string s;
|
||||
for (const auto &e : t) e != *(t.end() - 1) ? s += e + delm : s += e;
|
||||
return s;
|
||||
}
|
||||
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 = ' ') {
|
||||
string s;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
s += argv[i];
|
||||
if (i != argc - 1) s += delm;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool validCgroup(const string cgroup) {
|
||||
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
||||
}
|
||||
|
||||
bool validCgroup(const vector<string> 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 int port) { return port > 0; }
|
||||
bool validCgroup(const string cgroup);
|
||||
bool validCgroup(const vector<string> cgroup);
|
||||
bool validPid(const string pid);
|
||||
bool validPort(const int port);
|
||||
|
||||
#endif
|
||||
130
src/config.cpp
Normal file
130
src/config.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <set>
|
||||
using json = nlohmann::json;
|
||||
|
||||
#define add2json(v) j[#v] = v;
|
||||
#define tryassign(v) \
|
||||
try { \
|
||||
j.at(#v).get_to(v); \
|
||||
} catch (exception & e) {}
|
||||
#define merge(v) \
|
||||
{ \
|
||||
v.erase(std::remove(v.begin(), v.end(), v##_preserved), v.end()); \
|
||||
v.insert(v.begin(), v##_preserved); \
|
||||
}
|
||||
|
||||
namespace CGPROXY::CONFIG {
|
||||
|
||||
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_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 Config::saveToFile(const string f) {
|
||||
ofstream o(f);
|
||||
if (!o.is_open()) return FILE_ERROR;
|
||||
string js = toJsonStr();
|
||||
o << setw(4) << js << endl;
|
||||
o.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
string Config::toJsonStr() {
|
||||
json j;
|
||||
add2json(program_proxy);
|
||||
add2json(program_noproxy);
|
||||
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);
|
||||
return j.dump();
|
||||
}
|
||||
|
||||
int Config::loadFromFile(const string f) {
|
||||
debug("loading config: %s", f.c_str());
|
||||
ifstream ifs(f);
|
||||
if (ifs.is_open()) {
|
||||
string js = to_str(ifs.rdbuf());
|
||||
ifs.close();
|
||||
return loadFromJsonStr(js);
|
||||
} else {
|
||||
error("open failed: %s", f.c_str());
|
||||
return FILE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int Config::loadFromJsonStr(const string js) {
|
||||
if (!validateJsonStr(js)) {
|
||||
error("json validate fail");
|
||||
return PARAM_ERROR;
|
||||
}
|
||||
json j = json::parse(js);
|
||||
tryassign(program_proxy);
|
||||
tryassign(program_noproxy);
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Config::mergeReserved() {
|
||||
merge(cgroup_proxy);
|
||||
merge(cgroup_noproxy);
|
||||
}
|
||||
|
||||
bool Config::validateJsonStr(const string js) {
|
||||
json j = json::parse(js);
|
||||
bool status = true;
|
||||
const set<string> boolset = {"enable_gateway", "enable_dns", "enable_tcp",
|
||||
"enable_udp", "enable_ipv4", "enable_ipv6"};
|
||||
const set<string> allowset = {"program_proxy", "program_noproxy"};
|
||||
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<int> etc.
|
||||
if (value.is_array() && !validCgroup((vector<string>)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 if (allowset.find(key) != allowset.end()) {
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::CONFIG
|
||||
40
src/config.h
Normal file
40
src/config.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
#include "common.h"
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::CONFIG {
|
||||
|
||||
class Config {
|
||||
public:
|
||||
const string cgroup_proxy_preserved = CGROUP_PROXY_PRESVERED;
|
||||
const string cgroup_noproxy_preserved = CGROUP_NOPROXY_PRESVERED;
|
||||
|
||||
vector<string> program_proxy;
|
||||
vector<string> program_noproxy;
|
||||
vector<string> cgroup_proxy;
|
||||
vector<string> 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;
|
||||
|
||||
void toEnv();
|
||||
int saveToFile(const string f);
|
||||
string toJsonStr();
|
||||
int loadFromFile(const string f);
|
||||
int loadFromJsonStr(const string js);
|
||||
|
||||
private:
|
||||
void mergeReserved();
|
||||
bool validateJsonStr(const string js);
|
||||
};
|
||||
|
||||
} // namespace CGPROXY::CONFIG
|
||||
#endif
|
||||
155
src/config.hpp
155
src/config.hpp
@@ -1,155 +0,0 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
#include "common.hpp"
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
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<string> cgroup_proxy;
|
||||
vector<string> 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;
|
||||
}
|
||||
|
||||
#define add2json(v) j[#v] = v;
|
||||
json toJson() {
|
||||
json j;
|
||||
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);
|
||||
return j;
|
||||
}
|
||||
#undef add2json
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#define tryassign(v) \
|
||||
try { \
|
||||
j.at(#v).get_to(v); \
|
||||
} catch (exception & e) {}
|
||||
int loadFromJson(const json &j) {
|
||||
if (!validateJson(j)) {
|
||||
error("json validate fail");
|
||||
return PARAM_ERROR;
|
||||
}
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
#undef assign
|
||||
|
||||
#define merge(v) \
|
||||
{ \
|
||||
v.erase(std::remove(v.begin(), v.end(), v##_preserved), v.end()); \
|
||||
v.insert(v.begin(), v##_preserved); \
|
||||
}
|
||||
void mergeReserved() {
|
||||
merge(cgroup_proxy);
|
||||
merge(cgroup_noproxy);
|
||||
}
|
||||
#undef merge
|
||||
|
||||
bool validateJson(const json &j) {
|
||||
bool status = true;
|
||||
const set<string> 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;
|
||||
// TODO what if vector<int> etc.
|
||||
if (value.is_array() && !validCgroup((vector<string>)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;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace CGPROXY::CONFIG
|
||||
#endif
|
||||
17
src/main.cpp
Normal file
17
src/main.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "cgproxy.hpp"
|
||||
#include "cgproxyd.hpp"
|
||||
|
||||
bool as_cgproxyd = false;
|
||||
void processArgs(const int argc, char *argv[]) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--daemon") == 0) { as_cgproxyd = true; }
|
||||
if (argv[i][0] != '-') { break; }
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
processArgs(argc, argv);
|
||||
if (as_cgproxyd) ::CGPROXY::CGPROXYD::main(argc, argv);
|
||||
else
|
||||
::CGPROXY::CGPROXY::main(argc, argv);
|
||||
}
|
||||
@@ -1,20 +1,9 @@
|
||||
#ifndef SOCKET_CLIENT_H
|
||||
#define SOCKET_CLIENT_H
|
||||
|
||||
#include "common.hpp"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include "socket_client.h"
|
||||
#include "common.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
#define return_if_error(flag, msg) \
|
||||
if (flag == -1) { \
|
||||
perror(msg); \
|
||||
@@ -23,6 +12,8 @@ namespace CGPROXY::SOCKET {
|
||||
return; \
|
||||
}
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
void send(const char *msg, int &status) {
|
||||
debug("send msg: %s", msg);
|
||||
status = UNKNOWN_ERROR;
|
||||
@@ -59,5 +50,4 @@ void send(const string msg, int &status) {
|
||||
debug("return status: %d", status);
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
#endif
|
||||
} // namespace CGPROXY::SOCKET
|
||||
14
src/socket_client.h
Normal file
14
src/socket_client.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef SOCKET_CLIENT_H
|
||||
#define SOCKET_CLIENT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
void send(const char *msg, int &status);
|
||||
void send(const string msg, int &status);
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
#endif
|
||||
65
src/socket_server.cpp
Normal file
65
src/socket_server.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "socket_server.h"
|
||||
#include "common.h"
|
||||
#include <filesystem>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
void SocketServer::socketListening(function<int(char *)> callback) {
|
||||
debug("starting socket listening");
|
||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (fs::exists(SOCKET_PATH) && unlink(SOCKET_PATH) == -1) {
|
||||
error("%s exist, and can't unlink", SOCKET_PATH);
|
||||
return;
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void *SocketServer::startThread(void *arg) {
|
||||
thread_arg *p = (thread_arg *)arg;
|
||||
SocketServer server;
|
||||
server.socketListening(p->handle_msg);
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
SocketServer::~SocketServer() {
|
||||
close(sfd);
|
||||
close(cfd);
|
||||
unlink(SOCKET_PATH);
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
33
src/socket_server.h
Normal file
33
src/socket_server.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef SOCKET_SERVER_H
|
||||
#define SOCKET_SERVER_H
|
||||
|
||||
#include <functional>
|
||||
#include <stdlib.h>
|
||||
#include <sys/un.h>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
#define continue_if_error(flag, msg) \
|
||||
if (flag == -1) { \
|
||||
perror(msg); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
struct thread_arg {
|
||||
function<int(char *)> handle_msg;
|
||||
};
|
||||
|
||||
class SocketServer {
|
||||
public:
|
||||
int sfd = -1, cfd = -1, flag = -1;
|
||||
struct sockaddr_un unix_socket;
|
||||
|
||||
void socketListening(function<int(char *)> callback);
|
||||
~SocketServer();
|
||||
static void *startThread(void *arg);
|
||||
};
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
|
||||
#endif
|
||||
@@ -1,93 +0,0 @@
|
||||
#ifndef SOCKET_SERVER_H
|
||||
#define SOCKET_SERVER_H
|
||||
|
||||
#include "common.hpp"
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
#define continue_if_error(flag, msg) \
|
||||
if (flag == -1) { \
|
||||
perror(msg); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
struct thread_arg {
|
||||
function<int(char *)> handle_msg;
|
||||
};
|
||||
|
||||
class SocketServer {
|
||||
public:
|
||||
int sfd = -1, cfd = -1, flag = -1;
|
||||
struct sockaddr_un unix_socket;
|
||||
|
||||
void socketListening(function<int(char *)> callback) {
|
||||
debug("starting socket listening");
|
||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (fs::exists(SOCKET_PATH) && unlink(SOCKET_PATH) == -1) {
|
||||
error("%s exist, and can't unlink", SOCKET_PATH);
|
||||
return;
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
~SocketServer() {
|
||||
close(sfd);
|
||||
close(cfd);
|
||||
unlink(SOCKET_PATH);
|
||||
}
|
||||
|
||||
static void *startThread(void *arg) {
|
||||
thread_arg *p = (thread_arg *)arg;
|
||||
SocketServer server;
|
||||
server.socketListening(p->handle_msg);
|
||||
return (void *)0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
|
||||
#endif
|
||||
@@ -2,5 +2,6 @@ include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
add_executable(client_test socket_client_test.cpp)
|
||||
add_executable(client_test socket_client_test.cpp
|
||||
../src/socket_client.cpp ../src/common.cpp ../src/config.cpp)
|
||||
target_link_libraries(client_test nlohmann_json::nlohmann_json)
|
||||
@@ -1,48 +1,48 @@
|
||||
#include "socket_client.hpp"
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "socket_client.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace std;
|
||||
using json = nlohmann::json;
|
||||
using namespace CGPROXY;
|
||||
using namespace CGPROXY::CONFIG;
|
||||
|
||||
void test_json() {
|
||||
void send_config(Config &config, int &status) {
|
||||
json j;
|
||||
j["type"] = MSG_TYPE_JSON;
|
||||
j["data"]["cgroup_proxy"] = "/";
|
||||
j["data"]["enable_dns"] = false;
|
||||
int status;
|
||||
j["type"] = MSG_TYPE_CONFIG_JSON;
|
||||
j["data"] = config.toJsonStr();
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
void test_json_array() {
|
||||
json j;
|
||||
j["type"] = MSG_TYPE_JSON;
|
||||
j["data"]["cgroup_proxy"] = "/proxy.slice";
|
||||
j["data"]["cgroup_noproxy"] = {"/noproxy.slice", "/system.slice/v2ray.service"};
|
||||
int status;
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
void test_file() {
|
||||
void send_config_path(const string s, int &status) {
|
||||
json j;
|
||||
j["type"] = MSG_TYPE_CONFIG_PATH;
|
||||
j["data"] = "/etc/cgproxy.conf";
|
||||
int status;
|
||||
j["data"] = s;
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
void test_pid() {
|
||||
void send_pid(const pid_t pid, bool proxy, int &status) {
|
||||
json j;
|
||||
j["type"] = MSG_TYPE_PROXY_PID;
|
||||
j["data"] = "9999";
|
||||
int status;
|
||||
j["type"] = proxy ? MSG_TYPE_PROXY_PID : MSG_TYPE_NOPROXY_PID;
|
||||
j["data"] = pid;
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
void test_config() {
|
||||
Config config;
|
||||
config.cgroup_proxy = {"/"};
|
||||
int status;
|
||||
send_config(config, status);
|
||||
}
|
||||
|
||||
void test_config_path() {
|
||||
string path = "/etc/cgproxy/config.json";
|
||||
int status;
|
||||
send_config_path(path, status);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_json_array();
|
||||
test_file();
|
||||
test_json();
|
||||
test_pid();
|
||||
test_config();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// #include "common.h"
|
||||
#include "config.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <pthread.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
using namespace CGPROXY::CONFIG;
|
||||
|
||||
int main() {
|
||||
Config c;
|
||||
c.saveToFile("./config.json");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
add_executable(cgattach cgattach.cpp)
|
||||
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
||||
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "cgroup_attach.hpp"
|
||||
#include "common.hpp"
|
||||
#include "cgroup_attach.h"
|
||||
#include "common.h"
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
using namespace std;
|
||||
|
||||
void print_usage() { fprintf(stdout, "usage: cgattach <pid> <cgroup>\n"); }
|
||||
|
||||
@@ -7,7 +7,7 @@ Wants=network-online.target
|
||||
[Service]
|
||||
Type=exec
|
||||
ExecStart=/usr/lib/v2ray/v2ray -config /etc/v2ray/config.json
|
||||
User=nobody
|
||||
DynamicUser=yes
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
NoNewPrivileges=yes
|
||||
Restart=on-failure
|
||||
@@ -15,4 +15,4 @@ Restart=on-failure
|
||||
RestartPreventExitStatus=23
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Reference in New Issue
Block a user