Преминаване към основното съдържание
OpenAI

Изграждане на безопасен и ефективен пясъчник за Codex в Windows

От David Wiesen, член на техническия екип

Зареждане…

Когато се присъединих към инженерния екип на Codex през септември 2025 г., Codex за Windows нямаше реализация на пясъчник, което означаваше, че потребителите на Windows бяха принудени да избират между две по-лоши опции, когато използват агентите за програмиране на OpenAI:

  1. Да одобряват почти всяка команда (дори четенията), която агент за програмиране иска да изпълни, което е неефективно и досадно. Голямо предимство на използването на Codex е, че не се налага да вършите цялата досадна работа сами.
  2. Да включат режим с пълен достъп: да позволят на Codex да изпълнява всички команди без одобрение или ограничения, което премахва триенето за сметка на контрола.

CodexCodex, нашият агент за програмиране, работи на лаптопите на разработчиците — независимо дали чрез CLI, разширението за IDE или настолното приложение. Той управлява разговор между човек пред клавиатура и модел, работещ в облака, за да обработва изводите.

По подразбиране Codex работи с разрешенията на истински потребител, което означава, че може да прави всичко, което може и потребителят. Това е мощно и потенциално опасно. Моделът за програмиране може да инструктира връзката да изпълнява команди локално, от пускане на тестове до четене или редактиране на файл и създаване на Git клон, така че режимът по подразбиране на Codex се опитва да намери правилния баланс между ефективност и безопасност. Този режим по подразбиране позволява на Codex да чете файлове почти навсякъде и да записва файлове във Вашето работно пространство (т.е. директорията, в която изпълнявате Codex), без достъп до интернет, освен ако не посочите, че го искате. За да се постигне това автоматично ограничаване на записването на файлове и достъпа до мрежата в безопасни граници, Codex се нуждае от среда тип пясъчник, която действително да налага тези ограничения.

Пясъчникът е ограничена среда за изпълнение. Когато разработчик използва Codex, операционната система на неговия компютър стартира команда с намалени разрешения и тези ограничения се разпространяват надолу по дървото на процесите. Всяка команда на Codex е в пясъчник още от самото начало и всеки наследен процес остава в същата граница.

Диаграма, показваща границите на изолация на операционната система за пясъчника на Codex.

Codex се нуждае от функции за изолация, налагани от операционната система на компютъра, за да реализира ефективен пясъчник. Някои операционни системи предоставят инструменти, които се справят добре с това (например Seatbelt в MacOs, seccomp или bubblewrap в Linux). Windows обаче в момента не предоставя такъв тип възможност по подразбиране.

За да направим Codex също толкова безопасен и приятен за използване в Windows, колкото вече е навсякъде другаде, трябваше да реализираме собствен пясъчник.

Къде съществуващите инструменти на Windows не са достатъчни

Windows предлага някои инструменти и примитиви за изолация. Макар че никой от тях не отговаряше напълно на изискванията ни, разгледахме редица потенциални решения, а именно AppContainer, Windows Sandbox и етикетиране чрез Mandatory Integrity Control.

AppContainer

  • Какво: AppContainer е вграденият пясъчник на Windows — модел за изолация, базиран на способности, създаден за приложения, които предварително знаят точно до какво им е нужен достъп.
  • Защо: Привлекателен е, защото предлага реална граница на ниво операционната система вместо ограничения тип „по най-добро усилие“.
  • Защо не: Codex не е едно приложение с тясно определен обхват. Той управлява отворени работни потоци на разработчици: командни обвивки, Git, Python, мениджъри на пакети, инструменти за компилиране и каквито още двоични файлове агентът реши, че са му нужни. На практика това направи AppContainer неподходящ за проблема. Това беше силна изолация, но за много по-тесен клас натоварвания от „нека агент работи като разработчик“.

Windows Sandbox

  • Какво: Windows Sandbox е лека виртуална машина (VM) на Microsoft за еднократна употреба. Получавате чист работен плот на Windows със силна изолационна граница и всичко, което правите в него, изчезва, когато сесията приключи.
  • Защо: Интересен по очевидни причини — много по-съвместим с произволен софтуер от AppContainer и от гледна точка на сигурността е много по-силна кутия.
  • Защо не: Codex трябва да действа директно върху реалното плащане, инструменти и среда на потребителя, а не в отделен временен работен плот, който ще изисква настройка и мост между хост и гост. Освен това имаше основен продуктов проблем: Windows Sandbox дори не е наличен в изданията на Windows Home SKU.

Mandatory Integrity Control (MIC) integrity labeling

  • Какво: Windows има концепция, наречена „нива на цялост“, като ниско, средно и високо, които определят доколко системата се доверява на обекти и процеси. Основното правило е, че процес с по-ниско ниво на цялост не може да записва в обект с по-високо ниво на цялост, дори ако обичайният ACL иначе би го позволил. Например процес с ниска цялост се третира като по-малко надежден, така че Windows му блокира записването в нормални обекти със средна цялост, освен ако тези обекти не са изрично преетикетирани, за да го позволят.
  • Защо: На хартия MIC изглеждаше елегантно — да пуснем Codex с нисък интегритет, да преетикетираме записващите корени като нисък интегритет и да оставим Windows да налага забрана за запис навсякъде другаде. Това щеше да ни даде вариант без администраторски права, подкрепен от реален механизъм на операционната система зад него.
  • Защо не: Подобно на ACL списъците, етикетите за цялост променят реалната файлова система на хоста, а в този случай семантичната промяна е особено всеобхватна. Маркирането на работно пространство като такова с нисък интегритет не означава само „Codex може да записва тук“. Това означава, че процесите с ниско ниво на интегритет по принцип могат да записват там. На реална машина на разработчик това превръща действителното работно копие на потребителя в приемник с ниско ниво на интегритет за хоста, което е много по-рисковано от предоставянето на внимателно насочени ACL списъци за контрол на достъпа към един дизайн на пясъчна среда. Дори ако инструментите за разработчици със средно ниво на интегритет продължат да работят, основният модел на доверие на работното пространство се е променил по начин, който е трудно да бъде овладян и още по-трудно да бъде оправдан.

След като оценихме всички опции като непрактични, започнахме да проектираме собствено решение, за да осигурим добро изживяване с Codex за потребителите на Windows.

Първият прототип: „неповишеният пясъчник“

Първият ни работещ прототип използваше комбинация от концепции и инструменти на Windows, за да реализира необходимата ни изолация. От самото начало една от целите беше това да работи, без да се изисква повишаване на привилегиите, което означава, че Codex няма да трябва да извежда подкана към потребителя за администраторски права само за да настрои или стартира изолираната среда. Това означаваше да се намери начин да се наложат разумни ограничения върху две неща: операциите за запис във файлове и достъпът до мрежата.

Ограничаване на записването на файлове

Ако изобщо не ограничим записването на файлове, ще имаме проблем със сигурността. Ако го ограничим твърде много, пясъчникът ще навреди на продуктивността на потребителя, като постоянно ще иска одобрение. За да решим този проблем, се опряхме на два важни градивни елемента на Windows: SID и токени с ограничен запис.

SID ни позволяват да дадем идентичност на пясъчника

SID или идентификатор за защита е идентичността, която Windows свързва с разрешенията. Всеки потребител има SID, групите имат SID, а дори и една сесия за вход получава собствен SID. Например текуща сесия с влязъл потребител може да има SID като S-1-5-5-X-Y. SID, присвоен на групата на локалните администратори, може да е S-1-5-32-544.

Windows също така Ви позволява да създавате синтетични SID, които не съответстват на реален потребител, но все пак могат да се появяват в ACL (списъци за контрол на достъпа), които определят кой може да чете/пише/изпълнява конкретни файлове или директории. Това прави SID полезен примитив за нашия пясъчник: можем да създаваме SID изключително за пясъчника на Codex, без да пречим на нищо друго на машината.

Токените с ограничен запис ограничават къде Codex може да променя файлове

Токените на процесите са обекти за сигурност в Windows, които определят идентичността и привилегиите на изпълняващ се процес. Те определят какви действия може да извършва даден процес. Токен с ограничение за запис е определен тип токен на процес, който кара Windows да извършва допълнителна проверка на достъпа при операции за запис.

За да бъде записът успешен, трябва да минат две проверки:

  1. Нормалната потребителска идентичност („собственикът“ на токена) трябва да има право да го направи
  2. Поне един SID в списъка с ограничени SID на токена също трябва да има предоставен достъп
Диаграма със заглавие „Записът в пясъчника изисква както обикновен потребителски достъп, така и достъп чрез SID sandbox-write“.

На практика тези проверки ни позволиха да използваме ACL, за да определим точно къде пясъчникът може да променя файловата система, което ни даде нужната прецизност около операциите по запис.

Със SID и токени с ограничен запис нашият неповишен пясъчник работеше така:

  1. Настройката на пясъчника създаваше синтетичен SID, наречен sandbox-write.
  2. На SID sandbox-write беше предоставен достъп за запис, изпълнение и изтриване на
    1. Текущата работна директория
    2. Всички допълнителни writable_roots, конфигурирани в config.toml.
  3. Настройката на пясъчника изрично забраняваше на същия SID достъп за запис до местоположения „само за четене в рамките на записа“, като:
    1. <cwd>/.git
    2. <cwd>/.codex
    3. <cwd>/.agents
  4. Codex стартираше команди под токен с ограничен запис, чийто списък с ограничени SID включва Everyone, SID на текущата влязла сесия и синтетичния SID sandbox-write.

Този поток на практика реши ограничаването на записването на файлове и изглеждаше обещаващ. Сега ни трябваше решение за ограничаване на мрежовия достъп на пясъчника.

Ограничаване на мрежовия достъп

Ограничаването на мрежовия достъп е важна част от пясъчника. Без него злонамерен код би могъл да изнесе данни от машината към интернет. Тъй като искахме да избегнем изискване за повишаване на привилегиите, имахме ограничени възможности да блокираме силно мрежовия трафик. Инструментите, които искахме да използваме, като Windows Firewall, по принцип не можеха да бъдат инсталирани без администраторски права.

Без защитната стена на Windows като опция бяхме ограничени в това, което можехме да контролираме. Опитахме се да направим така, че дъщерната среда да отказва в затворен режим за видовете мрежови инструменти, които разработчиците действително използват, така че Git команди, инсталатори на пакети и т.н. да се провалят в пясъчника и потребителят да трябва да одобри всички операции с достъп към интернет. Идеята беше да се обезвредят очевидните аварийни изходи: трафикът, отчитащ прокси настройките, да се изпраща към неработеща крайна точка, HTTP(S) транспортът на Git да се накара да прави същото, а Git през SSH да се проваля незабавно. Освен това добавихме малка директория denybin в началото на PATH и пренаредихме PATHEXT, така че stub скриптовете за SSH и SCP да се намират преди реалните изпълними файлове.

Например ето някои от конкретните презаписвания на средата, които използвахме, за да ограничим мрежовия достъп:

  • HTTPS_PROXY=http://127.0.0.1:9
  • ALL_PROXY=http://127.0.0.1:9
  • GIT_HTTPS_PROXY=http://127.0.0.1:9
  • NO_PROXY=локален хост,127.0.0.1,::1
  • GIT_SSH_COMMAND=cmd /c exit 1
Диаграма, показваща архитектурата на повишения пясъчник с правила на защитната стена и специален потребител на Windows.

Това прихващаше голяма част от нормалния трафик, генериран от инструменти, но все пак беше само препоръчително. Един процес можеше да игнорира средата, да заобиколи PATH или просто да отвори сокети директно — твърде рисковано.

Неповишеният подход идваше с компромиси

Както при всяка интересна софтуерна имплементация, първият прототип имаше някои плюсове и минуси. Макар че вършеше работата само с няколко стандартни възможности на Windows, позволяваше много изрични и прецизни записи във файловата система и работеше без повишаване на привилегиите — намалявайки нуждата потребителите да приемат прекомерни подкани за повишаване на привилегиите или да бъдат администратори на локалната си машина — той имаше някои реални недостатъци, част от които не му позволиха да стане окончателният ни дизайн:

  • Скорост на настройка: Прилагането на ACL върху работното пространство може да бъде скъпо в зависимост от топологията на директорията на работното пространство.
  • Отпечатък: Прилагахме реални ACL върху системата на разработчика, макар че отпечатъкът не е особено инвазивен, защото всички приложени ACL се отнасят до синтетичен SID, създаден по поръчка и използван само от пясъчника.
  • Семантика, трудна за промяна: Зависимостта от списъци за контрол на достъпа (ACL) за ограничения, базирани на файлове, означава, че промяната на семантиката на пясъчника е скъпа и сложна. Докато в macOS можем динамично да променяме начина, по който генерираме .sbpl файл, използван за конфигуриране на Seatbelt, пясъчната среда на Windows може да изисква бавна и интензивна операция за коригиране на ACL.
  • Защитата на мрежата е слаба. Както споменахме по-рано, тя беше „препоръчителна“, със сигурност щеше да бъде заобиколена от някои програми, които реализират собствен мрежов стек, и не беше проектирана да издържа на враждебен код.

Първите три проблема са присъщи на персонализирана реализация на пясъчник, която е достатъчно гъвкава за агентни потоци. Историята с потискането на мрежата обаче беше различна.

Потискането на мрежата е твърде важно

Освен че злонамерен агент можеше лесно да заобиколи потискането на мрежата, базирано на средата, много добронамерен код/двоични файлове също биха го заобиколили просто ако не спазват proxy променливите на средата или ако реализират собствен мрежов код, базиран на сокети. Смятахме, че този аспект е достатъчен, за да обмислим инвестиция в по-добър режим на пясъчник.

За да постигнем по-добро потискане на мрежата, искахме да използваме Windows Firewall, който ни позволява да блокираме изходящия мрежов трафик за потребители или програми. За съжаление, не можехме ефективно да създадем функционално правило на защитната стена, което да се прилага само за командите, породени от връзката на Codex, по няколко причини:

  • Windows не позволява съпоставяне на правило на защитната стена с идентичност, която не е основен субект, на ограничен токен. Това означава, че не можахме да приложим правило на защитната стена към „всеки токен, който включва нашия синтетичен SID в списъка си с ограничени SID“.
  • Макар че можехме да създадем правило на защитната стена, което съответства на конкретен двоичен файл, това ни позволява да ограничим мрежата само за самия codex.exe. Това няма да се прилага за процесите, които агентът стартира от името на потребителя, като например процеси на Git или Python.
  • Другите измерения за съпоставяне на защитната стена също бяха с неправилна форма. Правилата с потребителски обхват продължаваха да съответстват на действителния потребител на Windows в архитектурата без повишени привилегии, а не само на ограничения дъщерен процес. Правилата, базирани на пътя до програмата, бяха твърде общи: те можеха да блокират codex.exe или python.exe като цяло, но не и това конкретно изпълнение на python.exe в изолирана среда. Правилата, базирани на портове или адреси, също бяха напълно погрешна политика. Например не искахме да блокираме порт 443. Искахме да блокираме произволен изходящ достъп за това конкретно ограничено дърво от процеси.

За да приложим правило на защитната стена конкретно към нашите команди в пясъчника, трябваше да ги изпълняваме като отделен принципал, а не като „истинския“ потребител. Този подход ни поведе по нов път, по който разхлабихме ограничението си „без повишаване на привилегиите“.

Преработката: „повишеният пясъчник“

Следващата итерация на пясъчника, която представлява текущата ни реализация, изисква повишени администраторски права по време на настройването. Затова го наричам „пясъчникът с повишени права“. На границата, където Codex стартира команда в системата, пясъчникът с повишени привилегии изглежда като този без повишени привилегии. Той все още изпълнява дъщерни процеси под ограничен токен — по сходен начин токен write_restricted със същия списък с ограничени SID [Everyone, Logon, Synthetic]— но принципалът на този токен вече не е действителният потребител на Windows, а един от двама локални потребители, създадени от самия Codex:

  • CodexSandboxOffline (този, към който са насочени правилата на защитната стена)
  • CodexSandboxOnline (този, към който правилата на защитната стена не са насочени)

Тази на пръв поглед малка подробност всъщност има големи последици за пясъчника, за това кой може да го използва и за сложността на неговата настройка и изпълнение по време на работа.

Диаграма, показваща презаписванията на мрежовата среда за неповишения пясъчник.

Визуално той е подобен на неповишения прототип, като са въведени правила на защитната стена и специален потребител на Windows, който реално изпълнява командите. (Въпреки това въвеждането на тези нови концепции означава, че има повече работа по настройката, преди пясъчникът да започне да изпълнява и защитава команди.)

Вече ни трябва първокласна стъпка за настройка

Дизайнът на неповишения пясъчник имаше проста стъпка за настройка, но тя беше относително малка:

  • Създаване на синтетичен SID при нужда
  • Прилагане на ACL за синтетичния SID sandbox-write

Повишеният пясъчник обаче има повече работа.

  • Създаване на синтетичен SID, ако още не е създаден
  • Създаване на онлайн и офлайн потребителите на пясъчника, ако още не са създадени
  • Съхраняване локално на идентификационните данни на новосъздадените потребители и криптирането им чрез Windows Data Protection API (DPAPI) на място, което потребителите на пясъчника всъщност не могат да четат
  • Създаване на правила на защитната стена, които блокират целия изходящ мрежов достъп за потребителя CodexSandboxOffline, или ако вече съществуват — валидиране, че са правилни

Има една допълнителна особеност на етапа на настройката. Очаква се изолираната среда на Codex да има достъп за четене, еквивалентен на този на действителния потребител на Windows. В пясъчника без повишени права, където SID на принципала на ограничения токен беше този на потребителя на Windows, това беше постигнато. Това обаче не идва безплатно, когато принципалът стане нов потребител на CodexSandbox. Много от съответните директории в Windows ще предоставят разрешения за четене/изпълнение на „Удостоверени потребители“. Един показателен пример е директорията с потребителски профили. По подразбиране потребителите на Windows не могат да четат директориите на профилите на други потребители на Windows, така че дори простите операции за четене на файлове в много сценарии биха били неуспешни.

За да решим това, добавихме още един слой към процеса по настройка на пясъчника — такъв за предоставяне на ACL за четене на потребителите на пясъчника там, където такива ACL може още да не съществуват. Например към някои често използвани директории на Windows:

  • C:\Users\<real-user>
  • C:\Windows\
  • C:\Program Files\
  • C:\Program Files (x86)\
  • C:\ProgramData\

Тъй като този списък с директории е от типа „най-добри усилия“ и инсталирането на ACL върху всяка от тях може да бъде доста скъпо, изпълняваме тази логика асинхронно, така че стъпката за настройка на пясъчника, която блокира потребителите, да не трябва да чака завършването ѝ.

Капсулирахме логиката за настройка в отделен изпълним файл, отчасти, за да преминаваме границата на UAC само когато е необходимо. Но по-дълбоката причина беше архитектурна: настройката на пясъчника има фундаментално различна задача от codex.exe. Поддържането на логиката за настройка на пясъчника в специален двоичен файл позволи на codex.exe да остане нормална, неповишена връзка, предпази специфичната за Windows настройваща логика от това да преувеличава codex.exe на други платформи, разкачи по-дълго изпълняващата се работа по настройка от живота на основния процес и ни даде едно място, където да обработваме различните пътища за настройка, от които пясъчникът се нуждаеше.

Диаграма, показваща стъпката за настройка на първокласния повишен пясъчник.

Изпълнителят на команди е нов двоичен файл, който реално изпълнява потребителските команди

Поради начина, по който работят границите между входовете на потребители и токени в Windows, не можехме да продължим да създаваме ограничен токен и да стартираме процес под него по начина, по който можехме с неповишения пясъчник. За да стартираме реално команди като различен потребител на Windows, първата ни идея беше следният поток:

  • codex.exe се изпълнява от името на действителния потребител на Windows. След това последователно Codex:
    • Извиква LogonUserW(...) за потребителя на изолираната среда.
    • Извиква CreateRestrictedToken(...) върху този токен на потребителя в изолираната среда.
    • Използвайки този ограничен токен за потребител в изолираната среда, извиква CreateProcessAsUserW(...), за да стартира крайния дъщерен процес.

На практика този желан поток не проработи заради бариера, свързана с привилегиите при CreateProcessAsUserW(...). Това означава, че codex.exe можеше да създаде ограничен токен за потребителя в изолираната среда, но не можеше надеждно да стартира дъщерен процес с този токен от страната на реалния потребител на границата. Нуждаехме се от процес, който вече се изпълнява като потребител в изолираната среда — това би позволило стъпката за налагане на ограничения и финалното стартиране на процес да се случат от страната на потребителя в изолираната среда на границата, вместо от страната на реалния потребител.

Това изискване доведе до codex-command-runner.exe - нов изпълним файл, чието единствено предназначение е да създава ограничен токен и да стартира заявената команда. Вместо да искаме от codex.exe сам да изпълни целия поток (реален потребител → потребител в изолираната среда → ограничен токен → дъщерен процес), разделихме потока на две:

Част 1

  • codex.exe извиква CreateProcessWithLogonW(...), за да стартира codex-command-runner.exe като потребителя на пясъчника, без още да използва ограничен токен.

Част 2

  • Вътре в програмата за изпълнение OpenProcessToken(GetCurrentProcess(), ...) отваря собствения токен на програмата за изпълнение, който вече принадлежи на потребителя в изолираната среда.
  • Програмата за изпълнение извиква GetTokenInformation(...), за да извлече SID на влизането в пясъчника, след което CreateRestrictedToken(...), за да създаде крайния ограничен токен.
  • Все още вътре в програмата за изпълнение, тя извиква CreateProcessAsUserW(...) с този ограничен токен, за да стартира истинския дъщерен процес.
Диаграма, показваща потока на command runner за стартиране на ограничени команди.

Пълната картина

Алберт Айнщайн е казал: „Всичко трябва да бъде направено възможно най-просто, но не по-просто.“ В този дух нашият дизайн реши адекватно всеки проблем. Финалната архитектура има четирите слоя, които вече разгледахме:

  • самият codex.exe
  • codex-windows-sandbox-setup.exe за обработка на цялата работа по настройка, свързана с повишени привилегии
  • codex-command-runner.exe за изпълнение на команди с ограничен токен
  • Дъщерният процес

Когато за първи път се заех с този проект, нямах ясна представа докъде ще стигне. Подходът ми беше да започна с инструментализиране на възможността за пясъчник в границата между Codex и операционната система. Този подход тясно съответства на начина, по който пясъчникът на Codex е реализиран в MacOs и Linux.

С натрупването на повече знания за конкретните инструменти, които Windows предоставя, и след десетки решения, балансиращи между сигурност и лекота на използване, системата нарасна до сегашната си форма — множество двоични файлове, персонализирани потребители, правила на защитната стена, повишена стъпка за настройка, асинхронни процеси и още.

Това не е особено проста система, но всяко парче сложност беше добавено по необходимост, за да се изгради пясъчник, който е едновременно безопасен и, доколкото е възможно, да не пречи на потребителя.

Диаграма, показваща финалната архитектура на пясъчника за Windows.

Балансиране на безопасността с реалната полезност

Работейки за добро потребителско изживяване за потребителите на Codex в Windows, нашата цел беше да създадем нещо безопасно, без компромис с полезността — целият смисъл от използването на Codex е агентите да могат да вършат работа без постоянното Ви внимание.

Един от най-големите уроци от този проект беше, че Windows не ни даде един примитив, който ясно да се свързва с „безопасен автономен агент за програмиране“. Комбинирахме няколко инструмента и концепции, за да изградим нещо последователно. Някои ранни идеи се оказаха задънени улици. Финалният дизайн беше хибрид от по-ранни прототипи, всеки от които решаваше част от проблема.

Другият урок беше, че сигурността за кодиращ агент е съвсем различна от по-класическата сигурност на приложенията. Codex трябва да работи за реални работни потоци на разработчиците. Инженерната работа беше свързана с балансиране между съвместимост с агентни работни натоварвания и реално налагане на ограничения. Това напрежение оформи компромисите във финалния дизайн.

Любопитни сте да видите пясъчника на Codex в действие? Изпробвайте го.