Skip to content

Commit 92167a9

Browse files
liqiang-fit2cloudliuruibin
authored andcommitted
fix: deny access to local services by IPv6 and IPv6-mapped IPv4 addresses for sandbox.
1 parent 77e6aac commit 92167a9

File tree

2 files changed

+110
-60
lines changed

2 files changed

+110
-60
lines changed

installer/Dockerfile-base

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ ENV PATH=/opt/py3/bin:$PATH \
4646
MAXKB_SANDBOX=1 \
4747
MAXKB_SANDBOX_HOME=/opt/maxkb-app/sandbox \
4848
MAXKB_SANDBOX_PYTHON_PACKAGE_PATHS="/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages" \
49-
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" \
49+
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,0.0.0.0/32,::1/128" \
5050
MAXKB_ADMIN_PATH=/admin
5151

5252
EXPOSE 6379

installer/sandbox.c

Lines changed: 109 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <regex.h>
99
#include <unistd.h>
1010
#include <sys/socket.h>
11+
#include <sys/un.h>
1112
#include <errno.h>
1213
#include <limits.h>
1314
#include <libgen.h>
@@ -89,109 +90,158 @@ static int is_sandbox_user() {
8990
* 限制网络访问
9091
*/
9192
// ------------------ 匹配 域名 黑名单 ------------------
92-
static int match_banned_domain(const char *target, const char *env_val) {
93-
if (!target || !env_val || !*env_val) return 0;
94-
char *patterns = strdup(env_val);
95-
char *token = strtok(patterns, ",");
93+
static int match_banned_domain(const char *target, const char *rules) {
94+
if (!target || !rules || !*rules) return 0;
95+
char *list = strdup(rules);
96+
char *token = strtok(list, ",");
9697
int matched = 0;
9798
while (token) {
9899
while (*token == ' ' || *token == '\t') token++;
99-
char *end = token + strlen(token) - 1;
100-
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
101100
if (*token) {
102-
regex_t regex;
103-
char fullpattern[512];
104-
snprintf(fullpattern, sizeof(fullpattern), "^%s$", token);
105-
if (regcomp(&regex, fullpattern, REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0) {
106-
if (regexec(&regex, target, 0, NULL, 0) == 0) {
101+
regex_t re;
102+
char buf[512];
103+
snprintf(buf, sizeof(buf), "^%s$", token);
104+
if (regcomp(&re, buf, REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0) {
105+
if (regexec(&re, target, 0, NULL, 0) == 0)
107106
matched = 1;
108-
regfree(&regex);
109-
break;
110-
}
111-
regfree(&regex);
107+
regfree(&re);
112108
}
113109
}
110+
if (matched) break;
114111
token = strtok(NULL, ",");
115112
}
116-
free(patterns);
113+
free(list);
117114
return matched;
118115
}
119116
// ------------------ 匹配 IP/CIDR 黑名单 ------------------
120-
static int match_banned_ip(const char *ip_str, const char *banned_list) {
121-
if (!ip_str || !banned_list || !*banned_list) return 0;
122-
char *list = strdup(banned_list);
117+
static int match_banned_ip(const char *ip_str, const char *rules) {
118+
if (!ip_str || !rules || !*rules) return 0;
119+
struct in_addr ip4;
120+
struct in6_addr ip6;
121+
int is_v4 = inet_pton(AF_INET, ip_str, &ip4) == 1;
122+
int is_v6 = inet_pton(AF_INET6, ip_str, &ip6) == 1;
123+
if (!is_v4 && !is_v6) return 0;
124+
char *list = strdup(rules);
123125
char *token = strtok(list, ",");
124126
int blocked = 0;
125127
while (token) {
126128
while (*token == ' ' || *token == '\t') token++;
127-
char *end = token + strlen(token) - 1;
128-
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
129-
if (*token) {
130-
char *slash = strchr(token, '/');
131-
if (!slash) {
132-
if (strcmp(ip_str, token) == 0) {
133-
blocked = 1;
134-
break;
129+
if (!*token) goto next;
130+
char *slash = strchr(token, '/');
131+
int prefix = -1;
132+
if (slash) {
133+
*slash++ = '\0';
134+
prefix = atoi(slash);
135+
}
136+
/* ---------- IPv4 ---------- */
137+
if (is_v4) {
138+
struct in_addr net4;
139+
if (inet_pton(AF_INET, token, &net4) == 1) {
140+
if (prefix < 0) {
141+
/* 单 IP */
142+
if (ip4.s_addr == net4.s_addr) {
143+
blocked = 1;
144+
break;
145+
}
146+
} else if (prefix >= 0 && prefix <= 32) {
147+
uint32_t mask = prefix == 0
148+
? 0
149+
: htonl(0xFFFFFFFFu << (32 - prefix));
150+
if ((ip4.s_addr & mask) == (net4.s_addr & mask)) {
151+
blocked = 1;
152+
break;
153+
}
135154
}
136-
} else {
137-
*slash = 0;
138-
int prefix = atoi(slash + 1);
139-
struct in_addr ip, net, mask;
140-
if (inet_pton(AF_INET, token, &net) == 1 &&
141-
inet_pton(AF_INET, ip_str, &ip) == 1) {
142-
mask.s_addr = prefix == 0 ? 0 : htonl(0xFFFFFFFF << (32 - prefix));
143-
if ((ip.s_addr & mask.s_addr) == (net.s_addr & mask.s_addr)) {
155+
}
156+
}
157+
/* ---------- IPv6 ---------- */
158+
if (is_v6) {
159+
struct in6_addr net6;
160+
if (inet_pton(AF_INET6, token, &net6) == 1) {
161+
if (prefix < 0) {
162+
/* 单 IP */
163+
if (memcmp(&ip6, &net6, sizeof(ip6)) == 0) {
144164
blocked = 1;
145165
break;
146166
}
167+
} else if (prefix >= 0 && prefix <= 128) {
168+
int full = prefix / 8;
169+
int rem = prefix % 8;
170+
if (full &&
171+
memcmp(ip6.s6_addr, net6.s6_addr, full) != 0)
172+
goto next;
173+
if (rem) {
174+
uint8_t mask = (uint8_t)(0xFF << (8 - rem));
175+
if ((ip6.s6_addr[full] & mask) !=
176+
(net6.s6_addr[full] & mask))
177+
goto next;
178+
}
179+
blocked = 1;
180+
break;
147181
}
148182
}
149183
}
184+
next:
150185
token = strtok(NULL, ",");
151186
}
152187
free(list);
153188
return blocked;
154189
}
190+
155191
// ------------------ 网络拦截 ------------------
156192
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
157193
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
158194
if (!real_connect)
159195
real_connect = dlsym(RTLD_NEXT, "connect");
160196
ensure_config_loaded();
197+
if (is_sandbox_user() && addr->sa_family == AF_UNIX) {
198+
struct sockaddr_un *un = (struct sockaddr_un *)addr;
199+
fprintf(stderr,
200+
"Permission denied to access unix socket: %s\n",
201+
un->sun_path[0] ? un->sun_path : "(abstract)");
202+
errno = EACCES;
203+
return -1;
204+
}
161205
char ip[INET6_ADDRSTRLEN] = {0};
162-
if (addr->sa_family == AF_INET)
163-
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
164-
else if (addr->sa_family == AF_INET6)
165-
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
166-
167-
if (is_sandbox_user() && banned_hosts && *banned_hosts) {
168-
if (ip[0] && match_banned_ip(ip, banned_hosts)) {
169-
fprintf(stderr, "Permission denied to access %s.\n", ip);
170-
errno = EACCES; // Permission denied
171-
return -1;
206+
if (addr->sa_family == AF_INET) {
207+
inet_ntop(AF_INET,
208+
&((struct sockaddr_in *)addr)->sin_addr,
209+
ip, sizeof(ip));
210+
} else if (addr->sa_family == AF_INET6) {
211+
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
212+
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
213+
struct in_addr v4;
214+
memcpy(&v4, &sin6->sin6_addr.s6_addr[12], sizeof(v4));
215+
inet_ntop(AF_INET, &v4, ip, sizeof(ip));
216+
} else {
217+
inet_ntop(AF_INET6, &sin6->sin6_addr, ip, sizeof(ip));
172218
}
173219
}
220+
if (is_sandbox_user() && match_banned_ip(ip, banned_hosts)) {
221+
fprintf(stderr, "Permission denied to access %s.\n", ip);
222+
errno = EACCES;
223+
return -1;
224+
}
174225
return real_connect(sockfd, addr, addrlen);
175226
}
176227
int getaddrinfo(const char *node, const char *service,
177-
const struct addrinfo *hints, struct addrinfo **res) {
228+
const struct addrinfo *hints,
229+
struct addrinfo **res) {
178230
static int (*real_getaddrinfo)(const char *, const char *,
179-
const struct addrinfo *, struct addrinfo **) = NULL;
231+
const struct addrinfo *,
232+
struct addrinfo **) = NULL;
180233
if (!real_getaddrinfo)
181234
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
182235
ensure_config_loaded();
183-
if (banned_hosts && *banned_hosts && node && is_sandbox_user()) {
184-
struct in_addr ipv4;
185-
struct in6_addr ipv6;
186-
int is_ip = inet_pton(AF_INET, node, &ipv4) == 1 ||
187-
inet_pton(AF_INET6, node, &ipv6) == 1;
188-
if (!is_ip) {
189-
// 仅对域名进行阻塞
190-
if (match_banned_domain(node, banned_hosts)) {
191-
fprintf(stderr, "Permission denied to access %s.\n", node);
192-
errno = EACCES;
193-
return EAI_SYSTEM;
194-
}
236+
if (node && is_sandbox_user()) {
237+
struct in_addr ip4;
238+
struct in6_addr ip6;
239+
int is_ip = inet_pton(AF_INET, node, &ip4) == 1 ||
240+
inet_pton(AF_INET6, node, &ip6) == 1;
241+
if (!is_ip && match_banned_domain(node, banned_hosts)) {
242+
fprintf(stderr, "Permission denied to access %s.\n", node);
243+
errno = EACCES;
244+
return EAI_SYSTEM;
195245
}
196246
}
197247
return real_getaddrinfo(node, service, hints, res);

0 commit comments

Comments
 (0)