mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
refactor: rename MAXKB_SANDBOX_PYTHON_BANNED_HOSTS to MAXKB_SANDBOX_PYTHON_ALLOW_HOSTS_REGEXES
This commit is contained in:
parent
88fc5dd398
commit
9029240931
|
|
@ -28,7 +28,7 @@ class ToolExecutor:
|
|||
if self.sandbox:
|
||||
os.system(f"chown -R {self.user}:root {self.sandbox_path}")
|
||||
self.banned_keywords = CONFIG.get("SANDBOX_PYTHON_BANNED_KEYWORDS", 'nothing_is_banned').split(',');
|
||||
self.banned_hosts = CONFIG.get("SANDBOX_PYTHON_BANNED_HOSTS", 'no_host_is_banned');
|
||||
self.allow_hosts_regexes = CONFIG.get("SANDBOX_PYTHON_ALLOW_HOSTS_REGEXES", '');
|
||||
|
||||
def _createdir(self):
|
||||
old_mask = os.umask(0o077)
|
||||
|
|
@ -200,7 +200,7 @@ exec({dedent(code)!a})
|
|||
kwargs = {'cwd': BASE_DIR}
|
||||
kwargs['env'] = {
|
||||
'LD_PRELOAD': '/opt/maxkb-app/sandbox/sandbox.so',
|
||||
'MAXKB_SANDBOX_PYTHON_BANNED_HOSTS': self.banned_hosts,
|
||||
'SANDBOX_ALLOW_HOSTS_REGEXES': self.allow_hosts_regexes,
|
||||
}
|
||||
subprocess_result = subprocess.run(
|
||||
['su', '-s', python_directory, '-c', "exec(open('" + exec_python_file + "').read())", self.user],
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ ENV PATH=/opt/py3/bin:$PATH \
|
|||
MAXKB_SANDBOX=1 \
|
||||
MAXKB_SANDBOX_PYTHON_PACKAGE_PATHS="/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages" \
|
||||
MAXKB_SANDBOX_PYTHON_BANNED_KEYWORDS="subprocess.,system(,exec(,execve(,pty.,eval(,compile(,shutil.,input(,__import__" \
|
||||
MAXKB_SANDBOX_PYTHON_BANNED_HOSTS="127.0.0.1,localhost" \
|
||||
MAXKB_SANDBOX_PYTHON_ALLOW_HOSTS_REGEXES=".*,!=127\.0\.0\.1,!=localhost,!=maxkb,!=pgsql,!=redis" \
|
||||
MAXKB_ADMIN_PATH=/admin
|
||||
|
||||
EXPOSE 6379
|
||||
|
|
@ -1,98 +1,125 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
#include <dlfcn.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <regex.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
static const char *ENV_NAME = "MAXKB_SANDBOX_PYTHON_BANNED_HOSTS";
|
||||
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
|
||||
static int (*real_getaddrinfo)(const char *, const char *, const struct addrinfo *, struct addrinfo **) = NULL;
|
||||
static __thread char last_resolved_host[256] = {0};
|
||||
static __thread int last_host_checked = 0; // 标记是否已检查过域名(1=已检查且允许)
|
||||
|
||||
static int match_env_patterns(const char *target, const char *env_val) {
|
||||
if (!target || !env_val || !*env_val) return 0;
|
||||
/** 检查是否符合允许规则 */
|
||||
static int is_allowed_by_env(const char *target, const char *env_val) {
|
||||
if (!target) return 0;
|
||||
if (!env_val || !*env_val) {
|
||||
fprintf(stderr, "[sandbox] ❌ No allow rules set — deny all by default\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *patterns = strdup(env_val);
|
||||
char *token = strtok(patterns, ",");
|
||||
int matched = 0;
|
||||
int allowed = 0;
|
||||
|
||||
while (token) {
|
||||
// 去掉前后空格
|
||||
while (*token == ' ' || *token == '\t') token++;
|
||||
char *end = token + strlen(token) - 1;
|
||||
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
|
||||
|
||||
if (*token) {
|
||||
regex_t regex;
|
||||
if (regcomp(®ex, token, REG_EXTENDED | REG_NOSUB) == 0) {
|
||||
if (regexec(®ex, target, 0, NULL, 0) == 0) {
|
||||
matched = 1;
|
||||
if (strncmp(token, "!=", 2) == 0) {
|
||||
const char *pattern = token + 2;
|
||||
regex_t regex;
|
||||
if (regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
|
||||
fprintf(stderr, "[sandbox] ⚠️ Invalid regex ignored: %s\n", pattern);
|
||||
} else {
|
||||
if (regexec(®ex, target, 0, NULL, 0) == 0) {
|
||||
fprintf(stderr, "[sandbox] ❌ Deny %s (matched deny /%s/)\n", target, pattern);
|
||||
regfree(®ex);
|
||||
free(patterns);
|
||||
return 0;
|
||||
}
|
||||
regfree(®ex);
|
||||
}
|
||||
} else {
|
||||
regex_t regex;
|
||||
if (regcomp(®ex, token, REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
|
||||
fprintf(stderr, "[sandbox] ⚠️ Invalid regex ignored: %s\n", token);
|
||||
} else {
|
||||
if (regexec(®ex, target, 0, NULL, 0) == 0)
|
||||
allowed = 1;
|
||||
regfree(®ex);
|
||||
break;
|
||||
}
|
||||
regfree(®ex);
|
||||
}
|
||||
}
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(patterns);
|
||||
return matched;
|
||||
return allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截 connect() —— 屏蔽直接 IP 访问
|
||||
*/
|
||||
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
|
||||
static char *banned_env = NULL;
|
||||
static int initialized = 0;
|
||||
/** 检查逻辑封装 */
|
||||
static int check_host(const char *host) {
|
||||
const char *env = getenv("SANDBOX_ALLOW_HOSTS_REGEXES");
|
||||
return is_allowed_by_env(host, env);
|
||||
}
|
||||
|
||||
/** 拦截 getaddrinfo() — 检查域名 */
|
||||
int getaddrinfo(const char *node, const char *service,
|
||||
const struct addrinfo *hints, struct addrinfo **res) {
|
||||
if (!real_getaddrinfo)
|
||||
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
|
||||
|
||||
if (node) {
|
||||
strncpy(last_resolved_host, node, sizeof(last_resolved_host) - 1);
|
||||
last_resolved_host[sizeof(last_resolved_host) - 1] = '\0';
|
||||
last_host_checked = 0;
|
||||
|
||||
// 判断是否为纯 IP(跳过 IPv4/IPv6)
|
||||
struct in_addr ipv4;
|
||||
struct in6_addr ipv6;
|
||||
int is_ip = (inet_pton(AF_INET, node, &ipv4) == 1) ||
|
||||
(inet_pton(AF_INET6, node, &ipv6) == 1);
|
||||
|
||||
if (!is_ip) {
|
||||
if (!check_host(node)) {
|
||||
fprintf(stderr, "[sandbox] 🚫 Blocked DNS lookup for %s\n", node);
|
||||
return EAI_FAIL;
|
||||
}
|
||||
last_host_checked = 1; // 已检查并通过
|
||||
}
|
||||
}
|
||||
|
||||
return real_getaddrinfo(node, service, hints, res);
|
||||
}
|
||||
|
||||
/** 拦截 connect() — 检查 IP(仅当没检查过域名) */
|
||||
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
if (!real_connect)
|
||||
real_connect = dlsym(RTLD_NEXT, "connect");
|
||||
|
||||
if (!initialized) {
|
||||
banned_env = getenv(ENV_NAME);
|
||||
initialized = 1;
|
||||
if (banned_env)
|
||||
fprintf(stderr, "[ban] Loaded banned hosts: %s\n", banned_env);
|
||||
}
|
||||
|
||||
if (!banned_env || !*banned_env)
|
||||
return real_connect(sockfd, addr, addrlen);
|
||||
|
||||
char ip[INET6_ADDRSTRLEN] = {0};
|
||||
if (addr->sa_family == AF_INET) {
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
|
||||
} else if (addr->sa_family == AF_INET6) {
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
|
||||
|
||||
// 如果域名已经检查通过,则跳过 IP 检查
|
||||
if (last_host_checked) {
|
||||
return real_connect(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
if (match_env_patterns(ip, banned_env)) {
|
||||
fprintf(stderr, "Access to host %s is banned for sandbox\n", ip);
|
||||
// 没有检查过域名(可能是 IP 直连,如 curl)
|
||||
if (!check_host(ip)) {
|
||||
fprintf(stderr, "[sandbox] 🚫 Blocked connect to %s (no domain check)\n", ip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return real_connect(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截 getaddrinfo() —— 屏蔽域名解析
|
||||
*/
|
||||
int getaddrinfo(const char *node, const char *service,
|
||||
const struct addrinfo *hints, struct addrinfo **res) {
|
||||
static int (*real_getaddrinfo)(const char *, const char *,
|
||||
const struct addrinfo *, struct addrinfo **) = NULL;
|
||||
if (!real_getaddrinfo)
|
||||
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
|
||||
|
||||
const char *banned_env = getenv(ENV_NAME);
|
||||
if (banned_env && node && match_env_patterns(node, banned_env)) {
|
||||
fprintf(stderr, "Access to host %s is banned for sandbox\n", node);
|
||||
return EAI_FAIL; // 模拟 DNS 失败
|
||||
}
|
||||
|
||||
return real_getaddrinfo(node, service, hints, res);
|
||||
}
|
||||
Loading…
Reference in New Issue