Skip to content

Commit 9971df1

Browse files
feat: add MAXKB_SANDBOX_PYTHON_BANNED_HOSTS env to ban host for sandbox in tools code.
1 parent a30d0d0 commit 9971df1

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed

apps/common/utils/tool_code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def _exec_sandbox(self, _code, _id):
197197
file.write(_code)
198198
os.system(f"chown {self.user}:root {exec_python_file}")
199199
kwargs = {'cwd': BASE_DIR}
200-
kwargs['env'] = {}
200+
kwargs['env'] = {'LD_PRELOAD': '/opt/maxkb-app/apps/sanbox_ban_host.so'}
201201
subprocess_result = subprocess.run(
202202
['su', '-s', python_directory, '-c', "exec(open('" + exec_python_file + "').read())", self.user],
203203
text=True,

installer/Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ RUN cd ui && ls -la && if [ -d "dist" ]; then exit 0; fi && \
99
FROM ghcr.io/1panel-dev/maxkb-base:python3.11-pg17.6 AS stage-build
1010
COPY --chmod=700 . /opt/maxkb-app
1111
RUN apt-get update && \
12-
apt-get install -y --no-install-recommends gettext libexpat1-dev libffi-dev && \
12+
apt-get install -y --no-install-recommends gcc g++ gettext libexpat1-dev libffi-dev && \
1313
apt-get clean all && \
1414
rm -rf /var/lib/apt/lists/*
1515
WORKDIR /opt/maxkb-app
16-
RUN rm -rf /opt/maxkb-app/ui && \
16+
RUN gcc -shared -fPIC -o /opt/maxkb-app/apps/sanbox_ban_host.so /opt/maxkb-app/installer/sanbox_ban_host.c -ldl && \
17+
rm -rf /opt/maxkb-app/ui && \
1718
pip install uv --break-system-packages && \
1819
python -m uv pip install -r pyproject.toml && \
1920
find /opt/maxkb-app -depth \( -name ".git*" -o -name ".docker*" -o -name ".idea*" -o -name ".editorconfig*" -o -name ".prettierrc*" -o -name "README.md" -o -name "poetry.lock" -o -name "pyproject.toml" \) -exec rm -rf {} + && \

installer/Dockerfile-base

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ ENV PATH=/opt/py3/bin:$PATH \
4747
MAXKB_SANDBOX=1 \
4848
MAXKB_SANDBOX_PYTHON_PACKAGE_PATHS="/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages" \
4949
MAXKB_SANDBOX_PYTHON_BANNED_KEYWORDS="subprocess.,system(,exec(,execve(,pty.,eval(,compile(,shutil.,input(,__import__" \
50+
MAXKB_SANDBOX_PYTHON_BANNED_HOSTS="127.0.0.1,localhost" \
5051
MAXKB_ADMIN_PATH=/admin
5152

5253
EXPOSE 6379

sanbox_ban_host.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#define _GNU_SOURCE
2+
#include <dlfcn.h>
3+
#include <netdb.h>
4+
#include <arpa/inet.h>
5+
#include <string.h>
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <regex.h>
9+
#include <unistd.h>
10+
11+
static const char *ENV_NAME = "MAXKB_SANDBOX_PYTHON_BANNED_HOSTS";
12+
13+
static int match_env_patterns(const char *target, const char *env_val) {
14+
if (!target || !env_val || !*env_val) return 0;
15+
16+
char *patterns = strdup(env_val);
17+
char *token = strtok(patterns, ",");
18+
int matched = 0;
19+
20+
while (token) {
21+
// 去掉前后空格
22+
while (*token == ' ' || *token == '\t') token++;
23+
char *end = token + strlen(token) - 1;
24+
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
25+
26+
if (*token) {
27+
regex_t regex;
28+
if (regcomp(&regex, token, REG_EXTENDED | REG_NOSUB) == 0) {
29+
if (regexec(&regex, target, 0, NULL, 0) == 0) {
30+
matched = 1;
31+
regfree(&regex);
32+
break;
33+
}
34+
regfree(&regex);
35+
}
36+
}
37+
38+
token = strtok(NULL, ",");
39+
}
40+
41+
free(patterns);
42+
return matched;
43+
}
44+
45+
/**
46+
* 拦截 connect() —— 屏蔽直接 IP 访问
47+
*/
48+
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
49+
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
50+
static char *banned_env = NULL;
51+
static int initialized = 0;
52+
53+
if (!real_connect)
54+
real_connect = dlsym(RTLD_NEXT, "connect");
55+
56+
if (!initialized) {
57+
banned_env = getenv(ENV_NAME);
58+
initialized = 1;
59+
if (banned_env)
60+
fprintf(stderr, "[ban] Loaded banned hosts: %s\n", banned_env);
61+
}
62+
63+
if (!banned_env || !*banned_env)
64+
return real_connect(sockfd, addr, addrlen);
65+
66+
char ip[INET6_ADDRSTRLEN] = {0};
67+
if (addr->sa_family == AF_INET) {
68+
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
69+
} else if (addr->sa_family == AF_INET6) {
70+
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
71+
}
72+
73+
if (match_env_patterns(ip, banned_env)) {
74+
fprintf(stderr, "Access to host %s is banned for sandbox\n", ip);
75+
return -1;
76+
}
77+
78+
return real_connect(sockfd, addr, addrlen);
79+
}
80+
81+
/**
82+
* 拦截 getaddrinfo() —— 屏蔽域名解析
83+
*/
84+
int getaddrinfo(const char *node, const char *service,
85+
const struct addrinfo *hints, struct addrinfo **res) {
86+
static int (*real_getaddrinfo)(const char *, const char *,
87+
const struct addrinfo *, struct addrinfo **) = NULL;
88+
if (!real_getaddrinfo)
89+
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
90+
91+
const char *banned_env = getenv(ENV_NAME);
92+
if (banned_env && node && match_env_patterns(node, banned_env)) {
93+
fprintf(stderr, "Access to host %s is banned for sandbox\n", node);
94+
return EAI_FAIL; // 模拟 DNS 失败
95+
}
96+
97+
return real_getaddrinfo(node, service, hints, res);
98+
}

0 commit comments

Comments
 (0)