Difference between revisions of "Ej-suid-container"
(Created page with "Навигация: Главная страница/Система ejudge/Использование/Общая архитектура системы...") |
(...) |
||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Навигация: [[Main Page|Главная страница]]/[[Система ejudge]]/[[Использование]]/[[Общая архитектура системы]]/[[ej-suid-container]] | Навигация: [[Main Page|Главная страница]]/[[Система ejudge]]/[[Использование]]/[[Общая архитектура системы]]/[[ej-suid-container]] | ||
− | + | Программа доступна с версии [[Изменения в версии 3.9.0|3.9.0]]. | |
+ | |||
+ | Данная программа реализует защищенный запуск программ на выполнение | ||
+ | с использованием контейнеров Linux. Для программы создается изолированное | ||
+ | окружение, в котором недоступна сеть, не видны другие процессы в системе, | ||
+ | и скрыты некоторые каталоги файловой системы, а вместо некоторых других | ||
+ | каталогов отображаются специально подготовленные образы. По умолчанию | ||
+ | тестируемой программе запрещено исполнять некоторые системные вызовы. | ||
+ | |||
+ | Для правильной работы программа должна быть установлена как suid root программа. | ||
+ | Она заменяет собой программы ej-suid-chown, ej-suid-exec, ej-suid-ipcrm, | ||
+ | ej-suid-kill, то есть при использовании ej-suid-container данные | ||
+ | программы могут быть удалены. | ||
+ | |||
+ | В текущей версии программа предназначена прежде всего для использования | ||
+ | из других программ, поэтому не ставит целью вернуть информацию | ||
+ | о завершившейся программе в человеко-читаемом виде или принимать | ||
+ | аргументы командной строки в удобном для человека виде. | ||
+ | |||
+ | === Изоляция запускаемой программы === | ||
+ | |||
+ | Программа запускается в новом пространстве идентификаторов процессов Linux | ||
+ | (pid namespaces). Идентификатор процесса 1 имеет процесс, который | ||
+ | мониторит поведение выполняющейся программы. Он играет роль локального | ||
+ | процесса init. Запускаемая программа получает идентификатор процесса 2. | ||
+ | Если программа будет создавать новые процессы, им будут выдаваться | ||
+ | последовательные идентификаторы. По умолчанию для пользователя, | ||
+ | под которым выполняется запускаемая программа, устанавливается ограничение | ||
+ | в 5 процессов. | ||
+ | |||
+ | Программа запускается в новом пространстве имен для объектов IPC. | ||
+ | Все семафоры, сегменты разделяемой памяти, очереди сообщений, | ||
+ | которые были созданы программой, будут уничтожены автоматически | ||
+ | при ее завершении. Запускаемая программа не имеет доступ | ||
+ | к объектам IPC на хост-системе. | ||
+ | |||
+ | Программа запускается в новом пространстве имен сетевой подсистемы. | ||
+ | В этом пространстве имен не создаются никакие сетевые интерфейсы, то | ||
+ | есть запускаемая программа работает с отключенной сетью. Она | ||
+ | не имеет доступа к сетевым интерфейсам хост-системы. | ||
+ | |||
+ | Программа запускается в новом пространстве имен монтируемых | ||
+ | каталогов файловой системы (mount namespace). Для запускаемой | ||
+ | программы файловая система хост-системы модифицируется следующим образом: | ||
+ | |||
+ | * Демонтируются все файловые системы типов <code>fusectl</code>, <code>rpc_pipefs</code>, <code>securityfs</code> и аналогичных, которые могут использоваться для доступа к системной информации. | ||
+ | |||
+ | * Рабочий каталог запускаемой программы монтируется в <code>/sandbox</code>. Он будет установлен как текущий каталог при запуске. | ||
+ | |||
+ | * Каталог <code>/proc</code> перекрывается пустым каталогом. | ||
+ | |||
+ | * Каталог <code>/sys</code> перекрывается пустым каталогом. | ||
+ | |||
+ | * Каталог <code>/boot</code> перекрывается пустым каталогом. | ||
+ | |||
+ | * Каталог <code>/srv</code> перекрывается пустым каталогом. | ||
+ | |||
+ | * Каталог <code>/data</code> перекрывается пустым каталогом. | ||
+ | |||
+ | * Если подготовлен образ каталога /root, то этот образ перекрывает каталог <code>/root</code>. | ||
+ | |||
+ | * Если подготовлен образ каталога /etc, этот образ перекрывает каталог <code>/etc</code>, однако <code>/etc/alternatives</code> и <code>/etc/java</code> монтируется с файловой системы хоста. | ||
+ | |||
+ | * Если подготовлен образ каталога <code>/var</code>, этот образ перекрывает каталог <code>/var</code> файловой системы. | ||
+ | |||
+ | * Если подготовлен образ каталога <code>/dev</code>, этот образ перекрывает каталог <code>/dev</code> файловой системы. | ||
+ | |||
+ | * Каталоги <code>/tmp</code>, <code>/run</code>, <code>/dev/mqueue</code>, <code>/dev/shm</code> инициализируются как пустые каталоги для временных файлов (файловая система tmpfs). | ||
+ | |||
+ | * Если подготовлен образ каталога <code>/home</code>, этот образ перекрывает каталог <code>/home</code> файловой системы, за исключеним каталога <code>/home/judges/compile</code>, который монтируется с файловой системы хоста. В противном случае каталоги /home/judges/data и /home/judges/var перекрываются пустыми каталогами. | ||
+ | |||
+ | Подготовленные образы каталогов должны располагаться в каталоге | ||
+ | <code>EJUDGE_PREFIX_DIR/share/ejudge/container</code>. Eсли | ||
+ | EJUDGE_PREFIX_DIR установлен в <code>/opt/ejudge</code>, то каталог | ||
+ | должен выглядеть так: | ||
+ | |||
+ | [~]$ ls /opt/ejudge/share/ejudge/container | ||
+ | dev empty etc home root var | ||
+ | |||
+ | У запускаемой программы блокируется возможность выполнения | ||
+ | "небезопасных" системных вызовов. В текущей версии — это | ||
+ | системные вызовы семейства fork (то есть fork, vfork, clone, clone3) | ||
+ | и системные вызовы семейства exec (то есть execve, execveat). | ||
+ | В дальнейшем список блокируемых системных вызовов может быть расширен. | ||
+ | Если программа пытается выполнить запрещенный системный вызов, | ||
+ | она получит сигнал SIGSYS (Bad system call). | ||
+ | |||
+ | У запускаемой программы блокируется возможность повышения | ||
+ | привилегий с помощью исполнения suid-программ. | ||
+ | |||
+ | === Использование === | ||
+ | |||
+ | Использование: | ||
+ | |||
+ | ej-suid-container [OPTIONS] PROGRAM [ARGUMENTS] | ||
+ | |||
+ | Здесь <code>OPTIONS</code> — дополнительные опции для настройки | ||
+ | контейнеров. <code>PROGRAM</code> — запускаемая программа, | ||
+ | <code>ARGUMENTS</code> — аргументы для запускаемой программы. | ||
+ | |||
+ | Все опции для ej-suid-container задаются в одном аргументе командной строки. | ||
+ | Между опциями отсутствуют разделители, но может использоваться символ | ||
+ | '<code>,</code>' (запятая), который в опциях игнорируется. | ||
+ | Опции начинаются со знака "минус". | ||
+ | |||
+ | Если опция требует целого параметра, параметр записывается сразу после опции | ||
+ | без разделителя, например <code>lo10</code>. | ||
+ | |||
+ | Если опция требует параметра-размера, то после целого числа может | ||
+ | следовать суффикс 'k', 'm' или 'g', задающий соответствующий множитель. | ||
+ | Чтобы отделить значение с суффиксом от следующей опции можно использовать | ||
+ | запятую. | ||
+ | |||
+ | Если опция требует параметр-строку, сразу же после опции записывается | ||
+ | длина строки, затем символ "запятая", затем строка-параметр, | ||
+ | например, <code>ri10,/tmp/a.txt</code>. Либо сразу после опции записывается | ||
+ | символ-терминатор строки, а далее идет строка до этого символа-терминатора, | ||
+ | например, <code>ri|/tmp/a.txt|</code>. | ||
+ | |||
+ | {| | ||
+ | | <code>f<FD></code> | ||
+ | | файловый дескриптор - целое число | ||
+ | | задать ф. д. для получения результата выполнения программы Если опция отсутствует, то строка результата выводится на stderr | ||
+ | |- | ||
+ | | <code>mg</code> | ||
+ | | | ||
+ | | не создавать linux control group (не использовать!) | ||
+ | |- | ||
+ | | <code>mi</code> | ||
+ | | | ||
+ | | не создавать пространство имен IPC, запускаемая программа будет иметь доступ к объектам IPC хост-системы | ||
+ | |- | ||
+ | | <code>mn</code> | ||
+ | | | ||
+ | | не создавать пространство имен сетевых интерфейсов, запускаемая программа будет иметь неограниченный доступ к сети | ||
+ | |- | ||
+ | | <code>ml</code> | ||
+ | | | ||
+ | | в изолированном пространстве сетевых интерфейсов поднять интерфейс lo (loopback) и настроить его на адрес 127.0.0.1 ([[Изменения в версии 3.11.0|3.11.0]]) | ||
+ | |- | ||
+ | | <code>mm</code> | ||
+ | | | ||
+ | | не создавать пространство имен файловой системы, запускаемая программа будет иметь доступ ко всей файловой системе хост-системы | ||
+ | |- | ||
+ | | <code>mp</code> | ||
+ | | | ||
+ | | не создавать пространство имен процессов, запускаемая программа будет иметь идентификатор процесса среди процессов хост-системы (не использовать!) | ||
+ | |- | ||
+ | | <code>mP</code> | ||
+ | | | ||
+ | | не перекрывать файловую систему /proc, хотя в ней будут отображаться только процессы из пространства имен процессов, но общесистемные файлы, например, /proc/sys будут доступны | ||
+ | |- | ||
+ | | <code>mS</code> | ||
+ | | | ||
+ | | не перекрывать доступ к файловой системе /sys | ||
+ | |- | ||
+ | | <code>mv</code> | ||
+ | | | ||
+ | | не подменять файловую систему /var, запускаемая программа будет иметь доступ к каталогу /var хост-системы | ||
+ | |- | ||
+ | | <code>me</code> | ||
+ | | | ||
+ | | не подменять файловую систему /etc, запускаемая программа будет иметь доступ к каталогу /etc хост-системы | ||
+ | |- | ||
+ | | <code>ms</code> | ||
+ | | | ||
+ | | не привязывать рабочий каталог программы к каталогу /sandbox | ||
+ | |- | ||
+ | | <code>mh</code> | ||
+ | | | ||
+ | | не подменять файловую систему /home, запускаемая программа будет иметь доступ к каталогу /home хост-системы | ||
+ | |- | ||
+ | | <code>md</code> | ||
+ | | | ||
+ | | не подменять файловую систему /dev, запускаемая программа будет иметь доступ к каталогу /dev хост-системы | ||
+ | |- | ||
+ | | <code>mr</code> | ||
+ | | | ||
+ | | не подменять файловую систему /run, запускаемая программа будет иметь доступ к каталогу /run хост-системы ([[Изменения в версии 3.11.0|3.11.0]]) | ||
+ | |- | ||
+ | | <code>mo</code> | ||
+ | | | ||
+ | | не менять пользователя на ejexec/ejcompile (не использовать!) | ||
+ | |- | ||
+ | | <code>mG</code> | ||
+ | | | ||
+ | | не создавать новую группу процессов (не использовать!) | ||
+ | |- | ||
+ | | <code>mc</code> | ||
+ | | | ||
+ | | после завершения запущенной программы посчитать количество оставшихся незавершенных процессов. Может быть полезно, когда запускаемая программа создает другие процессы и необходимо проконтролировать, что все запущенные процессы были завершены перед завершением основной программы. Независимо от значения этой опции все такие процессы будут принудительно завершены, эта опция позволяет получить их количество. | ||
+ | |- | ||
+ | | <code>mI</code> | ||
+ | | | ||
+ | | посчитать количество объектов IPC, оставшихся неудаленными после завершения программы. | ||
+ | |- | ||
+ | | <code>ma</code> | ||
+ | | | ||
+ | | не ограничивать процессорное время для запускаемой программы | ||
+ | |- | ||
+ | | <code>mb</code> | ||
+ | | | ||
+ | | не ограничивать реальное время для запускаемой программы | ||
+ | |- | ||
+ | | <code>mD</code> | ||
+ | | | ||
+ | | переносить в контейнер не сам рабочий каталог, а его родительский каталог. Используется когда при тестировании в ejudge установлена опция <code>use_tgz</code>. | ||
+ | |- | ||
+ | | <code>mC</code> | ||
+ | | | ||
+ | | запускать программу в режиме контейнера для компиляции | ||
+ | |- | ||
+ | | <code>mV</code> | ||
+ | | | ||
+ | | отменить установку значения по умолчанию для лимита виртуальной памяти | ||
+ | |- | ||
+ | | <code>mE</code> | ||
+ | | | ||
+ | | включить определение ошибки "security violation" | ||
+ | |- | ||
+ | | <code>mM</code> | ||
+ | | | ||
+ | | включить эвристическое определение ошибки превышения лимита памяти | ||
+ | |- | ||
+ | | <code>w<DIR></code> | ||
+ | | путь к каталогу - строка | ||
+ | | задать рабочий каталог для запускаемой программы | ||
+ | |- | ||
+ | | <code>rn</code> | ||
+ | | | ||
+ | | перенаправить stdin, stdout и stderr на /dev/null | ||
+ | |- | ||
+ | | <code>rm</code> | ||
+ | | | ||
+ | | слить вывод на stdout и stderr в один поток, задаваемый спецификацией перенаправления для stdout | ||
+ | |- | ||
+ | | <code>ri<FILE></code> | ||
+ | | путь к файлу - строка | ||
+ | | перенаправить stdin из файла FILE | ||
+ | |- | ||
+ | | <code>ro<FILE></code> | ||
+ | | путь к файлу - строка | ||
+ | | перенаправить stdout в файл FILE в режиме перезаписи | ||
+ | |- | ||
+ | | <code>rO<FILE></code> | ||
+ | | путь к файлу - строка | ||
+ | | перенаправить stdout в файл FILE в режиме добавления | ||
+ | |- | ||
+ | | <code>re<FILE></code> | ||
+ | | путь к файлу - строка | ||
+ | | перенаправить stderr в файл FILE в режиме перезаписи | ||
+ | |- | ||
+ | | <code>rE<FILE></code> | ||
+ | | путь к файлу - строка | ||
+ | | перенаправить stderr в файл FILE в режиме добавления | ||
+ | |- | ||
+ | | <code>rp<FILE></code> | ||
+ | | путь к файлу - строка | ||
+ | | запускать на выполнение файл FILE, а не файл, имя которого указано в командной строке. Опция используется, когда у запускаемой программы имя запускаемого файла отличается от argv[0]. | ||
+ | |- | ||
+ | | <code>ra<FD></code> | ||
+ | | ф.д. - целое число | ||
+ | | перенаправить stdin из заданного открытого файлового дескриптора | ||
+ | |- | ||
+ | | <code>rb<FD></code> | ||
+ | | ф.д. - целое число | ||
+ | | перенаправить stdout в заданный открытый файловый дескриптор | ||
+ | |- | ||
+ | | <code>lt<T></code> | ||
+ | | миллисекунды - целое число | ||
+ | | установить ограничение на процессорное время в миллисекундах | ||
+ | |- | ||
+ | | <code>lr<T></code> | ||
+ | | миллисекунды - целое число | ||
+ | | установить ограничение на реальное время в миллисекундах | ||
+ | |- | ||
+ | | <code>lm<M></code> | ||
+ | | восьмеричное число | ||
+ | | установить параметр umask для запускаемой программы | ||
+ | |- | ||
+ | | <code>lo<N></code> | ||
+ | | целое число | ||
+ | | установить лимит на количество открытых файловых дескрипторов | ||
+ | |- | ||
+ | | <code>lu<N></code> | ||
+ | | целое число | ||
+ | | установить лимит на количество процессов | ||
+ | |- | ||
+ | | <code>ls<Z></code> | ||
+ | | размер | ||
+ | | установить лимит на размер стека в байтах | ||
+ | |- | ||
+ | | <code>lv<Z></code> | ||
+ | | размер | ||
+ | | установить лимит на размер виртуального адресного пространства в байтах | ||
+ | |- | ||
+ | | <code>lR<Z></code> | ||
+ | | размер | ||
+ | | установить лимит на размер потребленной оперативной памяти (RSS) в байтах | ||
+ | |- | ||
+ | | <code>lf<Z></code> | ||
+ | | размер | ||
+ | | установить лимит на размер файлов, создаваемых запускаемой программой в байтах | ||
+ | |- | ||
+ | | <code>ol<S></code> | ||
+ | | имя языка - строка | ||
+ | | задать язык программирования, на котором написана запускаемая программа | ||
+ | |- | ||
+ | | <code>s0</code> | ||
+ | | | ||
+ | | отключить фильтрацию системных вызовов | ||
+ | |- | ||
+ | | <code>se</code> | ||
+ | | | ||
+ | | разрешить системные вызовы семейства exec | ||
+ | |- | ||
+ | | <code>sf</code> | ||
+ | | | ||
+ | | разрешить системные вызовы семейства fork | ||
+ | |- | ||
+ | | <code>su</code> | ||
+ | | | ||
+ | | разрешить системный вызов <code>unshare</code> | ||
+ | |- | ||
+ | | <code>sm</code> | ||
+ | | | ||
+ | | разрешить системный вызов <code>memfd_create</code> | ||
+ | |- | ||
+ | | <code>cf<FD></code> | ||
+ | | ф.д. - целое число | ||
+ | | задать файловый дескриптор для управляющего сокета для управления выполняющейся программой извне | ||
+ | |- | ||
+ | | <code>cu<N></code> | ||
+ | | целое число | ||
+ | | задать последовательный номер пользователя, под которым выполнять запущенную программу | ||
+ | |} | ||
+ | |||
+ | Не гарантируется корректная работа программы при использовании | ||
+ | опций <code>mo</code>, <code>mG</code>, <code>mp</code>. FIXME: удалить их? | ||
+ | |||
+ | ==== Режим компиляции ==== | ||
+ | |||
+ | Опция <code>mC</code> определяет, что программа должна запускаться в режиме "компилятора". | ||
+ | Этот режим предназначен для использования при компиляции программ. Компилятор ограничивается | ||
+ | в доступе к различным важным с точки зрения безопасности файлам файловой системы хоста. | ||
+ | В режиме компиляции: | ||
+ | |||
+ | * отключается фильтрация системных вызовов (<code>s0</code>) | ||
+ | * отключается перекрытие файловой системы /proc (<code>mP</code>) | ||
+ | * отключается перекрытие файловой системы /sys (<code>mS</code>) | ||
+ | * отключается перекрытие файловой системы /dev (<code>md</code>) | ||
+ | * лимит количества процессов устанавливается в 100 | ||
+ | * отключается лимит размера виртуальной памяти | ||
+ | * отключается лимит реального времени | ||
+ | * лимит процессорного времени устанавливается в 60000 мс | ||
+ | * для запуска программы используется пользователь <code>ejcompile</code> | ||
+ | |||
+ | ==== Настройка исходного языка программирования ==== | ||
+ | |||
+ | Опция <code>ol</code> позволяет указать, на каком языке программирования была написана запускаемая программа. | ||
+ | В зависимости от этого выполняется донастройка параметров запуска программы. | ||
+ | Это сделано для того, чтобы запуск программ на разных языках программирования не требовал | ||
+ | бы указания различных опций настройки, зависящих от языка, то есть чтобы любые поддерживаемые языки | ||
+ | программирования работали "из коробки". | ||
+ | |||
+ | В таблице перечислены дополнительные настройки для поддерживаемых языков программирования. | ||
+ | |||
+ | {| | ||
+ | | языки | ||
+ | | настройки | ||
+ | | комментарий | ||
+ | |- | ||
+ | | javac7, javac, kotlin, scala | ||
+ | | <code>s0mPls1M,lu40lv-1</code> | ||
+ | | отключается лимит размера виртуальной памяти, ограничение размера выполняется средствами jvm | ||
+ | |- | ||
+ | | mcs, vbnc, pasabc-linux | ||
+ | | <code>s0mPls1M</code> | ||
+ | | если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. | ||
+ | |- | ||
+ | | pypy, pypy3 | ||
+ | | <code>mP</code> | ||
+ | | | ||
+ | |- | ||
+ | | gcc-vg, g++-vg | ||
+ | | <code>s0mP</code> | ||
+ | | если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. | ||
+ | |- | ||
+ | | dotnet-cs, dotnet-vb | ||
+ | | <code>s0mPls1M,lu40</code> | ||
+ | | если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. | ||
+ | |- | ||
+ | | make | ||
+ | | <code>s0mP</code> | ||
+ | | | ||
+ | |- | ||
+ | | make-vg | ||
+ | | <code>s0mP</code> | ||
+ | | если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. | ||
+ | |- | ||
+ | | gccgo | ||
+ | | <code>s0mPlu20</code> | ||
+ | | если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. | ||
+ | |- | ||
+ | | node | ||
+ | | <code>s0mPls1M,lu20</code> | ||
+ | | если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. | ||
+ | |} | ||
+ | |||
+ | ==== Настройка пользователя, под которым выполняется программа ==== | ||
+ | |||
+ | Опция <code>cu</code> позволяет модифицировать пользователя, под которым запускается программа. | ||
+ | Если опция не указана, то используется пользователь <code>ejexec</code>. Если указан номер, например, <code>cu1</code>, | ||
+ | то используется пользователь <code>ejexec1</code>. Соответствующий пользователь должен быть создан. | ||
+ | |||
+ | ==== Внешнее управление выполняющейся программой ==== | ||
+ | |||
+ | Опция <code>cf</code> позволяет задать файловый дескриптор, с помощью которого внешняя программа может | ||
+ | управлять выполняющейся программой. Этот файловый дескриптор должен быть двусторонним UNIX-сокетом. | ||
+ | Его следует создавать с помощью системного вызова <code>socketpair</code>. | ||
+ | |||
+ | Команда занимает 4 байта и передается в бинарном виде. Поддерживается следующие команды. | ||
+ | |||
+ | * <code>0xe00001SS</code> - отправить в тестируемую программу сигнал SS. | ||
+ | Номер сигнала занимает младший байт сообщения. | ||
+ | |||
+ | === Возвращаемая информация о работе программы === | ||
+ | |||
+ | Программа ej-suid-container завершается с кодом 0, если процесс для запуска указанной программы был создан. | ||
+ | В случае фатальной ошибки при создании процесса возвращается код 1. | ||
+ | |||
+ | Информация о выполнении запускаемой программы выводится в файловый дескриптор, переданный с опцией <code>f</code>, | ||
+ | либо на стандартный поток ошибок, если опция не задана. Информация выводится в виде закодированной строки | ||
+ | в следующем формате. | ||
+ | |||
+ | Сначала идет информация о том, как завершилась программа: | ||
+ | |||
+ | * <code>t</code> (одна буква 't') — исчерпан лимит процессорного времени | ||
+ | * <code>r</code> — исчерпан лимит реального времени | ||
+ | * <code>m</code> — исчерпан лимит памяти | ||
+ | * <code>v</code> — нарушение ограничений безопасности | ||
+ | * <code>e<N></code> (буква 'e', за которой следует число) — программа завершилась с кодом завершения N | ||
+ | * <code>s<N></code> (буква 's', за которой следует число) — программа завершилась из-за сигнала N | ||
+ | |||
+ | Далее в произвольном порядке идет детальная информация о завершении программы: | ||
+ | |||
+ | * <code>T<T></code> — затраченное процессорное время в микросекундах | ||
+ | * <code>R<T></code> — затраченное астрономическое время в микросекундах | ||
+ | * <code>u<T></code> — затраченное пользовательское время в микросекундах | ||
+ | * <code>s<T></code> — затраченное системное время в микросекундах | ||
+ | * <code>v<Z></code> — максимальный размер виртуальной памяти в байтах (неточно) | ||
+ | * <code>e<Z></code> — максимальный размер использованной оперативной памяти (RSS) в байтах | ||
+ | * <code>a<N></code> — поле ru_nvcsw | ||
+ | * <code>b<N></code> — поле ru_nivcsw | ||
+ | * <code>i<N></code> — количество объектов IPC, оставшихся после завершения | ||
+ | * <code>o<N></code> — количество процессов-сирот, оставшихся после завершения | ||
+ | * <code>ct<T></code> — затраченное процессорное время в микросекундах по всем процессам в совокупности | ||
+ | * <code>cu<T></code> — затраченное пользовательское время в микросекундах по всем процессам в совокупности | ||
+ | * <code>cs<T></code> — затраченное системное время в микросекундах по всем процессам в совокупности | ||
+ | * <code>L<S></code> — дополнительные сообщения - строка в формате, описанном выше (длина - запятая - содержимое) | ||
+ | |||
+ | FIXME: выводить на stderr в JSON-формате? |
Latest revision as of 06:54, 8 January 2024
Навигация: Главная страница/Система ejudge/Использование/Общая архитектура системы/ej-suid-container
Программа доступна с версии 3.9.0.
Данная программа реализует защищенный запуск программ на выполнение с использованием контейнеров Linux. Для программы создается изолированное окружение, в котором недоступна сеть, не видны другие процессы в системе, и скрыты некоторые каталоги файловой системы, а вместо некоторых других каталогов отображаются специально подготовленные образы. По умолчанию тестируемой программе запрещено исполнять некоторые системные вызовы.
Для правильной работы программа должна быть установлена как suid root программа. Она заменяет собой программы ej-suid-chown, ej-suid-exec, ej-suid-ipcrm, ej-suid-kill, то есть при использовании ej-suid-container данные программы могут быть удалены.
В текущей версии программа предназначена прежде всего для использования из других программ, поэтому не ставит целью вернуть информацию о завершившейся программе в человеко-читаемом виде или принимать аргументы командной строки в удобном для человека виде.
Contents
Изоляция запускаемой программы
Программа запускается в новом пространстве идентификаторов процессов Linux (pid namespaces). Идентификатор процесса 1 имеет процесс, который мониторит поведение выполняющейся программы. Он играет роль локального процесса init. Запускаемая программа получает идентификатор процесса 2. Если программа будет создавать новые процессы, им будут выдаваться последовательные идентификаторы. По умолчанию для пользователя, под которым выполняется запускаемая программа, устанавливается ограничение в 5 процессов.
Программа запускается в новом пространстве имен для объектов IPC. Все семафоры, сегменты разделяемой памяти, очереди сообщений, которые были созданы программой, будут уничтожены автоматически при ее завершении. Запускаемая программа не имеет доступ к объектам IPC на хост-системе.
Программа запускается в новом пространстве имен сетевой подсистемы. В этом пространстве имен не создаются никакие сетевые интерфейсы, то есть запускаемая программа работает с отключенной сетью. Она не имеет доступа к сетевым интерфейсам хост-системы.
Программа запускается в новом пространстве имен монтируемых каталогов файловой системы (mount namespace). Для запускаемой программы файловая система хост-системы модифицируется следующим образом:
- Демонтируются все файловые системы типов
fusectl
,rpc_pipefs
,securityfs
и аналогичных, которые могут использоваться для доступа к системной информации.
- Рабочий каталог запускаемой программы монтируется в
/sandbox
. Он будет установлен как текущий каталог при запуске.
- Каталог
/proc
перекрывается пустым каталогом.
- Каталог
/sys
перекрывается пустым каталогом.
- Каталог
/boot
перекрывается пустым каталогом.
- Каталог
/srv
перекрывается пустым каталогом.
- Каталог
/data
перекрывается пустым каталогом.
- Если подготовлен образ каталога /root, то этот образ перекрывает каталог
/root
.
- Если подготовлен образ каталога /etc, этот образ перекрывает каталог
/etc
, однако/etc/alternatives
и/etc/java
монтируется с файловой системы хоста.
- Если подготовлен образ каталога
/var
, этот образ перекрывает каталог/var
файловой системы.
- Если подготовлен образ каталога
/dev
, этот образ перекрывает каталог/dev
файловой системы.
- Каталоги
/tmp
,/run
,/dev/mqueue
,/dev/shm
инициализируются как пустые каталоги для временных файлов (файловая система tmpfs).
- Если подготовлен образ каталога
/home
, этот образ перекрывает каталог/home
файловой системы, за исключеним каталога/home/judges/compile
, который монтируется с файловой системы хоста. В противном случае каталоги /home/judges/data и /home/judges/var перекрываются пустыми каталогами.
Подготовленные образы каталогов должны располагаться в каталоге
EJUDGE_PREFIX_DIR/share/ejudge/container
. Eсли
EJUDGE_PREFIX_DIR установлен в /opt/ejudge
, то каталог
должен выглядеть так:
[~]$ ls /opt/ejudge/share/ejudge/container dev empty etc home root var
У запускаемой программы блокируется возможность выполнения "небезопасных" системных вызовов. В текущей версии — это системные вызовы семейства fork (то есть fork, vfork, clone, clone3) и системные вызовы семейства exec (то есть execve, execveat). В дальнейшем список блокируемых системных вызовов может быть расширен. Если программа пытается выполнить запрещенный системный вызов, она получит сигнал SIGSYS (Bad system call).
У запускаемой программы блокируется возможность повышения привилегий с помощью исполнения suid-программ.
Использование
Использование:
ej-suid-container [OPTIONS] PROGRAM [ARGUMENTS]
Здесь OPTIONS
— дополнительные опции для настройки
контейнеров. PROGRAM
— запускаемая программа,
ARGUMENTS
— аргументы для запускаемой программы.
Все опции для ej-suid-container задаются в одном аргументе командной строки.
Между опциями отсутствуют разделители, но может использоваться символ
',
' (запятая), который в опциях игнорируется.
Опции начинаются со знака "минус".
Если опция требует целого параметра, параметр записывается сразу после опции
без разделителя, например lo10
.
Если опция требует параметра-размера, то после целого числа может следовать суффикс 'k', 'm' или 'g', задающий соответствующий множитель. Чтобы отделить значение с суффиксом от следующей опции можно использовать запятую.
Если опция требует параметр-строку, сразу же после опции записывается
длина строки, затем символ "запятая", затем строка-параметр,
например, ri10,/tmp/a.txt
. Либо сразу после опции записывается
символ-терминатор строки, а далее идет строка до этого символа-терминатора,
например, ri|/tmp/a.txt|
.
f<FD>
|
файловый дескриптор - целое число | задать ф. д. для получения результата выполнения программы Если опция отсутствует, то строка результата выводится на stderr |
mg
|
не создавать linux control group (не использовать!) | |
mi
|
не создавать пространство имен IPC, запускаемая программа будет иметь доступ к объектам IPC хост-системы | |
mn
|
не создавать пространство имен сетевых интерфейсов, запускаемая программа будет иметь неограниченный доступ к сети | |
ml
|
в изолированном пространстве сетевых интерфейсов поднять интерфейс lo (loopback) и настроить его на адрес 127.0.0.1 (3.11.0) | |
mm
|
не создавать пространство имен файловой системы, запускаемая программа будет иметь доступ ко всей файловой системе хост-системы | |
mp
|
не создавать пространство имен процессов, запускаемая программа будет иметь идентификатор процесса среди процессов хост-системы (не использовать!) | |
mP
|
не перекрывать файловую систему /proc, хотя в ней будут отображаться только процессы из пространства имен процессов, но общесистемные файлы, например, /proc/sys будут доступны | |
mS
|
не перекрывать доступ к файловой системе /sys | |
mv
|
не подменять файловую систему /var, запускаемая программа будет иметь доступ к каталогу /var хост-системы | |
me
|
не подменять файловую систему /etc, запускаемая программа будет иметь доступ к каталогу /etc хост-системы | |
ms
|
не привязывать рабочий каталог программы к каталогу /sandbox | |
mh
|
не подменять файловую систему /home, запускаемая программа будет иметь доступ к каталогу /home хост-системы | |
md
|
не подменять файловую систему /dev, запускаемая программа будет иметь доступ к каталогу /dev хост-системы | |
mr
|
не подменять файловую систему /run, запускаемая программа будет иметь доступ к каталогу /run хост-системы (3.11.0) | |
mo
|
не менять пользователя на ejexec/ejcompile (не использовать!) | |
mG
|
не создавать новую группу процессов (не использовать!) | |
mc
|
после завершения запущенной программы посчитать количество оставшихся незавершенных процессов. Может быть полезно, когда запускаемая программа создает другие процессы и необходимо проконтролировать, что все запущенные процессы были завершены перед завершением основной программы. Независимо от значения этой опции все такие процессы будут принудительно завершены, эта опция позволяет получить их количество. | |
mI
|
посчитать количество объектов IPC, оставшихся неудаленными после завершения программы. | |
ma
|
не ограничивать процессорное время для запускаемой программы | |
mb
|
не ограничивать реальное время для запускаемой программы | |
mD
|
переносить в контейнер не сам рабочий каталог, а его родительский каталог. Используется когда при тестировании в ejudge установлена опция use_tgz .
| |
mC
|
запускать программу в режиме контейнера для компиляции | |
mV
|
отменить установку значения по умолчанию для лимита виртуальной памяти | |
mE
|
включить определение ошибки "security violation" | |
mM
|
включить эвристическое определение ошибки превышения лимита памяти | |
w<DIR>
|
путь к каталогу - строка | задать рабочий каталог для запускаемой программы |
rn
|
перенаправить stdin, stdout и stderr на /dev/null | |
rm
|
слить вывод на stdout и stderr в один поток, задаваемый спецификацией перенаправления для stdout | |
ri<FILE>
|
путь к файлу - строка | перенаправить stdin из файла FILE |
ro<FILE>
|
путь к файлу - строка | перенаправить stdout в файл FILE в режиме перезаписи |
rO<FILE>
|
путь к файлу - строка | перенаправить stdout в файл FILE в режиме добавления |
re<FILE>
|
путь к файлу - строка | перенаправить stderr в файл FILE в режиме перезаписи |
rE<FILE>
|
путь к файлу - строка | перенаправить stderr в файл FILE в режиме добавления |
rp<FILE>
|
путь к файлу - строка | запускать на выполнение файл FILE, а не файл, имя которого указано в командной строке. Опция используется, когда у запускаемой программы имя запускаемого файла отличается от argv[0]. |
ra<FD>
|
ф.д. - целое число | перенаправить stdin из заданного открытого файлового дескриптора |
rb<FD>
|
ф.д. - целое число | перенаправить stdout в заданный открытый файловый дескриптор |
lt<T>
|
миллисекунды - целое число | установить ограничение на процессорное время в миллисекундах |
lr<T>
|
миллисекунды - целое число | установить ограничение на реальное время в миллисекундах |
lm<M>
|
восьмеричное число | установить параметр umask для запускаемой программы |
lo<N>
|
целое число | установить лимит на количество открытых файловых дескрипторов |
lu<N>
|
целое число | установить лимит на количество процессов |
ls<Z>
|
размер | установить лимит на размер стека в байтах |
lv<Z>
|
размер | установить лимит на размер виртуального адресного пространства в байтах |
lR<Z>
|
размер | установить лимит на размер потребленной оперативной памяти (RSS) в байтах |
lf<Z>
|
размер | установить лимит на размер файлов, создаваемых запускаемой программой в байтах |
ol<S>
|
имя языка - строка | задать язык программирования, на котором написана запускаемая программа |
s0
|
отключить фильтрацию системных вызовов | |
se
|
разрешить системные вызовы семейства exec | |
sf
|
разрешить системные вызовы семейства fork | |
su
|
разрешить системный вызов unshare
| |
sm
|
разрешить системный вызов memfd_create
| |
cf<FD>
|
ф.д. - целое число | задать файловый дескриптор для управляющего сокета для управления выполняющейся программой извне |
cu<N>
|
целое число | задать последовательный номер пользователя, под которым выполнять запущенную программу |
Не гарантируется корректная работа программы при использовании
опций mo
, mG
, mp
. FIXME: удалить их?
Режим компиляции
Опция mC
определяет, что программа должна запускаться в режиме "компилятора".
Этот режим предназначен для использования при компиляции программ. Компилятор ограничивается
в доступе к различным важным с точки зрения безопасности файлам файловой системы хоста.
В режиме компиляции:
- отключается фильтрация системных вызовов (
s0
) - отключается перекрытие файловой системы /proc (
mP
) - отключается перекрытие файловой системы /sys (
mS
) - отключается перекрытие файловой системы /dev (
md
) - лимит количества процессов устанавливается в 100
- отключается лимит размера виртуальной памяти
- отключается лимит реального времени
- лимит процессорного времени устанавливается в 60000 мс
- для запуска программы используется пользователь
ejcompile
Настройка исходного языка программирования
Опция ol
позволяет указать, на каком языке программирования была написана запускаемая программа.
В зависимости от этого выполняется донастройка параметров запуска программы.
Это сделано для того, чтобы запуск программ на разных языках программирования не требовал
бы указания различных опций настройки, зависящих от языка, то есть чтобы любые поддерживаемые языки
программирования работали "из коробки".
В таблице перечислены дополнительные настройки для поддерживаемых языков программирования.
языки | настройки | комментарий |
javac7, javac, kotlin, scala | s0mPls1M,lu40lv-1
|
отключается лимит размера виртуальной памяти, ограничение размера выполняется средствами jvm |
mcs, vbnc, pasabc-linux | s0mPls1M
|
если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. |
pypy, pypy3 | mP
|
|
gcc-vg, g++-vg | s0mP
|
если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. |
dotnet-cs, dotnet-vb | s0mPls1M,lu40
|
если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. |
make | s0mP
|
|
make-vg | s0mP
|
если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. |
gccgo | s0mPlu20
|
если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. |
node | s0mPls1M,lu20
|
если задано ограничение размера виртуальной памяти, оно переустанавливается на ограничение RSS. Ограничение VM снимается. |
Настройка пользователя, под которым выполняется программа
Опция cu
позволяет модифицировать пользователя, под которым запускается программа.
Если опция не указана, то используется пользователь ejexec
. Если указан номер, например, cu1
,
то используется пользователь ejexec1
. Соответствующий пользователь должен быть создан.
Внешнее управление выполняющейся программой
Опция cf
позволяет задать файловый дескриптор, с помощью которого внешняя программа может
управлять выполняющейся программой. Этот файловый дескриптор должен быть двусторонним UNIX-сокетом.
Его следует создавать с помощью системного вызова socketpair
.
Команда занимает 4 байта и передается в бинарном виде. Поддерживается следующие команды.
0xe00001SS
- отправить в тестируемую программу сигнал SS.
Номер сигнала занимает младший байт сообщения.
Возвращаемая информация о работе программы
Программа ej-suid-container завершается с кодом 0, если процесс для запуска указанной программы был создан. В случае фатальной ошибки при создании процесса возвращается код 1.
Информация о выполнении запускаемой программы выводится в файловый дескриптор, переданный с опцией f
,
либо на стандартный поток ошибок, если опция не задана. Информация выводится в виде закодированной строки
в следующем формате.
Сначала идет информация о том, как завершилась программа:
t
(одна буква 't') — исчерпан лимит процессорного времениr
— исчерпан лимит реального времениm
— исчерпан лимит памятиv
— нарушение ограничений безопасностиe<N>
(буква 'e', за которой следует число) — программа завершилась с кодом завершения Ns<N>
(буква 's', за которой следует число) — программа завершилась из-за сигнала N
Далее в произвольном порядке идет детальная информация о завершении программы:
T<T>
— затраченное процессорное время в микросекундахR<T>
— затраченное астрономическое время в микросекундахu<T>
— затраченное пользовательское время в микросекундахs<T>
— затраченное системное время в микросекундахv<Z>
— максимальный размер виртуальной памяти в байтах (неточно)e<Z>
— максимальный размер использованной оперативной памяти (RSS) в байтахa<N>
— поле ru_nvcswb<N>
— поле ru_nivcswi<N>
— количество объектов IPC, оставшихся после завершенияo<N>
— количество процессов-сирот, оставшихся после завершенияct<T>
— затраченное процессорное время в микросекундах по всем процессам в совокупностиcu<T>
— затраченное пользовательское время в микросекундах по всем процессам в совокупностиcs<T>
— затраченное системное время в микросекундах по всем процессам в совокупностиL<S>
— дополнительные сообщения - строка в формате, описанном выше (длина - запятая - содержимое)
FIXME: выводить на stderr в JSON-формате?