mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-02-09 21:14:57 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40bd709995 | ||
|
|
221a75ae7b | ||
|
|
16a341205f | ||
|
|
076651b984 | ||
|
|
1c72a204a1 | ||
|
|
f501c7e476 | ||
|
|
0ec9caefe1 | ||
|
|
94b73b5103 | ||
|
|
c30df999b8 | ||
|
|
932f2bbc94 | ||
|
|
1bbd1ab6ec |
@@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.10)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
project(cgproxy VERSION 0.13)
|
project(cgproxy VERSION 0.14)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result")
|
||||||
|
|
||||||
set(build_tools OFF)
|
option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
|
||||||
set(build_test OFF)
|
option(build_tools OFF)
|
||||||
|
option(build_test OFF)
|
||||||
|
|
||||||
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
|
||||||
@@ -24,7 +25,6 @@ install(FILES cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
|||||||
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
|
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
|
||||||
install(FILES config.json DESTINATION /etc/cgproxy/)
|
install(FILES config.json DESTINATION /etc/cgproxy/)
|
||||||
install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/ PERMISSIONS ${basic_permission})
|
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/)
|
install(FILES readme.md DESTINATION /usr/share/doc/cgproxy/)
|
||||||
|
|
||||||
# man pages
|
# man pages
|
||||||
|
|||||||
135
execsnoop.py
135
execsnoop.py
@@ -1,135 +0,0 @@
|
|||||||
#!/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()
|
|
||||||
18
readme.md
18
readme.md
@@ -1,16 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
# Transparent Proxy with cgroup v2
|
# Transparent Proxy powered with cgroup v2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
cgproxy will transparent proxy anything running in specific cgroup. It resembles with *proxychains* and *tsock*s in default setting.
|
cgproxy will transparent proxy anything running in specific cgroup. It resembles with *proxychains* and *tsock*s in default setting.
|
||||||
|
|
||||||
It aslo supports global transparent proxy and gateway proxy. See [Global transparent proxy](#global-transparent-proxy) and [Gateway proxy](#gateway-proxy).
|
Main feature:
|
||||||
|
|
||||||
|
- supports cgroup/program level proxy control.
|
||||||
|
- supports global transparent proxy and gateway proxy.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
<!--ts-->
|
<!--ts-->
|
||||||
|
|
||||||
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
|
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
* [Prerequest](#prerequest)
|
* [Prerequest](#prerequest)
|
||||||
@@ -103,7 +109,7 @@ Config file: **/etc/cgproxy/config.json**
|
|||||||
|
|
||||||
- **port** tproxy listenning port
|
- **port** tproxy listenning port
|
||||||
|
|
||||||
- program level proxy controll, need `python-bcc` installed to work
|
- program level proxy control, need `python-bcc` installed to work
|
||||||
|
|
||||||
- **program_proxy** program need to be proxied
|
- **program_proxy** program need to be proxied
|
||||||
- **program_noproxy** program that won't be proxied
|
- **program_noproxy** program that won't be proxied
|
||||||
@@ -152,8 +158,10 @@ sudo systemctl restart cgproxy.service
|
|||||||
|
|
||||||
example: `cgnoproxy qv2ray`
|
example: `cgnoproxy qv2ray`
|
||||||
|
|
||||||
- passive way, set it's cgroup in configuration, very useful for service
|
- passive way, persistent config
|
||||||
|
|
||||||
|
example: `"program_noproxy":["v2ray" ,"qv2ray"]`
|
||||||
|
|
||||||
example: `"cgroup_noproxy":["/system.slice/v2ray.service"]`
|
example: `"cgroup_noproxy":["/system.slice/v2ray.service"]`
|
||||||
|
|
||||||
- Finally, restart cgproxy service, that's all
|
- Finally, restart cgproxy service, that's all
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ find_package(nlohmann_json REQUIRED)
|
|||||||
include_directories(${PROJECT_SOURCE_DIR})
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if (with_execsnoop)
|
||||||
|
add_library(execsnoop MODULE execsnoop.cpp common.cpp)
|
||||||
|
target_link_libraries(execsnoop bcc)
|
||||||
|
install(TARGETS execsnoop DESTINATION /usr/lib/cgproxy/ PERMISSIONS ${basic_permission})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(main main.cpp
|
add_executable(main main.cpp
|
||||||
common.cpp config.cpp cgroup_attach.cpp
|
common.cpp config.cpp cgroup_attach.cpp
|
||||||
socket_client.cpp socket_server.cpp)
|
socket_client.cpp socket_server.cpp)
|
||||||
|
target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json Threads::Threads dl)
|
||||||
target_link_libraries(main nlohmann_json::nlohmann_json Threads::Threads)
|
|
||||||
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
||||||
|
|
||||||
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ int main(int argc, char *argv[]) {
|
|||||||
if (status == 1) error("maybe cgproxy.service not running");
|
if (status == 1) error("maybe cgproxy.service not running");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
// if just attach pid, return here
|
||||||
|
if (attach_pid) return 0;
|
||||||
|
|
||||||
string s = join2str(argc - shift, argv + shift, ' ');
|
string s = join2str(argc - shift, argv + shift, ' ');
|
||||||
return system(s.c_str());
|
return system(s.c_str());
|
||||||
|
|||||||
155
src/cgproxyd.hpp
155
src/cgproxyd.hpp
@@ -4,10 +4,17 @@
|
|||||||
#include "cgroup_attach.h"
|
#include "cgroup_attach.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "execsnoop.h"
|
||||||
#include "socket_server.h"
|
#include "socket_server.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -17,17 +24,44 @@ using json = nlohmann::json;
|
|||||||
using namespace ::CGPROXY::SOCKET;
|
using namespace ::CGPROXY::SOCKET;
|
||||||
using namespace ::CGPROXY::CONFIG;
|
using namespace ::CGPROXY::CONFIG;
|
||||||
using namespace ::CGPROXY::CGROUP;
|
using namespace ::CGPROXY::CGROUP;
|
||||||
|
// using namespace ::CGPROXY::EXECSNOOP;
|
||||||
|
|
||||||
|
namespace CGPROXY::EXECSNOOP {
|
||||||
|
typedef void *(*startThread_t)(void *arg);
|
||||||
|
startThread_t _startThread;
|
||||||
|
bool loadExecsnoopLib() {
|
||||||
|
try {
|
||||||
|
info("loading %s", LIBEXECSNOOP_SO);
|
||||||
|
void *handle_dl = dlopen(LIBEXECSNOOP_SO, RTLD_NOW);
|
||||||
|
if (handle_dl == NULL) {
|
||||||
|
error("dlopen %s failed: %s", LIBEXECSNOOP_SO, dlerror());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_startThread = reinterpret_cast<startThread_t>(dlsym(handle_dl, "_startThread"));
|
||||||
|
if (_startThread == NULL) {
|
||||||
|
error("dlsym startThread failed: %s", dlerror());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
info("dlsym startThread success");
|
||||||
|
return true;
|
||||||
|
} catch (exception &e) { return false; }
|
||||||
|
}
|
||||||
|
} // namespace CGPROXY::EXECSNOOP
|
||||||
|
|
||||||
namespace CGPROXY::CGPROXYD {
|
namespace CGPROXY::CGPROXYD {
|
||||||
|
|
||||||
bool print_help = false;
|
bool print_help = false;
|
||||||
|
bool enable_socketserver = true;
|
||||||
bool enable_execsnoop = false;
|
bool enable_execsnoop = false;
|
||||||
|
|
||||||
class cgproxyd {
|
class cgproxyd {
|
||||||
thread_arg arg_t;
|
SOCKET::thread_arg socketserver_thread_arg;
|
||||||
|
pthread_t socket_thread_id = THREAD_UNDEF;
|
||||||
|
|
||||||
|
EXECSNOOP::thread_arg execsnoop_thread_arg;
|
||||||
|
pthread_t execsnoop_thread_id = THREAD_UNDEF;
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
pthread_t socket_thread_id = -1;
|
|
||||||
pid_t exec_snoop_pid = -1;
|
|
||||||
|
|
||||||
static cgproxyd *instance;
|
static cgproxyd *instance;
|
||||||
static int handle_msg_static(char *msg) {
|
static int handle_msg_static(char *msg) {
|
||||||
@@ -38,6 +72,40 @@ class cgproxyd {
|
|||||||
return instance->handle_msg(msg);
|
return instance->handle_msg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_pid_static(int pid) {
|
||||||
|
if (!instance) {
|
||||||
|
error("no cgproxyd instance assigned");
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
return instance->handle_pid(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_pid(int pid) {
|
||||||
|
auto path = realpath(to_str("/proc/", pid, "/exe").c_str(), NULL);
|
||||||
|
if (path == NULL) {
|
||||||
|
debug("pid %d live life too short", pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
debug("execsnoop: %d %s", pid, path);
|
||||||
|
|
||||||
|
vector<string> v;
|
||||||
|
|
||||||
|
v = config.program_noproxy;
|
||||||
|
if (find(v.begin(), v.end(), path) != v.end()) {
|
||||||
|
info("execsnoop noproxy: %d %s", pid, path);
|
||||||
|
free(path);
|
||||||
|
return attach(pid, config.cgroup_noproxy_preserved);
|
||||||
|
}
|
||||||
|
v = config.program_proxy;
|
||||||
|
if (find(v.begin(), v.end(), path) != v.end()) {
|
||||||
|
info("execsnoop proxied: %d %s", pid, path);
|
||||||
|
free(path);
|
||||||
|
return attach(pid, config.cgroup_proxy_preserved);
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void signalHandler(int signum) {
|
static void signalHandler(int signum) {
|
||||||
debug("Signal %d received.", signum);
|
debug("Signal %d received.", signum);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
@@ -85,12 +153,12 @@ class cgproxyd {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case MSG_TYPE_CONFIG_JSON:
|
case MSG_TYPE_CONFIG_JSON:
|
||||||
status = config.loadFromJsonStr(j.at("data").dump());
|
status = config.loadFromJsonStr(j.at("data").dump());
|
||||||
if (status == SUCCESS) status = applyConfig(&config);
|
if (status == SUCCESS) status = applyConfig();
|
||||||
return status;
|
return status;
|
||||||
break;
|
break;
|
||||||
case MSG_TYPE_CONFIG_PATH:
|
case MSG_TYPE_CONFIG_PATH:
|
||||||
status = config.loadFromFile(j.at("data").get<string>());
|
status = config.loadFromFile(j.at("data").get<string>());
|
||||||
if (status == SUCCESS) status = applyConfig(&config);
|
if (status == SUCCESS) status = applyConfig();
|
||||||
return status;
|
return status;
|
||||||
break;
|
break;
|
||||||
case MSG_TYPE_PROXY_PID:
|
case MSG_TYPE_PROXY_PID:
|
||||||
@@ -111,27 +179,46 @@ class cgproxyd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pthread_t startSocketListeningThread() {
|
pthread_t startSocketListeningThread() {
|
||||||
arg_t.handle_msg = &handle_msg_static;
|
socketserver_thread_arg.handle_msg = &handle_msg_static;
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
int status = pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t);
|
int status =
|
||||||
if (status != 0) error("socket thread create failed");
|
pthread_create(&thread_id, NULL, &SOCKET::startThread, &socketserver_thread_arg);
|
||||||
|
if (status != 0) {
|
||||||
|
error("socket thread create failed");
|
||||||
|
return THREAD_UNDEF;
|
||||||
|
}
|
||||||
return thread_id;
|
return thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void startExecSnoopProc() {
|
pthread_t startExecsnoopThread() {
|
||||||
if (exec_snoop_pid != -1){
|
if (!EXECSNOOP::loadExecsnoopLib() || EXECSNOOP::_startThread == NULL) {
|
||||||
kill(exec_snoop_pid, SIGINT);
|
error("execsnoop start failed, maybe bcc not installed");
|
||||||
exec_snoop_pid=-1;
|
return THREAD_UNDEF;
|
||||||
}
|
}
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid == 0) {
|
execsnoop_thread_arg.handle_pid = &handle_pid_static;
|
||||||
execl(BPF_EXEC_SNOOP_START, (char *) NULL);
|
pthread_t thread_id;
|
||||||
exit(0);
|
int status =
|
||||||
} else if (pid<0){
|
pthread_create(&thread_id, NULL, EXECSNOOP::_startThread, &execsnoop_thread_arg);
|
||||||
error("fork precess failed");
|
if (status != 0) {
|
||||||
}else {
|
error("execsnoop thread create failed");
|
||||||
exec_snoop_pid = pid;
|
return THREAD_UNDEF;
|
||||||
}
|
}
|
||||||
|
return thread_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processRunningProgram() {
|
||||||
|
debug("process running program") for (auto &path :
|
||||||
|
config.program_noproxy) for (auto &pid :
|
||||||
|
bash_pidof(path)) {
|
||||||
|
int status = attach(pid, config.cgroup_noproxy_preserved);
|
||||||
|
if (status == 0) info("noproxy running process %d %s", pid, path.c_str());
|
||||||
|
}
|
||||||
|
for (auto &path : config.program_proxy)
|
||||||
|
for (auto &pid : bash_pidof(path)) {
|
||||||
|
int status = attach(pid, config.cgroup_proxy_preserved);
|
||||||
|
if (status == 0) info("proxied running process %d %s", pid, path.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void assignStaticInstance() { instance = this; }
|
void assignStaticInstance() { instance = this; }
|
||||||
@@ -143,19 +230,31 @@ public:
|
|||||||
signal(SIGTERM, &signalHandler);
|
signal(SIGTERM, &signalHandler);
|
||||||
signal(SIGHUP, &signalHandler);
|
signal(SIGHUP, &signalHandler);
|
||||||
|
|
||||||
config.loadFromFile(DEFAULT_CONFIG_FILE);
|
|
||||||
applyConfig(&config);
|
|
||||||
|
|
||||||
assignStaticInstance();
|
assignStaticInstance();
|
||||||
socket_thread_id = startSocketListeningThread();
|
|
||||||
|
config.loadFromFile(DEFAULT_CONFIG_FILE);
|
||||||
|
applyConfig();
|
||||||
|
processRunningProgram();
|
||||||
|
|
||||||
|
if (enable_socketserver) {
|
||||||
|
socket_thread_id = startSocketListeningThread();
|
||||||
|
if (socket_thread_id > 0) info("socket server listening thread started");
|
||||||
|
}
|
||||||
|
if (enable_execsnoop) {
|
||||||
|
execsnoop_thread_id = startExecsnoopThread();
|
||||||
|
if (execsnoop_thread_id > 0) info("execsnoop thread started");
|
||||||
|
}
|
||||||
|
cout << flush;
|
||||||
|
|
||||||
pthread_join(socket_thread_id, NULL);
|
pthread_join(socket_thread_id, NULL);
|
||||||
|
pthread_join(execsnoop_thread_id, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int applyConfig(Config *c) {
|
int applyConfig() {
|
||||||
system(TPROXY_IPTABLS_CLEAN);
|
system(TPROXY_IPTABLS_CLEAN);
|
||||||
c->toEnv();
|
config.print_summary();
|
||||||
|
config.toEnv();
|
||||||
system(TPROXY_IPTABLS_START);
|
system(TPROXY_IPTABLS_START);
|
||||||
if (enable_execsnoop) startExecSnoopProc();
|
|
||||||
// no need to track running status
|
// no need to track running status
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -163,7 +262,6 @@ public:
|
|||||||
void stop() {
|
void stop() {
|
||||||
debug("stopping");
|
debug("stopping");
|
||||||
system(TPROXY_IPTABLS_CLEAN);
|
system(TPROXY_IPTABLS_CLEAN);
|
||||||
// if (exec_snoop_pid != -1) kill(exec_snoop_pid, SIGINT);
|
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +286,7 @@ void processArgs(const int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
setbuf(stdout, NULL);
|
||||||
processArgs(argc, argv);
|
processArgs(argc, argv);
|
||||||
if (print_help) {
|
if (print_help) {
|
||||||
print_usage();
|
print_usage();
|
||||||
|
|||||||
@@ -13,48 +13,47 @@
|
|||||||
|
|
||||||
namespace CGPROXY::CGROUP {
|
namespace CGPROXY::CGROUP {
|
||||||
|
|
||||||
|
string cgroup2_mount_point = get_cgroup2_mount_point();
|
||||||
|
|
||||||
bool exist(string path) {
|
bool exist(string path) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.c_str(), &st) != -1) { return S_ISDIR(st.st_mode); }
|
if (stat(path.c_str(), &st) != -1) { return S_ISDIR(st.st_mode); }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string get_cgroup2_mount_point() {
|
||||||
|
stringstream buffer;
|
||||||
|
FILE *fp = popen("findmnt -t cgroup2 -n -o TARGET", "r");
|
||||||
|
if (!fp) return "";
|
||||||
|
char buf[64];
|
||||||
|
while (fgets(buf, 64, fp) != NULL) { buffer << buf; }
|
||||||
|
pclose(fp);
|
||||||
|
string s = buffer.str();
|
||||||
|
s.pop_back(); // remove newline character
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
bool validate(string pid, string cgroup) {
|
bool validate(string pid, string cgroup) {
|
||||||
bool pid_v = validPid(pid);
|
bool pid_v = validPid(pid);
|
||||||
bool cg_v = validCgroup(cgroup);
|
bool cg_v = validCgroup(cgroup);
|
||||||
if (pid_v && cg_v) return true;
|
if (pid_v && cg_v) return true;
|
||||||
|
|
||||||
error("attach paramater validate error");
|
error("attach paramater validate error");
|
||||||
return_error
|
return_error;
|
||||||
}
|
|
||||||
|
|
||||||
string get_cgroup2_mount_point(int &status) {
|
|
||||||
char cgroup2_mount_point[100] = "";
|
|
||||||
FILE *fp = popen("findmnt -t cgroup2 -n -o TARGET", "r");
|
|
||||||
int count = fscanf(fp, "%s", cgroup2_mount_point);
|
|
||||||
fclose(fp);
|
|
||||||
if (count == 0) {
|
|
||||||
error("cgroup2 not supported");
|
|
||||||
status = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
status = 0;
|
|
||||||
return cgroup2_mount_point;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int attach(const string pid, const string cgroup_target) {
|
int attach(const string pid, const string cgroup_target) {
|
||||||
if (getuid() != 0) {
|
if (getuid() != 0) {
|
||||||
error("need root to attach cgroup");
|
error("need root to attach cgroup");
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
if (!validate(pid, cgroup_target))
|
if (!validate(pid, cgroup_target)) return_error;
|
||||||
return_error string cgroup_mount_point = get_cgroup2_mount_point(status);
|
if (cgroup2_mount_point.empty()) return_error;
|
||||||
if (status != 0)
|
string cgroup_target_path = cgroup2_mount_point + cgroup_target;
|
||||||
return_error string cgroup_target_path = cgroup_mount_point + cgroup_target;
|
|
||||||
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
||||||
|
|
||||||
// check if exist, we will create it if not exist
|
// check if exist, we will create it if not exist
|
||||||
@@ -64,7 +63,7 @@ int attach(const string pid, const string cgroup_target) {
|
|||||||
debug("created cgroup %s success", cgroup_target.c_str());
|
debug("created cgroup %s success", cgroup_target.c_str());
|
||||||
} else {
|
} else {
|
||||||
error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno);
|
error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno);
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
// error("cgroup %s not exist",cgroup_target.c_str());
|
// error("cgroup %s not exist",cgroup_target.c_str());
|
||||||
// return_error
|
// return_error
|
||||||
@@ -74,7 +73,7 @@ int attach(const string pid, const string cgroup_target) {
|
|||||||
ofstream procs(cgroup_target_procs, ofstream::app);
|
ofstream procs(cgroup_target_procs, ofstream::app);
|
||||||
if (!procs.is_open()) {
|
if (!procs.is_open()) {
|
||||||
error("open file %s failed", cgroup_target_procs.c_str());
|
error("open file %s failed", cgroup_target_procs.c_str());
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
procs << pid.c_str() << endl;
|
procs << pid.c_str() << endl;
|
||||||
procs.close();
|
procs.close();
|
||||||
@@ -83,9 +82,9 @@ int attach(const string pid, const string cgroup_target) {
|
|||||||
if (!procs) {
|
if (!procs) {
|
||||||
error("write %s to %s failed, maybe process %s not exist", pid.c_str(),
|
error("write %s to %s failed, maybe process %s not exist", pid.c_str(),
|
||||||
cgroup_target_procs.c_str(), pid.c_str());
|
cgroup_target_procs.c_str(), pid.c_str());
|
||||||
return_error
|
return_error;
|
||||||
}
|
}
|
||||||
return_success
|
return_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
int attach(const int pid, const string cgroup_target) {
|
int attach(const int pid, const string cgroup_target) {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace CGPROXY::CGROUP {
|
namespace CGPROXY::CGROUP {
|
||||||
|
extern string cgroup2_mount_point;
|
||||||
bool exist(string path);
|
bool exist(string path);
|
||||||
bool validate(string pid, string cgroup);
|
bool validate(string pid, string cgroup);
|
||||||
string get_cgroup2_mount_point(int &status);
|
string get_cgroup2_mount_point();
|
||||||
int attach(const string pid, const string cgroup_target);
|
int attach(const string pid, const string cgroup_target);
|
||||||
int attach(const int pid, const string cgroup_target);
|
int attach(const int pid, const string cgroup_target);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
bool enable_debug = false;
|
bool enable_debug = false;
|
||||||
|
bool enable_info = true;
|
||||||
|
|
||||||
string join2str(const vector<string> t, const char delm) {
|
string join2str(const vector<string> t, const char delm) {
|
||||||
string s;
|
string s;
|
||||||
@@ -32,3 +38,58 @@ bool validCgroup(const vector<string> cgroup) {
|
|||||||
bool validPid(const string pid) { return regex_match(pid, regex("^[0-9]+$")); }
|
bool validPid(const string pid) { return regex_match(pid, regex("^[0-9]+$")); }
|
||||||
|
|
||||||
bool validPort(const int port) { return port > 0; }
|
bool validPort(const int port) { return port > 0; }
|
||||||
|
|
||||||
|
bool fileExist(const string &path) {
|
||||||
|
struct stat st;
|
||||||
|
return (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dirExist(const string &path) {
|
||||||
|
struct stat st;
|
||||||
|
return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<int> bash_pidof(const string &path) {
|
||||||
|
vector<int> pids;
|
||||||
|
FILE *fp = popen(to_str("pidof ", path).c_str(), "r");
|
||||||
|
if (!fp) return pids;
|
||||||
|
int pid;
|
||||||
|
char buf[64];
|
||||||
|
while (fscanf(fp, "%d", &pid) != EOF) { pids.push_back(pid); }
|
||||||
|
pclose(fp);
|
||||||
|
return pids;
|
||||||
|
}
|
||||||
|
|
||||||
|
string bash_which(const string &name) {
|
||||||
|
stringstream buffer;
|
||||||
|
FILE *fp = popen(to_str("which ", name).c_str(), "r");
|
||||||
|
if (!fp) return "";
|
||||||
|
char buf[64];
|
||||||
|
while (fgets(buf, 64, fp) != NULL) { buffer << buf; }
|
||||||
|
pclose(fp);
|
||||||
|
string s = buffer.str();
|
||||||
|
s.pop_back(); // remove newline character
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string bash_readlink(const string &path) {
|
||||||
|
stringstream buffer;
|
||||||
|
FILE *fp = popen(to_str("readlink -e ", path).c_str(), "r");
|
||||||
|
if (!fp) return "";
|
||||||
|
char buf[64];
|
||||||
|
while (fgets(buf, 64, fp) != NULL) { buffer << buf; }
|
||||||
|
pclose(fp);
|
||||||
|
string s = buffer.str();
|
||||||
|
s.pop_back(); // remove newline character
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string getRealExistPath(const string &name) {
|
||||||
|
if (name[0] == '/' && fileExist(name)) return name;
|
||||||
|
string path;
|
||||||
|
path = bash_which(name);
|
||||||
|
if (path.empty()) return "";
|
||||||
|
path = bash_readlink(path);
|
||||||
|
if (!fileExist(path)) return "";
|
||||||
|
return path;
|
||||||
|
}
|
||||||
25
src/common.h
25
src/common.h
@@ -9,8 +9,8 @@ using namespace std;
|
|||||||
|
|
||||||
#define TPROXY_IPTABLS_START "/usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
#define TPROXY_IPTABLS_START "/usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
||||||
#define TPROXY_IPTABLS_CLEAN "/usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
#define TPROXY_IPTABLS_CLEAN "/usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
||||||
#define BPF_EXEC_SNOOP_START "/usr/share/cgproxy/scripts/execsnoop.py"
|
|
||||||
|
|
||||||
|
#define LIBEXECSNOOP_SO "/usr/lib/cgproxy/libexecsnoop.so"
|
||||||
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
||||||
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
||||||
#define LISTEN_BACKLOG 64
|
#define LISTEN_BACKLOG 64
|
||||||
@@ -19,6 +19,8 @@ using namespace std;
|
|||||||
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
||||||
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
||||||
|
|
||||||
|
#define THREAD_UNDEF 0
|
||||||
|
|
||||||
#define MSG_TYPE_CONFIG_JSON 1
|
#define MSG_TYPE_CONFIG_JSON 1
|
||||||
#define MSG_TYPE_CONFIG_PATH 2
|
#define MSG_TYPE_CONFIG_PATH 2
|
||||||
#define MSG_TYPE_PROXY_PID 3
|
#define MSG_TYPE_PROXY_PID 3
|
||||||
@@ -36,6 +38,7 @@ using namespace std;
|
|||||||
#define FILE_ERROR 7
|
#define FILE_ERROR 7
|
||||||
|
|
||||||
extern bool enable_debug;
|
extern bool enable_debug;
|
||||||
|
extern bool enable_info;
|
||||||
|
|
||||||
#define error(...) \
|
#define error(...) \
|
||||||
{ \
|
{ \
|
||||||
@@ -46,13 +49,20 @@ extern bool enable_debug;
|
|||||||
|
|
||||||
#define debug(...) \
|
#define debug(...) \
|
||||||
if (enable_debug) { \
|
if (enable_debug) { \
|
||||||
fprintf(stderr, "debug: "); \
|
fprintf(stdout, "debug: "); \
|
||||||
fprintf(stdout, __VA_ARGS__); \
|
fprintf(stdout, __VA_ARGS__); \
|
||||||
fprintf(stdout, "\n"); \
|
fprintf(stdout, "\n"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define return_error return -1;
|
#define info(...) \
|
||||||
#define return_success return 0;
|
if (enable_info) { \
|
||||||
|
fprintf(stdout, "info: "); \
|
||||||
|
fprintf(stdout, __VA_ARGS__); \
|
||||||
|
fprintf(stdout, "\n"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define return_error return -1
|
||||||
|
#define return_success return 0
|
||||||
|
|
||||||
template <typename... T> string to_str(T... args) {
|
template <typename... T> string to_str(T... args) {
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
@@ -70,4 +80,11 @@ bool validCgroup(const vector<string> cgroup);
|
|||||||
bool validPid(const string pid);
|
bool validPid(const string pid);
|
||||||
bool validPort(const int port);
|
bool validPort(const int port);
|
||||||
|
|
||||||
|
bool fileExist(const string &path);
|
||||||
|
bool dirExist(const string &path);
|
||||||
|
vector<int> bash_pidof(const string &path);
|
||||||
|
string bash_which(const string &name);
|
||||||
|
string bash_readlink(const string &path);
|
||||||
|
string getRealExistPath(const string &name);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
#define add2json(v) j[#v] = v;
|
#define add2json(v) j[#v] = v;
|
||||||
@@ -89,6 +90,11 @@ int Config::loadFromJsonStr(const string js) {
|
|||||||
tryassign(enable_udp);
|
tryassign(enable_udp);
|
||||||
tryassign(enable_ipv4);
|
tryassign(enable_ipv4);
|
||||||
tryassign(enable_ipv6);
|
tryassign(enable_ipv6);
|
||||||
|
|
||||||
|
// e.g. v2ray -> /usr/bin/v2ray -> /usr/lib/v2ray/v2ray
|
||||||
|
toRealProgramPath(program_noproxy);
|
||||||
|
toRealProgramPath(program_proxy);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,4 +133,22 @@ bool Config::validateJsonStr(const string js) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::print_summary() {
|
||||||
|
info("noproxy program: %s", join2str(program_noproxy).c_str());
|
||||||
|
info("proxied program: %s", join2str(program_proxy).c_str());
|
||||||
|
info("noproxy cgroup: %s", join2str(cgroup_noproxy).c_str());
|
||||||
|
info("proxied cgroup: %s", join2str(cgroup_proxy).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::toRealProgramPath(vector<string> &v) {
|
||||||
|
vector<string> tmp;
|
||||||
|
for (auto &p : v) {
|
||||||
|
auto rpath = getRealExistPath(p);
|
||||||
|
if (!rpath.empty()) tmp.push_back(rpath);
|
||||||
|
else
|
||||||
|
error("%s not exist or broken link", p.c_str());
|
||||||
|
}
|
||||||
|
v = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CGPROXY::CONFIG
|
} // namespace CGPROXY::CONFIG
|
||||||
@@ -30,10 +30,12 @@ public:
|
|||||||
string toJsonStr();
|
string toJsonStr();
|
||||||
int loadFromFile(const string f);
|
int loadFromFile(const string f);
|
||||||
int loadFromJsonStr(const string js);
|
int loadFromJsonStr(const string js);
|
||||||
|
void print_summary();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void mergeReserved();
|
void mergeReserved();
|
||||||
bool validateJsonStr(const string js);
|
bool validateJsonStr(const string js);
|
||||||
|
void toRealProgramPath(vector<string> &v);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace CGPROXY::CONFIG
|
} // namespace CGPROXY::CONFIG
|
||||||
|
|||||||
102
src/execsnoop.cpp
Normal file
102
src/execsnoop.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#include "execsnoop.h"
|
||||||
|
#include "bcc/BPF.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <bcc/libbpf.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::EXECSNOOP {
|
||||||
|
|
||||||
|
const string BPF_PROGRAM = R"(
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <uapi/linux/ptrace.h>
|
||||||
|
|
||||||
|
struct data_t {
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
BPF_PERF_OUTPUT(events);
|
||||||
|
|
||||||
|
int syscall_execve(struct pt_regs *ctx,
|
||||||
|
const char __user *filename,
|
||||||
|
const char __user *const __user *__argv,
|
||||||
|
const char __user *const __user *__envp)
|
||||||
|
{
|
||||||
|
struct data_t data = {};
|
||||||
|
data.pid = bpf_get_current_pid_tgid();
|
||||||
|
events.perf_submit(ctx, &data, sizeof(struct data_t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret_syscall_execve(struct pt_regs *ctx){
|
||||||
|
struct data_t data = {};
|
||||||
|
data.pid = bpf_get_current_pid_tgid();
|
||||||
|
int retval = PT_REGS_RC(ctx);
|
||||||
|
if (retval==0)
|
||||||
|
events.perf_submit(ctx, &data, sizeof(struct data_t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
struct data_t {
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
function<int(int)> callback = NULL;
|
||||||
|
|
||||||
|
void handle_events(void *cb_cookie, void *data, int data_size) {
|
||||||
|
auto event = static_cast<data_t *>(data);
|
||||||
|
int pid = event->pid;
|
||||||
|
|
||||||
|
if (callback) callback(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int execsnoop() {
|
||||||
|
debug("starting execsnoop");
|
||||||
|
ebpf::BPF bpf;
|
||||||
|
auto init_res = bpf.init(BPF_PROGRAM);
|
||||||
|
if (init_res.code() != 0) {
|
||||||
|
std::cerr << init_res.msg() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string execve_fnname = bpf.get_syscall_fnname("execve");
|
||||||
|
// auto attach_res = bpf.attach_kprobe(execve_fnname, "syscall_execve");
|
||||||
|
auto attach_res =
|
||||||
|
bpf.attach_kprobe(execve_fnname, "ret_syscall_execve", 0, BPF_PROBE_RETURN);
|
||||||
|
if (attach_res.code() != 0) {
|
||||||
|
std::cerr << attach_res.msg() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto open_res = bpf.open_perf_buffer("events", &handle_events);
|
||||||
|
if (open_res.code() != 0) {
|
||||||
|
std::cerr << open_res.msg() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpf.free_bcc_memory()) {
|
||||||
|
std::cerr << "Failed to free llvm/clang memory" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) bpf.poll_perf_buffer("events");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *startThread(void *arg) {
|
||||||
|
thread_arg *p = (thread_arg *)arg;
|
||||||
|
callback = p->handle_pid;
|
||||||
|
execsnoop();
|
||||||
|
return (void *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CGPROXY::EXECSNOOP
|
||||||
|
|
||||||
|
extern "C" void *_startThread(void *arg) { return CGPROXY::EXECSNOOP::startThread(arg); }
|
||||||
22
src/execsnoop.h
Normal file
22
src/execsnoop.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef EXECSNOOP_HPP
|
||||||
|
#define EXECSNOOP_HPP 1
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::EXECSNOOP {
|
||||||
|
|
||||||
|
extern const string BPF_PROGRAM;
|
||||||
|
struct data_t;
|
||||||
|
extern function<int(int)> callback;
|
||||||
|
void handle_events(void *cb_cookie, void *data, int data_size);
|
||||||
|
int execsnoop();
|
||||||
|
|
||||||
|
struct thread_arg {
|
||||||
|
function<int(int)> handle_pid;
|
||||||
|
};
|
||||||
|
void *startThread(void *arg);
|
||||||
|
|
||||||
|
} // namespace CGPROXY::EXECSNOOP
|
||||||
|
#endif
|
||||||
@@ -49,7 +49,7 @@ void SocketServer::socketListening(function<int(char *)> callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *SocketServer::startThread(void *arg) {
|
void *startThread(void *arg) {
|
||||||
thread_arg *p = (thread_arg *)arg;
|
thread_arg *p = (thread_arg *)arg;
|
||||||
SocketServer server;
|
SocketServer server;
|
||||||
server.socketListening(p->handle_msg);
|
server.socketListening(p->handle_msg);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace CGPROXY::SOCKET {
|
|||||||
struct thread_arg {
|
struct thread_arg {
|
||||||
function<int(char *)> handle_msg;
|
function<int(char *)> handle_msg;
|
||||||
};
|
};
|
||||||
|
void *startThread(void *arg);
|
||||||
|
|
||||||
class SocketServer {
|
class SocketServer {
|
||||||
public:
|
public:
|
||||||
@@ -25,7 +26,6 @@ public:
|
|||||||
|
|
||||||
void socketListening(function<int(char *)> callback);
|
void socketListening(function<int(char *)> callback);
|
||||||
~SocketServer();
|
~SocketServer();
|
||||||
static void *startThread(void *arg);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace CGPROXY::SOCKET
|
} // namespace CGPROXY::SOCKET
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
include_directories(${PROJECT_SOURCE_DIR})
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
|
||||||
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
||||||
|
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
|
||||||
|
if (with_execsnoop)
|
||||||
|
add_executable(execsnoop_exec execsnoop.cpp ../src/common.cpp ../src/execsnoop.cpp)
|
||||||
|
set_target_properties(execsnoop_exec PROPERTIES OUTPUT_NAME execsnoop)
|
||||||
|
target_link_libraries(execsnoop_exec bcc)
|
||||||
|
install(TARGETS execsnoop_exec DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
endif()
|
||||||
24
tools/execsnoop.cpp
Normal file
24
tools/execsnoop.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "execsnoop.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
using namespace std;
|
||||||
|
using namespace CGPROXY::EXECSNOOP;
|
||||||
|
|
||||||
|
#define PATH_MAX_LEN 128
|
||||||
|
|
||||||
|
int handle_pid(int pid) {
|
||||||
|
char path[PATH_MAX_LEN];
|
||||||
|
auto size = readlink(to_str("/proc/", pid, "/exe").c_str(), path, PATH_MAX_LEN);
|
||||||
|
if (size == -1) error("readlink: %s", to_str("/proc/", pid, "/exe").c_str());
|
||||||
|
path[size] = '\0';
|
||||||
|
info("%d %s", pid, path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
enable_debug = true;
|
||||||
|
enable_info = true;
|
||||||
|
callback = handle_pid;
|
||||||
|
execsnoop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user