Назад к обновлениям
New releaseSep 12, 2026

sandbox-runtime v0.0.76

Легковесный инструмент для песочницы, обеспечивающий соблюдение ограничений файловой системы и сети для произвольных процессов на уровне ОС, без необходимости в контейнере.

Поделиться

Anthropic Sandbox Runtime (srt)

Легковесный инструмент для создания песочницы, позволяющий применять ограничения файловой системы и сети к произвольным процессам на уровне ОС без необходимости использования контейнера.

srt использует нативные примитивы песочницы ОС (sandbox-exec в macOS, bubblewrap в Linux) и прокси-фильтрацию сети. Его можно использовать для изоляции поведения агентов, локальных MCP-серверов, bash-команд и произвольных процессов.

Бета-версия исследовательского превью

Sandbox Runtime — это исследовательское превью, разработанное для Claude Code с целью обеспечения безопасности AI-агентов. Оно предоставляется в качестве раннего превью с открытым исходным кодом, чтобы помочь более широкой экосистеме создавать более безопасные агентные системы. Поскольку это раннее исследовательское превью, API и форматы конфигурации могут меняться. Мы приветствуем отзывы и вклад, чтобы сделать AI-агентов безопасными по умолчанию!

Установка```bash

npm install -g @anthropic-ai/sandbox-runtime

## Базовое использование```bash
# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html>  # Request succeeds

$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist  # Request blocked

# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb...  # Current directory access allowed

$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted  # Specific file blocked

Обзор

Этот пакет предоставляет автономную реализацию песочницы, которую можно использовать как в качестве инструмента CLI, так и в качестве библиотеки. Он разработан с философией secure-by-default, ориентированной на типичные сценарии использования разработчиками: процессы запускаются с минимальным доступом, и вы явно открываете только те отверстия, которые вам нужны.

Ключевые возможности:

  • Сетевые ограничения: управление доступом к хостам/доменам через HTTP/HTTPS и другие протоколы
  • Ограничения файловой системы: управление доступом к файлам/каталогам для чтения/записи
  • Ограничения Unix-сокетов: управление доступом к локальным IPC-сокетам
  • Мониторинг нарушений: в macOS — доступ к системному хранилищу журнала нарушений песочницы для оповещений в реальном времени

Пример использования: изоляция серверов MCP

Ключевой сценарий — изоляция серверов Model Context Protocol (MCP) для ограничения их возможностей. Например, чтобы изолировать файловый сервер MCP:

Без песочницы (.mcp.json):```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem"] } } }

**С песочницей** (`.mcp.json`):```json
{
  "mcpServers": {
    "filesystem": {
      "command": "srt",
      "args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
    }
  }
}

Затем настройте ограничения в ~/.srt-settings.json:```json { "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": ["~/sensitive-folder"] }, "network": { "allowedDomains": [], "deniedDomains": [] } }

Теперь MCP-серверу будет запрещена запись в запрещённый путь:```
> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'

Как это работает

Песочница использует примитивы уровня ОС для применения ограничений, которые действуют на всё дерево процессов:

  • macOS: Использует sandbox-exec с динамически генерируемыми профилями Seatbelt
  • Linux: Использует bubblewrap для контейнеризации с изоляцией сетевого пространства имён
  • Windows: Запускает изолированный процесс под выделенной локальной учётной записью srt-sandbox, с Windows Filtering Platform для блокировки исходящего трафика, привязанной к SID этой учётной записи, и явными ACE для каждой сессии на рабочем дереве

0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305

Модель двойной изоляции

Для эффективной изоляции необходима как файловая, так и сетевая изоляция. Без файловой изоляции скомпрометированный процесс мог бы похитить SSH-ключи или другие конфиденциальные файлы. Без сетевой изоляции процесс мог бы вырваться из песочницы и получить неограниченный доступ к сети.

Файловая изоляция применяет ограничения на чтение и запись:

  • Чтение (шаблон «сначала запретить, затем разрешить»): По умолчанию доступ на чтение разрешён везде. Вы можете запретить широкие области (например, /Users), а затем снова разрешить конкретные пути внутри них (например, .). allowRead имеет приоритет над denyRead — в отличие от записи, где denyWrite имеет приоритет над allowWrite. Запись denyRead, которая более конкретна, чем область allowRead, в которую она попадает (например, denyRead: ["**/.env"] или ["./secrets"] при allowRead: ["."]), всё равно остаётся запрещённой.
  • Запись (шаблон «только разрешить»): По умолчанию доступ на запись запрещён везде. Вы должны явно разрешить пути (например, ., /tmp). Пустой список разрешений означает отсутствие доступа на запись.

Сетевая изоляция (шаблон «только разрешить»): По умолчанию весь сетевой доступ запрещён. Вы должны явно разрешить домены. Пустой список allowedDomains означает отсутствие сетевого доступа. Сетевой трафик маршрутизируется через прокси-серверы, работающие на хосте:

  • Linux: Запросы маршрутизируются через файловую систему по Unix-сокету. Сетевое пространство имён изолированного процесса полностью удаляется, поэтому весь сетевой трафик должен проходить через прокси, работающие на хосте (прослушивающие Unix-сокеты, которые примонтированы в песочницу)

  • macOS: Профиль Seatbelt разрешает связь только с определённым портом localhost. Прокси прослушивают этот порт, создавая контролируемый канал для всего сетевого доступа

  • Windows: Набор фильтров WFP на уровне всей машины блокирует все исходящие соединения, исходящие от учётной записи srt-sandbox, кроме loopback к диапазону портов прокси. Прокси прослушивают внутри этого диапазона, создавая контролируемый канал для всего сетевого доступа

Как HTTP/HTTPS (через HTTP-прокси), так и другой TCP-трафик (через SOCKS5-прокси) опосредуются этими прокси, которые применяют ваши списки разрешённых и запрещённых доменов.

Для получения дополнительной информации об изоляции в Claude Code см.:

Архитектура```

src/ ├── index.ts # Library exports ├── cli.ts # CLI entrypoint (srt command) ├── utils/ # Shared utilities │ ├── debug.ts # Debug logging │ ├── settings.ts # Settings reader (permissions + sandbox config) │ ├── platform.ts # Platform detection │ └── exec.ts # Command execution utilities └── sandbox/ # Sandbox implementation ├── sandbox-manager.ts # Main sandbox manager ├── sandbox-schemas.ts # Zod schemas for validation ├── sandbox-violation-store.ts # Violation tracking ├── sandbox-utils.ts # Shared sandbox utilities ├── http-proxy.ts # HTTP/HTTPS proxy for network filtering ├── socks-proxy.ts # SOCKS5 proxy for network filtering ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing ├── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing └── windows-sandbox-utils.ts # Windows srt-win sandboxing

## Использование

### Как инструмент CLI

Команда `srt` (Anthropic Sandbox Runtime) оборачивает любую команду в границы безопасности:```bash
# Run a command in the sandbox
srt echo "hello world"

# With debug logging
srt --debug curl https://example.com

# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install

Как библиотека```typescript

import { SandboxManager, type SandboxRuntimeConfig, } from '@anthropic-ai/sandbox-runtime' import { spawn } from 'child_process'

// Define your sandbox configuration const config: SandboxRuntimeConfig = { network: { allowedDomains: ['example.com', 'api.github.com'], deniedDomains: [], }, filesystem: { denyRead: ['~/.ssh'], allowWrite: ['.', '/tmp'], denyWrite: ['.env'], }, }

// Initialize the sandbox (starts proxy servers, etc.) await SandboxManager.initialize(config)

// Wrap a command with sandbox restrictions const sandboxedCommand = await SandboxManager.wrapWithSandbox( 'curl https://example.com', )

// Execute the sandboxed command const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })

// Handle exit and cleanup after child process completes child.on('exit', async code => { console.log(Command exited with code ${code}) // Cleanup when done (optional, happens automatically on process exit) await SandboxManager.reset() })

**Атрибуция нарушений (`commandId` / `commandText`).** Нарушения, зафиксированные во время выполнения обёрнутой команды (строки журнала seatbelt, события seccomp, отказы прокси), сохраняются под ключом атрибуции, и `annotateStderrWithSandboxFailures(key, stderr)` / `getViolationsForCommand(key)` извлекают их по тому же ключу. По умолчанию ключом служит сама обёрнутая строка. Передайте непрозрачный `commandId` для каждого вызова (например, идентификатор использования инструмента), чтобы использовать его в качестве ключа — рекомендуется: ключи сравниваются по первым 100 символам, поэтому длинные команды с общим префиксом иначе перекрёстно атрибутировались бы, а повторный запуск того же текста унаследовал бы события предыдущего запуска. Если строка, которую вы *выполняете*, не является командой, которую представляет вызов (например, вы оборачиваете собранную `source <snapshot> && eval '<cmd>'`), также передайте `commandText: '<cmd>'`: именно с ним сопоставляются шаблоны команд `ignoreViolations` и именно его каждое нарушение указывает как свою `command`.```typescript
const wrapped = await SandboxManager.wrapWithSandbox(
  assembledCommand, // what actually runs
  undefined,
  undefined,
  undefined,
  { commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(invocationId, stderr)

Доступные экспорты```typescript

// Main sandbox manager export { SandboxManager } from '@anthropic-ai/sandbox-runtime'

// Violation tracking export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'

// TypeScript types export type { SandboxRuntimeConfig, NetworkConfig, FilesystemConfig, IgnoreViolationsConfig, SandboxAskCallback, FsReadRestrictionConfig, FsWriteRestrictionConfig, NetworkRestrictionConfig, } from '@anthropic-ai/sandbox-runtime'

## Конфигурация

### Расположение файла настроек

По умолчанию среда выполнения песочницы ищет конфигурацию по пути `~/.srt-settings.json`. Вы можете указать собственный путь с помощью флага `--settings`:```bash
srt --settings /path/to/srt-settings.json <command>

Полный пример конфигурации```json

{ "network": { "allowedDomains": [ "github.com", ".github.com", "lfs.github.com", "api.github.com", "npmjs.org", ".npmjs.org" ], "deniedDomains": ["malicious.com"], "allowUnixSockets": ["/var/run/docker.sock"], "allowLocalBinding": false }, "filesystem": { "denyRead": ["~/.ssh"], "allowRead": [], "allowWrite": [".", "src/", "test/", "/tmp"], "denyWrite": [".env", "config/production.json"] }, "ignoreViolations": { "*": ["/usr/bin", "/System"], "git push": ["/usr/bin/nc"], "npm": ["/private/tmp"] }, "enableWeakerNestedSandbox": false, "enableWeakerNetworkIsolation": false, "allowAppleEvents": false }

### Параметры конфигурации

#### Конфигурация сети

Использует **шаблон «только разрешённое»** — весь сетевой доступ по умолчанию запрещён.

- `network.allowedDomains` — Массив разрешённых доменов (поддерживает подстановочные знаки, например `*.example.com`). Пустой массив = нет сетевого доступа. Необязательный суффикс `:port` (`api.example.com:443`, `*.example.com:8443`) ограничивает запись этим портом назначения; записи без порта соответствуют любому порту.
  - Литералы IPv6 должны быть заключены в квадратные скобки в стиле RFC 3986: `[::1]`, `[2001:db8::1]:443`. Запись с несколькими двоеточиями без скобок отклоняется как неоднозначная (`2001:db8::1:443` сам по себе является допустимым адресом).
- `network.deniedDomains` — Массив запрещённых доменов (проверяется первым, имеет приоритет над allowedDomains). Тот же суффикс `:port`, и голый `*` (или `*:22`) принимается для запрета всего.
- `network.deniedDomainReasons` — Необязательная карта от записи `deniedDomains` (сопоставляемой по точной строке) к причине, отображаемой модели, которая появляется в строке `<sandbox_violations>`, когда эта запись запрещает соединение — укажите, что заблокировано, и санкционированную альтернативу (например, `{"github.com:22": "SSH pushes to GitHub are blocked; use an https:// remote"}`). Записи без причины сообщают общую причину. Для SSH-назначений (порт 22) причина также доставляется внутриполосно: SSH-клиент, туннелированный через SOCKS ProxyCommand без аутентификации (например, BSD `nc -X 5`), получает разрыв SSH до обмена ключами, описание которого является причиной, и OpenSSH печатает его дословно — держите такие причины короче ~400 ASCII-символов, начиная с императива, поскольку OpenSSH усекает и экранирует не-ASCII.
- `network.allowLocalBinding` — Разрешить привязку к локальным портам (логическое значение, по умолчанию: false)

**Проверка разрешённого адреса.** Списки разрешения/запрета сопоставляются по _имени_, но тот, кто контролирует DNS разрешённого имени (или любой метки под разрешённым подстановочным знаком), контролирует, во что оно разрешается. Поэтому перед прямым подключением к разрешённому **имени хоста** прокси разрешает его один раз, отбрасывает любой адрес из запрещённого набора и подключается к выжившему адресу (именно тот адрес, который прошёл проверку, и набирается — второго поиска нет). Если ничего не выживает, соединение отклоняется, как и любой другой отказ по политике: HTTP/CONNECT получают `403` (`X-Proxy-Error: blocked-by-sandbox-runtime`, причина в теле), SOCKS получает «connection not allowed by ruleset», а строка `deny network-outbound host:port (resolved to a loopback address)` — называющая класс адреса (loopback, link-local, this host's, cloud metadata, deny-listed, listed, …), а не сам адрес, который несёт только журнал отладки — записывается в хранилище нарушений.

Запрещённый набор: loopback (`127.0.0.0/8`, `::1`), unspecified (`0.0.0.0/8`, `::`), link-local (`169.254.0.0/16`, `fe80::/10`), multicast (`224.0.0.0/4`, `ff00::/8`), broadcast, конечные точки облачных метаданных экземпляра / платформы, находящиеся вне link-local (`100.100.100.200`, `168.63.129.16`, `192.0.0.192`, `fd00:ec2::/32`, `fd20:ce::254`, `fd00:c1::a9fe:a9fe`, `fd00:42::42`), каждый адрес, назначенный в данный момент одному из собственных сетевых интерфейсов этого хоста (служба, привязанная к `0.0.0.0`, отвечает на LAN или глобальном адресе точно так же, как на loopback), каждый IP-литерал, перечисленный в `deniedDomains` (с учётом его `:port`, если он есть), и всё в `deniedResolvedAddresses`. Записи IPv4 также соответствуют формам IPv6, несущим адрес IPv4 — IPv4-mapped, IPv4-compatible и IPv4-translated адреса, well-known префикс NAT64 (`64:ff9b::/96`) и 6to4 (`2002::/16`) оцениваются по встроенному в них адресу IPv4. Префикс NAT64 local-use `64:ff9b:1::/48` и сетевые префиксы не декодируются — их расположение (RFC 6052 допускает IPv4 в нескольких позициях) невозможно распознать по одному адресу; в такой сети перечислите трансляции префикса для диапазонов, которые вы запрещаете (например, `<prefix>::a00:0/104` для `10.0.0.0/8`). Адреса, достигающие этого хоста без назначения ему — публичный адрес 1:1-NAT облачного экземпляра, проброс портов маршрутизатора, псевдоним host-gateway контейнера или ВМ — не покрываются автоматически; перечислите их в `deniedResolvedAddresses`.

Что проверка не трогает: записи списка разрешений, которые **являются** IP-литералами (разрешение `127.0.0.1:3000` — это явный выбор) — и, по той же логике, имя хоста может разрешиться в иначе запрещённый адрес, когда этот IP-литерал (на этом порту) сам находится в `allowedDomains`, поскольку достижение его по имени не даёт ничего, чего не даёт запись литерала (IP-литерал в `deniedDomains` всё равно побеждает, точно так же, как для запроса литерала). Так что в dev-настройке, где `myapp.test` сопоставляется с локальным сервером через `/etc/hosts`, список разрешений — `["myapp.test", "127.0.0.1:3000"]`; отдельного списка исключений нет. `localhost` и имена под `.localhost` разрешаются в loopback (или разрешённый литерал) и ни во что другое. Проверка не вычисляется для соединений, маршрутизируемых через `parentProxy` (включая тот, что подхвачен из `HTTP_PROXY` / `HTTPS_PROXY` в собственном окружении srt) или `mitmProxy` — этот хоп разрешает имя и владеет собственной политикой адресов — и она управляет только тем, к чему подключается прокси: в macOS `allowLocalBinding` отдельно позволяет процессу в песочнице подключаться к loopback-портам, вообще не проходя через прокси.

- `network.deniedResolvedAddresses` — Дополнительные IP-адреса / CIDR-диапазоны (IPv4 или IPv6, без скобок, любой порт), в которые разрешённые имена хостов не должны разрешаться. Пространство частного использования не запрещено по умолчанию, поскольку разрешение имени хоста интрасети является законным; перечислите его здесь, когда разрешённые имена должны оставаться вне его, например `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "fc00::/7"]`. Перечисляйте диапазоны IPv4 и IPv6 отдельно — диапазон IPv6, достаточно широкий, чтобы охватить блок IPv4-mapped (`::ffff:0:0/96`), такой как `::/0`, соответствует ответам IPv4 на одних средах выполнения, но не на других, поэтому не полагайтесь на него для запрета IPv4.

**Завершение TLS** (`network.tlsTerminate`, экспериментально): когда задано, HTTPS CONNECT завершаются внутри процесса, чтобы SRT мог видеть (и фильтровать через `network.filterRequest`) расшифрованные запросы. Процесс в песочнице направляется на доверенный набор, содержащий MITM CA (`caCertPath`/`caKeyPath`, или эфемерный CA, если опущено) плюс обычные корневые сертификаты хоста, так что и сертификаты, выпущенные прокси, и реальные вышестоящие сертификаты проходят проверку.

- `network.tlsTerminate.excludeDomains` — Шаблоны доменов (тот же синтаксис, что и `allowedDomains`), которые **не** завершаются. Соответствующие CONNECT вместо этого туннелируются непрозрачно: они по-прежнему подчиняются списку разрешённых доменов, но клиент внутри песочницы завершает собственное рукопожатие TLS с реальным вышестоящим узлом, и `filterRequest` / внедрение учётных данных не применяются к их HTTPS-трафику. Используйте это для двух случаев, которые завершение TLS принципиально ломает:
  - **Вышестоящие узлы mTLS** — только клиент внутри песочницы владеет клиентским сертификатом, поэтому прокси не может переинициировать соединение от его имени.
  - **Клиенты с закреплением сертификатов** — клиенты, которые сами проверяют идентичность вышестоящего узла (пользовательские CA, закрепление SAN) и отклоняют сертификат MITM.
- `network.tlsTerminate.extraCaCertPaths` — Пути к PEM-файлам сертификатов CA, добавляемым к этому доверенному набору, после MITM CA и обычных корневых сертификатов хоста. Исключённые (не завершаемые) хосты проверяются клиентом внутри песочницы, а переменные окружения доверия, устанавливаемые SRT (`SSL_CERT_FILE`, `GIT_SSL_CAINFO`, ...), _заменяют_ собственную конфигурацию доверия каждого инструмента, поэтому локальный корневой сертификат сайта (например, внутренний mTLS CA) должен быть в наборе, иначе эти хосты никогда не смогут быть проверены. В набор копируются только блоки `CERTIFICATE` каждого файла (всё остальное, например закрытый ключ в комбинированном PEM, никогда не раскрывается песочнице); файлы, которые отсутствуют, недоступны для чтения или не содержат PEM-блок `CERTIFICATE`, пропускаются, поэтому безопасно перечислять пути, которые существуют только на некоторых хостах.```json
{
  "network": {
    "allowedDomains": ["*.example.com", "internal-mtls.example.net"],
    "deniedDomains": [],
    "tlsTerminate": {
      "excludeDomains": ["internal-mtls.example.net"],
      "extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
    }
  }
}

Настройки Unix-сокетов (поведение, зависящее от платформы):

НастройкаmacOSLinux
allowUnixSockets: string[]Список разрешённых путей сокетовИгнорируется (seccomp не может фильтровать по пути)
allowAllUnixSockets: booleanРазрешить все сокетыОтключить блокировку seccomp

Unix-сокеты заблокированы по умолчанию на обеих платформах.

  • macOS: Используйте allowUnixSockets для разрешения конкретных путей (например, ["/var/run/docker.sock"]), или allowAllUnixSockets: true для разрешения всех.
  • Linux: Блокировка использует фильтры seccomp (только x64/arm64). Если seccomp недоступен, сокеты не ограничиваются и выводится предупреждение. Используйте allowAllUnixSockets: true для явного отключения блокировки.

Конфигурация файловой системы

Использует два разных шаблона:

Ограничения чтения (шаблон «запретить, затем разрешить») — все чтения разрешены по умолчанию:

  • filesystem.denyRead — Массив путей, для которых запрещено чтение. Пустой массив = полный доступ на чтение.
  • filesystem.allowRead — Массив путей, для которых повторно разрешается чтение в запрещённых областях (имеет приоритет над denyRead). Примечание: это противоположность записи, где denyWrite имеет приоритет над allowWrite.

Ограничения записи (шаблон «только разрешить») — все записи запрещены по умолчанию:

  • filesystem.allowWrite — Массив путей, для которых разрешена запись. Пустой массив = нет доступа на запись.
  • filesystem.denyWrite — Массив путей, для которых запрещена запись в разрешённых путях (имеет приоритет над allowWrite)

Синтаксис путей (macOS):

Пути поддерживают glob-шаблоны в стиле git на macOS, аналогично синтаксису .gitignore:

  • * — Соответствует любым символам, кроме / (например, *.ts соответствует foo.ts, но не foo/bar.ts)
  • ** — Соответствует любым символам, включая / (например, src/**/*.ts соответствует всем файлам .ts в src/)
  • ? — Соответствует любому одиночному символу, кроме / (например, file?.txt соответствует file1.txt)
  • [abc] — Соответствует любому символу из набора (например, file[0-9].txt соответствует file3.txt)

Примеры:

  • "allowWrite": ["src/"] — Разрешить запись во всю директорию src/
  • "allowWrite": ["src/**/*.ts"] — Разрешить запись во все файлы .ts в src/ и поддиректориях
  • "denyRead": ["~/.ssh"] — Запретить чтение директории SSH
  • "denyRead": ["/Users"], "allowRead": ["."] — Запретить чтение всего /Users, но повторно разрешить текущую директорию
  • "denyWrite": [".env"] — Запретить запись в файл .env (даже если текущая директория разрешена)

Синтаксис путей (Linux):

Linux в настоящее время не поддерживает сопоставление по glob-шаблонам. Используйте только буквальные пути:

  • "allowWrite": ["src/"] — Разрешить запись в директорию src/
  • "denyRead": ["/home/user/.ssh"] — Запретить чтение директории SSH
  • "denyRead": ["/home"], "allowRead": ["."] — Запретить чтение всего /home, но повторно разрешить текущую директорию

Все платформы:

  • Пути могут быть абсолютными (например, /home/user/.ssh) или относительными к текущей рабочей директории (например, ./src)
  • ~ раскрывается в домашнюю директорию пользователя

Прочая конфигурация

  • ignoreViolations — Объект, сопоставляющий шаблоны команд с массивами путей, для которых нарушения должны игнорироваться
  • enableWeakerNestedSandbox — Включить более слабый режим песочницы для сред Docker (boolean, по умолчанию: false)
  • javaAgentJarPath — macOS/Linux: абсолютный путь к srt-proxy-agent.jar, JVM-агенту, внедряемому через JAVA_TOOL_OPTIONS (см. «JVM tools» в разделе «Сетевая изоляция»). Требуется только потребителям, которые включают в себя sandbox-runtime и поставляют jar отдельно; обычная установка через npm находит его в vendor/java-proxy-agent/.
  • enableWeakerNetworkIsolation — Разрешить доступ к com.apple.trustd.agent в песочнице macOS (boolean, по умолчанию: false). Это необходимо для программ на Go (gh, gcloud, terraform, kubectl и т. д.) для проверки TLS-сертификатов при использовании httpProxyPort с MITM-прокси и пользовательским CA. Предупреждение о безопасности: включение этого открывает потенциальный вектор эксфильтрации данных через службу trustd.
  • allowAppleEvents — Разрешить отправку Apple Events и запросов на открытие Launch Services из песочницы macOS (boolean, по умолчанию: false). Без этого команды вроде open, osascript и всё, что открывает URL-адреса или скрипты в других приложениях через AppleScript, завершаются ошибкой AppleScript -600 («Application isn't running») или ошибками LaunchServices (-10822, -54). Предупреждение о безопасности: включение этого означает, что песочница больше не обеспечивает изоляцию выполнения кода. Команда в песочнице может запускать другие приложения через open без запроса пользователя, и всё, что она запускает, выполняется вне ограничений файловой системы и сети песочницы; управление уже запущенными приложениями через Apple Events дополнительно ограничено согласием пользователя на автоматизацию TCC для каждого приложения. Встраивающие системы должны получать эту опцию только из доверенной конфигурации уровня пользователя — никогда из локальных файлов проекта в проверенном репозитории, что позволило бы проекту, созданному злоумышленником, повысить собственные разрешения песочницы.

Типовые рецепты конфигурации

Разрешить доступ к GitHub (все необходимые эндпоинты):```json { "network": { "allowedDomains": [ "github.com", "*.github.com", "lfs.github.com", "api.github.com" ], "deniedDomains": [] }, "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": [] } }

**Ограничить определёнными каталогами:**```json
{
  "network": {
    "allowedDomains": [],
    "deniedDomains": []
  },
  "filesystem": {
    "denyRead": ["~/.ssh"],
    "allowWrite": [".", "src/", "test/"],
    "denyWrite": [".env", "secrets/"]
  }
}

Доступ к файловой системе только в пределах рабочей области (запретить чтение за пределами рабочей области):```json { "network": { "allowedDomains": [], "deniedDomains": [] }, "filesystem": { "denyRead": ["/Users"], "allowRead": ["."], "allowWrite": ["."], "denyWrite": [] } }

Это запрещает чтение всего, что находится в `/Users` (или `/home` в Linux), а затем снова разрешает текущий рабочий каталог. Системные пути (`/usr`, `/lib` и т. д.) остаются доступными для чтения.

### Распространённые проблемы и советы

**Запуск Jest:** Используйте флаг `--no-watchman`, чтобы избежать нарушений песочницы:```bash
srt "jest --no-watchman"

Watchman обращается к файлам за пределами песочницы, что вызывает ошибки прав доступа. Его отключение позволяет Jest использовать встроенный наблюдатель за файлами.

Поддержка платформ

  • macOS: Использует sandbox-exec с пользовательскими профилями (без дополнительных зависимостей)
  • Linux: Использует bubblewrap (bwrap) для контейнеризации
  • Windows: Альфа — использует встроенный помощник srt-win.exe (без дополнительных зависимостей). См. Windows (альфа) ниже для настройки, модели безопасности и известных ограничений

Зависимости для конкретных платформ

Linux требует:

  • bubblewrap - Среда выполнения контейнеров
    • Ubuntu/Debian: apt-get install bubblewrap
    • Fedora: dnf install bubblewrap
    • Arch: pacman -S bubblewrap
  • socat - Ретранслятор сокетов для прокси-моста
    • Ubuntu/Debian: apt-get install socat
    • Fedora: dnf install socat
    • Arch: pacman -S socat
  • ripgrep - Быстрый инструмент поиска для обнаружения запрещённых путей
    • Ubuntu/Debian: apt-get install ripgrep
    • Fedora: dnf install ripgrep
    • Arch: pacman -S ripgrep

Примечание для Ubuntu 24.04+: В этих выпусках по умолчанию включён параметр kernel.apparmor_restrict_unprivileged_userns, который разрешает unshare(CLONE_NEWUSER), но лишает возможности пространство имён, полученное в результате. Как bubblewrap, так и слой изоляции seccomp нуждаются в пользовательских пространствах имён с возможностями. Отключите ограничение с помощью:```bash sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

или добавьте профиль AppArmor, предоставляющий `userns` соответствующим бинарным файлам.

**Опциональные зависимости Linux (для резервного варианта seccomp):**

Пакет включает предварительно сгенерированные фильтры seccomp BPF для архитектур x86-64 и arm. Эти зависимости нужны только в том случае, если вы используете другую архитектуру, для которой предварительно сгенерированные фильтры недоступны:

- `gcc` или `clang` — компилятор C
- `libseccomp-dev` — файлы разработки библиотеки Seccomp
  - Ubuntu/Debian: `apt-get install gcc libseccomp-dev`
  - Fedora: `dnf install gcc libseccomp-devel`
  - Arch: `pacman -S gcc libseccomp`

**macOS требует:**

- `ripgrep` — быстрый инструмент поиска для обнаружения запрещённых путей
  - Установка через Homebrew: `brew install ripgrep`
  - Или скачайте с: https://github.com/BurntSushi/ripgrep/releases

**Windows требует:**

- Дополнительные зависимости не нужны. Вспомогательный файл `srt-win.exe` (x64 и arm64) поставляется вместе с npm-пакетом. Требуется однократный шаг `windows-install` с повышенными правами — см. ниже.

## Windows (alpha)

Поддержка Windows находится в стадии **alpha**. Процесс в песочнице выполняется под выделенной локальной учётной записью `srt-sandbox`, изолированной от вызывающего пользователя с помощью нативных примитивов безопасности Windows — барьера исходящего трафика Windows Filtering Platform (WFP), привязанного к SID учётной записи песочницы, и явных ACE для каждого сеанса, которые предоставляют или запрещают этому SID доступ к настроенным путям файловой системы.

### Настройка

Выполните один раз на машине (автоматически повышает права; один запрос UAC):```powershell
npx @anthropic-ai/sandbox-runtime windows-install

Это создаёт локальную учётную запись srt-sandbox (со случайным паролем, хранящимся в зашифрованном через DPAPI виде в HKLM\SOFTWARE\sandbox-runtime — на уровне всей машины, поэтому установки на парке машин, работающие как SYSTEM, функционируют, и ротация пароля одним пользователем обновляет копию, которую читают остальные), локальную группу sandbox-runtime-users и устанавливает набор WFP-фильтров на уровне всей машины, привязанных к SID srt-sandbox. Он идемпотентен — повторный запуск ротирует пароль учётной записи песочницы и приводит набор фильтров в согласованное состояние.

Выход из системы не требуется. WFP-фильтры привязаны к SID выделенной учётной записи песочницы, поэтому ваша собственная сеть, службы и все остальные субъекты на машине не затрагиваются.

После установки SandboxManager.initialize() и CLI srt работают так же, как на других платформах. initialize() проверяет, что учётная запись песочницы и WFP-ограждение активны, и завершается с actionable-ошибкой, если это не так.

Программные установка/удаление экспортируются как installWindowsSandbox() / uninstallWindowsSandbox().

Модель безопасности

Команда в песочнице выполняется от имени учётной записи srt-sandbox, а не от имени вызывающего пользователя. Входящий в комплект помощник srt-win.exe выполняет двухступенчатый запуск: брокер вызывает CreateProcessWithLogonW для запуска раннера от имени srt-sandbox, а раннер порождает целевой процесс под ограниченным токеном внутри объекта задания. Дочерний процесс наследует изолированный профиль учётной записи песочницы (%USERPROFILE%, %TEMP%, HKCU) и свежее окружение, поверх которого наложены только PATH брокера и сгенерированные переменные прокси.

Работа под отдельным SID пользователя структурно закрывает класс побега через суррогатное порождение (Task Scheduler, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS на процесс, принадлежащий брокеру, BITS, внепроцессный COM с RunAs="Interactive User"): любой процесс, который дочернему удастся породить вне обычного канала, всё равно несёт SID srt-sandbox, поэтому он остаётся под действием WFP-ограждения исходящего трафика и не имеет прав на файлы вызывающего пользователя.

Сетевая изоляция — это набор из двух WFP-фильтров на уровне FWPM_LAYER_ALE_AUTH_CONNECT_V4/V6: PERMIT для loopback-назначений внутри настроенного диапазона портов прокси (по умолчанию 60080–60089) и BLOCK для любого подключения, чей токен несёт SID srt-sandbox. Процесс в песочнице достигает интернета только через JS-прокси HTTP/SOCKS5, слушающие в этом диапазоне; процесс, который удаляет своё прокси-окружение и подключается напрямую, блокируется на уровне ядра.

Изоляция файловой системы обеспечивается дискреционными ACL NTFS. Учётная запись srt-sandbox не имеет собственных прав на файлы вызывающего пользователя, поэтому при initialize() песочница записывает аддитивные наследуемые явные ACE только для SID srt-sandbox — она никогда не перезаписывает и не заменяет существующий дескриптор безопасности пути:

  • filesystem.allowWrite → наследуемый ALLOW ACE MODIFY (READ|WRITE|EXECUTE|DELETE, с удержанием FILE_DELETE_CHILD). Процесс в песочнице может создавать, изменять и удалять файлы внутри рабочего дерева; удержание FILE_DELETE_CHILD от предоставления — это эшелонированная защита для описанных ниже запрещающих меток, а не защита корня дерева.
  • filesystem.allowRead → наследуемый ALLOW ACE READ|EXECUTE
  • filesystem.denyRead / filesystem.denyWrite → наследуемый DENY ACE на цель, плюс наследуемый DENY FILE_DELETE_CHILD на её родителя — вместе с удержанным FILE_DELETE_CHILD в предоставлении на рабочее дерево это не даёт процессу в песочнице переименовать или удалить запрещённый путь через его родительский каталог

reset() удаляет каждый ACE, добавленный этой сессией (с подсчётом ссылок между параллельными хостами этого пользователя через пользовательскую сессионную БД; проход восстановления после сбоя при следующем initialize() убирает последствия некорректного завершения). Целевые каталоги поддерживаются (ACE наследуются на всё поддерево). Glob-шаблоны разворачиваются в конкретные пути во время initialize() — соответствующий путь, появившийся позже, не покрывается.

Завершение TLS на Windows

network.tlsTerminate требует, чтобы MITM CA присутствовал в хранилище сертификатов CurrentUser\Root пользователя песочницы (schannel — TLS-бэкенд, используемый System32\curl.exe, PowerShell Invoke-WebRequest, .NET и git с бэкендом по умолчанию — доверяет только хранилищу ОС, а не переменным окружения). Это шаг времени установки, отдельный от windows-install:```typescript import { windowsTrustCa } from '@anthropic-ai/sandbox-runtime' windowsTrustCa('/path/to/mitm-ca.crt') // or: srt-win user trust-ca

`initialize()` сравнивает отпечаток CA сессии с установленным и завершается с информативным сообщением при несовпадении, поэтому устаревший CA, установленный во время установки, не может незаметно нарушить работу TLS внутри песочницы.

Клиенты на базе OpenSSL (msys2 `curl`, `git -c http.sslBackend=openssl`, Node, Python, cargo) покрываются слоем доверия через переменные окружения: тот же пакет доверия, что используется в macOS/Linux, передаётся в песочницу через `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `CARGO_HTTP_CAINFO` и т. д., а путь к пакету добавляется в `allowRead`-разрешение сессии, чтобы учётная запись песочницы могла его открыть.

### Конфигурация, специфичная для Windows

Кроссплатформенные блоки `filesystem` и `network` применяются, как описано выше. Настройки, специфичные только для Windows, находятся в разделе `windows`:

- `windows.proxyPortRange` — включающий диапазон портов `[low, high]`, внутри которого привязываются JS-прокси. **Должен совпадать** с диапазоном, переданным в `windows-install --proxy-port-range` (по умолчанию `[60080, 60089]`) — WFP-разрешение для loopback покрывает только этот диапазон.
- `windows.sublayerGuid` — GUID подслоя WFP, под которым были установлены фильтры. Опустите, чтобы использовать значение по умолчанию, заданное при компиляции; указывайте только если корпоративные инструменты установили фильтры под пользовательским подслоем.
- `windows.srtWin.path` — путь к бинарному файлу `srt-win`. Опустите, чтобы использовать упакованный `vendor/srt-win/<arch>/srt-win.exe`. Указывайте при встраивании CLI `srt-win` в мультивызываемый бинарный файл; тогда при запуске передаётся `--srt-win` как `argv[1]`, чтобы диспетчер встраивающей программы мог направить вызов в `srt_win::run_from_args`.

### Известные ограничения

- **Отзыв сертификатов под schannel.** Запрос CRL/OCSP через CryptoAPI выполняется через WinHTTP под токеном вызывающего, игнорируя окружение прокси, поэтому блокируется WFP-ограничением исходящего трафика. Инструменты, использующие schannel с включённой по умолчанию проверкой отзыва, завершаются с ошибкой `CRYPT_E_REVOCATION_OFFLINE` (`0x80092013`), если только отзыв не отключён для конкретного инструмента: `curl --ssl-no-revoke`, `git -c http.schannelCheckRevoke=false`, `CARGO_HTTP_CHECK_REVOKE=false`. `Invoke-WebRequest`, .NET `HttpClient` и `gh` не проверяют отзыв по умолчанию и не затронуты. Для устранения этого обходного пути планируется точка распространения CRL, обслуживаемая через loopback-прокси.
- **Установки инструментов для отдельного пользователя недоступны.** Процесс в песочнице выполняется как `srt-sandbox`, а не от вашего имени, поэтому инструменты, установленные в вашем профиле (Node под управлением nvm/fnm, пакеты `winget`/Scoop для отдельного пользователя, `pip install --user`, `%LOCALAPPDATA%\Programs\…`), разрешаются по унаследованному `PATH`, но не могут быть открыты учётной записью песочницы. Предпочитайте установки для всей машины (`Program Files`, `choco`/`winget --scope machine`) или добавьте конкретные пути профиля в `filesystem.allowRead`.
- **Переопределения `filesystem.allowRead` / `filesystem.allowWrite` для отдельного выполнения не поддерживаются.** Разрешения `allowRead`/`allowWrite` уровня сессии (в конфигурации, переданной в `initialize()`) работают, как описано выше; их передача для отдельной команды в `customConfig` у `wrapWithSandbox` вызывает исключение — разрешения применяются на уровне всей сессии через `srt-win acl grant` при `initialize()`, а `srt-win exec` предоставляет только запреты для отдельного выполнения.
- **`proxyAuthToken` виден в командной строке раннера.** Окружение прокси (включая `HTTP_PROXY=http://srt:<token>@127.0.0.1:…`) передаётся двухшаговому раннеру как аргументы `--env` в argv `srt-win exec`, поэтому токен доступен для чтения любому локальному субъекту, который может открыть процесс раннера для `PROCESS_QUERY_LIMITED_INFORMATION`. Токен существует, чтобы процесс в песочнице мог аутентифицироваться в loopback-прокси, поэтому он не является секретом от самой песочницы; на однопользовательской машине разработчика это, как правило, приемлемо, но на общем хосте считайте список разрешённых прокси доступным для других субъектов той же сессии.
- **Разрешение DNS через системный резолвер не ограничивается.** `getaddrinfo()` обслуживается службой `Dnscache`, работающей как `NETWORK SERVICE`, поэтому разрешение имён успешно, даже если последующий `connect()` из процесса в песочнице блокируется. Инструменты, выполняющие собственный UDP/53 (`nslookup`, `dig`), ограничиваются. Это повторяет поведение macOS.

### Удаление```powershell
npx @anthropic-ai/sandbox-runtime windows-uninstall

Удаляет набор фильтров WFP, учётную запись srt-sandbox и её профиль, группу sandbox-runtime-users и удаляет ключ HKLM\SOFTWARE\sandbox-runtime (учётные данные, маркер, запись CA) — один запрос UAC. %ProgramData%\sandbox-runtime (материал ключа CA) остаётся на месте; удалите его (и %LOCALAPPDATA%\sandbox-runtime для каждого пользователя) вручную для полной очистки.

Разработка```bash

Install dependencies

npm install

Build the project

npm run build

Run tests

npm test

Type checking

npm run typecheck

Lint code

npm run lint

Format code

npm run format

### Сборка бинарных файлов Seccomp

Фильтр BPF и загрузчик `apply-seccomp` компилируются из исходного кода на C в `vendor/seccomp-src/` с помощью `npm run build:seccomp` (только Linux; требуются `gcc` и `libseccomp-dev`). CI запускает это перед тестами на каждой архитектуре Linux, а рабочий процесс релиза собирает обе архитектуры и включает их в публикуемый пакет.

## Детали реализации

### Архитектура сетевой изоляции

Песочница запускает HTTP- и SOCKS5-прокси-серверы на хост-машине, которые фильтруют все сетевые запросы на основе правил разрешений:

1. **HTTP/HTTPS-трафик**: HTTP-прокси-сервер перехватывает запросы и проверяет их на соответствие разрешённым/запрещённым доменам
2. **Другой сетевой трафик**: SOCKS5-прокси обрабатывает все остальные TCP-соединения (SSH, подключения к базам данных и т. д.)
3. **Применение разрешений**: Прокси применяют правила `permissions` из вашей конфигурации

**Платформозависимая связь с прокси:**

- **Linux**: Запросы маршрутизируются через файловую систему по Unix-сокетам (с использованием `socat` для моста). Сетевое пространство имён удаляется из контейнера bubblewrap, что гарантирует прохождение всего сетевого трафика через прокси.

- **macOS**: Профиль Seatbelt разрешает связь только с определёнными портами localhost, на которых слушают прокси. Весь остальной сетевой доступ блокируется.

- **Windows**: Фильтр WFP `ALE_AUTH_CONNECT` блокирует любое исходящее подключение от учётной записи `srt-sandbox`, кроме loopback к настроенному диапазону портов прокси. Прокси привязываются внутри этого диапазона. Переменные окружения (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, …) указывают инструментам на прокси, но границей является фильтр WFP — процесс, который их игнорирует или сбрасывает, всё равно остаётся изолированным.

**Инструменты JVM (macOS/Linux):** JVM игнорирует `HTTPS_PROXY`/`NO_PROXY` и не имеет переменной окружения для учётных данных прокси — выбор прокси происходит через системные свойства `https.proxyHost`, а учётные данные могут быть переданы только через `java.net.Authenticator`. Поэтому инструменты на основе JVM (gRPC-кэш Bazel, Gradle, Maven, …) иначе подключались бы к цели напрямую и терпели неудачу, либо обращались бы к прокси без его токена и получали 407. Чтобы закрыть этот пробел, srt внедряет небольшой `-javaagent` через `JAVA_TOOL_OPTIONS` (переменная окружения содержит только путь к jar, учётные данные остаются в `HTTPS_PROXY`). При запуске JVM агент устанавливает `http[s].proxyHost`/`Port` и `http.nonProxyHosts` из переменных окружения прокси, повторно включает Basic-аутентификацию для CONNECT-туннелей и устанавливает Authenticator для конечной точки прокси. Явные свойства прокси `-D` в командной строке JVM всё ещё имеют приоритет, а любые унаследованные `JAVA_TOOL_OPTIONS` сохраняются (если это не запрещённая переменная окружения с учётными данными). В результате каждая JVM выводит в stderr строку `Picked up JAVA_TOOL_OPTIONS: …`; среда выполнения, собранная через jlink без модуля `java.instrument`, не может загружать агенты и откажется запускаться в песочнице — для такого инструмента снимите `JAVA_TOOL_OPTIONS` в команде. Jar поставляется в npm-пакете как `vendor/java-proxy-agent/srt-proxy-agent.jar` (исходники: `vendor/java-proxy-agent-src/`; собирается рабочим процессом релиза или локально с помощью `npm run build:java-agent` — требуется JDK ≥ 17). Если он не найден, `JAVA_TOOL_OPTIONS` остаётся нетронутым, и JVM ведут себя как раньше; сборщики могут указать на свою копию с помощью `javaAgentJarPath`.

### Изоляция файловой системы

Ограничения файловой системы применяются на уровне ОС:

- **macOS**: Использует `sandbox-exec` с динамически генерируемыми профилями Seatbelt, которые указывают разрешённые пути для чтения/записи
- **Linux**: Использует `bubblewrap` с bind-монтированием, помечая каталоги как доступные только для чтения или для чтения-записи в зависимости от конфигурации
- **Windows**: Записывает аддитивные явные ACE `(OI)(CI)` для SID `srt-sandbox` на настроенные пути (ALLOW для `allowRead`/`allowWrite`, DENY для `denyRead`/`denyWrite`), затем удаляет их при `reset()`

**Разрешения файловой системы по умолчанию:**

- **Чтение** (сначала запрет, затем разрешение): По умолчанию разрешено везде. Вы можете запретить широкие области, а затем повторно разрешить конкретные пути внутри них. `allowRead` имеет приоритет над `denyRead`.

  - Пример: `denyRead: ["~/.ssh"]` для блокировки доступа к SSH-ключам
  - Пример: `denyRead: ["/Users"], allowRead: ["."]` для блокировки всего `/Users`, кроме рабочего пространства
  - Пустой `denyRead: []` = полный доступ на чтение (ничего не запрещено)

- **Запись** (только разрешение): По умолчанию запрещено везде. Вы должны явно разрешить пути.
  - Пример: `allowWrite: [".", "/tmp"]` для разрешения записи в текущий каталог и /tmp
  - Пустой `allowWrite: []` = нет доступа на запись (ничего не разрешено)
  - `denyWrite` создаёт исключения внутри разрешённых путей (запрет имеет приоритет)

**Приоритет намеренно противоположен для чтения и записи:** `allowRead` переопределяет `denyRead`, тогда как `denyWrite` переопределяет `allowWrite`. Это позволяет выделять читаемые области внутри запрещённых зон и защищённые области внутри зон, доступных для записи.

### Обязательные запрещённые пути (автоматически защищённые файлы)

Некоторые конфиденциальные файлы и каталоги **всегда блокируются для записи**, даже если они попадают в разрешённый путь для записи. Это обеспечивает эшелонированную защиту от побегов из песочницы и подмены конфигурации.

**Всегда блокируемые файлы:**

- Файлы конфигурации оболочки: `.bashrc`, `.bash_profile`, `.zshrc`, `.zprofile`, `.profile`
- Файлы конфигурации Git: `.gitconfig`, `.gitmodules`
- Другие конфиденциальные файлы: `.ripgreprc`, `.mcp.json`

**Всегда блокируемые каталоги:**

- Каталоги IDE: `.vscode/`, `.idea/`
- Каталоги конфигурации Claude: `.claude/commands/`, `.claude/agents/`
- Хуки и конфигурация Git: `.git/hooks/`, `.git/config`

Эти пути блокируются автоматически — вам не нужно добавлять их в `denyWrite`. Например, даже при `allowWrite: ["."]` запись в `.bashrc` или `.git/hooks/pre-commit` завершится ошибкой:```bash
$ srt 'echo "malicious" >> .bashrc'
/bin/bash: .bashrc: Operation not permitted

$ srt 'echo "bad" > .git/hooks/pre-commit'
/bin/bash: .git/hooks/pre-commit: Operation not permitted

Примечание (Linux): В Linux обязательные запрещённые пути блокируют только уже существующие файлы. Несуществующие файлы, соответствующие этим шаблонам, не могут быть заблокированы подходом bubblewrap с bind-mount. В macOS используются glob-шаблоны, которые блокируют как существующие, так и новые файлы.

Глубина поиска в Linux: В Linux песочница использует ripgrep для сканирования опасных файлов в подкаталогах в пределах разрешённых путей для записи. По умолчанию для производительности поиск выполняется на глубину до 3 уровней. Это можно настроить с помощью mandatoryDenySearchDepth:```json { "mandatoryDenySearchDepth": 5, "filesystem": { "allowWrite": ["."] } }

- По умолчанию: `3` (поиск на глубину до 3 уровней)
- Диапазон: от `1` до `10`
- Более высокие значения обеспечивают лучшую защиту, но снижают производительность
- Файлы в CWD (глубина 0) всегда защищены независимо от этой настройки

### Ограничения Unix-сокетов (Linux)

В Linux песочница использует **seccomp BPF (Berkeley Packet Filter)** для блокировки создания Unix-доменных сокетов на уровне системных вызовов. Это обеспечивает дополнительный уровень безопасности, предотвращая создание процессами новых Unix-доменных сокетов для локального IPC (если это явно не разрешено).

**Как это работает:**

1. **Встроенный BPF-фильтр**: Пакет поставляется со статическим бинарником `apply-seccomp` для x64 и arm64 со скомпилированным seccomp BPF-фильтром. Фильтр зависит от архитектуры, но не зависит от libc, поэтому бинарник работает как с glibc, так и с musl.

2. **Определение во время выполнения**: Песочница автоматически определяет архитектуру вашей системы и использует соответствующий бинарник `apply-seccomp`.

3. **Фильтрация системных вызовов**: BPF-фильтр перехватывает системный вызов `socket()` и блокирует создание сокетов `AF_UNIX`, возвращая `EPERM`. Это предотвращает создание новых Unix-доменных сокетов кодом, запущенным в песочнице.

4. **Двухэтапное применение с использованием бинарника apply-seccomp**:
   - Внешний bwrap создаёт песочницу с ограничениями файловой системы, сети и пространства имён PID
   - Процессы сетевого моста (socat) запускаются внутри песочницы (нужны Unix-сокеты)
   - apply-seccomp создаёт вложенное пространство имён user+PID+mount и перемонтирует `/proc`
   - Внутри вложенного пространства имён apply-seccomp действует как PID 1 (non-dumpable init/reaper)
   - apply-seccomp выполняет fork, применяет seccomp-фильтр через `prctl()` и выполняет exec пользовательской команды
   - Пользовательская команда выполняется со всеми ограничениями песочницы плюс блокировкой создания Unix-сокетов

**Изоляция пространства имён PID**: Вложенное пространство имён PID гарантирует, что пользовательская команда не может видеть или адресовать любой процесс, работающий без seccomp-фильтра (init от bwrap, обёртка shell или помощники socat). Это сохраняет целостность границы seccomp независимо от `kernel.yama.ptrace_scope`, поскольку нефильтрованные помощники недоступны через `ptrace` или `/proc/N/mem`. Внутренний PID 1 устанавливает `PR_SET_DUMPABLE=0`, поэтому он также не доступен для ptrace. Если создание вложенного пространства имён не удаётся, apply-seccomp прерывает работу, а не запускается без изоляции.

**Ограничения безопасности**: Фильтр блокирует `socket(AF_UNIX, ...)` и системные вызовы `io_uring_setup`/`io_uring_enter`/`io_uring_register` (последние три — потому что `IORING_OP_SOCKET` в Linux 5.19+ иначе обошёл бы правило `socket()`). Он не предотвращает операции с файловыми дескрипторами Unix-сокетов, унаследованными от родительских процессов или переданными через `SCM_RIGHTS`. Для большинства сценариев песочницы блокировки создания сокетов достаточно для предотвращения несанкционированного IPC.

**Нулевые зависимости во время выполнения**: Предварительно собранные статические бинарники apply-seccomp и предварительно сгенерированные BPF-фильтры включены для архитектур x64 и arm64. Инструменты компиляции или внешние зависимости во время выполнения не требуются.

**Поддержка архитектур**: x64 и arm64 полностью поддерживаются с предварительно собранными бинарниками. Другие архитектуры в настоящее время не поддерживаются. Чтобы использовать песочницу без блокировки Unix-сокетов на неподдерживаемых архитектурах, установите `allowAllUnixSockets: true` в вашей конфигурации.

### Обнаружение и мониторинг нарушений

Когда процесс в песочнице пытается получить доступ к ограниченному ресурсу:

1. **Блокирует операцию** на уровне ОС (возвращает ошибку `EPERM`)
2. **Регистрирует нарушение** (механизмы, зависящие от платформы)
3. **Уведомляет пользователя** (в Claude Code это вызывает запрос разрешения)

**macOS**: Среда выполнения песочницы подключается к системному хранилищу журнала нарушений песочницы macOS. Это обеспечивает уведомления в реальном времени с подробной информацией о том, что было предпринято и почему это было заблокировано. Это тот же механизм, который Claude Code использует для обнаружения нарушений.```bash
# View sandbox violations in real-time
log stream --predicate 'process == "sandbox-exec"' --style syslog

Linux: Bubblewrap не предоставляет встроенной отчётности о нарушениях. Используйте strace для трассировки системных вызовов и выявления заблокированных операций:```bash

Trace all denied operations

strace -f srt 2>&1 | grep EPERM

Trace specific file operations

strace -f -e trace=open,openat,stat,access srt 2>&1 | grep EPERM

Trace network operations

strace -f -e trace=network srt 2>&1 | grep EPERM

### Продвинутый уровень: Использование собственного прокси

Для более сложной фильтрации сети вы можете настроить песочницу на использование собственного прокси вместо встроенных. Это позволяет:

- **Инспекция трафика**: Используйте такие инструменты, как [mitmproxy](https://mitmproxy.org/), для просмотра и изменения трафика
- **Пользовательская логика фильтрации**: Реализуйте сложные правила, выходящие за рамки простых списков разрешённых доменов
- **Журналирование аудита**: Записывайте все сетевые запросы для соответствия требованиям или отладки

**Пример с mitmproxy:**```bash
# Start mitmproxy with custom filtering script
mitmproxy -s custom_filter.py --listen-port 8888

Примечание: Пользовательская конфигурация прокси пока не поддерживается в новом формате конфигурации. Эта функция будет добавлена в будущем выпуске.

Важное соображение безопасности: Даже при наличии списков разрешённых доменов могут существовать векторы эксфильтрации данных. Например, разрешение github.com позволяет процессу выполнять push в любой репозиторий. С помощью пользовательского MITM-прокси и правильной настройки сертификатов вы можете инспектировать и фильтровать конкретные API-вызовы, чтобы предотвратить это.

Ограничения безопасности

  • Ограничения сетевой изоляции: Система сетевой фильтрации работает путём ограничения доменов, к которым процессам разрешено подключаться. Она не инспектирует иным образом трафик, проходящий через прокси, и пользователи несут ответственность за то, чтобы в их политике были разрешены только доверенные домены. Разрешённые имена хостов дополнительно проверяются по набору запрещённых разрешённых адресов перед прямым подключением (см. Проверка разрешённых адресов выше), поэтому разрешённое имя не может быть направлено на loopback, link-local, собственные адреса этого хоста или IP-адрес, указанный вами в deniedDomains; другие частные диапазоны покрываются только если вы перечислите их в deniedResolvedAddresses (запись с подстановочным знаком для домена, DNS которого вы не контролируете, иначе может быть нацелена на сервисы в вашей локальной сети), а соединения, выходящие через parentProxy/mitmProxy, полагаются на этот промежуточный узел для выполнения эквивалентной проверки.
Пользователи должны осознавать потенциальные риски, связанные с разрешением широких доменов, таких как `github.com`, которые могут допускать эксфильтрацию данных. Кроме того, в некоторых случаях возможно обойти сетевую фильтрацию с помощью [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting).
  • Повышение привилегий через Unix-сокеты: Конфигурация allowUnixSockets может непреднамеренно предоставить доступ к мощным системным сервисам, что может привести к обходу песочницы. Например, если она используется для разрешения доступа к /var/run/docker.sock, это фактически предоставит доступ к хост-системе через эксплуатацию docker-сокета. Пользователям рекомендуется тщательно обдумывать любые unix-сокеты, которые они разрешают через песочницу.
  • Повышение прав доступа к файловой системе: Чрезмерно широкие права на запись в файловую систему могут позволить атаки с повышением привилегий. Разрешение записи в каталоги, содержащие исполняемые файлы в $PATH, системные каталоги конфигурации или файлы конфигурации пользовательской оболочки (.bashrc, .zshrc), может привести к выполнению кода в других контекстах безопасности, когда другие пользователи или системные процессы обращаются к этим файлам.
  • Стойкость песочницы Linux: Реализация для Linux обеспечивает надёжную изоляцию файловой системы и сети, но включает режим enableWeakerNestedSandbox, который позволяет ей работать внутри сред Docker без привилегированных пространств имён. Эта опция значительно ослабляет безопасность и должна использоваться только в случаях, когда иным образом обеспечивается дополнительная изоляция.
  • Ослабленная сетевая изоляция (macOS): Опция enableWeakerNetworkIsolation повторно включает доступ к com.apple.trustd.agent, который необходим программам на Go для проверки TLS-сертификатов через фреймворк безопасности macOS. Это открывает потенциальный вектор эксфильтрации данных через сервис trustd и должно включаться только тогда, когда требуется проверка TLS в Go (например, при использовании httpProxyPort с MITM-прокси и пользовательским CA).
  • Apple Events (macOS): Опция allowAppleEvents повторно включает отправку Apple Events и запросов на открытие Launch Services ((allow appleevent-send), (allow lsopen) и mach-lookups для com.apple.coreservices.appleevents, com.apple.CoreServices.coreservicesd и com.apple.coreservices.quarantine-resolver), которые требуются для open, osascript и помощников открытия URL. При их разрешении команда в песочнице может запускать произвольные приложения без запроса пользователя, а запущенные приложения работают полностью вне песочницы — таким образом, эта опция устраняет изоляцию выполнения кода, а не просто ослабляет её. Скриптинг уже запущенных приложений через Apple Events дополнительно контролируется согласием на автоматизацию macOS TCC, но запуск через open — нет. Включайте это только тогда, когда командам внутри песочницы действительно нужно открывать URL или приложения.

Известные ограничения и будущая работа

Обход прокси в Linux: В настоящее время используются переменные окружения (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY) для направления трафика через прокси. Это работает для большинства приложений, но может игнорироваться программами, которые не учитывают эти переменные, что приводит к невозможности подключения к интернету.

Будущие улучшения:

  • Поддержка Proxychains: Добавить поддержку proxychains с LD_PRELOAD в Linux для перехвата сетевых вызовов на более низком уровне, что затруднит обход

  • Мониторинг нарушений в Linux: Реализовать автоматическое обнаружение нарушений на основе strace для Linux, интегрированное с хранилищем нарушений. В настоящее время пользователям Linux приходится вручную запускать strace, чтобы увидеть нарушения, в отличие от macOS, где есть автоматический мониторинг нарушений через системное хранилище логов

Категории