Skip to content

Commit ee53932

Browse files
feat: banned host support CIDR.
1 parent 953fc5f commit ee53932

File tree

2 files changed

+79
-41
lines changed

2 files changed

+79
-41
lines changed

installer/Dockerfile-base

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ ENV PATH=/opt/py3/bin:$PATH \
4949
MAXKB_SANDBOX=1 \
5050
MAXKB_SANDBOX_HOME=/opt/maxkb-app/sandbox \
5151
MAXKB_SANDBOX_PYTHON_PACKAGE_PATHS="/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages" \
52-
MAXKB_SANDBOX_PYTHON_BANNED_HOSTS="127.0.0.1,localhost,host.docker.internal,maxkb,pgsql,redis" \
52+
MAXKB_SANDBOX_PYTHON_BANNED_HOSTS="127.0.0.0/8,localhost,host.docker.internal,172.17.0.0/16,maxkb,pgsql,redis,172.31.250.192/26" \
5353
MAXKB_ADMIN_PATH=/admin
5454

5555
EXPOSE 6379

installer/sandbox.c

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
#include <sys/types.h>
1919
#include <time.h>
2020
#include <execinfo.h>
21-
#include <dlfcn.h>
2221
#include <linux/sched.h>
2322
#include <pty.h>
23+
#include <stdint.h>
2424

2525
#define CONFIG_FILE ".sandbox.conf"
2626
#define KEY_BANNED_HOSTS "SANDBOX_PYTHON_BANNED_HOSTS"
@@ -30,25 +30,26 @@ static char *banned_hosts = NULL;
3030
static int allow_subprocess = 0; // 默认禁止
3131

3232
static void load_sandbox_config() {
33-
Dl_info info;
34-
if (dladdr((void *)load_sandbox_config, &info) == 0 || !info.dli_fname) {
35-
banned_hosts = strdup("");
36-
allow_subprocess = 0;
37-
return;
38-
}
39-
char so_path[PATH_MAX];
40-
strncpy(so_path, info.dli_fname, sizeof(so_path));
41-
so_path[sizeof(so_path) - 1] = '\0';
42-
char *dir = dirname(so_path);
43-
char config_path[PATH_MAX];
44-
snprintf(config_path, sizeof(config_path), "%s/%s", dir, CONFIG_FILE);
45-
FILE *fp = fopen(config_path, "r");
46-
if (!fp) {
47-
banned_hosts = strdup("");
48-
allow_subprocess = 0;
49-
return;
50-
}
33+
Dl_info info;
34+
if (dladdr((void *)load_sandbox_config, &info) == 0 || !info.dli_fname) {
35+
banned_hosts = strdup("");
36+
allow_subprocess = 0;
37+
return;
38+
}
39+
char so_path[PATH_MAX];
40+
strncpy(so_path, info.dli_fname, sizeof(so_path));
41+
so_path[sizeof(so_path) - 1] = '\0';
42+
char *dir = dirname(so_path);
43+
char config_path[PATH_MAX];
44+
snprintf(config_path, sizeof(config_path), "%s/%s", dir, CONFIG_FILE);
45+
FILE *fp = fopen(config_path, "r");
46+
if (!fp) {
47+
banned_hosts = strdup("");
48+
allow_subprocess = 0;
49+
return;
50+
}
5151
char line[512];
52+
if (banned_hosts) { free(banned_hosts); banned_hosts = NULL; }
5253
banned_hosts = strdup("");
5354
allow_subprocess = 0;
5455
while (fgets(line, sizeof(line), fp)) {
@@ -77,15 +78,15 @@ static int is_sandbox_user() {
7778
uid_t uid = getuid();
7879
struct passwd *pw = getpwuid(uid);
7980
if (!pw || !pw->pw_name) {
80-
return 1; // 无法识别用户 → 认为是sandbox
81+
return 1; // 无法识别用户 → 认为是 sandbox
8182
}
8283
if (strcmp(pw->pw_name, "sandbox") == 0) {
8384
return 1;
8485
}
8586
return 0;
8687
}
8788
/**
88-
* 精确匹配黑名单
89+
* 匹配黑名单(用于域名或具体字符串匹配)
8990
*/
9091
static int match_env_patterns(const char *target, const char *env_val) {
9192
if (!target || !env_val || !*env_val) return 0;
@@ -114,8 +115,43 @@ static int match_env_patterns(const char *target, const char *env_val) {
114115
free(patterns);
115116
return matched;
116117
}
117-
118-
/** 拦截 connect() —— 精确匹配 IP */
118+
// ------------------ IP/CIDR 黑名单 ------------------
119+
static int match_banned_ip(const char *ip_str, const char *banned_list) {
120+
if (!ip_str || !banned_list || !*banned_list) return 0;
121+
char *list = strdup(banned_list);
122+
char *token = strtok(list, ",");
123+
int blocked = 0;
124+
while (token) {
125+
while (*token == ' ' || *token == '\t') token++;
126+
char *end = token + strlen(token) - 1;
127+
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
128+
if (*token) {
129+
char *slash = strchr(token, '/');
130+
if (!slash) {
131+
if (strcmp(ip_str, token) == 0) {
132+
blocked = 1;
133+
break;
134+
}
135+
} else {
136+
*slash = 0;
137+
int prefix = atoi(slash + 1);
138+
struct in_addr ip, net, mask;
139+
if (inet_pton(AF_INET, token, &net) == 1 &&
140+
inet_pton(AF_INET, ip_str, &ip) == 1) {
141+
mask.s_addr = prefix == 0 ? 0 : htonl(0xFFFFFFFF << (32 - prefix));
142+
if ((ip.s_addr & mask.s_addr) == (net.s_addr & mask.s_addr)) {
143+
blocked = 1;
144+
break;
145+
}
146+
}
147+
}
148+
}
149+
token = strtok(NULL, ",");
150+
}
151+
free(list);
152+
return blocked;
153+
}
154+
// ------------------ 网络拦截 ------------------
119155
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
120156
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
121157
if (!real_connect)
@@ -126,32 +162,34 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
126162
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
127163
else if (addr->sa_family == AF_INET6)
128164
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
129-
if (is_sandbox_user() && banned_hosts && *banned_hosts && match_env_patterns(ip, banned_hosts)) {
130-
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned\n", ip);
131-
errno = EACCES; // EACCES 的值是 13, 意思是 Permission denied
132-
return -1;
165+
166+
if (is_sandbox_user() && banned_hosts && *banned_hosts) {
167+
if (ip[0] && match_banned_ip(ip, banned_hosts)) {
168+
fprintf(stderr, "[sandbox] 🚫 Access to IP %s is banned\n", ip);
169+
errno = EACCES; // Permission denied
170+
return -1;
171+
}
133172
}
134173
return real_connect(sockfd, addr, addrlen);
135174
}
136-
137-
/** 拦截 getaddrinfo() —— 只拦截域名,不拦截纯 IP */
138175
int getaddrinfo(const char *node, const char *service,
139176
const struct addrinfo *hints, struct addrinfo **res) {
140177
static int (*real_getaddrinfo)(const char *, const char *,
141178
const struct addrinfo *, struct addrinfo **) = NULL;
142179
if (!real_getaddrinfo)
143180
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
144181
ensure_config_loaded();
145-
if (banned_hosts && *banned_hosts && node) {
146-
// 检测 node 是否是 IP
182+
if (banned_hosts && *banned_hosts && node && is_sandbox_user()) {
147183
struct in_addr ipv4;
148184
struct in6_addr ipv6;
149-
int is_ip = (inet_pton(AF_INET, node, &ipv4) == 1) ||
150-
(inet_pton(AF_INET6, node, &ipv6) == 1);
151-
// 只对“非IP的域名”进行屏蔽
152-
if (is_sandbox_user() && !is_ip && match_env_patterns(node, banned_hosts )) {
153-
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned (DNS blocked)\n", node);
154-
return EAI_FAIL; // 模拟 DNS 层禁止
185+
int is_ip = inet_pton(AF_INET, node, &ipv4) == 1 ||
186+
inet_pton(AF_INET6, node, &ipv6) == 1;
187+
if (!is_ip) {
188+
// 仅对域名进行阻塞
189+
if (match_env_patterns(node, banned_hosts)) {
190+
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned (DNS blocked)\n", node);
191+
return EAI_FAIL;
192+
}
155193
}
156194
}
157195
return real_getaddrinfo(node, service, hints, res);
@@ -287,7 +325,6 @@ int posix_spawn(pid_t *pid, const char *path,
287325
if (!allow_create_subprocess()) return deny();
288326
return real_posix_spawn(pid, path, file_actions, attrp, argv, envp);
289327
}
290-
291328
int posix_spawnp(pid_t *pid, const char *file,
292329
const posix_spawn_file_actions_t *file_actions,
293330
const posix_spawnattr_t *attrp,
@@ -304,7 +341,6 @@ int __posix_spawn(pid_t *pid, const char *path,
304341
if (!allow_create_subprocess()) return deny();
305342
return real___posix_spawn(pid, path, file_actions, attrp, argv, envp);
306343
}
307-
308344
int __posix_spawnp(pid_t *pid, const char *file,
309345
const posix_spawn_file_actions_t *file_actions,
310346
const posix_spawnattr_t *attrp,
@@ -351,6 +387,7 @@ pid_t __libc_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, .
351387
va_end(ap);
352388
return real___libc_clone(fn, child_stack, flags, arg, (void *)a4, (void *)a5);
353389
}
390+
354391
pid_t forkpty(int *amaster, char *name, const struct termios *termp, const struct winsize *winp) {
355392
RESOLVE_REAL(forkpty);
356393
if (!allow_create_subprocess()) return deny();
@@ -361,6 +398,7 @@ pid_t __forkpty(int *amaster, char *name, const struct termios *termp, const str
361398
if (!allow_create_subprocess()) return deny();
362399
return real___forkpty(amaster, name, termp, winp);
363400
}
401+
/* syscall wrapper to intercept syscalls that directly create processes */
364402
long (*real_syscall)(long, ...) = NULL;
365403
long syscall(long number, ...) {
366404
RESOLVE_REAL(syscall);
@@ -393,4 +431,4 @@ long syscall(long number, ...) {
393431
if (!allow_create_subprocess()) return deny();
394432
}
395433
return real_syscall(number, a1, a2, a3, a4, a5, a6);
396-
}
434+
}

0 commit comments

Comments
 (0)