mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-01-07 13:07:56 +08:00
Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45d883d66d | ||
|
|
ae781fc184 | ||
|
|
5da82723dd | ||
|
|
70b6f29b72 | ||
|
|
cf647c223d | ||
|
|
b2d49eb6ca | ||
|
|
46b0e4bc95 | ||
|
|
89619a3afc | ||
|
|
c417d5050c | ||
|
|
55d67f4799 | ||
|
|
aa05c07c01 | ||
|
|
5f03c52923 | ||
|
|
88e2bc09b3 | ||
|
|
49afaf2ad7 | ||
|
|
2daa46c019 | ||
|
|
655290bf97 | ||
|
|
4b480c9eff | ||
|
|
8c2072f3bf | ||
|
|
d6394f25f3 | ||
|
|
0eca327785 | ||
|
|
032b780e07 | ||
|
|
48780c749c | ||
|
|
538f1722cf | ||
|
|
cba7800e2b | ||
|
|
f397900adc | ||
|
|
134caa9c78 | ||
|
|
7123254ebb | ||
|
|
892c6587dc | ||
|
|
8ea4062384 | ||
|
|
85e7bb3317 | ||
|
|
9d2c26e765 | ||
|
|
00cd293842 | ||
|
|
b8b70dcad5 | ||
|
|
5398740bf3 | ||
|
|
f5dc84e34a | ||
|
|
720806317b | ||
|
|
5509be3926 | ||
|
|
ab98bae840 | ||
|
|
4753d2be68 | ||
|
|
9f4c980c3e | ||
|
|
e4437071c9 | ||
|
|
3b5378a79d | ||
|
|
7f132c5d63 | ||
|
|
6cb169522c | ||
|
|
0d2725d00c | ||
|
|
a4628cfed8 | ||
|
|
77b57247ea | ||
|
|
4618b8f475 | ||
|
|
46fb9bae2b | ||
|
|
c223af9d71 | ||
|
|
25d64f7cdb | ||
|
|
c5ef430652 | ||
|
|
58159dbc15 | ||
|
|
3f1bee745b | ||
|
|
049741249d | ||
|
|
bc94e58cb1 | ||
|
|
840a8338a8 | ||
|
|
06da477373 | ||
|
|
314ff64b8b | ||
|
|
d1bf9620bf | ||
|
|
b7ea8e441b | ||
|
|
b031aa8064 | ||
|
|
8226db37e0 | ||
|
|
5bbec4d1bf | ||
|
|
9b4ec44897 | ||
|
|
a1a1ed5bd9 | ||
|
|
34e5d81329 | ||
|
|
dfc688b5e5 | ||
|
|
ec9609cec8 | ||
|
|
cc83c1ae55 | ||
|
|
75751f4887 | ||
|
|
af78ad2012 | ||
|
|
c32457a1aa | ||
|
|
1d29828d1b | ||
|
|
4fea0d39a2 | ||
|
|
badf282842 | ||
|
|
41f856acd2 | ||
|
|
45adf0a233 | ||
|
|
1f4dd2fde2 | ||
|
|
40bd709995 | ||
|
|
221a75ae7b | ||
|
|
16a341205f | ||
|
|
076651b984 | ||
|
|
1c72a204a1 | ||
|
|
f501c7e476 | ||
|
|
0ec9caefe1 | ||
|
|
94b73b5103 | ||
|
|
c30df999b8 | ||
|
|
932f2bbc94 | ||
|
|
1bbd1ab6ec | ||
|
|
fa7d877de5 | ||
|
|
3475001ca3 | ||
|
|
0b25b5263a | ||
|
|
388ba6a4c8 | ||
|
|
5dbce18f95 | ||
|
|
792a156647 | ||
|
|
92abcb1851 | ||
|
|
a73b697cab | ||
|
|
aace8c3d31 | ||
|
|
665e02ceaa | ||
|
|
bfe3289201 | ||
|
|
2c8625c110 | ||
|
|
ba0b780adf | ||
|
|
1fa0d51e1d | ||
|
|
aedebf4e31 | ||
|
|
051742eef1 | ||
|
|
619fcaae8e | ||
|
|
78c10e3e3e | ||
|
|
99b29195f2 | ||
|
|
53fccbe72d | ||
|
|
db4757316a | ||
|
|
3a4e62b3c2 | ||
|
|
4525d83a53 | ||
|
|
c5ec1027ad | ||
|
|
ee3a5d0fa2 | ||
|
|
f2210f9bda | ||
|
|
e86ea01f6e | ||
|
|
bc9f6d4d4e | ||
|
|
6de88897b2 | ||
|
|
39275452da | ||
|
|
a16cefbfb2 | ||
|
|
2adba75b3e | ||
|
|
fbcc499ba8 | ||
|
|
1c16f57193 | ||
|
|
87cd5a6d99 | ||
|
|
138fa698be | ||
|
|
ffea0fb2b9 | ||
|
|
696fcb6b4e | ||
|
|
9b7d6804f7 | ||
|
|
ad362998d8 | ||
|
|
2b5ff745ac | ||
|
|
b2b3168463 | ||
|
|
2838ffbb70 | ||
|
|
749fe38ca8 | ||
|
|
c0668fd8d2 | ||
|
|
06ae0b9fc5 | ||
|
|
4e04dcf84a | ||
|
|
c0e9ea24c1 | ||
|
|
b5701d8b49 | ||
|
|
4e37bccc1a | ||
|
|
f8e0abbb55 | ||
|
|
6c24c68831 | ||
|
|
d3b2dc0465 | ||
|
|
4be7be2083 | ||
|
|
25f94968ae | ||
|
|
3b4b67df33 | ||
|
|
31ae519193 | ||
|
|
7f0ebe9d35 | ||
|
|
236c08172b | ||
|
|
c07ae13030 | ||
|
|
d5ea832b4f | ||
|
|
aa5ca6f204 | ||
|
|
a80187f947 | ||
|
|
dca895c7cc | ||
|
|
08097a54d7 | ||
|
|
bce568d802 | ||
|
|
98c07a31af | ||
|
|
916c11d280 | ||
|
|
72579bc84a | ||
|
|
5c16fdfb9f | ||
|
|
f55b09ec12 | ||
|
|
6ae12bf5c4 | ||
|
|
31627dd956 | ||
|
|
9f8d540c78 | ||
|
|
b8204126c5 | ||
|
|
daa68f20ea | ||
|
|
836a34cdc8 | ||
|
|
3c21882b88 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,3 +1,9 @@
|
||||
build
|
||||
build*
|
||||
.directory
|
||||
.vscode
|
||||
.clangd
|
||||
v2ray_config/proxy
|
||||
v2ray_config/06_outbounds_proxy.json
|
||||
aur-*
|
||||
gh-md-toc
|
||||
add_toc.sh
|
||||
compile_commands.json
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "execsnoop-libbpf/libbpf"]
|
||||
path = execsnoop-libbpf/libbpf
|
||||
url = https://github.com/libbpf/libbpf.git
|
||||
15
.vscode/c_cpp_properties.json
vendored
Normal file
15
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [],
|
||||
"defines": ["USE_VMLINUX"],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c18",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "gcc-x64",
|
||||
"configurationProvider": "ms-vscode.cmake-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
2
.vscode/gdb-root
vendored
Executable file
2
.vscode/gdb-root
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec sudo -E /usr/bin/gdb "$@"
|
||||
23
.vscode/launch.json
vendored
Normal file
23
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(gdb) cgproxyd",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/src/cgproxy",
|
||||
"args": ["--daemon", "--debug", "--execsnoop"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "${workspaceFolder}/.vscode/gdb-root",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"cmake.configureOnOpen": true,
|
||||
"cmake.configureArgs": [
|
||||
"-Dbuild_static=OFF",
|
||||
"-Dbuild_execsnoop_dl=OFF"
|
||||
]
|
||||
}
|
||||
@@ -1,23 +1,49 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
project(cgproxy VERSION 1.0)
|
||||
add_executable(cgattach cgattach.cpp)
|
||||
project(cgproxy VERSION 0.18)
|
||||
|
||||
install(TARGETS cgattach DESTINATION /usr/bin
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID)
|
||||
install(FILES cgproxy.sh DESTINATION /usr/bin
|
||||
RENAME cgproxy
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
install(FILES cgnoproxy.sh DESTINATION /usr/bin
|
||||
RENAME cgnoproxy
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
# install(FILES run_in_cgroup.sh DESTINATION /usr/bin
|
||||
# RENAME run_in_cgroup
|
||||
# PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(FILES cgproxy.service
|
||||
DESTINATION /usr/lib/systemd/system/)
|
||||
install(FILES cgproxy.conf
|
||||
DESTINATION /etc/)
|
||||
install(FILES cgroup-tproxy.sh
|
||||
DESTINATION /usr/share/cgproxy/scripts/)
|
||||
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter -Wl,--no-undefined)
|
||||
|
||||
# for clangd
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
|
||||
option(build_execsnoop_dl "build libexecsnoop.so which will be dynamic loaded, otherwise built directly into cgproxy" ON)
|
||||
option(build_static "build with static link prefered" OFF)
|
||||
option(build_tools OFF)
|
||||
option(build_test "for develop" OFF)
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(execsnoop-kernel)
|
||||
add_subdirectory(pack)
|
||||
if (build_tools)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
if (build_test)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
configure_file(cgnoproxy.cmake cgnoproxy)
|
||||
configure_file(cgproxyd.cmake cgproxyd)
|
||||
configure_file(cgproxy.service.cmake cgproxy.service)
|
||||
|
||||
# instal scripts and other things
|
||||
install(PROGRAMS ${CMAKE_BINARY_DIR}/cgproxyd TYPE BIN)
|
||||
install(PROGRAMS ${CMAKE_BINARY_DIR}/cgnoproxy TYPE BIN)
|
||||
install(PROGRAMS cgroup-tproxy.sh DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/cgproxy/scripts)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/cgproxy.service DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/systemd/system)
|
||||
install(FILES config.json DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/cgproxy)
|
||||
install(FILES readme.md DESTINATION ${CMAKE_INSTALL_FULL_DOCDIR})
|
||||
|
||||
# man pages
|
||||
set(man_gz ${PROJECT_SOURCE_DIR}/man/cgproxyd.1.gz ${PROJECT_SOURCE_DIR}/man/cgproxy.1.gz ${PROJECT_SOURCE_DIR}/man/cgnoproxy.1.gz)
|
||||
add_custom_command(OUTPUT ${man_gz}
|
||||
COMMAND gzip -fk cgproxyd.1 cgproxy.1 cgnoproxy.1
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/man
|
||||
)
|
||||
add_custom_target(man ALL DEPENDS ${man_gz})
|
||||
install(FILES ${man_gz} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/)
|
||||
|
||||
137
_clang-format
Normal file
137
_clang-format
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Always
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 90
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentCaseLabels: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
Standard: Latest
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
94
cgattach.cpp
94
cgattach.cpp
@@ -1,94 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
using namespace std;
|
||||
|
||||
void print_usage() { fprintf(stdout, "usage: cgattach <pid> <cgroup>\n"); }
|
||||
|
||||
bool exist(string path) {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != -1) {
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool validate(string pid, string cgroup) {
|
||||
bool pid_v = regex_match(pid, regex("^[0-9]+$"));
|
||||
bool cg_v = regex_match(cgroup, regex("^\\/[a-zA-Z0-9\\-_./@]*$"));
|
||||
if (pid_v && cg_v)
|
||||
return true;
|
||||
|
||||
fprintf(stderr, "paramater validate error\n");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string get_cgroup2_mount_point(){
|
||||
char cgroup2_mount_point[100];
|
||||
FILE* fp = popen("findmnt -t cgroup2 -n |cut -d' ' -f 1", "r");
|
||||
fscanf(fp,"%s",&cgroup2_mount_point);
|
||||
fclose(fp);
|
||||
return cgroup2_mount_point;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
setuid(0);
|
||||
setgid(0);
|
||||
if (getuid() != 0 || getgid() != 0) {
|
||||
fprintf(stderr, "cgattach need suid sticky bit or run with root\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "only need 2 paramaters\n");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string pid = string(argv[1]);
|
||||
string cgroup_target = string(argv[2]);
|
||||
validate(pid, cgroup_target);
|
||||
// string cgroup_mount_point = "/sys/fs/cgroup";
|
||||
string cgroup_mount_point = get_cgroup2_mount_point();
|
||||
string cgroup_target_path = cgroup_mount_point + cgroup_target;
|
||||
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
||||
|
||||
// check if exist, we will create it if not exist
|
||||
if (!exist(cgroup_target_path)) {
|
||||
if (mkdir(cgroup_target_path.c_str(),
|
||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) {
|
||||
fprintf(stdout, "created cgroup %s success\n", cgroup_target.c_str());
|
||||
} else {
|
||||
fprintf(stderr, "created cgroup %s failed, errno %d\n",
|
||||
cgroup_target.c_str(), errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// fprintf(stderr, "cgroup %s not exist\n",cgroup_target.c_str());
|
||||
// exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// put pid to target cgroup
|
||||
ofstream procs(cgroup_target_procs, ofstream::app);
|
||||
if (!procs.is_open()) {
|
||||
fprintf(stderr, "open file %s failed\n", cgroup_target_procs.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
procs << pid.c_str() << endl;
|
||||
procs.close();
|
||||
|
||||
// maybe there some write error, for example process pid may not exist
|
||||
if (!procs) {
|
||||
fprintf(stderr, "write %s to %s failed, maybe process %s not exist\n",
|
||||
pid.c_str(), cgroup_target_procs.c_str(), pid.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
2
cgnoproxy.cmake
Normal file
2
cgnoproxy.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec @CMAKE_INSTALL_FULL_BINDIR@/cgproxy --noproxy $@
|
||||
16
cgnoproxy.sh
16
cgnoproxy.sh
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
config="/etc/cgproxy.conf"
|
||||
source $config
|
||||
|
||||
# test suid bit
|
||||
if [ -u "$(which cgattach)" ]; then
|
||||
cgattach $$ $cgroup_noproxy && attached=1
|
||||
else
|
||||
sudo cgattach $$ $cgroup_noproxy && attached=1
|
||||
fi
|
||||
|
||||
# test attach success or not
|
||||
[[ -z "$attached" ]] && echo "attach error" && exit 1
|
||||
|
||||
exec "$@"
|
||||
33
cgproxy.conf
33
cgproxy.conf
@@ -1,33 +0,0 @@
|
||||
# see how to configure
|
||||
# https://github.com/springzfx/cgproxy
|
||||
|
||||
########################################################################
|
||||
## cgroup transparent proxy
|
||||
## any process in cgroup_proxy will be proxied, and cgroup_noproxy the opposite
|
||||
## cgroup must start with slash '/'
|
||||
# cgroup_proxy="/"
|
||||
cgroup_proxy="/proxy.slice"
|
||||
cgroup_noproxy="/noproxy.slice"
|
||||
|
||||
########################################################################
|
||||
## allow as gateway for local network
|
||||
enable_gateway=false
|
||||
|
||||
########################################################################
|
||||
## listening port of another proxy process, for example v2ray
|
||||
port=12345
|
||||
|
||||
########################################################################
|
||||
## if you set to false, it's traffic won't go through proxy, but still can go direct to internet
|
||||
enable_tcp=true
|
||||
enable_udp=true
|
||||
enable_ipv4=true
|
||||
enable_ipv6=true
|
||||
enable_dns=true
|
||||
|
||||
########################################################################
|
||||
## do not modify this if you don't known what you are doing
|
||||
table=100
|
||||
mark_proxy=0x01
|
||||
mark_noproxy=0xff
|
||||
mark_newin=0x02
|
||||
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=proxy cgroup
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh --config=/etc/cgproxy.conf
|
||||
ExecStop= sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop
|
||||
RemainAfterExit=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
10
cgproxy.service.cmake
Normal file
10
cgproxy.service.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=cgproxy service
|
||||
After=network.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/cgproxyd --execsnoop
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
16
cgproxy.sh
16
cgproxy.sh
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
config="/etc/cgproxy.conf"
|
||||
source $config
|
||||
|
||||
# test suid bit
|
||||
if [ -u "$(which cgattach)" ]; then
|
||||
cgattach $$ $cgroup_proxy && attached=1
|
||||
else
|
||||
sudo cgattach $$ $cgroup_proxy && attached=1
|
||||
fi
|
||||
|
||||
# test attach success or not
|
||||
[[ -z "$attached" ]] && echo "attach error" && exit 1
|
||||
|
||||
exec "$@"
|
||||
2
cgproxyd.cmake
Normal file
2
cgproxyd.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec @CMAKE_INSTALL_FULL_BINDIR@/cgproxy --daemon $@
|
||||
379
cgroup-tproxy.sh
Normal file → Executable file
379
cgroup-tproxy.sh
Normal file → Executable file
@@ -1,189 +1,274 @@
|
||||
#!/bin/bash
|
||||
### This script will proxy/noproxy anything running in specific cgroup
|
||||
### need cgroup2 support, and iptables cgroup2 path match support
|
||||
###
|
||||
### script usage:
|
||||
### cgroup-tproxy.sh [--help|--config|stop]
|
||||
### options:
|
||||
### --config=FILE load config from file
|
||||
### --help show help info
|
||||
### stop clean then stop. Variables may change when stopping, which should be avoid
|
||||
### so always stop first in the last context before start new context
|
||||
###
|
||||
### available variables with default value:
|
||||
### cgroup_noproxy="/noproxy.slice"
|
||||
### cgroup_proxy="/proxy.slice"
|
||||
### port=12345
|
||||
### enable_dns=true
|
||||
### enable_udp=true
|
||||
### enable_tcp=true
|
||||
### enable_ipv4=true
|
||||
### enable_ipv6=true
|
||||
### enable_gateway=false
|
||||
### table=10007
|
||||
### fwmark=0x9973
|
||||
### cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET | head -n 1)
|
||||
###
|
||||
### semicolon to seperate multi cgroup:
|
||||
### cgroup_noproxy="/noproxy1.slice:/noproxy2.slice"
|
||||
### cgroup_proxy="/proxy1.slice:/proxy2.slice"
|
||||
|
||||
print_help(){
|
||||
cat << 'DOC'
|
||||
#############################################################################
|
||||
#
|
||||
# 1. This script need cgroup v2
|
||||
#
|
||||
# 2. Listening port is expected to accept iptables TPROXY, while REDIRECT
|
||||
# will not work in this script, because REDIRECT only support tcp/ipv4
|
||||
#
|
||||
# 3. TPROXY need root or special capability whatever process is listening on port
|
||||
# v2ray as example:
|
||||
# sudo setcap "cap_net_bind_service=+ep cap_net_admin=+ep" /usr/lib/v2ray/v2ray
|
||||
#
|
||||
# 4. this script will proxy anything running in specific cgroup
|
||||
#
|
||||
# script usage:
|
||||
# cgroup-tproxy.sh [--help|--config|stop]
|
||||
# --config=FILE
|
||||
# load config from file
|
||||
# --help
|
||||
# show help info
|
||||
# stop
|
||||
# clean then stop
|
||||
#
|
||||
# proxy usage:
|
||||
# cgproxy <program>
|
||||
#
|
||||
#############################################################################
|
||||
DOC
|
||||
sed -rn 's/^### ?//;T;p' "$0"
|
||||
}
|
||||
|
||||
## check root
|
||||
[ ! $(id -u) -eq 0 ] && { >&2 echo "iptables: need root to modify iptables";exit -1; }
|
||||
|
||||
## any process in this cgroup will be proxied
|
||||
cgroup_proxy="/proxy.slice"
|
||||
cgroup_noproxy="/noproxy.slice"
|
||||
if [ -z ${cgroup_proxy+x} ]; then
|
||||
cgroup_proxy="/proxy.slice"
|
||||
else
|
||||
IFS=':' read -r -a cgroup_proxy <<< "$cgroup_proxy"
|
||||
fi
|
||||
|
||||
# allow as gateway for local network
|
||||
enable_gateway=false
|
||||
## any process in this cgroup will not be proxied
|
||||
if [ -z ${cgroup_noproxy+x} ]; then
|
||||
cgroup_noproxy="/noproxy.slice"
|
||||
else
|
||||
IFS=':' read -r -a cgroup_noproxy <<< "$cgroup_noproxy"
|
||||
fi
|
||||
|
||||
## some variables
|
||||
port=12345
|
||||
enable_tcp=true
|
||||
enable_udp=true
|
||||
enable_ipv4=true
|
||||
enable_ipv6=true
|
||||
enable_dns=true
|
||||
## tproxy listening port
|
||||
[ -z ${port+x} ] && port=12345
|
||||
|
||||
## do not modify this if you don't known what you are doing
|
||||
table=100
|
||||
mark_proxy=0x01
|
||||
mark_noproxy=0xff
|
||||
make_newin=0x02
|
||||
## controll options
|
||||
[ -z ${enable_dns+x} ] && enable_dns=true
|
||||
[ -z ${enable_udp+x} ] && enable_udp=true
|
||||
[ -z ${enable_tcp+x} ] && enable_tcp=true
|
||||
[ -z ${enable_ipv4+x} ] && enable_ipv4=true
|
||||
[ -z ${enable_ipv6+x} ] && enable_ipv6=true
|
||||
[ -z ${enable_gateway+x} ] && enable_gateway=false
|
||||
|
||||
## cgroup things
|
||||
# cgroup_mount_point=$(findmnt -t cgroup,cgroup2 -n -J|jq '.filesystems[0].target')
|
||||
# cgroup_type=$(findmnt -t cgroup,cgroup2 -n -J|jq '.filesystems[0].fstype')
|
||||
cgroup_mount_point=$(findmnt -t cgroup2 -n |cut -d' ' -f 1)
|
||||
cgroup_type="cgroup2"
|
||||
cgroup_procs_file="cgroup.procs"
|
||||
## mark/route things
|
||||
[ -z ${table+x} ] && table=10007
|
||||
[ -z ${fwmark+x} ] && fwmark=0x9973
|
||||
[ -z ${table_reroute+x} ] && table_reroute=$table
|
||||
[ -z ${table_tproxy+x} ] && table_tproxy=$table
|
||||
[ -z ${fwmark_reroute+x} ] && fwmark_reroute=$fwmark
|
||||
[ -z ${fwmark_tproxy+x} ] && fwmark_tproxy=$fwmark
|
||||
|
||||
## cgroup mount point things
|
||||
[ -z ${cgroup_mount_point+x} ] && cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET | head -n 1)
|
||||
|
||||
|
||||
stop(){
|
||||
iptables -w 60 -t mangle -L TPROXY_ENT &> /dev/null || return
|
||||
echo "iptables: cleaning tproxy iptables"
|
||||
|
||||
iptables -w 60 -t mangle -D PREROUTING -j TPROXY_PRE
|
||||
iptables -w 60 -t mangle -D OUTPUT -j TPROXY_OUT
|
||||
|
||||
iptables -w 60 -t mangle -F TPROXY_PRE
|
||||
iptables -w 60 -t mangle -F TPROXY_ENT
|
||||
iptables -w 60 -t mangle -F TPROXY_OUT
|
||||
iptables -w 60 -t mangle -F TPROXY_MARK
|
||||
|
||||
iptables -w 60 -t mangle -X TPROXY_PRE
|
||||
iptables -w 60 -t mangle -X TPROXY_ENT
|
||||
iptables -w 60 -t mangle -X TPROXY_OUT
|
||||
iptables -w 60 -t mangle -X TPROXY_MARK
|
||||
|
||||
ip rule delete fwmark $fwmark_tproxy lookup $table_tproxy
|
||||
ip rule delete fwmark $fwmark_reroute lookup $table_reroute &> /dev/null
|
||||
ip route flush table $table_tproxy
|
||||
ip route flush table $table_reroute &> /dev/null
|
||||
|
||||
ip6tables -w 60 -t mangle -D PREROUTING -j TPROXY_PRE
|
||||
ip6tables -w 60 -t mangle -D OUTPUT -j TPROXY_OUT
|
||||
|
||||
ip6tables -w 60 -t mangle -F TPROXY_PRE
|
||||
ip6tables -w 60 -t mangle -F TPROXY_OUT
|
||||
ip6tables -w 60 -t mangle -F TPROXY_ENT
|
||||
ip6tables -w 60 -t mangle -F TPROXY_MARK
|
||||
|
||||
ip6tables -w 60 -t mangle -X TPROXY_PRE
|
||||
ip6tables -w 60 -t mangle -X TPROXY_OUT
|
||||
ip6tables -w 60 -t mangle -X TPROXY_ENT
|
||||
ip6tables -w 60 -t mangle -X TPROXY_MARK
|
||||
|
||||
ip -6 rule delete fwmark $fwmark_tproxy lookup $table_tproxy
|
||||
ip -6 rule delete fwmark $fwmark_reroute lookup $table_reroute &> /dev/null
|
||||
ip -6 route flush table $table_tproxy
|
||||
ip -6 route flush table $table_reroute &> /dev/null
|
||||
|
||||
## may not exist, just ignore, and tracking their existence is not reliable
|
||||
iptables -w 60 -t nat -D POSTROUTING -m owner ! --socket-exists -j MASQUERADE &> /dev/null
|
||||
ip6tables -w 60 -t nat -D POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE &> /dev/null
|
||||
|
||||
## unmount cgroup2
|
||||
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" = "cgroup2" ] && umount $cgroup_mount_point
|
||||
}
|
||||
|
||||
## parse parameter
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
stop)
|
||||
iptables -t mangle -D PREROUTING -j TPROXY_PRE
|
||||
iptables -t mangle -D OUTPUT -j TPROXY_OUT
|
||||
iptables -t mangle -F TPROXY_PRE
|
||||
iptables -t mangle -F TPROXY_OUT
|
||||
iptables -t mangle -X TPROXY_PRE
|
||||
iptables -t mangle -X TPROXY_OUT
|
||||
ip6tables -t mangle -D PREROUTING -j TPROXY_PRE
|
||||
ip6tables -t mangle -D OUTPUT -j TPROXY_OUT
|
||||
ip6tables -t mangle -F TPROXY_PRE
|
||||
ip6tables -t mangle -F TPROXY_OUT
|
||||
ip6tables -t mangle -X TPROXY_PRE
|
||||
ip6tables -t mangle -X TPROXY_OUT
|
||||
ip rule delete fwmark $mark_proxy lookup $table
|
||||
ip route flush table $table
|
||||
ip -6 rule delete fwmark $mark_proxy lookup $table
|
||||
ip -6 route flush table $table
|
||||
## may not exist, just ignore, and tracking their existence is reliable
|
||||
iptables -t nat -D POSTROUTING -m addrtype ! --src-type LOCAL -j MASQUERADE &> /dev/null
|
||||
ip6tables -t nat -D POSTROUTING -m addrtype ! --src-type LOCAL -j MASQUERADE &> /dev/null
|
||||
stop
|
||||
exit 0
|
||||
;;
|
||||
--config=*)
|
||||
config=${i#*=}
|
||||
source $config
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
## TODO cgroup need to exists before using in iptables since 5.6.5, maybe it's bug
|
||||
|
||||
## check cgroup_mount_point, create and mount if necessary
|
||||
[ -z $cgroup_mount_point ] && { >&2 echo "iptables: no cgroup2 mount point available"; exit -1; }
|
||||
[ ! -d $cgroup_mount_point ] && mkdir -p $cgroup_mount_point
|
||||
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && mount -t cgroup2 none $cgroup_mount_point
|
||||
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && { >&2 echo "iptables: mount $cgroup_mount_point failed"; exit -1; }
|
||||
|
||||
## only create the first one in arrary
|
||||
test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_proxy || exit -1;
|
||||
test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1;
|
||||
|
||||
## use TPROXY
|
||||
#ipv4#
|
||||
ip rule add fwmark $mark_proxy table $table
|
||||
ip route add local default dev lo table $table
|
||||
iptables -t mangle -N TPROXY_PRE
|
||||
iptables -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
|
||||
iptables -t mangle -A TPROXY_PRE -m pkttype --pkt-type broadcast -j RETURN
|
||||
iptables -t mangle -A TPROXY_PRE -m pkttype --pkt-type multicast -j RETURN
|
||||
iptables -t mangle -A TPROXY_PRE -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $mark_proxy
|
||||
iptables -t mangle -A TPROXY_PRE -p udp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $mark_proxy
|
||||
iptables -t mangle -A PREROUTING -j TPROXY_PRE
|
||||
## filter cgroup that not exist
|
||||
_cgroup_noproxy=()
|
||||
for cg in ${cgroup_noproxy[@]}; do
|
||||
test -d $cgroup_mount_point$cg && _cgroup_noproxy+=($cg) || { >&2 echo "iptables: $cg not exist, ignore";}
|
||||
done
|
||||
unset cgroup_noproxy && cgroup_noproxy=${_cgroup_noproxy[@]}
|
||||
|
||||
iptables -t mangle -N TPROXY_OUT
|
||||
iptables -t mangle -A TPROXY_OUT -o lo -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -p icmp -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN # return incoming connection directly
|
||||
iptables -t mangle -A TPROXY_OUT -m mark --mark $mark_noproxy -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_noproxy -j RETURN
|
||||
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_proxy -j MARK --set-mark $mark_proxy
|
||||
iptables -t mangle -A OUTPUT -j TPROXY_OUT
|
||||
## filter cgroup that not exist
|
||||
_cgroup_proxy=()
|
||||
for cg in ${cgroup_proxy[@]}; do
|
||||
test -d $cgroup_mount_point$cg && _cgroup_proxy+=($cg) || { >&2 echo "iptables: $cg not exist, ignore";}
|
||||
done
|
||||
unset cgroup_proxy && cgroup_proxy=${_cgroup_proxy[@]}
|
||||
|
||||
#ipv6#
|
||||
ip -6 rule add fwmark $mark_proxy table $table
|
||||
ip -6 route add local default dev lo table $table
|
||||
ip6tables -t mangle -N TPROXY_PRE
|
||||
ip6tables -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_PRE -m pkttype --pkt-type broadcast -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_PRE -m pkttype --pkt-type multicast -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_PRE -p tcp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $mark_proxy
|
||||
ip6tables -t mangle -A TPROXY_PRE -p udp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $mark_proxy
|
||||
ip6tables -t mangle -A PREROUTING -j TPROXY_PRE
|
||||
|
||||
ip6tables -t mangle -N TPROXY_OUT
|
||||
ip6tables -t mangle -A TPROXY_OUT -o lo -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -p icmp -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN # return incoming connection directly
|
||||
ip6tables -t mangle -A TPROXY_OUT -m mark --mark $mark_noproxy -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_noproxy -j RETURN
|
||||
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cgroup_proxy -j MARK --set-mark $mark_proxy
|
||||
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
|
||||
## ipv4 #########################################################################
|
||||
echo "iptables: applying tproxy iptables"
|
||||
## mangle prerouting
|
||||
ip rule add fwmark $fwmark_tproxy table $table_tproxy
|
||||
ip route add local default dev lo table $table_tproxy
|
||||
# core
|
||||
iptables -w 60 -t mangle -N TPROXY_ENT
|
||||
iptables -w 60 -t mangle -A TPROXY_ENT -m socket -j MARK --set-mark $fwmark_tproxy
|
||||
iptables -w 60 -t mangle -A TPROXY_ENT -m socket -j ACCEPT
|
||||
iptables -w 60 -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark_tproxy
|
||||
iptables -w 60 -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark_tproxy
|
||||
# filter
|
||||
iptables -w 60 -t mangle -N TPROXY_PRE
|
||||
iptables -w 60 -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
|
||||
iptables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
|
||||
$enable_gateway || iptables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
|
||||
$enable_dns && iptables -w 60 -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
|
||||
$enable_udp && iptables -w 60 -t mangle -A TPROXY_PRE -p udp -j TPROXY_ENT
|
||||
$enable_tcp && iptables -w 60 -t mangle -A TPROXY_PRE -p tcp -j TPROXY_ENT
|
||||
# hook
|
||||
iptables -w 60 -t mangle -A PREROUTING -j TPROXY_PRE
|
||||
|
||||
## allow to disable, order is important
|
||||
$enable_dns || iptables -t mangle -I TPROXY_OUT -p udp --dport 53 -j RETURN
|
||||
$enable_dns || ip6tables -t mangle -I TPROXY_OUT -p udp --dport 53 -j RETURN
|
||||
$enable_udp || iptables -t mangle -I TPROXY_OUT -p udp -j RETURN
|
||||
$enable_udp || ip6tables -t mangle -I TPROXY_OUT -p udp -j RETURN
|
||||
$enable_tcp || iptables -t mangle -I TPROXY_OUT -p tcp -j RETURN
|
||||
$enable_tcp || ip6tables -t mangle -I TPROXY_OUT -p tcp -j RETURN
|
||||
$enable_ipv4 || iptables -t mangle -I TPROXY_OUT -j RETURN
|
||||
$enable_ipv6 || ip6tables -t mangle -I TPROXY_OUT -j RETURN
|
||||
|
||||
if $enable_gateway; then
|
||||
$enable_dns || iptables -t mangle -I TPROXY_PRE -p udp --dport 53 -j RETURN
|
||||
$enable_dns || ip6tables -t mangle -I TPROXY_PRE -p udp --dport 53 -j RETURN
|
||||
$enable_udp || iptables -t mangle -I TPROXY_PRE -p udp -j RETURN
|
||||
$enable_udp || ip6tables -t mangle -I TPROXY_PRE -p udp -j RETURN
|
||||
$enable_tcp || iptables -t mangle -I TPROXY_PRE -p tcp -j RETURN
|
||||
$enable_tcp || ip6tables -t mangle -I TPROXY_PRE -p tcp -j RETURN
|
||||
$enable_ipv4 || iptables -t mangle -I TPROXY_PRE -j RETURN
|
||||
$enable_ipv6 || ip6tables -t mangle -I TPROXY_PRE -j RETURN
|
||||
## mangle output
|
||||
if [ $fwmark_reroute != $fwmark_tproxy ]; then
|
||||
ip rule add fwmark $fwmark_reroute table $table_reroute
|
||||
ip route add local default dev lo table $table_reroute
|
||||
fi
|
||||
# filter
|
||||
iptables -w 60 -t mangle -N TPROXY_MARK
|
||||
iptables -w 60 -t mangle -A TPROXY_MARK -m addrtype ! --dst-type UNICAST -j RETURN
|
||||
$enable_dns && iptables -w 60 -t mangle -A TPROXY_MARK -p udp --dport 53 -j MARK --set-mark $fwmark_reroute
|
||||
$enable_udp && iptables -w 60 -t mangle -A TPROXY_MARK -p udp -j MARK --set-mark $fwmark_reroute
|
||||
$enable_tcp && iptables -w 60 -t mangle -A TPROXY_MARK -p tcp -j MARK --set-mark $fwmark_reroute
|
||||
# cgroup
|
||||
iptables -w 60 -t mangle -N TPROXY_OUT
|
||||
iptables -w 60 -t mangle -A TPROXY_OUT -m conntrack --ctdir REPLY -j RETURN
|
||||
for cg in ${cgroup_noproxy[@]}; do
|
||||
iptables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
|
||||
done
|
||||
for cg in ${cgroup_proxy[@]}; do
|
||||
iptables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j TPROXY_MARK
|
||||
done
|
||||
# hook
|
||||
$enable_ipv4 && iptables -w 60 -t mangle -A OUTPUT -j TPROXY_OUT
|
||||
|
||||
## do not handle local device connection through tproxy if gateway is not enabled
|
||||
$enable_gateway || iptables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m addrtype ! --dst-type LOCAL -j RETURN
|
||||
$enable_gateway || ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m addrtype ! --dst-type LOCAL -j RETURN
|
||||
## allow back to local device if gateway enabled, and avoid through tproxy again
|
||||
$enable_gateway && iptables -t mangle -I TPROXY_OUT -m addrtype ! --src-type LOCAL -m addrtype ! --dst-type LOCAL -j RETURN
|
||||
$enable_gateway && ip6tables -t mangle -I TPROXY_OUT -m addrtype ! --src-type LOCAL -m addrtype ! --dst-type LOCAL -j RETURN
|
||||
## ipv6 #########################################################################
|
||||
## mangle prerouting
|
||||
ip -6 rule add fwmark $fwmark_tproxy table $table_tproxy
|
||||
ip -6 route add local default dev lo table $table_tproxy
|
||||
# core
|
||||
ip6tables -w 60 -t mangle -N TPROXY_ENT
|
||||
ip6tables -w 60 -t mangle -A TPROXY_ENT -m socket -j MARK --set-mark $fwmark_tproxy
|
||||
ip6tables -w 60 -t mangle -A TPROXY_ENT -m socket -j ACCEPT
|
||||
ip6tables -w 60 -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark_tproxy
|
||||
ip6tables -w 60 -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark_tproxy
|
||||
# filter
|
||||
ip6tables -w 60 -t mangle -N TPROXY_PRE
|
||||
ip6tables -w 60 -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
|
||||
ip6tables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
|
||||
$enable_gateway || ip6tables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
|
||||
$enable_dns && ip6tables -w 60 -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
|
||||
$enable_udp && ip6tables -w 60 -t mangle -A TPROXY_PRE -p udp -j TPROXY_ENT
|
||||
$enable_tcp && ip6tables -w 60 -t mangle -A TPROXY_PRE -p tcp -j TPROXY_ENT
|
||||
# hook
|
||||
ip6tables -w 60 -t mangle -A PREROUTING -j TPROXY_PRE
|
||||
|
||||
## make sure following rules are the first in chain TPROXY_PRE to mark new incoming connection
|
||||
## so must put at last to insert first
|
||||
iptables -t mangle -I TPROXY_PRE -m addrtype --dst-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
|
||||
ip6tables -t mangle -I TPROXY_PRE -m addrtype --dst-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
|
||||
## mangle output
|
||||
if [ $fwmark_reroute != $fwmark_tproxy ]; then
|
||||
ip -6 rule add fwmark $fwmark_reroute table $table_reroute
|
||||
ip -6 route add local default dev lo table $table_reroute
|
||||
fi
|
||||
# filter
|
||||
ip6tables -w 60 -t mangle -N TPROXY_MARK
|
||||
ip6tables -w 60 -t mangle -A TPROXY_MARK -m addrtype ! --dst-type UNICAST -j RETURN
|
||||
$enable_dns && ip6tables -w 60 -t mangle -A TPROXY_MARK -p udp --dport 53 -j MARK --set-mark $fwmark_reroute
|
||||
$enable_udp && ip6tables -w 60 -t mangle -A TPROXY_MARK -p udp -j MARK --set-mark $fwmark_reroute
|
||||
$enable_tcp && ip6tables -w 60 -t mangle -A TPROXY_MARK -p tcp -j MARK --set-mark $fwmark_reroute
|
||||
# cgroup
|
||||
ip6tables -w 60 -t mangle -N TPROXY_OUT
|
||||
ip6tables -w 60 -t mangle -A TPROXY_OUT -m conntrack --ctdir REPLY -j RETURN
|
||||
for cg in ${cgroup_noproxy[@]}; do
|
||||
ip6tables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
|
||||
done
|
||||
for cg in ${cgroup_proxy[@]}; do
|
||||
ip6tables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j TPROXY_MARK
|
||||
done
|
||||
# hook
|
||||
$enable_ipv6 && ip6tables -w 60 -t mangle -A OUTPUT -j TPROXY_OUT
|
||||
|
||||
## forward #######################################################################
|
||||
if $enable_gateway; then
|
||||
iptables -t nat -A POSTROUTING -m owner ! --socket-exists -j MASQUERADE
|
||||
ip6tables -w 60 -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 "iptables: gateway enabled"
|
||||
fi
|
||||
|
||||
## message for user
|
||||
cat << DOC
|
||||
proxied cgroup: $cgroup_proxy
|
||||
iptables: noproxy cgroup: ${cgroup_noproxy[@]}
|
||||
iptables: proxied cgroup: ${cgroup_proxy[@]}
|
||||
DOC
|
||||
|
||||
|
||||
if $enable_gateway; then
|
||||
iptables -t nat -A POSTROUTING -m addrtype ! --src-type LOCAL -j MASQUERADE
|
||||
ip6tables -t nat -A POSTROUTING -m addrtype ! --src-type LOCAL -j MASQUERADE
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1
|
||||
echo "gateway enabled"
|
||||
fi
|
||||
|
||||
17
config.json
Normal file
17
config.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"comment":"For usage, see https://github.com/springzfx/cgproxy",
|
||||
|
||||
"port": 12345,
|
||||
"program_noproxy": ["v2ray", "qv2ray"],
|
||||
"program_proxy": [],
|
||||
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
||||
"cgroup_proxy": [],
|
||||
"enable_gateway": false,
|
||||
"enable_dns": true,
|
||||
"enable_udp": true,
|
||||
"enable_tcp": true,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true,
|
||||
"table": 10007,
|
||||
"fwmark": 39283
|
||||
}
|
||||
6
execsnoop-bcc/CMakeLists.txt
Normal file
6
execsnoop-bcc/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_library(execsnoop MODULE execsnoop.cpp ../src/common.cpp)
|
||||
target_link_libraries(execsnoop bcc)
|
||||
install(TARGETS execsnoop DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/cgproxy/ PERMISSIONS ${basic_permission})
|
||||
104
execsnoop-bcc/execsnoop.cpp
Normal file
104
execsnoop-bcc/execsnoop.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#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;
|
||||
promise<void> status;
|
||||
|
||||
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) {
|
||||
error("bpf init failed, maybe linux-headers not installed");
|
||||
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;
|
||||
}
|
||||
|
||||
status.set_value();
|
||||
|
||||
while (true) bpf.poll_perf_buffer("events");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void startThread(function<int(int)> c, promise<void> _status) {
|
||||
status = move(_status);
|
||||
callback = c;
|
||||
execsnoop();
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
23
execsnoop-bcc/execsnoop.h
Normal file
23
execsnoop-bcc/execsnoop.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef EXECSNOOP_HPP
|
||||
#define EXECSNOOP_HPP 1
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#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();
|
||||
|
||||
extern "C" void startThread(function<int(int)> c, promise<void> _status);
|
||||
// typedef void startThread_t(function<int(int)>, promise<void>);
|
||||
using startThread_t=decltype(startThread);
|
||||
startThread_t *_startThread; // only for dlsym()
|
||||
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
#endif
|
||||
3
execsnoop-bcc/readme.md
Normal file
3
execsnoop-bcc/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
- depend [bcc](https://github.com/iovisor/bcc)
|
||||
|
||||
- huge memory usage, at least 50M
|
||||
15
execsnoop-kernel/CMakeLists.txt
Normal file
15
execsnoop-kernel/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
# find libbpf
|
||||
if (build_static)
|
||||
find_library(LIBBPF libbpf.a REQUIRED)
|
||||
else()
|
||||
find_library(LIBBPF bpf REQUIRED)
|
||||
endif()
|
||||
|
||||
if (build_execsnoop_dl)
|
||||
add_library(execsnoop MODULE execsnoop_share.cpp)
|
||||
install(TARGETS execsnoop DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/cgproxy/)
|
||||
target_link_libraries(execsnoop PRIVATE ${LIBBPF} -lelf -lz)
|
||||
else()
|
||||
add_library(execsnoop STATIC execsnoop_share.cpp)
|
||||
target_link_libraries(execsnoop PRIVATE ${LIBBPF} -l:libelf.a -l:libz.a)
|
||||
endif()
|
||||
216
execsnoop-kernel/aarch64/execsnoop_kern_skel.h
Normal file
216
execsnoop-kernel/aarch64/execsnoop_kern_skel.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/* THIS FILE IS AUTOGENERATED! */
|
||||
#ifndef __EXECSNOOP_KERN_SKEL_H__
|
||||
#define __EXECSNOOP_KERN_SKEL_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
struct execsnoop_kern {
|
||||
struct bpf_object_skeleton *skeleton;
|
||||
struct bpf_object *obj;
|
||||
struct {
|
||||
struct bpf_map *perf_events;
|
||||
struct bpf_map *records;
|
||||
} maps;
|
||||
struct {
|
||||
struct bpf_program *syscall_enter_execve;
|
||||
struct bpf_program *syscall_exit_execve;
|
||||
} progs;
|
||||
struct {
|
||||
struct bpf_link *syscall_enter_execve;
|
||||
struct bpf_link *syscall_exit_execve;
|
||||
} links;
|
||||
};
|
||||
|
||||
static void
|
||||
execsnoop_kern__destroy(struct execsnoop_kern *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->skeleton)
|
||||
bpf_object__destroy_skeleton(obj->skeleton);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj);
|
||||
|
||||
static inline struct execsnoop_kern *
|
||||
execsnoop_kern__open_opts(const struct bpf_object_open_opts *opts)
|
||||
{
|
||||
struct execsnoop_kern *obj;
|
||||
|
||||
obj = (typeof(obj))calloc(1, sizeof(*obj));
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (execsnoop_kern__create_skeleton(obj))
|
||||
goto err;
|
||||
if (bpf_object__open_skeleton(obj->skeleton, opts))
|
||||
goto err;
|
||||
|
||||
return obj;
|
||||
err:
|
||||
execsnoop_kern__destroy(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct execsnoop_kern *
|
||||
execsnoop_kern__open(void)
|
||||
{
|
||||
return execsnoop_kern__open_opts(NULL);
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__load(struct execsnoop_kern *obj)
|
||||
{
|
||||
return bpf_object__load_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline struct execsnoop_kern *
|
||||
execsnoop_kern__open_and_load(void)
|
||||
{
|
||||
struct execsnoop_kern *obj;
|
||||
|
||||
obj = execsnoop_kern__open();
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (execsnoop_kern__load(obj)) {
|
||||
execsnoop_kern__destroy(obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__attach(struct execsnoop_kern *obj)
|
||||
{
|
||||
return bpf_object__attach_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline void
|
||||
execsnoop_kern__detach(struct execsnoop_kern *obj)
|
||||
{
|
||||
return bpf_object__detach_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj)
|
||||
{
|
||||
struct bpf_object_skeleton *s;
|
||||
|
||||
s = (typeof(s))calloc(1, sizeof(*s));
|
||||
if (!s)
|
||||
return -1;
|
||||
obj->skeleton = s;
|
||||
|
||||
s->sz = sizeof(*s);
|
||||
s->name = "execsnoop_kern";
|
||||
s->obj = &obj->obj;
|
||||
|
||||
/* maps */
|
||||
s->map_cnt = 2;
|
||||
s->map_skel_sz = sizeof(*s->maps);
|
||||
s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
|
||||
if (!s->maps)
|
||||
goto err;
|
||||
|
||||
s->maps[0].name = "perf_events";
|
||||
s->maps[0].map = &obj->maps.perf_events;
|
||||
|
||||
s->maps[1].name = "records";
|
||||
s->maps[1].map = &obj->maps.records;
|
||||
|
||||
/* programs */
|
||||
s->prog_cnt = 2;
|
||||
s->prog_skel_sz = sizeof(*s->progs);
|
||||
s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
|
||||
if (!s->progs)
|
||||
goto err;
|
||||
|
||||
s->progs[0].name = "syscall_enter_execve";
|
||||
s->progs[0].prog = &obj->progs.syscall_enter_execve;
|
||||
s->progs[0].link = &obj->links.syscall_enter_execve;
|
||||
|
||||
s->progs[1].name = "syscall_exit_execve";
|
||||
s->progs[1].prog = &obj->progs.syscall_exit_execve;
|
||||
s->progs[1].link = &obj->links.syscall_exit_execve;
|
||||
|
||||
s->data_sz = 2024;
|
||||
s->data = (void *)"\
|
||||
\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\x28\x05\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0b\0\
|
||||
\x01\0\x85\0\0\0\x0e\0\0\0\xbf\x06\0\0\0\0\0\0\x63\x6a\xfc\xff\0\0\0\0\x85\0\0\
|
||||
\0\x0f\0\0\0\xbf\x07\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\
|
||||
\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\
|
||||
\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\
|
||||
\xd0\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x04\0\0\x01\0\0\0\x85\
|
||||
\0\0\0\x02\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x19\0\0\0\0\0\
|
||||
\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x08\0\0\0\0\0\0\x15\x08\x12\0\0\0\0\0\x77\x06\0\
|
||||
\0\x20\0\0\0\x61\xa1\xfc\xff\0\0\0\0\x63\x78\x1c\0\0\0\0\0\x63\x68\x14\0\0\0\0\
|
||||
\0\x63\x18\x10\0\0\0\0\0\x85\0\0\0\x23\0\0\0\x07\0\0\0\x78\x04\0\0\xbf\xa1\0\0\
|
||||
\0\0\0\0\x07\x01\0\0\xf0\xff\xff\xff\xb7\x02\0\0\x08\0\0\0\xbf\x03\0\0\0\0\0\0\
|
||||
\x85\0\0\0\x04\0\0\0\x07\x08\0\0\x18\0\0\0\x79\xa3\xf0\xff\0\0\0\0\x07\x03\0\0\
|
||||
\x6c\x04\0\0\xbf\x81\0\0\0\0\0\0\xb7\x02\0\0\x04\0\0\0\x85\0\0\0\x04\0\0\0\xb7\
|
||||
\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\xbf\x16\0\0\0\0\0\0\x85\0\0\0\x0e\0\0\0\x63\
|
||||
\x0a\xfc\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\x15\x07\x13\
|
||||
\0\0\0\0\0\x79\x61\x10\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x6d\x12\x0b\0\0\0\0\0\xbf\
|
||||
\x71\0\0\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x85\0\0\0\x10\0\0\0\xbf\x61\0\0\0\0\0\0\
|
||||
\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x03\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\
|
||||
\0\xbf\x74\0\0\0\0\0\0\xb7\x05\0\0\x20\0\0\0\x85\0\0\0\x19\0\0\0\xbf\xa2\0\0\0\
|
||||
\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\
|
||||
\0\x03\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\x20\0\0\0\
|
||||
\0\x28\0\0\0\0\0\0\x04\0\0\0\x04\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x47\x50\x4c\
|
||||
\0\x06\x07\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\0\x04\
|
||||
\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdb\0\0\0\0\0\x03\0\x70\x01\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x05\0\xc0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcd\
|
||||
\0\0\0\0\0\x05\0\xe8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa3\0\0\0\x11\0\x08\0\0\0\0\
|
||||
\0\0\0\0\0\x04\0\0\0\0\0\0\0\x20\0\0\0\x11\0\x09\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\
|
||||
\0\0\0\x07\0\0\0\x11\0\x07\0\x14\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x18\0\0\0\x11\
|
||||
\0\x07\0\0\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x8e\0\0\0\x12\0\x03\0\0\0\0\0\0\0\0\
|
||||
\0\x80\x01\0\0\0\0\0\0\x51\0\0\0\x12\0\x05\0\0\0\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\
|
||||
\x70\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\xb8\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\
|
||||
\x28\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\x88\0\0\0\0\0\0\0\x01\0\0\0\x07\0\0\0\
|
||||
\xd0\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\0\x2e\x74\x65\x78\x74\0\x70\x65\x72\x66\
|
||||
\x5f\x65\x76\x65\x6e\x74\x73\0\x6d\x61\x70\x73\0\x72\x65\x63\x6f\x72\x64\x73\0\
|
||||
\x5f\x76\x65\x72\x73\x69\x6f\x6e\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\x6f\
|
||||
\x69\x6e\x74\x2f\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\x78\
|
||||
\x69\x74\x5f\x65\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\x65\x78\
|
||||
\x69\x74\x5f\x65\x78\x65\x63\x76\x65\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\
|
||||
\x6f\x69\x6e\x74\x2f\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\
|
||||
\x6e\x74\x65\x72\x5f\x65\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\
|
||||
\x65\x6e\x74\x65\x72\x5f\x65\x78\x65\x63\x76\x65\0\x5f\x6c\x69\x63\x65\x6e\x73\
|
||||
\x65\0\x65\x78\x65\x63\x73\x6e\x6f\x6f\x70\x5f\x6b\x65\x72\x6e\x2e\x63\0\x2e\
|
||||
\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x4c\x42\x42\x31\x5f\
|
||||
\x34\0\x4c\x42\x42\x31\x5f\x33\0\x4c\x42\x42\x30\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\x40\x04\0\0\0\0\0\0\xe2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\x69\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\
|
||||
\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x65\0\
|
||||
\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\x03\0\0\0\0\0\0\x20\0\0\0\0\
|
||||
\0\0\0\x0a\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x2d\0\0\0\x01\0\
|
||||
\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x01\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x29\0\0\0\x09\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\x10\x04\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x0a\0\0\0\x05\0\0\
|
||||
\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x13\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\xb8\x02\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\xe0\x02\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\x21\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe4\x02\0\0\0\
|
||||
\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc5\
|
||||
\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe8\x02\0\0\0\0\0\0\x08\x01\0\
|
||||
\0\0\0\0\0\x01\0\0\0\x05\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0";
|
||||
|
||||
return 0;
|
||||
err:
|
||||
bpf_object__destroy_skeleton(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* __EXECSNOOP_KERN_SKEL_H__ */
|
||||
82
execsnoop-kernel/arm64.md
Normal file
82
execsnoop-kernel/arm64.md
Normal file
@@ -0,0 +1,82 @@
|
||||
## Cross Compile
|
||||
|
||||
```bash
|
||||
docker pull debian:buster
|
||||
|
||||
# in container
|
||||
dpkg --add-architecture arm64
|
||||
apt update
|
||||
apt install gcc-8-aarch64-linux-gnu # cross compile toolchain
|
||||
apt install libelf-dev:arm64 # target depency library
|
||||
...
|
||||
```
|
||||
|
||||
## Emulation
|
||||
|
||||
### Register qemu-user-static
|
||||
|
||||
- before register binfmt
|
||||
|
||||
```bash
|
||||
docker run --rm -t arm64/ubuntu uname -m
|
||||
```
|
||||
|
||||
- register binfmt
|
||||
|
||||
```bash
|
||||
# through docker
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
# or through systemd
|
||||
pacman -S qemu-user-static binfmt-qemu-static
|
||||
systemctl restart systemd-binfmt.service
|
||||
# test
|
||||
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
|
||||
```
|
||||
|
||||
- after register binfmt
|
||||
|
||||
```bash
|
||||
docker run --rm -t arm64/ubuntu uname -m
|
||||
```
|
||||
|
||||
### M1: Docker
|
||||
|
||||
```bash
|
||||
# start container background
|
||||
docker run -dit --name arm64 -v /home/fancy/workspace-xps:/data arm64v8/ubuntu
|
||||
|
||||
# enter container
|
||||
docker exec -it arm64 bash
|
||||
```
|
||||
|
||||
### M2: Chroot
|
||||
|
||||
download image [ubuntu-base-20.04-base-arm64.tar.gz](http://cdimage.ubuntu.com/ubuntu-base/releases/20.04/release/ubuntu-base-20.04-base-arm64.tar.gz), extract and chroot to it.
|
||||
|
||||
```bash
|
||||
sudo arch-chroot ubuntu-base-20.04-base-arm64
|
||||
```
|
||||
|
||||
### Refer
|
||||
|
||||
- https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/
|
||||
|
||||
- https://github.com/junaruga/fedora-workshop-multiarch/blob/master/slides/Lets-add-Fedora-multiarch-to-CI.pdf
|
||||
|
||||
- https://wiki.debian.org/QemuUserEmulation
|
||||
|
||||
### Compile
|
||||
|
||||
ready some depencies.
|
||||
|
||||
```bash
|
||||
# maybe repository: https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/
|
||||
# install in container for kernel bpf build
|
||||
apt install dialog apt-utils
|
||||
apt install build-essential gcc clang llvm
|
||||
apt install bison flex bc rsync libssl-dev binutils-dev libreadline-dev libelf-dev
|
||||
apt install make cmake
|
||||
# for cgproxy
|
||||
apt install nlohmann-json3-dev rpm
|
||||
```
|
||||
|
||||
97
execsnoop-kernel/execsnoop_kern.c
Normal file
97
execsnoop-kernel/execsnoop_kern.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifdef USE_VMLINUX
|
||||
#include "vmlinux.h"
|
||||
#else
|
||||
#include "linux/sched.h"
|
||||
#include <linux/ptrace.h>
|
||||
#include <uapi/linux/bpf.h>
|
||||
#endif
|
||||
#include <linux/version.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define TASK_COMM_LEN 16
|
||||
struct event {
|
||||
char comm[TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
pid_t tgid;
|
||||
pid_t ppid;
|
||||
uid_t uid;
|
||||
};
|
||||
|
||||
/* /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/format */
|
||||
struct syscalls_enter_execve_args {
|
||||
__u64 unused;
|
||||
int syscall_nr;
|
||||
const char filename_ptr;
|
||||
const char *const * argv;
|
||||
const char *const * envp;
|
||||
};
|
||||
|
||||
/* /sys/kernel/debug/tracing/events/syscalls/sys_exit_execve/format */
|
||||
struct syscalls_exit_execve_args {
|
||||
__u64 unused;
|
||||
int syscall_nr;
|
||||
long ret;
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") records = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.key_size = sizeof(pid_t),
|
||||
.value_size = sizeof(struct event),
|
||||
.max_entries = 10240,
|
||||
};
|
||||
struct bpf_map_def SEC("maps") perf_events = {
|
||||
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
||||
.key_size = sizeof(u32),
|
||||
.value_size = sizeof(u32),
|
||||
.max_entries = 128,
|
||||
};
|
||||
|
||||
SEC("tracepoint/syscalls/sys_enter_execve")
|
||||
int syscall_enter_execve(struct syscalls_enter_execve_args *ctx){
|
||||
pid_t pid, tgid; uid_t uid;
|
||||
struct event *event;
|
||||
struct task_struct *task, *task_p;
|
||||
|
||||
u64 id = bpf_get_current_pid_tgid();
|
||||
pid = (pid_t)id;
|
||||
tgid = id >> 32;
|
||||
uid = (u32)bpf_get_current_uid_gid();
|
||||
|
||||
struct event empty_event={};
|
||||
if (bpf_map_update_elem(&records, &pid, &empty_event, BPF_NOEXIST)!=0) return 0;
|
||||
event = bpf_map_lookup_elem(&records, &pid);
|
||||
if (!event) return 0;
|
||||
|
||||
event->pid = pid;
|
||||
event->tgid = tgid;
|
||||
event->uid = uid;
|
||||
|
||||
/* ppid is not reliable here, normal in arch, but become 0 in ubuntu 20.04 */
|
||||
task = (struct task_struct*)bpf_get_current_task();
|
||||
bpf_probe_read(&task_p, sizeof(struct task_struct*),&task->real_parent);
|
||||
bpf_probe_read(&event->ppid,sizeof(pid_t),&task_p->tgid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_exit_execve")
|
||||
int syscall_exit_execve(struct syscalls_exit_execve_args *ctx){
|
||||
pid_t pid;
|
||||
struct event *event;
|
||||
|
||||
pid = (pid_t)bpf_get_current_pid_tgid();
|
||||
event = bpf_map_lookup_elem(&records,&pid);
|
||||
if (!event) return 0;
|
||||
if (ctx->ret < 0) goto cleanup;
|
||||
|
||||
/* get comm */
|
||||
bpf_get_current_comm(&event->comm,sizeof(event->comm));
|
||||
bpf_perf_event_output(ctx, &perf_events, BPF_F_CURRENT_CPU, event, sizeof(struct event));
|
||||
cleanup:
|
||||
bpf_map_delete_elem(&records, &pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
u32 _version SEC("version") = LINUX_VERSION_CODE;
|
||||
96
execsnoop-kernel/execsnoop_share.cpp
Normal file
96
execsnoop-kernel/execsnoop_share.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "execsnoop_share.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#include "x86_64/execsnoop_kern_skel.h"
|
||||
#elif defined(__aarch64__)
|
||||
#include "aarch64/execsnoop_kern_skel.h"
|
||||
#endif
|
||||
|
||||
namespace CGPROXY::EXECSNOOP {
|
||||
|
||||
#define PERF_BUFFER_PAGES 64
|
||||
#define TASK_COMM_LEN 16
|
||||
struct event {
|
||||
char comm[TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
pid_t tgid;
|
||||
pid_t ppid;
|
||||
uid_t uid;
|
||||
};
|
||||
|
||||
function<int(int)> callback = NULL;
|
||||
promise<void> status;
|
||||
|
||||
static void handle_event(void *ctx, int cpu, void *data, __u32 size) {
|
||||
auto e = static_cast<event*>(data);
|
||||
if (callback) callback(e->pid);
|
||||
}
|
||||
|
||||
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) {
|
||||
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
|
||||
}
|
||||
|
||||
int bump_memlock_rlimit(void) {
|
||||
struct rlimit rlim_new = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
|
||||
}
|
||||
|
||||
int execsnoop() {
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
struct perf_buffer *pb;
|
||||
int err;
|
||||
bool notified=false;
|
||||
|
||||
err = bump_memlock_rlimit();
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to increase rlimit: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct execsnoop_kern *obj=execsnoop_kern__open_and_load();
|
||||
if (!obj) {
|
||||
fprintf(stderr, "failed to open and/or load BPF object\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = execsnoop_kern__attach(obj);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to attach BPF programs\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
main_loop:
|
||||
pb_opts.sample_cb = handle_event;
|
||||
pb_opts.lost_cb = handle_lost_events;
|
||||
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts);
|
||||
err = libbpf_get_error(pb);
|
||||
if (err) {
|
||||
printf("failed to setup perf_buffer: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// notify
|
||||
if (!notified) {status.set_value(); notified=true;}
|
||||
|
||||
while ((err = perf_buffer__poll(pb, -1)) >= 0) {}
|
||||
perf_buffer__free(pb);
|
||||
/* handle Interrupted system call when sleep */
|
||||
if (err == -EINTR) goto main_loop;
|
||||
|
||||
perror("perf_buffer__poll");
|
||||
kill(0, SIGINT);
|
||||
return err;
|
||||
}
|
||||
|
||||
void startThread(function<int(int)> c, promise<void> _status) {
|
||||
status = move(_status);
|
||||
callback = c;
|
||||
execsnoop();
|
||||
}
|
||||
|
||||
}
|
||||
20
execsnoop-kernel/execsnoop_share.h
Normal file
20
execsnoop-kernel/execsnoop_share.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef EXECSNOOP_SHARE_HPP
|
||||
#define EXECSNOOP_SHARE_HPP 1
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::EXECSNOOP {
|
||||
|
||||
extern "C" void startThread(function<int(int)> c, promise<void> _status);
|
||||
|
||||
#ifdef BUIlD_EXECSNOOP_DL
|
||||
// only for dlsym()
|
||||
using startThread_t=decltype(startThread);
|
||||
startThread_t *_startThread;
|
||||
#endif
|
||||
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
#endif
|
||||
40
execsnoop-kernel/execsnoop_user.c
Normal file
40
execsnoop-kernel/execsnoop_user.c
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
#include <signal.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include "bpf_load.h"
|
||||
|
||||
#define TASK_COMM_LEN 16
|
||||
struct event {
|
||||
char comm[TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
pid_t tgid;
|
||||
pid_t ppid;
|
||||
uid_t uid;
|
||||
};
|
||||
static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size) {
|
||||
struct event *e=data;
|
||||
printf("comm: %s, pid: %d, tgid: %d, ppid: %d, uid: %d\n",e->comm,e->pid,e->tgid,e->ppid,e->uid);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
struct perf_buffer *pb;
|
||||
int ret;
|
||||
|
||||
if (load_bpf_file("execsnoop_kern.o")!=0) {
|
||||
printf("%s", bpf_log_buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
pb_opts.sample_cb = print_bpf_output;
|
||||
pb = perf_buffer__new(map_fd[1], 8, &pb_opts);
|
||||
ret = libbpf_get_error(pb);
|
||||
if (ret) {
|
||||
printf("failed to setup perf_buffer: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((ret = perf_buffer__poll(pb, -1)) >= 0) {}
|
||||
kill(0, SIGINT);
|
||||
return ret;
|
||||
}
|
||||
72
execsnoop-kernel/execsnoop_user_1.c
Normal file
72
execsnoop-kernel/execsnoop_user_1.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <signal.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#include "x86_64/execsnoop_kern_skel.h"
|
||||
#elif defined(__aarch64__)
|
||||
#include "aarch64/execsnoop_kern_skel.h"
|
||||
#endif
|
||||
|
||||
#define TASK_COMM_LEN 16
|
||||
struct event {
|
||||
char comm[TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
pid_t tgid;
|
||||
pid_t ppid;
|
||||
uid_t uid;
|
||||
};
|
||||
|
||||
#define PERF_BUFFER_PAGES 64
|
||||
|
||||
static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size) {
|
||||
struct event *e=data;
|
||||
printf("comm: %s, pid: %d, tgid: %d, ppid: %d, uid: %d\n",e->comm,e->pid,e->tgid,e->ppid,e->uid);
|
||||
}
|
||||
|
||||
int bump_memlock_rlimit(void)
|
||||
{
|
||||
struct rlimit rlim_new = {
|
||||
.rlim_cur = RLIM_INFINITY,
|
||||
.rlim_max = RLIM_INFINITY,
|
||||
};
|
||||
|
||||
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
struct perf_buffer *pb;
|
||||
int err;
|
||||
|
||||
err = bump_memlock_rlimit();
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to increase rlimit: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct execsnoop_kern *obj=execsnoop_kern__open_and_load();
|
||||
if (!obj) {
|
||||
fprintf(stderr, "failed to open and/or load BPF object\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = execsnoop_kern__attach(obj);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to attach BPF programs\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pb_opts.sample_cb = print_bpf_output;
|
||||
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts);
|
||||
err = libbpf_get_error(pb);
|
||||
if (err) {
|
||||
printf("failed to setup perf_buffer: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((err = perf_buffer__poll(pb, -1)) >= 0) {}
|
||||
kill(0, SIGINT);
|
||||
return err;
|
||||
}
|
||||
198
execsnoop-kernel/readme.md
Normal file
198
execsnoop-kernel/readme.md
Normal file
@@ -0,0 +1,198 @@
|
||||
|
||||
## Prons and cons
|
||||
|
||||
- use stable execve tracepoint, so build once and should work everywhere
|
||||
- build in kernel tree or build with VMLINUX
|
||||
|
||||
|
||||
|
||||
## Build `execsnoop_kern.o`
|
||||
|
||||
### M1: Build in kernel tree
|
||||
|
||||
- download kernel source code
|
||||
- ready and config kernel tree
|
||||
|
||||
```bash
|
||||
# kernel config
|
||||
#gunzip -c /proc/config.gz > .config
|
||||
#make oldconfig && make prepare
|
||||
make defconfig && make prepare
|
||||
# install headers to ./usr/include
|
||||
make headers_install -j8
|
||||
# build samples/bpf
|
||||
make M=samples/bpf -j8
|
||||
# build bpftool
|
||||
make tools/bpf -j8
|
||||
```
|
||||
|
||||
- put or link `execsnoop_kern.c` and `execsnoop_user.c` to *samples/bpf/*
|
||||
- edit *samples/bpf/makefile*
|
||||
|
||||
```makefile
|
||||
# in samples/bpf/makefile
|
||||
tprogs-y += execsnoop
|
||||
execsnoop-objs := bpf_load.o execsnoop_user.o $(TRACE_HELPERS)
|
||||
always-y += execsnoop_kern.o
|
||||
```
|
||||
|
||||
- compile again
|
||||
|
||||
```
|
||||
make M=samples/bpf -j8
|
||||
```
|
||||
|
||||
- run test
|
||||
|
||||
```bash
|
||||
cd samples/bpf
|
||||
sudo bash -c "ulimit -l unlimited && ./execsnoop"
|
||||
```
|
||||
|
||||
|
||||
**Detail build command**
|
||||
|
||||
using `make V=1 M=samples/bpf | tee -a log.txt` to get and filter following command
|
||||
|
||||
- build `execsnoop_kern.o`
|
||||
|
||||
|
||||
```bash
|
||||
clang -nostdinc \
|
||||
-isystem /usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/include \
|
||||
-I./arch/x86/include \
|
||||
-I./arch/x86/include/generated \
|
||||
-I./include \
|
||||
-I./arch/x86/include/uapi \
|
||||
-I./arch/x86/include/generated/uapi \
|
||||
-I./include/uapi \
|
||||
-I./include/generated/uapi \
|
||||
-include ./include/linux/kconfig.h \
|
||||
-I./samples/bpf \
|
||||
-I./tools/testing/selftests/bpf/ \
|
||||
-I./tools/lib/ \
|
||||
-include asm_goto_workaround.h \
|
||||
-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
|
||||
-D__TARGET_ARCH_x86 -Wno-compare-distinct-pointer-types \
|
||||
-Wno-gnu-variable-sized-type-not-at-end \
|
||||
-Wno-address-of-packed-member -Wno-tautological-compare \
|
||||
-Wno-unknown-warning-option \
|
||||
-fno-stack-protector \
|
||||
-O2 -emit-llvm -c samples/bpf/execsnoop_kern.c \
|
||||
-o - | llc -march=bpf -filetype=obj -o samples/bpf/execsnoop_kern.o
|
||||
```
|
||||
|
||||
|
||||
|
||||
### M2: Build with VMLINUX
|
||||
|
||||
- get `vmlinux.h`
|
||||
|
||||
```
|
||||
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
|
||||
```
|
||||
|
||||
- compile
|
||||
|
||||
note `-g` is needed if with BPF CO-RE
|
||||
|
||||
```
|
||||
clang -O2 -target bpf -DUSE_VMLINUX -c execsnoop_kern.c -o execsnoop_kern.o
|
||||
```
|
||||
|
||||
## Generate `execsnoop_kern_skel.h`
|
||||
|
||||
- generate `execsnoop_kern_skel.h`
|
||||
|
||||
```
|
||||
bpftool gen skeleton execsnoop_kern.o > execsnoop_kern_skel.h
|
||||
```
|
||||
|
||||
- build execsnoop
|
||||
|
||||
```
|
||||
gcc -Wall -O2 execsnoop_user_1.c -o execsnoop -lbpf
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Multiarch build
|
||||
|
||||
- Cross compile, fast, but library link can be mess
|
||||
- aarch64-linux-gnu-gcc
|
||||
- Emulation, the easist, but with perfomance cost
|
||||
- qemu-user-static + binfmt-qemu-static + docker/chroot
|
||||
- see `arm64.md` to see how to setup
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
# if cross compile
|
||||
export ARCH=arm64
|
||||
export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
export SYSROOT=/home/fancy/workspace-xps/linux/ArchLinuxARM-aarch64-latest
|
||||
export C_INCLUDE_PATH=$SYSROOT/usr/include
|
||||
```
|
||||
|
||||
- edit `tools/lib/bpf/makefile` #192 to:
|
||||
|
||||
```makefile
|
||||
$(OUTPUT)libbpf.so.$(LIBBPF_VERSION): $(BPF_IN_SHARED)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS)
|
||||
```
|
||||
|
||||
- make
|
||||
|
||||
```bash
|
||||
# clean
|
||||
make mrproper
|
||||
make clean
|
||||
make -C tools clean
|
||||
make -C samples/bpf clean
|
||||
# make
|
||||
make defconfig && make prepare
|
||||
make headers_install -j8
|
||||
# build samples/bpf
|
||||
make M=samples/bpf -j8
|
||||
# build bpftool
|
||||
make tools/bpf -j8
|
||||
```
|
||||
|
||||
- detail build `execsnoop_kern.o`
|
||||
|
||||
|
||||
```bash
|
||||
clang -nostdinc \
|
||||
-isystem /usr/lib/gcc/aarch64-linux-gnu/9/include \
|
||||
-I./arch/arm64/include -I./arch/arm64/include/generated \
|
||||
-I./include -I./arch/arm64/include/uapi \
|
||||
-I./arch/arm64/include/generated/uapi \
|
||||
-I./include/uapi \
|
||||
-I./include/generated/uapi \
|
||||
-include ./include/linux/kconfig.h \
|
||||
-I./samples/bpf \
|
||||
-I./tools/testing/selftests/bpf/ \
|
||||
-I./tools/lib/ \
|
||||
-include asm_goto_workaround.h \
|
||||
-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
|
||||
-D__TARGET_ARCH_arm64 -Wno-compare-distinct-pointer-types \
|
||||
-Wno-gnu-variable-sized-type-not-at-end \
|
||||
-Wno-address-of-packed-member -Wno-tautological-compare \
|
||||
-Wno-unknown-warning-option \
|
||||
-fno-stack-protector \
|
||||
-O2 -emit-llvm -c samples/bpf/execsnoop_kern.c \
|
||||
-o -| llc -march=bpf -filetype=obj -o samples/bpf/execsnoop_kern.o
|
||||
```
|
||||
|
||||
- generate
|
||||
|
||||
```
|
||||
bpftool gen skeleton execsnoop_kern.o > aarch64/execsnoop_kern_skel.h
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Refer
|
||||
|
||||
- [A thorough introduction to eBPF](https://lwn.net/Articles/740157/)
|
||||
|
||||
225
execsnoop-kernel/x86_64/execsnoop_kern_skel.h
Normal file
225
execsnoop-kernel/x86_64/execsnoop_kern_skel.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/* THIS FILE IS AUTOGENERATED! */
|
||||
#ifndef __EXECSNOOP_KERN_SKEL_H__
|
||||
#define __EXECSNOOP_KERN_SKEL_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
struct execsnoop_kern {
|
||||
struct bpf_object_skeleton *skeleton;
|
||||
struct bpf_object *obj;
|
||||
struct {
|
||||
struct bpf_map *perf_events;
|
||||
struct bpf_map *records;
|
||||
} maps;
|
||||
struct {
|
||||
struct bpf_program *syscall_enter_execve;
|
||||
struct bpf_program *syscall_exit_execve;
|
||||
} progs;
|
||||
struct {
|
||||
struct bpf_link *syscall_enter_execve;
|
||||
struct bpf_link *syscall_exit_execve;
|
||||
} links;
|
||||
};
|
||||
|
||||
static void
|
||||
execsnoop_kern__destroy(struct execsnoop_kern *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->skeleton)
|
||||
bpf_object__destroy_skeleton(obj->skeleton);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj);
|
||||
|
||||
static inline struct execsnoop_kern *
|
||||
execsnoop_kern__open_opts(const struct bpf_object_open_opts *opts)
|
||||
{
|
||||
struct execsnoop_kern *obj;
|
||||
|
||||
obj = (typeof(obj))calloc(1, sizeof(*obj));
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (execsnoop_kern__create_skeleton(obj))
|
||||
goto err;
|
||||
if (bpf_object__open_skeleton(obj->skeleton, opts))
|
||||
goto err;
|
||||
|
||||
return obj;
|
||||
err:
|
||||
execsnoop_kern__destroy(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct execsnoop_kern *
|
||||
execsnoop_kern__open(void)
|
||||
{
|
||||
return execsnoop_kern__open_opts(NULL);
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__load(struct execsnoop_kern *obj)
|
||||
{
|
||||
return bpf_object__load_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline struct execsnoop_kern *
|
||||
execsnoop_kern__open_and_load(void)
|
||||
{
|
||||
struct execsnoop_kern *obj;
|
||||
|
||||
obj = execsnoop_kern__open();
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (execsnoop_kern__load(obj)) {
|
||||
execsnoop_kern__destroy(obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__attach(struct execsnoop_kern *obj)
|
||||
{
|
||||
return bpf_object__attach_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline void
|
||||
execsnoop_kern__detach(struct execsnoop_kern *obj)
|
||||
{
|
||||
return bpf_object__detach_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline int
|
||||
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj)
|
||||
{
|
||||
struct bpf_object_skeleton *s;
|
||||
|
||||
s = (typeof(s))calloc(1, sizeof(*s));
|
||||
if (!s)
|
||||
return -1;
|
||||
obj->skeleton = s;
|
||||
|
||||
s->sz = sizeof(*s);
|
||||
s->name = "execsnoop_kern";
|
||||
s->obj = &obj->obj;
|
||||
|
||||
/* maps */
|
||||
s->map_cnt = 2;
|
||||
s->map_skel_sz = sizeof(*s->maps);
|
||||
s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
|
||||
if (!s->maps)
|
||||
goto err;
|
||||
|
||||
s->maps[0].name = "perf_events";
|
||||
s->maps[0].map = &obj->maps.perf_events;
|
||||
|
||||
s->maps[1].name = "records";
|
||||
s->maps[1].map = &obj->maps.records;
|
||||
|
||||
/* programs */
|
||||
s->prog_cnt = 2;
|
||||
s->prog_skel_sz = sizeof(*s->progs);
|
||||
s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
|
||||
if (!s->progs)
|
||||
goto err;
|
||||
|
||||
s->progs[0].name = "syscall_enter_execve";
|
||||
s->progs[0].prog = &obj->progs.syscall_enter_execve;
|
||||
s->progs[0].link = &obj->links.syscall_enter_execve;
|
||||
|
||||
s->progs[1].name = "syscall_exit_execve";
|
||||
s->progs[1].prog = &obj->progs.syscall_exit_execve;
|
||||
s->progs[1].link = &obj->links.syscall_exit_execve;
|
||||
|
||||
s->data_sz = 2320;
|
||||
s->data = (void *)"\
|
||||
\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\xd0\x05\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\
|
||||
\x01\0\x85\0\0\0\x0e\0\0\0\xbf\x06\0\0\0\0\0\0\x63\x6a\xfc\xff\0\0\0\0\x85\0\0\
|
||||
\0\x0f\0\0\0\xbf\x07\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\
|
||||
\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\
|
||||
\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\
|
||||
\xd0\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x04\0\0\x01\0\0\0\x85\
|
||||
\0\0\0\x02\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x19\0\0\0\0\0\
|
||||
\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x08\0\0\0\0\0\0\x15\x08\x12\0\0\0\0\0\x77\x06\0\
|
||||
\0\x20\0\0\0\x61\xa1\xfc\xff\0\0\0\0\x63\x78\x1c\0\0\0\0\0\x63\x68\x14\0\0\0\0\
|
||||
\0\x63\x18\x10\0\0\0\0\0\x85\0\0\0\x23\0\0\0\x07\0\0\0\xa0\x04\0\0\xbf\xa1\0\0\
|
||||
\0\0\0\0\x07\x01\0\0\xf0\xff\xff\xff\xb7\x02\0\0\x08\0\0\0\xbf\x03\0\0\0\0\0\0\
|
||||
\x85\0\0\0\x04\0\0\0\x07\x08\0\0\x18\0\0\0\x79\xa3\xf0\xff\0\0\0\0\x07\x03\0\0\
|
||||
\x94\x04\0\0\xbf\x81\0\0\0\0\0\0\xb7\x02\0\0\x04\0\0\0\x85\0\0\0\x04\0\0\0\xb7\
|
||||
\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\xbf\x16\0\0\0\0\0\0\x85\0\0\0\x0e\0\0\0\x63\
|
||||
\x0a\xfc\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\x15\x07\x13\
|
||||
\0\0\0\0\0\x79\x61\x10\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x6d\x12\x0b\0\0\0\0\0\xbf\
|
||||
\x71\0\0\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x85\0\0\0\x10\0\0\0\xbf\x61\0\0\0\0\0\0\
|
||||
\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x03\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\
|
||||
\0\xbf\x74\0\0\0\0\0\0\xb7\x05\0\0\x20\0\0\0\x85\0\0\0\x19\0\0\0\xbf\xa2\0\0\0\
|
||||
\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\
|
||||
\0\x03\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\x20\0\0\0\
|
||||
\0\x28\0\0\0\0\0\0\x04\0\0\0\x04\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x47\x50\x4c\
|
||||
\0\x06\x07\x05\0\x10\0\0\0\0\0\0\0\x01\x7a\x52\0\x08\x7c\x0b\x01\x0c\0\0\0\x18\
|
||||
\0\0\0\x18\0\0\0\0\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x34\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\xba\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\xe9\0\0\0\0\0\x03\0\x70\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe2\0\0\0\0\0\x05\0\
|
||||
\xc0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdb\0\0\0\0\0\x05\0\xe8\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\
|
||||
\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa3\0\0\0\x11\0\x08\0\0\0\0\0\0\0\0\0\
|
||||
\x04\0\0\0\0\0\0\0\x20\0\0\0\x11\0\x09\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\x07\
|
||||
\0\0\0\x11\0\x07\0\x14\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x18\0\0\0\x11\0\x07\0\0\
|
||||
\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x8e\0\0\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x80\x01\
|
||||
\0\0\0\0\0\0\x51\0\0\0\x12\0\x05\0\0\0\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\x70\0\0\0\
|
||||
\0\0\0\0\x01\0\0\0\x0a\0\0\0\xb8\0\0\0\0\0\0\0\x01\0\0\0\x0a\0\0\0\x28\0\0\0\0\
|
||||
\0\0\0\x01\0\0\0\x0a\0\0\0\x88\0\0\0\0\0\0\0\x01\0\0\0\x09\0\0\0\xd0\0\0\0\0\0\
|
||||
\0\0\x01\0\0\0\x0a\0\0\0\x1c\0\0\0\0\0\0\0\x01\0\0\0\x05\0\0\0\x38\0\0\0\0\0\0\
|
||||
\0\x01\0\0\0\x06\0\0\0\0\x2e\x74\x65\x78\x74\0\x70\x65\x72\x66\x5f\x65\x76\x65\
|
||||
\x6e\x74\x73\0\x6d\x61\x70\x73\0\x72\x65\x63\x6f\x72\x64\x73\0\x5f\x76\x65\x72\
|
||||
\x73\x69\x6f\x6e\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\x6f\x69\x6e\x74\x2f\
|
||||
\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\x78\x69\x74\x5f\x65\
|
||||
\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\x65\x78\x69\x74\x5f\x65\
|
||||
\x78\x65\x63\x76\x65\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\x6f\x69\x6e\x74\
|
||||
\x2f\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\x6e\x74\x65\x72\
|
||||
\x5f\x65\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\x65\x6e\x74\x65\
|
||||
\x72\x5f\x65\x78\x65\x63\x76\x65\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x72\
|
||||
\x65\x6c\x2e\x65\x68\x5f\x66\x72\x61\x6d\x65\0\x65\x78\x65\x63\x73\x6e\x6f\x6f\
|
||||
\x70\x5f\x6b\x65\x72\x6e\x2e\x63\0\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\
|
||||
\x6d\x74\x61\x62\0\x4c\x42\x42\x31\x5f\x34\0\x4c\x42\x42\x31\x5f\x33\0\x4c\x42\
|
||||
\x42\x30\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcb\0\0\0\
|
||||
\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe0\x04\0\0\0\0\0\0\xf0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x06\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x69\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\x40\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\x65\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\
|
||||
\x04\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\
|
||||
\0\0\0\0\0\0\x2d\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x01\0\0\
|
||||
\0\0\0\0\xf8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\x29\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04\0\0\0\0\0\0\x30\0\
|
||||
\0\0\0\0\0\0\x0c\0\0\0\x05\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x13\0\0\0\
|
||||
\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\x02\0\0\0\0\0\0\x28\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\x01\0\0\0\x03\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe0\x02\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x21\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\xe4\x02\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\xb0\0\0\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\xe8\x02\0\0\0\0\0\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\xac\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x04\0\0\0\
|
||||
\0\0\0\x20\0\0\0\0\0\0\0\x0c\0\0\0\x0a\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\
|
||||
\0\xd3\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x03\0\0\0\0\0\0\x38\
|
||||
\x01\0\0\0\0\0\0\x01\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0";
|
||||
|
||||
return 0;
|
||||
err:
|
||||
bpf_object__destroy_skeleton(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* __EXECSNOOP_KERN_SKEL_H__ */
|
||||
18
man/cgnoproxy.1
Normal file
18
man/cgnoproxy.1
Normal file
@@ -0,0 +1,18 @@
|
||||
.\" 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
|
||||
.br
|
||||
cgnoproxy [--debug] <CMD>
|
||||
.br
|
||||
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)
|
||||
16
man/cgproxy.1
Normal file
16
man/cgproxy.1
Normal file
@@ -0,0 +1,16 @@
|
||||
.\" 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
|
||||
.br
|
||||
cgproxy [--debug] <CMD>
|
||||
.br
|
||||
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 bcc installed to actually work
|
||||
.SH CONFIGURATION
|
||||
.I /etc/cgproxy/config.json
|
||||
.br
|
||||
.B port
|
||||
tproxy listenning port
|
||||
.br
|
||||
program level proxy controll, only work when execsnoop enabled:
|
||||
.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)
|
||||
|
||||
32
pack/CMakeLists.txt
Normal file
32
pack/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
## package for deb and rpm
|
||||
set(CPACK_GENERATOR "DEB;RPM")
|
||||
set(CPACK_PACKAGE_NAME "cgproxy")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything running in specific cgroup.It aslo supports global transparent proxy and gateway proxy")
|
||||
|
||||
## deb pack
|
||||
execute_process(COMMAND dpkg --print-architecture
|
||||
OUTPUT_VARIABLE DEBIAN_ARCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(CPACK_DEBIAN_FILE_NAME ${CPACK_PACKAGE_NAME}_${CMAKE_PROJECT_VERSION}_${DEBIAN_ARCH}.deb)
|
||||
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
|
||||
# set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "springzfx@gmail.com")
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/postinst;${CMAKE_CURRENT_SOURCE_DIR}/prerm")
|
||||
|
||||
## rpm pack
|
||||
execute_process(COMMAND uname -m
|
||||
OUTPUT_VARIABLE RPM_ARCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(CPACK_RPM_FILE_NAME ${CPACK_PACKAGE_NAME}_${CMAKE_PROJECT_VERSION}_${RPM_ARCH}.rpm)
|
||||
# set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
|
||||
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")
|
||||
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/prerm")
|
||||
|
||||
include(CPack)
|
||||
2
pack/postinst
Normal file
2
pack/postinst
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
systemctl enable --now cgproxy.service
|
||||
2
pack/prerm
Normal file
2
pack/prerm
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
systemctl disable --now cgproxy.service
|
||||
245
readme.md
245
readme.md
@@ -1,28 +1,37 @@
|
||||
# Transparent Proxy with cgroup v2
|
||||
|
||||
|
||||
# Transparent Proxy powered by cgroup v2
|
||||
|
||||
|
||||
|
||||
## 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)
|
||||
Main feature:
|
||||
|
||||
- supports cgroup/program level proxy control.
|
||||
- supports global transparent proxy and gateway proxy.
|
||||
|
||||
## Contents
|
||||
|
||||
<!--ts-->
|
||||
|
||||
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
|
||||
* [Transparent Proxy powered by cgroup v2](#transparent-proxy-powered-by-cgroup-v2)
|
||||
* [Introduction](#introduction)
|
||||
* [Contents](#contents)
|
||||
* [Prerequest](#prerequest)
|
||||
* [How to install](#how-to-install)
|
||||
* [How to use](#how-to-use)
|
||||
* [How to build and install](#how-to-build-and-install)
|
||||
* [Default usage](#default-usage)
|
||||
* [Configuration](#configuration)
|
||||
* [Global transparent proxy](#global-transparent-proxy)
|
||||
* [Gateway proxy](#gateway-proxy)
|
||||
* [Other useful tools provided in this project](#other-useful-tools-provided-in-this-project)
|
||||
* [NOTES](#notes)
|
||||
* [TIPS](#tips)
|
||||
* [Licences](#licences)
|
||||
* [Known Issues](#known-issues)
|
||||
|
||||
<!-- Added by: fancy, at: Thu 23 Apr 2020 01:23:57 PM HKT -->
|
||||
<!-- Added by: fancy, at: Sat 04 Jul 2020 03:52:07 PM CST -->
|
||||
|
||||
<!--te-->
|
||||
|
||||
@@ -36,75 +45,127 @@ It aslo supports global transparent proxy and gateway proxy. See [Global transpa
|
||||
|
||||
- TPROXY
|
||||
|
||||
A process listening on port (e.g. 12345) to accept iptables TPROXY, for example v2ray's dokodemo-door in tproxy mode.
|
||||
A process listening on port (e.g. 12345) to accept iptables TPROXY, for example v2ray's dokodemo-door in tproxy mode.
|
||||
|
||||
## How to install
|
||||
- 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 build and install
|
||||
|
||||
### distro install
|
||||
|
||||
- For debian and redhat series, download from [Release page](https://github.com/springzfx/cgproxy/releases)
|
||||
|
||||
- For archlinux series, already in archlinuxcn repo, or see [archlinux AUR](https://aur.archlinux.org/packages/?K=cgproxy)
|
||||
|
||||
- **Tested on archlinux, fedora 32, ubuntu 18.04, ubuntu 20.04, deepin 15.11, deepin v20 beta**
|
||||
|
||||
### build
|
||||
|
||||
- before build, install depencies: clang(if to build bpf obj from scratch), nlohmann-json, libbpf
|
||||
- then cmake standard build
|
||||
|
||||
```bash
|
||||
mkdir build && cd build && cmake .. && make && make install
|
||||
# ready build dir
|
||||
mkdir build
|
||||
cd build
|
||||
# generate
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-Dbuild_execsnoop_dl=ON \
|
||||
-Dbuild_static=OFF \
|
||||
..
|
||||
# compile
|
||||
make
|
||||
```
|
||||
|
||||
It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/cgproxy/).
|
||||
## Default usage
|
||||
|
||||
## How to use
|
||||
|
||||
- First enable service
|
||||
- First enable and start service
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now cgproxy.service
|
||||
sudo systemctl status cgproxy.service
|
||||
```
|
||||
|
||||
|
||||
- Then prefix with cgproxy with your command, just like proxychains
|
||||
|
||||
```
|
||||
cgproxy <CMD>
|
||||
```bash
|
||||
cgproxy [--debug] <CMD>
|
||||
```
|
||||
|
||||
- For example, test proxy
|
||||
|
||||
```bash
|
||||
cgproxy curl -vIs https://www.google.com
|
||||
cgproxy curl -vI https://www.google.com
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>More config in `/etc/cgproxy.conf` (click to expand)</summary>
|
||||
- To completely stop
|
||||
```
|
||||
sudo systemctl disable --now cgproxy.service
|
||||
```
|
||||
|
||||
```bash
|
||||
# see how to configure
|
||||
# https://github.com/springzfx/cgproxy
|
||||
########################################################################
|
||||
## cgroup transparent proxy
|
||||
## any process in cgroup_proxy will be proxied, and cgroup_noproxy the opposite
|
||||
## cgroup must start with slash '/'
|
||||
# cgroup_proxy="/"
|
||||
cgroup_proxy="/proxy.slice"
|
||||
cgroup_noproxy="/noproxy.slice"
|
||||
## Configuration
|
||||
|
||||
########################################################################
|
||||
## allow as gateway for local network
|
||||
enable_gateway=false
|
||||
Config file: **/etc/cgproxy/config.json**
|
||||
|
||||
########################################################################
|
||||
## listening port of another proxy process, for example v2ray
|
||||
port=12345
|
||||
```json
|
||||
{
|
||||
"port": 12345,
|
||||
"program_noproxy": ["v2ray", "qv2ray"],
|
||||
"program_proxy": [],
|
||||
"cgroup_noproxy": ["/system.slice/v2ray.service"],
|
||||
"cgroup_proxy": [],
|
||||
"enable_gateway": false,
|
||||
"enable_dns": true,
|
||||
"enable_udp": true,
|
||||
"enable_tcp": true,
|
||||
"enable_ipv4": true,
|
||||
"enable_ipv6": true,
|
||||
"table": 10007,
|
||||
"fwmark": 39283
|
||||
}
|
||||
|
||||
########################################################################
|
||||
## if you set to false, it's traffic won't go through proxy, but still can go direct to internet
|
||||
enable_tcp=true
|
||||
enable_udp=true
|
||||
enable_ipv4=true
|
||||
enable_ipv6=true
|
||||
enable_dns=true
|
||||
|
||||
########################################################################
|
||||
## do not modify this if you don't known what you are doing
|
||||
table=100
|
||||
mark_proxy=0x01
|
||||
mark_noproxy=0xff
|
||||
mark_newin=0x02
|
||||
```
|
||||
</details>
|
||||
|
||||
- **port** tproxy listenning port
|
||||
|
||||
- program level proxy control, need execsnoop enabled:
|
||||
|
||||
- **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**
|
||||
|
||||
- **table**, **fwmark** you can specify iptables and route table related parameter in case conflict.
|
||||
|
||||
- options priority
|
||||
|
||||
```
|
||||
program_noproxy > program_proxy > cgroup_noproxy > cgroup_proxy
|
||||
enable_ipv6 = enable_ipv4 > enable_dns > enable_tcp = enable_udp
|
||||
command cgproxy and cgnoproxy always have highest priority
|
||||
```
|
||||
|
||||
**Note**: cgroup in configuration need to be exist, otherwise ignored
|
||||
|
||||
If you changed config, remember to restart service
|
||||
|
||||
```bash
|
||||
@@ -113,63 +174,75 @@ sudo systemctl restart cgproxy.service
|
||||
|
||||
## Global transparent proxy
|
||||
|
||||
- First, set **cgroup_proxy="/"** in `/etc/cgproxy.conf`, this will proxy all connection
|
||||
- Set `"cgroup_proxy":["/"]` in configuration, this will proxy all connection
|
||||
|
||||
- Then, run your proxy software in cgroup_noproxy to allow direct to internet
|
||||
|
||||
```bash
|
||||
cgnoproxy <PROXY PROGRAM>
|
||||
# qv2ray as example
|
||||
cgnoproxy qv2ray
|
||||
# v2ray as example
|
||||
cgnoproxy sudo v2ray --config config_file
|
||||
```
|
||||
|
||||
- Finally, restart service `sudo systemctl restart cgproxy.service`, that's all
|
||||
- Allow your proxy program (v2ray) direct to internet to avoid loop. Two ways:
|
||||
|
||||
- active way, run command
|
||||
|
||||
example: `cgnoproxy sudo v2ray -config config_file`
|
||||
|
||||
example: `cgnoproxy qv2ray`
|
||||
|
||||
- passive way, persistent config
|
||||
|
||||
example: `"program_noproxy":["v2ray" ,"qv2ray"]`
|
||||
|
||||
example: `"cgroup_noproxy":["/system.slice/v2ray.service"]`
|
||||
|
||||
- Finally, restart cgproxy service, that's all
|
||||
|
||||
## Gateway proxy
|
||||
|
||||
- set **enable_gateway=true** in `/etc/cgproxy.conf` and restart service
|
||||
- other device set this host as gateway, and set public dns if necessary
|
||||
- Set `"enable_gateway":true` in configuration
|
||||
- And allow your proxy software (v2ray) direct to internet if necessary, described above
|
||||
- Other device set this host as gateway, and set public dns if need
|
||||
|
||||
## Other useful tools provided in this project
|
||||
|
||||
- `cgnoproxy` run program wihout proxy, very useful in global transparent proxy
|
||||
|
||||
```bash
|
||||
cgnoproxy <CMD>
|
||||
cgnoproxy [--debug] <CMD>
|
||||
cgnoproxy [--debug] --pid <PID>
|
||||
```
|
||||
|
||||
- `run_in_cgroup` run command in specific cgroup which will create if not exist , cgroup can be only one level down exist cgroup, otherwise created fail.
|
||||
|
||||
```bash
|
||||
run_in_cgroup --cgroup=CGROUP <COMMAND>
|
||||
# example
|
||||
run_in_cgroup --cgroup=/mycgroup.slice ping 127.0.0.1
|
||||
```
|
||||
|
||||
- `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.
|
||||
|
||||
```bash
|
||||
cgattch <pid> <cgroup>
|
||||
# example
|
||||
cgattch 9999 /proxy.slice
|
||||
```
|
||||
- For more detail command usage, see `man cgproxyd` `man cgproxy` `man cgnoproxy`
|
||||
|
||||
## NOTES
|
||||
|
||||
- `cgattach` attach pid to specific cgroup, and has *suid* bit set by default, be careful to use on multi-user server for securiry. To avoid this situation, you can remove the *suid* bit , then it will fallback to use *sudo*, with *visudo* you can restrict permission or set NOPASSWD for youself.
|
||||
|
||||
- v2ray TPROXY need root or special permission
|
||||
- v2ray TPROXY need root or special permission, use [service](/v2ray_config/v2ray.service) or
|
||||
|
||||
```bash
|
||||
sudo setcap "cap_net_admin,cap_net_bind_service=ep" /usr/lib/v2ray/v2ray
|
||||
```
|
||||
|
||||
- Why not outbound mark solution, because in v2ray [when `"localhost"` is used, out-going DNS traffic is not controlled by V2Ray](https://www.v2fly.org/en/configuration/dns.html), so no mark at all, that's pity.
|
||||
|
||||
## TIPS
|
||||
|
||||
- `systemd-cgls` to see the cgroup hierarchical tree.
|
||||
- Check cgroup2 support `findmnt -t cgroup2`
|
||||
- Offer you v2ray service and full config exmaple in [v2ray_config](https://github.com/springzfx/cgproxy/tree/master/v2ray_config)
|
||||
- Offer you qv2ray config example
|
||||
|
||||
|
||||

|
||||
|
||||
## Licences
|
||||
|
||||
cgproxy is licenced under [](https://www.gnu.org/licenses/gpl-2.0)
|
||||
|
||||
## Known Issues
|
||||
|
||||
- docker breaks cgroup v2 path match, add kernel parameter `cgroup_no_v1=net_cls,net_prio` to resolve, see [issue #3](https://github.com/springzfx/cgproxy/issues/3) for detail
|
||||
|
||||
- docker load `br_netfilter` module due to [hairpin nat](https://wiki.mikrotik.com/wiki/Hairpin_NAT), which is not a big deal, see [commit](https://github.com/moby/moby/pull/13162).
|
||||
|
||||
It enables data link layer packet to go through iptables and only once. However TPROXY do not accept this kind of packets. So to get it working, set following parameter to disable this behavior or unload br_netfilter module manualy. see [issue #10](https://github.com/springzfx/cgproxy/issues/10) for detail.
|
||||
|
||||
```
|
||||
sudo sysctl -w net.bridge.bridge-nf-call-iptables=0
|
||||
sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=0
|
||||
sudo sysctl -w net.bridge.bridge-nf-call-arptables = 0
|
||||
```
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
print_help(){
|
||||
cat << 'DOC'
|
||||
usage:
|
||||
run_in_cgroup --cgroup=CGROUP <COMMAND>
|
||||
run_in_cgroup --help
|
||||
note:
|
||||
CGROUP must start will slash '/' , and no special character
|
||||
example:
|
||||
run_in_cgroup --cggroup=/mycgroup.slice ping 127.0.0.1
|
||||
DOC
|
||||
}
|
||||
|
||||
## parse parameter
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
--cgroup=*)
|
||||
cgroup=${i#*=}
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
print_help
|
||||
exit 0
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$cgroup" ]] && print_help && exit 1
|
||||
[[ -z "$@" ]] && print_help && exit 1
|
||||
|
||||
# test suid bit
|
||||
if [ -u "$(which cgattach)" ]; then
|
||||
cgattach $$ $cgroup && attached=1
|
||||
else
|
||||
sudo cgattach $$ $cgroup && attached=1
|
||||
fi
|
||||
|
||||
# test attach success or not
|
||||
[[ -z "$attached" ]] && print_help && exit 1
|
||||
|
||||
exec "$@"
|
||||
26
src/CMakeLists.txt
Normal file
26
src/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/execsnoop-kernel/)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
configure_file(common.h.cmake common.h)
|
||||
|
||||
if (build_execsnoop_dl)
|
||||
add_definitions(-DBUIlD_EXECSNOOP_DL)
|
||||
set(DL_LIB "-ldl")
|
||||
set(EXECSNOOP_LIB "")
|
||||
else()
|
||||
set(EXECSNOOP_LIB "execsnoop")
|
||||
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 ${DL_LIB} ${EXECSNOOP_LIB})
|
||||
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
||||
install(TARGETS main RUNTIME)
|
||||
|
||||
if (build_static)
|
||||
target_link_libraries(main PRIVATE -static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive)
|
||||
else()
|
||||
target_link_libraries(main PRIVATE -lpthread)
|
||||
endif()
|
||||
85
src/cgproxy.hpp
Normal file
85
src/cgproxy.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#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);
|
||||
}
|
||||
// if just attach pid, return here
|
||||
if (attach_pid) return 0;
|
||||
|
||||
string s = join2str(argc - shift, argv + shift, ' ');
|
||||
debug("executing: %s", s.c_str());
|
||||
return system(s.c_str());
|
||||
}
|
||||
} // namespace CGPROXY::CGPROXY
|
||||
378
src/cgproxyd.hpp
Normal file
378
src/cgproxyd.hpp
Normal file
@@ -0,0 +1,378 @@
|
||||
#ifndef CGPROXYD_HPP
|
||||
#define CGPROXYD_HPP
|
||||
|
||||
#include "cgroup_attach.h"
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "execsnoop_share.h"
|
||||
#include "socket_server.h"
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <nlohmann/json.hpp>
|
||||
#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::EXECSNOOP;
|
||||
|
||||
#ifdef BUIlD_EXECSNOOP_DL
|
||||
namespace CGPROXY::EXECSNOOP {
|
||||
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 func failed: %s", dlerror());
|
||||
return false;
|
||||
}
|
||||
info("dlsym startThread func success");
|
||||
return true;
|
||||
} catch (exception &e) {
|
||||
debug("exception: %s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace CGPROXY::EXECSNOOP
|
||||
#endif
|
||||
|
||||
namespace CGPROXY::CGPROXYD {
|
||||
|
||||
bool print_help = false;
|
||||
bool enable_socketserver = true;
|
||||
bool enable_execsnoop = false;
|
||||
|
||||
class cgproxyd {
|
||||
thread socketserver_thread;
|
||||
thread execsnoop_thread;
|
||||
|
||||
Config config;
|
||||
|
||||
static cgproxyd *instance;
|
||||
static int handle_msg_static(char *msg) {
|
||||
if (!instance) {
|
||||
error("no cgproxyd instance assigned");
|
||||
return ERROR;
|
||||
}
|
||||
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) {
|
||||
unique_ptr<char[], decltype(&free)> path(
|
||||
realpath(to_str("/proc/", pid, "/exe").c_str(), NULL), &free);
|
||||
if (path == NULL) {
|
||||
debug("execsnoop: pid %d live life too short", pid);
|
||||
return 0;
|
||||
}
|
||||
debug("execsnoop: %d %s", pid, path.get());
|
||||
|
||||
vector<string> v;
|
||||
|
||||
v = config.program_noproxy;
|
||||
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)) {
|
||||
int res = attach(pid, config.cgroup_noproxy_preserved);
|
||||
if (res == 0) {
|
||||
info("execsnoop; noproxy: %d %s", pid, path.get());
|
||||
} else {
|
||||
info("execsnoop; noproxy failed: %d %s", pid, path.get());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
v = config.program_proxy;
|
||||
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)) {
|
||||
int res = attach(pid, config.cgroup_proxy_preserved);
|
||||
if (res == 0) {
|
||||
info("execsnoop: proxied: %d %s", pid, path.get());
|
||||
} else {
|
||||
info("execsnoop: proxied failed: %d %s", pid, path.get());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void signalHandler(int signum) {
|
||||
debug("Signal %d received.", signum);
|
||||
if (!instance) {
|
||||
error("no cgproxyd instance assigned");
|
||||
} else {
|
||||
instance->stop();
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// single process instance
|
||||
int lock_fd;
|
||||
void lock() {
|
||||
lock_fd = open(PID_LOCK_FILE, O_CREAT | O_RDWR, 0666);
|
||||
int rc = flock(lock_fd, LOCK_EX | LOCK_NB);
|
||||
if (rc == -1) {
|
||||
perror(PID_LOCK_FILE);
|
||||
error("maybe another cgproxyd is running");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
ofstream ofs(PID_LOCK_FILE);
|
||||
ofs << getpid() << endl;
|
||||
ofs.close();
|
||||
}
|
||||
}
|
||||
void unlock() {
|
||||
close(lock_fd);
|
||||
unlink(PID_LOCK_FILE);
|
||||
}
|
||||
|
||||
int handle_msg(char *msg) {
|
||||
debug("received msg: %s", msg);
|
||||
json j;
|
||||
try {
|
||||
j = json::parse(msg);
|
||||
} catch (exception &e) {
|
||||
debug("msg paser error");
|
||||
return MSG_ERROR;
|
||||
}
|
||||
|
||||
int type, status, 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:
|
||||
error("unknown msg");
|
||||
return MSG_ERROR;
|
||||
break;
|
||||
};
|
||||
} catch (out_of_range &e) { return MSG_ERROR; } catch (exception &e) {
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void startExecsnoopThread() {
|
||||
#ifdef BUIlD_EXECSNOOP_DL
|
||||
if (!EXECSNOOP::loadExecsnoopLib() || EXECSNOOP::_startThread == NULL) {
|
||||
error("execsnoop not ready to start, maybe missing libbpf");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
promise<void> status;
|
||||
future<void> status_f = status.get_future();
|
||||
#ifdef BUIlD_EXECSNOOP_DL
|
||||
thread th(EXECSNOOP::_startThread, handle_pid_static, move(status));
|
||||
#else
|
||||
thread th(EXECSNOOP::startThread, handle_pid_static, move(status));
|
||||
#endif
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void processRunningProgram() {
|
||||
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)) {
|
||||
info("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)) {
|
||||
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)) {
|
||||
info("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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assignStaticInstance() { instance = this; }
|
||||
|
||||
public:
|
||||
int start() {
|
||||
lock();
|
||||
signal(SIGINT, &signalHandler);
|
||||
signal(SIGTERM, &signalHandler);
|
||||
signal(SIGHUP, &signalHandler);
|
||||
|
||||
assignStaticInstance();
|
||||
|
||||
if (config.loadFromFile(DEFAULT_CONFIG_FILE)!=SUCCESS) {
|
||||
error("load config file failed");
|
||||
return -1;
|
||||
}
|
||||
applyConfig();
|
||||
|
||||
if (enable_socketserver) startSocketListeningThread();
|
||||
if (enable_execsnoop) startExecsnoopThread();
|
||||
|
||||
if (socketserver_thread.joinable()) socketserver_thread.join();
|
||||
if (execsnoop_thread.joinable()) execsnoop_thread.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int applyConfig() {
|
||||
system(TPROXY_IPTABLS_CLEAN);
|
||||
config.print_summary();
|
||||
config.toEnv();
|
||||
system(TPROXY_IPTABLS_START);
|
||||
// no need to track running status
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
debug("stopping");
|
||||
system(TPROXY_IPTABLS_CLEAN);
|
||||
unlock();
|
||||
}
|
||||
|
||||
~cgproxyd() { stop(); }
|
||||
};
|
||||
|
||||
cgproxyd *cgproxyd::instance = NULL;
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
processArgs(argc, argv);
|
||||
if (print_help) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (getuid() != 0) {
|
||||
error("permission denied, need root");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cgproxyd d;
|
||||
return d.start();
|
||||
}
|
||||
} // namespace CGPROXY::CGPROXYD
|
||||
#endif
|
||||
98
src/cgroup_attach.cpp
Normal file
98
src/cgroup_attach.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "cgroup_attach.h"
|
||||
#include "common.h"
|
||||
#include <errno.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace CGPROXY::CGROUP {
|
||||
|
||||
string cgroup2_mount_point = CGROUP2_MOUNT_POINT;
|
||||
|
||||
|
||||
bool validate(string pid, string cgroup) {
|
||||
bool pid_v = validPid(pid);
|
||||
bool cg_v = validCgroup(cgroup);
|
||||
if (pid_v && cg_v) return true;
|
||||
|
||||
error("attach paramater validate error");
|
||||
return_error;
|
||||
}
|
||||
|
||||
int attach(const string pid, const string cgroup_target) {
|
||||
if (getuid() != 0) {
|
||||
error("need root to attach cgroup");
|
||||
return_error;
|
||||
}
|
||||
|
||||
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
||||
|
||||
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 (!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());
|
||||
} else {
|
||||
error("created cgroup %s failed, errno %d", cgroup_target.c_str(), errno);
|
||||
return_error;
|
||||
}
|
||||
// error("cgroup %s not exist",cgroup_target.c_str());
|
||||
// return_error
|
||||
}
|
||||
|
||||
string cg;
|
||||
|
||||
cg = getCgroup(pid);
|
||||
if (cg.empty()) return_success;
|
||||
if (cg == cgroup_target) {
|
||||
debug("%s already in %s", pid.c_str(), cgroup_target.c_str());
|
||||
return_success;
|
||||
}
|
||||
|
||||
// put pid to target cgroup
|
||||
if (write2procs(pid, cgroup_target_procs) != 0) return_error;
|
||||
|
||||
// wait for small period and check again
|
||||
this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
cg = getCgroup(pid);
|
||||
if (cg.empty()) return_success;
|
||||
if (cg != cgroup_target && write2procs(pid, cgroup_target_procs) != 0)
|
||||
return_error;
|
||||
return_success;
|
||||
}
|
||||
|
||||
int write2procs(string pid, string procspath) {
|
||||
ofstream procs(procspath, ofstream::app);
|
||||
if (!procs.is_open()) {
|
||||
error("open file %s failed", procspath.c_str());
|
||||
return_error;
|
||||
}
|
||||
procs << pid.c_str() << endl;
|
||||
procs.close();
|
||||
|
||||
// maybe there some write error, for example process pid may not exist
|
||||
if (!procs) {
|
||||
error("write %s to %s failed, maybe process %s live too short", pid.c_str(),
|
||||
procspath.c_str(), pid.c_str());
|
||||
return_error;
|
||||
}
|
||||
return_success;
|
||||
}
|
||||
|
||||
int attach(const int pid, const string cgroup_target) {
|
||||
return attach(to_str(pid), cgroup_target);
|
||||
}
|
||||
|
||||
} // namespace CGPROXY::CGROUP
|
||||
17
src/cgroup_attach.h
Normal file
17
src/cgroup_attach.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef CGPROUP_ATTACH_H
|
||||
#define CGPROUP_ATTACH_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
namespace CGPROXY::CGROUP {
|
||||
extern string cgroup2_mount_point;
|
||||
bool validate(string pid, string cgroup);
|
||||
int attach(const string pid, const string cgroup_target);
|
||||
int attach(const int pid, const string cgroup_target);
|
||||
int write2procs(string pid, string procspath);
|
||||
|
||||
} // namespace CGPROXY::CGROUP
|
||||
|
||||
#endif
|
||||
124
src/common.cpp
Normal file
124
src/common.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "common.h"
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool enable_debug = false;
|
||||
bool enable_info = true;
|
||||
|
||||
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 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\\-_./@]*$"));
|
||||
}
|
||||
|
||||
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 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;
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("pidof ", path).c_str(), "r"),
|
||||
&pclose);
|
||||
if (!fp) return pids;
|
||||
int pid;
|
||||
while (fscanf(fp.get(), "%d", &pid) != EOF) { pids.push_back(pid); }
|
||||
return pids;
|
||||
}
|
||||
|
||||
string bash_which(const string &name) {
|
||||
stringstream buffer;
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("which ", name).c_str(), "r"),
|
||||
&pclose);
|
||||
if (!fp) return "";
|
||||
char buf[READ_SIZE_MAX];
|
||||
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||
string s = buffer.str();
|
||||
if (!s.empty()) s.pop_back(); // remove newline character
|
||||
return s;
|
||||
}
|
||||
|
||||
string bash_readlink(const string &path) {
|
||||
stringstream buffer;
|
||||
unique_ptr<FILE, decltype(&pclose)> fp(popen(to_str("readlink -e ", path).c_str(), "r"),
|
||||
&pclose);
|
||||
if (!fp) return "";
|
||||
char buf[READ_SIZE_MAX];
|
||||
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
|
||||
string s = buffer.str();
|
||||
if (!s.empty()) 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
112
src/common.h.cmake
Normal file
112
src/common.h.cmake
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H 1
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#define TPROXY_IPTABLS_START "@CMAKE_INSTALL_FULL_DATADIR@/cgproxy/scripts/cgroup-tproxy.sh"
|
||||
#define TPROXY_IPTABLS_CLEAN "@CMAKE_INSTALL_FULL_DATADIR@/cgproxy/scripts/cgroup-tproxy.sh stop"
|
||||
|
||||
#define LIBEXECSNOOP_SO "@CMAKE_INSTALL_FULL_LIBDIR@/cgproxy/libexecsnoop.so"
|
||||
#define CGROUP2_MOUNT_POINT "/var/run/cgproxy/cgroup2"
|
||||
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
||||
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
||||
#define LISTEN_BACKLOG 64
|
||||
#define DEFAULT_CONFIG_FILE "@CMAKE_INSTALL_FULL_SYSCONFDIR@/cgproxy/config.json"
|
||||
#define READ_SIZE_MAX 128
|
||||
|
||||
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
||||
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
||||
|
||||
#define THREAD_TIMEOUT 5
|
||||
|
||||
#define MSG_TYPE_CONFIG_JSON 1
|
||||
#define MSG_TYPE_CONFIG_PATH 2
|
||||
#define MSG_TYPE_PROXY_PID 3
|
||||
#define MSG_TYPE_NOPROXY_PID 4
|
||||
|
||||
#define UNKNOWN_ERROR 99
|
||||
#define ERROR -1
|
||||
#define SUCCESS 0
|
||||
#define CONN_ERROR 1
|
||||
#define MSG_ERROR 2
|
||||
#define PARSE_ERROR 3
|
||||
#define PARAM_ERROR 4
|
||||
#define APPLY_ERROR 5
|
||||
#define CGROUP_ERROR 6
|
||||
#define FILE_ERROR 7
|
||||
|
||||
extern bool enable_debug;
|
||||
extern bool enable_info;
|
||||
|
||||
#define error(...) \
|
||||
{ \
|
||||
fprintf(stderr, "error: "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
#define warning(...) \
|
||||
{ \
|
||||
fprintf(stderr, "warning: "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
#define debug(...) \
|
||||
if (enable_debug) { \
|
||||
fprintf(stdout, "debug: "); \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
fprintf(stdout, "\n"); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define info(...) \
|
||||
if (enable_info) { \
|
||||
fprintf(stdout, "info: "); \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
fprintf(stdout, "\n"); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define return_error return -1
|
||||
#define return_success return 0
|
||||
|
||||
template <typename... T> string to_str(T... args) {
|
||||
stringstream ss;
|
||||
ss.clear();
|
||||
ss << std::boolalpha;
|
||||
(ss << ... << args);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
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);
|
||||
bool validPid(const string pid);
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
170
src/config.cpp
Normal file
170
src/config.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
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() {
|
||||
setenv("cgroup_mount_point", CGROUP2_MOUNT_POINT, 1);
|
||||
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);
|
||||
setenv("table", to_str(table).c_str(), 1);
|
||||
setenv("fwmark", to_str(fwmark).c_str(), 1);
|
||||
setenv("mark_newin", to_str(mark_newin).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);
|
||||
add2json(table);
|
||||
add2json(fwmark);
|
||||
add2json(mark_newin);
|
||||
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);
|
||||
tryassign(table);
|
||||
tryassign(fwmark);
|
||||
tryassign(mark_newin);
|
||||
|
||||
// e.g. v2ray -> /usr/bin/v2ray -> /usr/lib/v2ray/v2ray
|
||||
toRealProgramPath(program_noproxy);
|
||||
toRealProgramPath(program_proxy);
|
||||
|
||||
mergeReserved();
|
||||
|
||||
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", "comment", "table", "fwmark", "mark_newin"};
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
info("table: %d, fwmark: %d, mark_newin: %d", table, fwmark, mark_newin);
|
||||
}
|
||||
|
||||
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
|
||||
warning("%s not exist or broken link", p.c_str());
|
||||
}
|
||||
v = tmp;
|
||||
}
|
||||
|
||||
#undef tryassign
|
||||
#undef add2json
|
||||
#undef merge
|
||||
|
||||
} // namespace CGPROXY::CONFIG
|
||||
47
src/config.h
Normal file
47
src/config.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#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 = {cgroup_proxy_preserved};
|
||||
vector<string> program_noproxy = {cgroup_noproxy_preserved};
|
||||
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;
|
||||
|
||||
// for iptables
|
||||
int table=10007;
|
||||
int fwmark=0x9973;
|
||||
int mark_newin=0x9967;
|
||||
|
||||
void toEnv();
|
||||
int saveToFile(const string f);
|
||||
string toJsonStr();
|
||||
int loadFromFile(const string f);
|
||||
int loadFromJsonStr(const string js);
|
||||
void print_summary();
|
||||
|
||||
private:
|
||||
void mergeReserved();
|
||||
bool validateJsonStr(const string js);
|
||||
void toRealProgramPath(vector<string> &v);
|
||||
};
|
||||
|
||||
} // 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);
|
||||
}
|
||||
49
src/socket_client.cpp
Normal file
49
src/socket_client.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "socket_client.h"
|
||||
#include "common.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define return_if_error(flag, msg) \
|
||||
if (flag == -1) { \
|
||||
perror(msg); \
|
||||
status = CONN_ERROR; \
|
||||
close(sfd); \
|
||||
return; \
|
||||
}
|
||||
|
||||
namespace CGPROXY::SOCKET {
|
||||
|
||||
void send(const char *msg, int &status) {
|
||||
debug("send msg: %s", msg);
|
||||
status = UNKNOWN_ERROR;
|
||||
|
||||
int flag;
|
||||
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
struct sockaddr_un unix_socket;
|
||||
memset(&unix_socket, '\0', sizeof(struct sockaddr_un));
|
||||
unix_socket.sun_family = AF_UNIX;
|
||||
strncpy(unix_socket.sun_path, SOCKET_PATH, sizeof(unix_socket.sun_path) - 1);
|
||||
|
||||
flag = connect(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un));
|
||||
return_if_error(flag, "connect");
|
||||
|
||||
int msg_len = strlen(msg);
|
||||
flag = write(sfd, &msg_len, sizeof(int));
|
||||
return_if_error(flag, "write length");
|
||||
flag = write(sfd, msg, msg_len * sizeof(char));
|
||||
return_if_error(flag, "write msg");
|
||||
|
||||
flag = read(sfd, &status, sizeof(int));
|
||||
return_if_error(flag, "read return value");
|
||||
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
void send(const string msg, int &status) {
|
||||
send(msg.c_str(), status);
|
||||
debug("return status: %d", status);
|
||||
}
|
||||
|
||||
} // 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
|
||||
67
src/socket_server.cpp
Normal file
67
src/socket_server.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "socket_server.h"
|
||||
#include "common.h"
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
#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, promise<void> status) {
|
||||
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);
|
||||
|
||||
status.set_value();
|
||||
|
||||
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
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
31
src/socket_server.h
Normal file
31
src/socket_server.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef SOCKET_SERVER_H
|
||||
#define SOCKET_SERVER_H
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#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; \
|
||||
}
|
||||
|
||||
class SocketServer {
|
||||
public:
|
||||
int sfd = -1, cfd = -1, flag = -1;
|
||||
struct sockaddr_un unix_socket;
|
||||
|
||||
void socketListening(function<int(char *)> callback, promise<void> status);
|
||||
~SocketServer();
|
||||
};
|
||||
|
||||
void startThread(function<int(char *)> callback, promise<void> status);
|
||||
|
||||
} // namespace CGPROXY::SOCKET
|
||||
|
||||
#endif
|
||||
7
test/CMakeLists.txt
Normal file
7
test/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
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)
|
||||
48
test/socket_client_test.cpp
Normal file
48
test/socket_client_test.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#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 send_config(Config &config, int &status) {
|
||||
json j;
|
||||
j["type"] = MSG_TYPE_CONFIG_JSON;
|
||||
j["data"] = config.toJsonStr();
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
void send_config_path(const string s, int &status) {
|
||||
json j;
|
||||
j["type"] = MSG_TYPE_CONFIG_PATH;
|
||||
j["data"] = s;
|
||||
SOCKET::send(j.dump(), status);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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_config();
|
||||
return 0;
|
||||
}
|
||||
5
tools/CMakeLists.txt
Normal file
5
tools/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
||||
install(TARGETS cgattach DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} PERMISSIONS ${basic_permission})
|
||||
31
tools/cgattach.cpp
Normal file
31
tools/cgattach.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#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"); }
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int flag = setuid(0);
|
||||
if (flag != 0) {
|
||||
perror("cgattach need root");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc != 3) {
|
||||
error("need exact 2 paramaters");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string pid = string(argv[1]);
|
||||
string cgroup_target = string(argv[2]);
|
||||
|
||||
if (validPid(pid) && validCgroup(cgroup_target)) {
|
||||
CGPROXY::CGROUP::attach(pid, cgroup_target);
|
||||
} else {
|
||||
error("param not valid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
5
v2ray_config/00_log.json
Normal file
5
v2ray_config/00_log.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "none"
|
||||
}
|
||||
}
|
||||
10
v2ray_config/01_api.json
Normal file
10
v2ray_config/01_api.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"api": {
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
],
|
||||
"tag": "API"
|
||||
}
|
||||
}
|
||||
22
v2ray_config/02_dns.json
Normal file
22
v2ray_config/02_dns.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"dns": {
|
||||
"hosts": {
|
||||
"geosite:category-ads": "127.0.0.1"
|
||||
},
|
||||
"servers": [
|
||||
"https+local://223.5.5.5/dns-query",
|
||||
"https://1.1.1.1/dns-query",
|
||||
{
|
||||
"address": "localhost",
|
||||
"port": 53,
|
||||
"domains": [
|
||||
"geosite:cn"
|
||||
],
|
||||
"expectIPs": [
|
||||
"geoip:cn"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tag": "dns_inbound"
|
||||
}
|
||||
}
|
||||
8
v2ray_config/03_policy.json
Normal file
8
v2ray_config/03_policy.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"policy": {
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true
|
||||
}
|
||||
}
|
||||
}
|
||||
54
v2ray_config/04_routing_00.json
Normal file
54
v2ray_config/04_routing_00.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"domain": [
|
||||
"geosite:category-ads-all"
|
||||
],
|
||||
"outboundTag": "outBound_BLACKHOLE",
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"inboundTag": [
|
||||
"inbound_API"
|
||||
],
|
||||
"outboundTag": "API",
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"outboundTag": "dns-out",
|
||||
"port": "53",
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"domain": [
|
||||
"geosite:google",
|
||||
"geosite:github",
|
||||
"geosite:netflix",
|
||||
"geosite:steam",
|
||||
"geosite:telegram",
|
||||
"geosite:tumblr",
|
||||
"geosite:bbc"
|
||||
],
|
||||
"outboundTag": "outBound_PROXY",
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"domain": [
|
||||
"geosite:cn"
|
||||
],
|
||||
"outboundTag": "outBound_DIRECT",
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"ip": [
|
||||
"geoip:cn",
|
||||
"geoip:private"
|
||||
],
|
||||
"outboundTag": "outBound_DIRECT",
|
||||
"type": "field"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
14
v2ray_config/05_inbounds_00_api.json
Normal file
14
v2ray_config/05_inbounds_00_api.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 15490,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
},
|
||||
"sniffing": {},
|
||||
"tag": "inbound_API"
|
||||
}
|
||||
]
|
||||
}
|
||||
30
v2ray_config/05_inbounds_01_tproxy_ipv4lo.json
Normal file
30
v2ray_config/05_inbounds_01_tproxy_ipv4lo.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 12345,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "",
|
||||
"followRedirect": true,
|
||||
"network": "tcp,udp",
|
||||
"port": 0,
|
||||
"timeout": 300,
|
||||
"userLevel": 0
|
||||
},
|
||||
"sniffing": {
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
"streamSettings": {
|
||||
"sockopt": {
|
||||
"tproxy": "tproxy"
|
||||
}
|
||||
},
|
||||
"tag": "tproxy_IN_ipv4lo"
|
||||
}
|
||||
]
|
||||
}
|
||||
30
v2ray_config/05_inbounds_02_tproxy_ipv6lo.json
Normal file
30
v2ray_config/05_inbounds_02_tproxy_ipv6lo.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "::1",
|
||||
"port": 12345,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "",
|
||||
"followRedirect": true,
|
||||
"network": "tcp,udp",
|
||||
"port": 0,
|
||||
"timeout": 300,
|
||||
"userLevel": 0
|
||||
},
|
||||
"sniffing": {
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
"streamSettings": {
|
||||
"sockopt": {
|
||||
"tproxy": "tproxy"
|
||||
}
|
||||
},
|
||||
"tag": "tproxy_IN_ipv6lo"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
v2ray_config/05_inbounds_03_http.json
Normal file
13
v2ray_config/05_inbounds_03_http.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 8888,
|
||||
"protocol": "http",
|
||||
"sniffing": {
|
||||
"enabled": false
|
||||
},
|
||||
"tag": "http_IN"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
v2ray_config/05_inbounds_04_socks5.json
Normal file
18
v2ray_config/05_inbounds_04_socks5.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 1080,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"udp": true,
|
||||
"auth": "noauth",
|
||||
"userLevel": 0
|
||||
},
|
||||
"sniffing": {
|
||||
"enabled": false
|
||||
},
|
||||
"tag": "socks_IN"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
v2ray_config/06_outbounds_00_blackhole.json
Normal file
19
v2ray_config/06_outbounds_00_blackhole.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "blackhole",
|
||||
"sendThrough": "0.0.0.0",
|
||||
"settings": {
|
||||
"response": {
|
||||
"type": "none"
|
||||
}
|
||||
},
|
||||
"streamSettings": {
|
||||
"sockopt": {
|
||||
"mark": 255
|
||||
}
|
||||
},
|
||||
"tag": "outBound_BLACKHOLE"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
v2ray_config/06_outbounds_01_freedom.json
Normal file
19
v2ray_config/06_outbounds_01_freedom.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"sendThrough": "0.0.0.0",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIP",
|
||||
"redirect": ":0",
|
||||
"userLevel": 0
|
||||
},
|
||||
"streamSettings": {
|
||||
"sockopt": {
|
||||
"mark": 255
|
||||
}
|
||||
},
|
||||
"tag": "outBound_DIRECT"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
v2ray_config/06_outbounds_02_dns.json
Normal file
13
v2ray_config/06_outbounds_02_dns.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"streamSettings": {
|
||||
"sockopt": {
|
||||
"mark": 255
|
||||
}
|
||||
},
|
||||
"tag": "dns-out"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
v2ray_config/06_outbounds_10_myproxy.json
Normal file
1
v2ray_config/06_outbounds_10_myproxy.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
v2ray_config/07_transport.json
Normal file
1
v2ray_config/07_transport.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
3
v2ray_config/08_stats.json
Normal file
3
v2ray_config/08_stats.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"stats": {}
|
||||
}
|
||||
1
v2ray_config/09_reverse.json
Normal file
1
v2ray_config/09_reverse.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
2
v2ray_config/merge.sh
Normal file
2
v2ray_config/merge.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
jq -rs 'reduce .[] as $item ({}; . + $item + {inbounds: (.inbounds + $item.inbounds)} + {outbounds: ($item.outbounds + .outbounds)})' *.json |sudo tee /etc/v2ray/config.json > /dev/null
|
||||
8
v2ray_config/readme.md
Normal file
8
v2ray_config/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## Usage
|
||||
- Fill `06_outbounds_myproxy.json` with your vmess proxy config with tag `outBound_PROXY`.
|
||||
- Start with `sudo v2ray -confdir .`
|
||||
|
||||
## Reference
|
||||
|
||||
- [v2ray multi-file config](https://www.v2fly.org/chapter_02/multiple_config.html)
|
||||
|
||||
18
v2ray_config/v2ray.service
Normal file
18
v2ray_config/v2ray.service
Normal file
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=V2Ray - A unified platform for anti-censorship
|
||||
Documentation=https://v2ray.com https://guide.v2fly.org
|
||||
After=network.target nss-lookup.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
ExecStart=/usr/lib/v2ray/v2ray -config /etc/v2ray/config.json
|
||||
DynamicUser=yes
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
NoNewPrivileges=yes
|
||||
Restart=on-failure
|
||||
# Don't restart in the case of configuration error
|
||||
RestartPreventExitStatus=23
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user