mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-01-07 13:07:56 +08:00
add execsnoop
This commit is contained in:
@@ -23,7 +23,8 @@ 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
|
||||
|
||||
@@ -4,7 +4,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/cgproxyd
|
||||
ExecStart=/usr/bin/cgproxyd --execsnoop
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{
|
||||
"port": 12345,
|
||||
"program_noproxy": ["/usr/lib/v2ray/v2ray", "/usr/bin/qv2ray"],
|
||||
"program_proxy": [],
|
||||
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
||||
"cgroup_proxy": [],
|
||||
"enable_dns": true,
|
||||
"enable_gateway": false,
|
||||
"enable_dns": true,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true,
|
||||
"enable_tcp": true,
|
||||
"enable_udp": true,
|
||||
"port": 12345
|
||||
"enable_udp": true
|
||||
}
|
||||
|
||||
133
execsnoop.py
Normal file
133
execsnoop.py
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/python
|
||||
# This won't catch all new processes: an application may fork() but not exec().
|
||||
|
||||
from __future__ import print_function
|
||||
from bcc import BPF
|
||||
from bcc.utils import ArgString, printb
|
||||
import bcc.utils as utils
|
||||
import argparse
|
||||
import re
|
||||
import time
|
||||
from collections import defaultdict
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import time
|
||||
import shutil
|
||||
|
||||
# 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
|
||||
|
||||
print("'{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):
|
||||
print("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):
|
||||
return map(int,check_output(["pidof",name]).split())
|
||||
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()
|
||||
@@ -11,7 +11,8 @@ using namespace CGPROXY::CONFIG;
|
||||
namespace CGPROXY::CGPROXY {
|
||||
|
||||
bool print_help = false, proxy = true;
|
||||
bool attach_pid=false; string arg_pid;
|
||||
bool attach_pid = false;
|
||||
string arg_pid;
|
||||
inline void print_usage() {
|
||||
if (proxy) {
|
||||
cout << "Run program with proxy" << endl;
|
||||
@@ -29,8 +30,8 @@ bool processArgs(const int argc, char *argv[], int &shift) {
|
||||
if (strcmp(argv[i], "--pid") == 0) {
|
||||
attach_pid = true;
|
||||
i++;
|
||||
if (i==argc) return false;
|
||||
arg_pid=argv[i];
|
||||
if (i == argc) return false;
|
||||
arg_pid = argv[i];
|
||||
if (!validPid(arg_pid)) return false;
|
||||
continue;
|
||||
}
|
||||
@@ -52,7 +53,7 @@ void send_pid(const pid_t pid, bool proxy, int &status) {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int shift = -1;
|
||||
if (!processArgs(argc, argv, shift)){
|
||||
if (!processArgs(argc, argv, shift)) {
|
||||
error("parameter error");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -68,10 +69,10 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
int status = -1;
|
||||
send_pid(attach_pid?stoi(arg_pid):getpid(), proxy, status);
|
||||
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");
|
||||
if (status == 1) error("maybe cgproxy.service not running");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <csignal>
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sched.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -19,10 +20,14 @@ using namespace ::CGPROXY::CGROUP;
|
||||
|
||||
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) {
|
||||
@@ -40,7 +45,7 @@ class cgproxyd {
|
||||
} else {
|
||||
instance->stop();
|
||||
}
|
||||
exit(signum);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// single process instance
|
||||
@@ -113,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:
|
||||
@@ -134,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;
|
||||
}
|
||||
@@ -141,6 +163,7 @@ public:
|
||||
void stop() {
|
||||
debug("stopping");
|
||||
system(TPROXY_IPTABLS_CLEAN);
|
||||
// if (exec_snoop_pid != -1) kill(exec_snoop_pid, SIGINT);
|
||||
unlock();
|
||||
}
|
||||
|
||||
@@ -149,8 +172,6 @@ public:
|
||||
|
||||
cgproxyd *cgproxyd::instance = NULL;
|
||||
|
||||
bool print_help = false;
|
||||
|
||||
void print_usage() {
|
||||
cout << "Start a daemon with unix socket to accept control" << endl;
|
||||
cout << "Usage: cgproxyd [--help] [--debug]" << endl;
|
||||
@@ -161,6 +182,7 @@ 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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#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 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"
|
||||
|
||||
@@ -21,6 +21,8 @@ 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);
|
||||
@@ -43,6 +45,8 @@ int Config::saveToFile(const string f) {
|
||||
|
||||
string Config::toJsonStr() {
|
||||
json j;
|
||||
add2json(program_proxy);
|
||||
add2json(program_noproxy);
|
||||
add2json(cgroup_proxy);
|
||||
add2json(cgroup_noproxy);
|
||||
add2json(enable_gateway);
|
||||
@@ -74,6 +78,8 @@ int Config::loadFromJsonStr(const string js) {
|
||||
return PARAM_ERROR;
|
||||
}
|
||||
json j = json::parse(js);
|
||||
tryassign(program_proxy);
|
||||
tryassign(program_noproxy);
|
||||
tryassign(cgroup_proxy);
|
||||
tryassign(cgroup_noproxy);
|
||||
tryassign(enable_gateway);
|
||||
@@ -96,6 +102,7 @@ bool Config::validateJsonStr(const string 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;
|
||||
@@ -106,6 +113,8 @@ bool Config::validateJsonStr(const string js) {
|
||||
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;
|
||||
|
||||
@@ -13,6 +13,8 @@ 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;
|
||||
|
||||
Reference in New Issue
Block a user