mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-01-07 13:07:56 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06da477373 | ||
|
|
314ff64b8b | ||
|
|
d1bf9620bf | ||
|
|
b7ea8e441b | ||
|
|
b031aa8064 | ||
|
|
8226db37e0 | ||
|
|
5bbec4d1bf | ||
|
|
9b4ec44897 | ||
|
|
a1a1ed5bd9 | ||
|
|
34e5d81329 | ||
|
|
dfc688b5e5 | ||
|
|
ec9609cec8 | ||
|
|
cc83c1ae55 | ||
|
|
75751f4887 | ||
|
|
af78ad2012 | ||
|
|
c32457a1aa | ||
|
|
1d29828d1b | ||
|
|
4fea0d39a2 | ||
|
|
badf282842 | ||
|
|
41f856acd2 | ||
|
|
45adf0a233 | ||
|
|
1f4dd2fde2 |
@@ -2,8 +2,11 @@ cmake_minimum_required(VERSION 3.10)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
project(cgproxy VERSION 0.14)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result")
|
||||
project(cgproxy VERSION 0.15)
|
||||
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter)
|
||||
|
||||
# for clangd
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
|
||||
option(build_tools OFF)
|
||||
|
||||
@@ -31,7 +31,7 @@ DOC
|
||||
}
|
||||
|
||||
## check root
|
||||
[ ! $(id -u) -eq 0 ] && { >&2 echo "need root to modify iptables";exit -1; }
|
||||
[ ! $(id -u) -eq 0 ] && { >&2 echo "iptables: need root to modify iptables";exit -1; }
|
||||
|
||||
## any process in this cgroup will be proxied
|
||||
if [ -z ${cgroup_proxy+x} ]; then
|
||||
@@ -73,7 +73,7 @@ cgroup_procs_file="cgroup.procs"
|
||||
|
||||
stop(){
|
||||
iptables -t mangle -L TPROXY_PRE &> /dev/null || return
|
||||
echo "cleaning tproxy iptables"
|
||||
echo "iptables: cleaning tproxy iptables"
|
||||
iptables -t mangle -D PREROUTING -j TPROXY_PRE
|
||||
iptables -t mangle -D OUTPUT -j TPROXY_OUT
|
||||
iptables -t mangle -F TPROXY_PRE
|
||||
@@ -124,7 +124,7 @@ test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_
|
||||
test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1;
|
||||
|
||||
|
||||
echo "applying tproxy iptables"
|
||||
echo "iptables: applying tproxy iptables"
|
||||
## use TPROXY
|
||||
#ipv4#
|
||||
ip rule add fwmark $fwmark table $table
|
||||
@@ -150,10 +150,10 @@ iptables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -m addrtype --dst-type LOCAL -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
|
||||
for cg in ${cgroup_noproxy[@]}; do
|
||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||
done
|
||||
for cg in ${cgroup_proxy[@]}; do
|
||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark
|
||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||
done
|
||||
iptables -t mangle -A OUTPUT -j TPROXY_OUT
|
||||
|
||||
@@ -181,10 +181,10 @@ ip6tables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -m addrtype --dst-type LOCAL -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
|
||||
for cg in ${cgroup_noproxy[@]}; do
|
||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||
done
|
||||
for cg in ${cgroup_proxy[@]}; do
|
||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark
|
||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark || { >&2 echo "iptables: $cg not exist, won't apply"; }
|
||||
done
|
||||
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
|
||||
|
||||
@@ -220,8 +220,8 @@ ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --
|
||||
|
||||
## message for user
|
||||
cat << DOC
|
||||
noproxy cgroup: ${cgroup_noproxy[@]}
|
||||
proxied cgroup: ${cgroup_proxy[@]}
|
||||
iptables: noproxy cgroup: ${cgroup_noproxy[@]}
|
||||
iptables: proxied cgroup: ${cgroup_proxy[@]}
|
||||
DOC
|
||||
|
||||
|
||||
@@ -230,5 +230,5 @@ if $enable_gateway; then
|
||||
ip6tables -t nat -A POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE # only masquerade ipv6 private address
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1
|
||||
echo "gateway enabled"
|
||||
echo "ipatbles: gateway enabled"
|
||||
fi
|
||||
|
||||
@@ -8,14 +8,14 @@ cgproxyd [--help] [--debug] [--execsnoop]
|
||||
cgproxyd = cgproxy --daemon
|
||||
.SH OPTIONS
|
||||
.B --execsnoop
|
||||
enable execsnoop to support program level proxy, need python-bcc installed to actually work
|
||||
enable execsnoop to support program level proxy, need bcc installed to actually work
|
||||
.SH CONFIGURATION
|
||||
.I /etc/cgproxy/config.json
|
||||
.br
|
||||
.B port
|
||||
tproxy listenning port
|
||||
.br
|
||||
program level proxy controll, need `python-bcc` installed to work:
|
||||
program level proxy controll, need `bcc` installed to work:
|
||||
.br
|
||||
.RS
|
||||
.B program_proxy
|
||||
|
||||
@@ -7,7 +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_SUGGESTS "libbpfcc")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
|
||||
@@ -17,7 +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_SUGGESTS "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")
|
||||
|
||||
@@ -109,7 +109,7 @@ Config file: **/etc/cgproxy/config.json**
|
||||
|
||||
- **port** tproxy listenning port
|
||||
|
||||
- program level proxy control, need `python-bcc` installed to work
|
||||
- program level proxy control, need `bcc` and `linux-headers` installed to work
|
||||
|
||||
- **program_proxy** program need to be proxied
|
||||
- **program_noproxy** program that won't be proxied
|
||||
|
||||
@@ -12,7 +12,7 @@ endif()
|
||||
add_executable(main main.cpp
|
||||
common.cpp config.cpp cgroup_attach.cpp
|
||||
socket_client.cpp socket_server.cpp)
|
||||
target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json Threads::Threads dl)
|
||||
target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json Threads::Threads ${CMAKE_DL_LIBS})
|
||||
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <unistd.h>
|
||||
using json = nlohmann::json;
|
||||
using namespace CGPROXY;
|
||||
using namespace CGPROXY::CONFIG;
|
||||
using namespace ::CGPROXY;
|
||||
using namespace ::CGPROXY::CONFIG;
|
||||
|
||||
namespace CGPROXY::CGPROXY {
|
||||
|
||||
@@ -81,4 +81,4 @@ int main(int argc, char *argv[]) {
|
||||
string s = join2str(argc - shift, argv + shift, ' ');
|
||||
return system(s.c_str());
|
||||
}
|
||||
} // namespace CGPROXY::CGPROXY
|
||||
} // namespace CGPROXY::CGPROXY
|
||||
|
||||
188
src/cgproxyd.hpp
188
src/cgproxyd.hpp
@@ -13,8 +13,8 @@
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
@@ -27,8 +27,6 @@ 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);
|
||||
@@ -37,14 +35,17 @@ bool loadExecsnoopLib() {
|
||||
error("dlopen %s failed: %s", LIBEXECSNOOP_SO, dlerror());
|
||||
return false;
|
||||
}
|
||||
_startThread = reinterpret_cast<startThread_t>(dlsym(handle_dl, "_startThread"));
|
||||
_startThread = reinterpret_cast<startThread_t *>(dlsym(handle_dl, "startThread"));
|
||||
if (_startThread == NULL) {
|
||||
error("dlsym startThread failed: %s", dlerror());
|
||||
error("dlsym startThread func failed: %s", dlerror());
|
||||
return false;
|
||||
}
|
||||
info("dlsym startThread success");
|
||||
info("dlsym startThread func success");
|
||||
return true;
|
||||
} catch (exception &e) { return false; }
|
||||
} catch (exception &e) {
|
||||
debug("exception: %s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
|
||||
@@ -55,11 +56,8 @@ bool enable_socketserver = true;
|
||||
bool enable_execsnoop = false;
|
||||
|
||||
class cgproxyd {
|
||||
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;
|
||||
thread socketserver_thread;
|
||||
thread execsnoop_thread;
|
||||
|
||||
Config config;
|
||||
|
||||
@@ -81,28 +79,53 @@ class cgproxyd {
|
||||
}
|
||||
|
||||
int handle_pid(int pid) {
|
||||
auto path = realpath(to_str("/proc/", pid, "/exe").c_str(), NULL);
|
||||
unique_ptr<char[], decltype(&free)> path(
|
||||
realpath(to_str("/proc/", pid, "/exe").c_str(), NULL), &free);
|
||||
if (path == NULL) {
|
||||
debug("pid %d live life too short", pid);
|
||||
debug("execsnoop: pid %d live life too short", pid);
|
||||
return 0;
|
||||
}
|
||||
debug("execsnoop: %d %s", pid, path);
|
||||
debug("execsnoop: %d %s", pid, path.get());
|
||||
|
||||
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);
|
||||
if (find(v.begin(), v.end(), path.get()) != v.end()) {
|
||||
string cg = getCgroup(pid);
|
||||
if (cg.empty()) {
|
||||
debug("execsnoop: cgroup get failed, ignore: %d %s", pid, path.get());
|
||||
return 0;
|
||||
}
|
||||
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
|
||||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
|
||||
info("execsnoop: already in preserverd cgroup, leave alone: %d %s", pid,
|
||||
path.get());
|
||||
return 0;
|
||||
}
|
||||
if (!belongToCgroup(cg, config.cgroup_noproxy)) {
|
||||
info("execsnoop; noproxy: %d %s", pid, path.get());
|
||||
return attach(pid, config.cgroup_noproxy_preserved);
|
||||
}
|
||||
}
|
||||
|
||||
v = config.program_proxy;
|
||||
if (find(v.begin(), v.end(), path) != v.end()) {
|
||||
info("execsnoop proxied: %d %s", pid, path);
|
||||
free(path);
|
||||
return attach(pid, config.cgroup_proxy_preserved);
|
||||
if (find(v.begin(), v.end(), path.get()) != v.end()) {
|
||||
string cg = getCgroup(pid);
|
||||
if (cg.empty()) {
|
||||
debug("execsnoop: cgroup get failed, ignore: %d %s", pid, path.get());
|
||||
return 0;
|
||||
}
|
||||
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
|
||||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
|
||||
info("execsnoop: already in preserverd cgroup, leave alone: %d %s", pid,
|
||||
path.get());
|
||||
return 0;
|
||||
}
|
||||
if (!belongToCgroup(cg, config.cgroup_proxy)) {
|
||||
info("execsnoop: proxied: %d %s", pid, path.get());
|
||||
return attach(pid, config.cgroup_proxy_preserved);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -146,78 +169,113 @@ class cgproxyd {
|
||||
return MSG_ERROR;
|
||||
}
|
||||
|
||||
int type, status;
|
||||
int pid, cgroup_target;
|
||||
int type, status, pid;
|
||||
try {
|
||||
type = j.at("type").get<int>();
|
||||
switch (type) {
|
||||
case MSG_TYPE_CONFIG_JSON:
|
||||
status = config.loadFromJsonStr(j.at("data").dump());
|
||||
info("process received config json msg");
|
||||
if (status == SUCCESS) status = applyConfig();
|
||||
return status;
|
||||
break;
|
||||
case MSG_TYPE_CONFIG_PATH:
|
||||
status = config.loadFromFile(j.at("data").get<string>());
|
||||
info("process received config path msg");
|
||||
if (status == SUCCESS) status = applyConfig();
|
||||
return status;
|
||||
break;
|
||||
case MSG_TYPE_PROXY_PID:
|
||||
pid = j.at("data").get<int>();
|
||||
info("process proxy pid msg: %d", pid);
|
||||
status = attach(pid, config.cgroup_proxy_preserved);
|
||||
return status;
|
||||
break;
|
||||
case MSG_TYPE_NOPROXY_PID:
|
||||
pid = j.at("data").get<int>();
|
||||
info("process noproxy pid msg: %d", pid);
|
||||
status = attach(pid, config.cgroup_noproxy_preserved);
|
||||
return status;
|
||||
break;
|
||||
default: return MSG_ERROR; break;
|
||||
default:
|
||||
error("unknown msg");
|
||||
return MSG_ERROR;
|
||||
break;
|
||||
};
|
||||
} catch (out_of_range &e) { return MSG_ERROR; } catch (exception &e) {
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_t startSocketListeningThread() {
|
||||
socketserver_thread_arg.handle_msg = &handle_msg_static;
|
||||
pthread_t thread_id;
|
||||
int status =
|
||||
pthread_create(&thread_id, NULL, &SOCKET::startThread, &socketserver_thread_arg);
|
||||
if (status != 0) {
|
||||
error("socket thread create failed");
|
||||
return THREAD_UNDEF;
|
||||
void startSocketListeningThread() {
|
||||
promise<void> status;
|
||||
future<void> status_f = status.get_future();
|
||||
thread th(SOCKET::startThread, handle_msg_static, move(status));
|
||||
socketserver_thread = move(th);
|
||||
|
||||
future_status fstatus = status_f.wait_for(chrono::seconds(THREAD_TIMEOUT));
|
||||
if (fstatus == std::future_status::ready) {
|
||||
info("socketserver thread started");
|
||||
} else {
|
||||
error("socketserver thread timeout, maybe failed");
|
||||
}
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
pthread_t startExecsnoopThread() {
|
||||
void startExecsnoopThread() {
|
||||
if (!EXECSNOOP::loadExecsnoopLib() || EXECSNOOP::_startThread == NULL) {
|
||||
error("execsnoop start failed, maybe bcc not installed");
|
||||
return THREAD_UNDEF;
|
||||
error("execsnoop not ready to start, maybe bcc not installed");
|
||||
return;
|
||||
}
|
||||
|
||||
execsnoop_thread_arg.handle_pid = &handle_pid_static;
|
||||
pthread_t thread_id;
|
||||
int status =
|
||||
pthread_create(&thread_id, NULL, EXECSNOOP::_startThread, &execsnoop_thread_arg);
|
||||
if (status != 0) {
|
||||
error("execsnoop thread create failed");
|
||||
return THREAD_UNDEF;
|
||||
promise<void> status;
|
||||
future<void> status_f = status.get_future();
|
||||
thread th(EXECSNOOP::_startThread, handle_pid_static, move(status));
|
||||
execsnoop_thread = move(th);
|
||||
|
||||
future_status fstatus = status_f.wait_for(chrono::seconds(THREAD_TIMEOUT));
|
||||
if (fstatus == std::future_status::ready) {
|
||||
info("execsnoop thread started");
|
||||
processRunningProgram();
|
||||
} else {
|
||||
error("execsnoop thread timeout, maybe failed");
|
||||
}
|
||||
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());
|
||||
}
|
||||
debug("process running program");
|
||||
for (auto &path : config.program_noproxy)
|
||||
for (auto &pid : bash_pidof(path)) {
|
||||
string cg = getCgroup(pid);
|
||||
if (cg.empty()) {
|
||||
debug("cgroup get failed, ignore: %d %s", pid, path.c_str());
|
||||
continue;
|
||||
}
|
||||
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
|
||||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
|
||||
debug("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
|
||||
continue;
|
||||
}
|
||||
if (!belongToCgroup(cg, config.cgroup_noproxy)) {
|
||||
int status = attach(pid, config.cgroup_noproxy_preserved);
|
||||
if (status == 0) info("noproxy running process %d %s", pid, path.c_str());
|
||||
}
|
||||
}
|
||||
for (auto &path : config.program_proxy)
|
||||
for (auto &pid : bash_pidof(path)) {
|
||||
int status = attach(pid, config.cgroup_proxy_preserved);
|
||||
if (status == 0) info("proxied running process %d %s", pid, path.c_str());
|
||||
string cg = getCgroup(pid);
|
||||
if (cg.empty()) {
|
||||
debug("cgroup get failed, ignore: %d %s", pid, path.c_str());
|
||||
continue;
|
||||
}
|
||||
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
|
||||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
|
||||
debug("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
|
||||
continue;
|
||||
}
|
||||
if (!belongToCgroup(cg, config.cgroup_proxy)) {
|
||||
int status = attach(pid, config.cgroup_proxy_preserved);
|
||||
if (status == 0) info("proxied running process %d %s", pid, path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,20 +292,13 @@ public:
|
||||
|
||||
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;
|
||||
if (enable_socketserver) startSocketListeningThread();
|
||||
if (enable_execsnoop) startExecsnoopThread();
|
||||
|
||||
if (socketserver_thread.joinable()) socketserver_thread.join();
|
||||
if (execsnoop_thread.joinable()) execsnoop_thread.join();
|
||||
|
||||
pthread_join(socket_thread_id, NULL);
|
||||
pthread_join(execsnoop_thread_id, NULL);
|
||||
return 0;
|
||||
}
|
||||
int applyConfig() {
|
||||
@@ -286,7 +337,6 @@ void processArgs(const int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
setbuf(stdout, NULL);
|
||||
processArgs(argc, argv);
|
||||
if (print_help) {
|
||||
print_usage();
|
||||
@@ -302,4 +352,4 @@ int main(int argc, char *argv[]) {
|
||||
return d.start();
|
||||
}
|
||||
} // namespace CGPROXY::CGPROXYD
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -15,19 +15,13 @@ namespace CGPROXY::CGROUP {
|
||||
|
||||
string cgroup2_mount_point = get_cgroup2_mount_point();
|
||||
|
||||
bool exist(string path) {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != -1) { return S_ISDIR(st.st_mode); }
|
||||
return false;
|
||||
}
|
||||
|
||||
string get_cgroup2_mount_point() {
|
||||
stringstream buffer;
|
||||
FILE *fp = popen("findmnt -t cgroup2 -n -o TARGET", "r");
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen("findmnt -t cgroup2 -n -o TARGET", "r"),
|
||||
&pclose);
|
||||
if (!fp) return "";
|
||||
char buf[64];
|
||||
while (fgets(buf, 64, fp) != NULL) { buffer << buf; }
|
||||
pclose(fp);
|
||||
char buf[READ_SIZE_MAX];
|
||||
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||
string s = buffer.str();
|
||||
s.pop_back(); // remove newline character
|
||||
return s;
|
||||
@@ -50,14 +44,13 @@ int attach(const string pid, const string cgroup_target) {
|
||||
|
||||
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
||||
|
||||
int status;
|
||||
if (!validate(pid, cgroup_target)) return_error;
|
||||
if (cgroup2_mount_point.empty()) return_error;
|
||||
string cgroup_target_path = cgroup2_mount_point + cgroup_target;
|
||||
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
||||
|
||||
// check if exist, we will create it if not exist
|
||||
if (!exist(cgroup_target_path)) {
|
||||
if (!dirExist(cgroup_target_path)) {
|
||||
if (mkdir(cgroup_target_path.c_str(),
|
||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) {
|
||||
debug("created cgroup %s success", cgroup_target.c_str());
|
||||
@@ -69,6 +62,11 @@ int attach(const string pid, const string cgroup_target) {
|
||||
// return_error
|
||||
}
|
||||
|
||||
if (getCgroup(pid) == cgroup_target) {
|
||||
debug("%s already in %s", pid.c_str(), cgroup_target.c_str());
|
||||
return_success;
|
||||
}
|
||||
|
||||
// put pid to target cgroup
|
||||
ofstream procs(cgroup_target_procs, ofstream::app);
|
||||
if (!procs.is_open()) {
|
||||
@@ -91,4 +89,4 @@ int attach(const int pid, const string cgroup_target) {
|
||||
return attach(to_str(pid), cgroup_target);
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::CGROUP
|
||||
} // namespace CGPROXY::CGROUP
|
||||
|
||||
@@ -7,7 +7,6 @@ using namespace std;
|
||||
|
||||
namespace CGPROXY::CGROUP {
|
||||
extern string cgroup2_mount_point;
|
||||
bool exist(string path);
|
||||
bool validate(string pid, string cgroup);
|
||||
string get_cgroup2_mount_point();
|
||||
int attach(const string pid, const string cgroup_target);
|
||||
@@ -15,4 +14,4 @@ int attach(const int pid, const string cgroup_target);
|
||||
|
||||
} // namespace CGPROXY::CGROUP
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "common.h"
|
||||
#include <fstream>
|
||||
#include <limits.h>
|
||||
#include <linux/limits.h>
|
||||
#include <regex>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
@@ -24,6 +22,8 @@ string join2str(const int argc, char **argv, const char delm) {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool startWith(string s, string prefix) { return s.rfind(prefix, 0) == 0; }
|
||||
|
||||
bool validCgroup(const string cgroup) {
|
||||
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
||||
}
|
||||
@@ -51,22 +51,21 @@ bool dirExist(const string &path) {
|
||||
|
||||
vector<int> bash_pidof(const string &path) {
|
||||
vector<int> pids;
|
||||
FILE *fp = popen(to_str("pidof ", path).c_str(), "r");
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("pidof ", path).c_str(), "r"),
|
||||
&pclose);
|
||||
if (!fp) return pids;
|
||||
int pid;
|
||||
char buf[64];
|
||||
while (fscanf(fp, "%d", &pid) != EOF) { pids.push_back(pid); }
|
||||
pclose(fp);
|
||||
while (fscanf(fp.get(), "%d", &pid) != EOF) { pids.push_back(pid); }
|
||||
return pids;
|
||||
}
|
||||
|
||||
string bash_which(const string &name) {
|
||||
stringstream buffer;
|
||||
FILE *fp = popen(to_str("which ", name).c_str(), "r");
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("which ", name).c_str(), "r"),
|
||||
&pclose);
|
||||
if (!fp) return "";
|
||||
char buf[64];
|
||||
while (fgets(buf, 64, fp) != NULL) { buffer << buf; }
|
||||
pclose(fp);
|
||||
char buf[READ_SIZE_MAX];
|
||||
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||
string s = buffer.str();
|
||||
s.pop_back(); // remove newline character
|
||||
return s;
|
||||
@@ -74,11 +73,11 @@ string bash_which(const string &name) {
|
||||
|
||||
string bash_readlink(const string &path) {
|
||||
stringstream buffer;
|
||||
FILE *fp = popen(to_str("readlink -e ", path).c_str(), "r");
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("readlink -e ", path).c_str(), "r"),
|
||||
&pclose);
|
||||
if (!fp) return "";
|
||||
char buf[64];
|
||||
while (fgets(buf, 64, fp) != NULL) { buffer << buf; }
|
||||
pclose(fp);
|
||||
char buf[READ_SIZE_MAX];
|
||||
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||
string s = buffer.str();
|
||||
s.pop_back(); // remove newline character
|
||||
return s;
|
||||
@@ -92,4 +91,34 @@ string getRealExistPath(const string &name) {
|
||||
path = bash_readlink(path);
|
||||
if (!fileExist(path)) return "";
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
bool belongToCgroup(string cg1, string cg2) { return startWith(cg1 + '/', cg2 + '/'); }
|
||||
|
||||
bool belongToCgroup(string cg1, vector<string> cg2) {
|
||||
for (const auto &s : cg2) {
|
||||
if (startWith(cg1 + '/', s + '/')) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
string getCgroup(const pid_t &pid) { return getCgroup(to_str(pid)); }
|
||||
|
||||
string getCgroup(const string &pid) {
|
||||
string cgroup_f = to_str("/proc/", pid, "/cgroup");
|
||||
if (!fileExist(cgroup_f)) return "";
|
||||
|
||||
string cgroup, line;
|
||||
ifstream ifs(cgroup_f);
|
||||
debug("prcessing file %s", cgroup_f.c_str());
|
||||
while (ifs.good() && getline(ifs, line)) {
|
||||
debug("process line: %s", line.c_str());
|
||||
if (line[0] == '0') {
|
||||
cgroup = line.substr(3);
|
||||
debug("get cgroup of %s: %s", pid.c_str(), cgroup.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
ifs.close();
|
||||
return cgroup;
|
||||
}
|
||||
|
||||
17
src/common.h
17
src/common.h
@@ -15,11 +15,12 @@ using namespace std;
|
||||
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
||||
#define LISTEN_BACKLOG 64
|
||||
#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json"
|
||||
#define READ_SIZE_MAX 128
|
||||
|
||||
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
||||
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
||||
|
||||
#define THREAD_UNDEF 0
|
||||
#define THREAD_TIMEOUT 5
|
||||
|
||||
#define MSG_TYPE_CONFIG_JSON 1
|
||||
#define MSG_TYPE_CONFIG_PATH 2
|
||||
@@ -45,6 +46,7 @@ extern bool enable_info;
|
||||
fprintf(stderr, "error: "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
#define debug(...) \
|
||||
@@ -52,6 +54,7 @@ extern bool enable_info;
|
||||
fprintf(stdout, "debug: "); \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
fprintf(stdout, "\n"); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define info(...) \
|
||||
@@ -59,6 +62,7 @@ extern bool enable_info;
|
||||
fprintf(stdout, "info: "); \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
fprintf(stdout, "\n"); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define return_error return -1
|
||||
@@ -74,6 +78,7 @@ template <typename... T> string to_str(T... args) {
|
||||
|
||||
string join2str(const vector<string> t, const char delm = ' ');
|
||||
string join2str(const int argc, char **argv, const char delm = ' ');
|
||||
bool startWith(string prefix);
|
||||
|
||||
bool validCgroup(const string cgroup);
|
||||
bool validCgroup(const vector<string> cgroup);
|
||||
@@ -87,4 +92,12 @@ string bash_which(const string &name);
|
||||
string bash_readlink(const string &path);
|
||||
string getRealExistPath(const string &name);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* whether cg1 belongs to cg2
|
||||
*/
|
||||
bool belongToCgroup(string cg1, string cg2);
|
||||
bool belongToCgroup(string cg1, vector<string> cg2);
|
||||
string getCgroup(const pid_t &pid);
|
||||
string getCgroup(const string &pid);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,7 +21,6 @@ using json = nlohmann::json;
|
||||
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);
|
||||
@@ -95,6 +94,8 @@ int Config::loadFromJsonStr(const string js) {
|
||||
toRealProgramPath(program_noproxy);
|
||||
toRealProgramPath(program_proxy);
|
||||
|
||||
mergeReserved();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -151,4 +152,8 @@ void Config::toRealProgramPath(vector<string> &v) {
|
||||
v = tmp;
|
||||
}
|
||||
|
||||
#undef tryassign
|
||||
#undef add2json
|
||||
#undef merge
|
||||
|
||||
} // namespace CGPROXY::CONFIG
|
||||
@@ -13,8 +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> program_proxy = {cgroup_proxy_preserved};
|
||||
vector<string> program_noproxy = {cgroup_noproxy_preserved};
|
||||
vector<string> cgroup_proxy;
|
||||
vector<string> cgroup_noproxy;
|
||||
bool enable_gateway = false;
|
||||
|
||||
@@ -48,6 +48,7 @@ struct data_t {
|
||||
};
|
||||
|
||||
function<int(int)> callback = NULL;
|
||||
promise<void> status;
|
||||
|
||||
void handle_events(void *cb_cookie, void *data, int data_size) {
|
||||
auto event = static_cast<data_t *>(data);
|
||||
@@ -59,8 +60,10 @@ void handle_events(void *cb_cookie, void *data, int data_size) {
|
||||
int execsnoop() {
|
||||
debug("starting execsnoop");
|
||||
ebpf::BPF bpf;
|
||||
|
||||
auto init_res = bpf.init(BPF_PROGRAM);
|
||||
if (init_res.code() != 0) {
|
||||
error("bpf init failed, maybe linux-headers not installed");
|
||||
std::cerr << init_res.msg() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
@@ -85,18 +88,17 @@ int execsnoop() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
status.set_value();
|
||||
|
||||
while (true) bpf.poll_perf_buffer("events");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *startThread(void *arg) {
|
||||
thread_arg *p = (thread_arg *)arg;
|
||||
callback = p->handle_pid;
|
||||
void startThread(function<int(int)> c, promise<void> _status) {
|
||||
status = move(_status);
|
||||
callback = c;
|
||||
execsnoop();
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
|
||||
extern "C" void *_startThread(void *arg) { return CGPROXY::EXECSNOOP::startThread(arg); }
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
@@ -2,6 +2,7 @@
|
||||
#define EXECSNOOP_HPP 1
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
@@ -13,10 +14,9 @@ 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);
|
||||
extern "C" void startThread(function<int(int)> c, promise<void> _status);
|
||||
typedef void startThread_t(function<int(int)>, promise<void>);
|
||||
startThread_t *_startThread; // only for dlsym()
|
||||
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
#endif
|
||||
@@ -42,11 +42,7 @@ void send(const char *msg, int &status) {
|
||||
}
|
||||
|
||||
void send(const string msg, int &status) {
|
||||
int msg_len = msg.length();
|
||||
char buff[msg_len];
|
||||
msg.copy(buff, msg_len, 0);
|
||||
buff[msg_len] = '\0';
|
||||
send(buff, status);
|
||||
send(msg.c_str(), status);
|
||||
debug("return status: %d", status);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "socket_server.h"
|
||||
#include "common.h"
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
@@ -9,7 +10,7 @@ namespace fs = std::filesystem;
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
void SocketServer::socketListening(function<int(char *)> callback) {
|
||||
void SocketServer::socketListening(function<int(char *)> callback, promise<void> status) {
|
||||
debug("starting socket listening");
|
||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
@@ -26,6 +27,8 @@ void SocketServer::socketListening(function<int(char *)> callback) {
|
||||
listen(sfd, LISTEN_BACKLOG);
|
||||
chmod(SOCKET_PATH, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
||||
status.set_value();
|
||||
|
||||
while (true) {
|
||||
close(cfd);
|
||||
cfd = accept(sfd, NULL, NULL);
|
||||
@@ -37,29 +40,28 @@ void SocketServer::socketListening(function<int(char *)> callback) {
|
||||
flag = read(cfd, &msg_len, sizeof(int));
|
||||
continue_if_error(flag, "read length");
|
||||
// read msg
|
||||
char msg[msg_len];
|
||||
auto msg = (char *)malloc(msg_len + 1);
|
||||
flag = read(cfd, msg, msg_len * sizeof(char));
|
||||
continue_if_error(flag, "read msg");
|
||||
msg[msg_len] = '\0';
|
||||
// handle msg
|
||||
int status = callback(msg);
|
||||
free(msg);
|
||||
// send back flag
|
||||
flag = write(cfd, &status, sizeof(int));
|
||||
continue_if_error(flag, "write back");
|
||||
}
|
||||
}
|
||||
|
||||
void *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);
|
||||
}
|
||||
|
||||
void startThread(function<int(char *)> callback, promise<void> status) {
|
||||
SocketServer server;
|
||||
server.socketListening(callback, move(status));
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SOCKET_SERVER_H
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <stdlib.h>
|
||||
#include <sys/un.h>
|
||||
using namespace std;
|
||||
@@ -14,20 +15,17 @@ namespace CGPROXY::SOCKET {
|
||||
continue; \
|
||||
}
|
||||
|
||||
struct thread_arg {
|
||||
function<int(char *)> handle_msg;
|
||||
};
|
||||
void *startThread(void *arg);
|
||||
|
||||
class SocketServer {
|
||||
public:
|
||||
int sfd = -1, cfd = -1, flag = -1;
|
||||
struct sockaddr_un unix_socket;
|
||||
|
||||
void socketListening(function<int(char *)> callback);
|
||||
void socketListening(function<int(char *)> callback, promise<void> status);
|
||||
~SocketServer();
|
||||
};
|
||||
|
||||
void startThread(function<int(char *)> callback, promise<void> status);
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user