v0.7.1: allow network inside the run_shell sandbox
ci / linux (clang) (push) Successful in 4m44s
ci / linux (gcc) (push) Successful in 5m36s
ci / musl (push) Failing after 29s
ci / windows-cross (push) Successful in 31m37s

Landlock no longer handles TCP, the Seatbelt profile drops (deny network*),
and the seccomp filter stops refusing IPv4/IPv6 sockets. Sandboxed commands
run without asking in auto mode on every backend (Low-IL no longer asks for
network commands), and connection errors are not treated as sandbox blocks.
This commit is contained in:
WHC
2026-10-08 21:05:14 +08:00
parent 895b47274c
commit 32b4afd772
8 changed files with 36 additions and 62 deletions
+6 -12
View File
@@ -259,7 +259,7 @@ static void put_safety(cst_buf_t *b, const cst_agent_config_t *cfg) {
"the task requires it. Never print secrets or API keys.\n");
if (tool_on(cfg, "run_shell"))
cst_buf_puts(b, "- run_shell may run in a sandbox (writes only inside the "
"workspace and temp dir, no network). If a command is "
"workspace and temp dir; the network works). If a command is "
"blocked the user is asked whether to rerun it outside; "
"do not try to work around the sandbox.\n");
}
@@ -827,13 +827,9 @@ static void sandbox_escalate(turn_t *t, cst_tool_ctx_t *ctx,
}
if (ans == CST_PERM_DENY) {
const char *note =
cst_sandbox_probe() == CST_SANDBOX_LOWIL
? "\n[cst: blocked by the sandbox (writes are limited to the "
"workspace and temp dir); the user did not allow running it "
"outside]\n"
: "\n[cst: blocked by the sandbox (writes are limited to "
"the workspace and temp dir, no network); the user did "
"not allow running it outside]\n";
"\n[cst: blocked by the sandbox (writes are limited to the "
"workspace and temp dir); the user did not allow running it "
"outside]\n";
cst_buf_t b = {0};
if (tr->out)
cst_buf_append(&b, tr->out, tr->out_len);
@@ -989,11 +985,9 @@ static void exec_begin(turn_t *t, pcall_t *pc, exec_t *x) {
if (def->perm == CST_TP_WRITE) {
ask = 0;
} else if (def->perm == CST_TP_SHELL) {
int sb = cst_sandbox_probe();
if (sb != CST_SANDBOX_NONE) {
if (cst_sandbox_probe() != CST_SANDBOX_NONE) {
ctx->sandbox = 1;
/* the Low-IL sandbox does not stop network access */
ask = sb == CST_SANDBOX_LOWIL && cmd && cst_shell_uses_network(cmd);
ask = 0;
} else if (cmd && cst_shell_is_safe(cmd)) {
ask = 0;
}
+5 -19
View File
@@ -86,8 +86,6 @@ static int g_abi;
#define LL_FS_MAKE_SYM (1ULL << 12)
#define LL_FS_REFER (1ULL << 13) /* ABI 2 */
#define LL_FS_TRUNCATE (1ULL << 14) /* ABI 3 */
#define LL_NET_BIND_TCP (1ULL << 0) /* ABI 4 */
#define LL_NET_CONNECT_TCP (1ULL << 1) /* ABI 4 */
struct ll_ruleset_attr {
uint64_t handled_access_fs;
@@ -137,9 +135,7 @@ static int ll_prepare(const char *ws, const char *tmp, cst_sandbox_plan_t *p) {
int abi = g_abi;
struct ll_ruleset_attr attr;
memset(&attr, 0, sizeof(attr));
attr.handled_access_fs = ll_write_rights(abi);
if (abi >= 4)
attr.handled_access_net = LL_NET_BIND_TCP | LL_NET_CONNECT_TCP;
attr.handled_access_fs = ll_write_rights(abi); /* network stays open */
int fd = (int)syscall(__NR_landlock_create_ruleset, &attr, sizeof(attr), 0);
if (fd < 0)
return -1;
@@ -193,8 +189,7 @@ static int sb_prepare(const char *ws, const char *tmp, cst_sandbox_plan_t *p) {
"(version 1)(allow default)(deny file-write*)"
"(allow file-write* (subpath \"%s\") (subpath \"%s\") "
"(literal \"/dev/null\") (literal \"/dev/tty\") "
"(literal \"/dev/dtracehelper\"))"
"(deny network*)",
"(literal \"/dev/dtracehelper\"))",
qws, qtmp);
return 0;
}
@@ -290,9 +285,8 @@ static const int SC_NOTIFY[] = {
#endif
};
/* Foreign ABIs are refused, IPv4/IPv6 sockets fail with EACCES, opens
* without write flags pass untouched, write-type calls go to the
* supervisor. */
/* Foreign ABIs are refused, opens without write flags pass untouched,
* write-type calls go to the supervisor. */
static void sc_build(sc_prog_t *p) {
p->n = 0;
SC_LD(offsetof(struct seccomp_data, arch));
@@ -303,12 +297,6 @@ static void sc_build(sc_prog_t *p) {
sc_emit(p, BPF_JMP | BPF_JGE | BPF_K, 0, 1, 0x40000000U); /* x32 */
SC_RET(SECCOMP_RET_ERRNO | EPERM);
#endif
SC_JEQ(__NR_socket, 0, 5);
SC_LD(SC_ARG(0));
SC_JEQ(AF_INET, 2, 0);
SC_JEQ(AF_INET6, 1, 0);
SC_RET(SECCOMP_RET_ALLOW);
SC_RET(SECCOMP_RET_ERRNO | EACCES);
SC_JEQ(__NR_io_uring_setup, 0, 1);
SC_RET(SECCOMP_RET_ERRNO | EPERM);
SC_JEQ(__NR_openat, 0, 4);
@@ -1446,9 +1434,7 @@ int cst_sandbox_blocked(const char *out, size_t n, int exit_code) {
static const char *const SIGNS[] = {
"Permission denied", "Read-only file system", "Operation not permitted",
"EACCES", "EPERM", "EROFS",
"[cst: sandbox", "Network is unreachable",
/* curl when the socket itself was refused (seccomp) */
"Could not connect to server", "Failed to connect to",
"[cst: sandbox",
/* localized strerror(EACCES/EPERM/EROFS) */
"权限不够", "不允许的操作", "只读文件系统", "權限不足",
"拒絕不符權限的操作", "許可がありません", "Keine Berechtigung",