id: Гость   вход   регистрация
текущее время 16:41 29/03/2024
создать
просмотр
редакции
ссылки

Это старая редакция страницы Разработки / gpgremote за 27/01/2015 17:51.


GPG Remote


Оглавление документа:

Постановка проблемы


Использование GnuPG в сетевой среде всегда несёт риск того, что противник, способный скомпрометировать какое-либо из клиентских приложений (email-клиент, IM-клиент и т.п.), сможет с лёгкостью извлечь закрытые ключи, выполнив gpg --export-secret-keys. Смарткарты являются общепринятым решением данной проблемы, но сами они, в свою очередь, являются специализированными устройствами, которые 1) не всегда могут быть доступны пользователю либо 2) могут быть недоверяемы по тем или иным причинам.

Предлагаемое решение


GPG Remote — это клиент-серверное приложение, переносящее операции с закрытыми ключами GnuPG на удалённый сервер, исполняемый в безопасной доверенной системе. Сервер отфильтровывает клиентский ввод в соответствии с заданными правилами и запускает GnuPG, выступая посредником между программой и клиентом.


GPG Remote позволяет разделить процесс исполнения GnuPG между клиентом-фронтэндом и сервером-бекэндом. Клиентская часть приложения эмулирует консольный интерфейс GnuPG: принимает аргументы командной строки и данные, передаваемые пользователем через стандартный ввод (STDIN). Затем производится парсинг консольных аргументов и определение файлов, которые пользователь хотел передать GnuPG для обработки. Из этих данных формируется пакет запроса, который передаётся серверу.


Сервер работает в доверенной среде, его задача — выполнить gpg безопасным образом. Этой цели служит белый список консольных опций gpg, согласно которому сервер отфильтровывает полученные от клиента аргументы командной строки (прежде всего, команды типа --export-secret-keys). Клиентские файлы сохраняются во временную директорию, а их пути в аргументах командной строки соответствующим образом заменяются. Наконец, сервер вызывает gpg, а вывод программы (состояющий из потоков STDERR и STDOUT, кода завершения программы, а также сгенерированных файлов) посылается обратно клиенту.

Установка


Убедитесь, что на системах, где вы планируете запускать клиентскую и серверную части приложения, установлен Python 3.2.x или выше. Клиентский и серверный модули не имеют внешних зависимостей, их можно разместить в произвольных директориях.


Для запуска клиента GPG Remote в качестве замены системному gpg необходимо переименовать скрипт gpgremote_client.py в /usr/bin/gpg либо поставить символическую ссылку с указанного пути на файл скрипта. Если клиентский и серверный компоненты запускаются на одной системе, убедитесь, чтобы связки ключей GnuPG были доступны (на чление и запись) только пользователю, из-под которого запускается сервер.

Настройка


Клиент считывает свои настройки (в частности, хост и порт сервера) из файла gpgremote_client.conf, находящегося в директории /.gnupg. Этот путь можно переопределить с помощью переменной окружения GNUPGHOME.


По умолчанию, сервер берёт свои настройки из файла gpgremote_server.conf, также находящегося в директории /.gnupg (если этот путь не был переопределён с помощью переменной окружения GNUPGHOME), однако с помощью опции -c/--config можно указать другой путь и имя файла конфигурации (используйте -h/--help для вывода всех опций, доступных для запуска сервера). Большинство других параметров сервера также можно изменить из командной строки.


Вторая часть серверных настроек — это белый список опций gpg, находящийся в файле whitelist.conf в одной директории с конфигурационным файлом. Его формат достаточно прост, но правильная настройка имеет критически важное значение для безопасности сервера (см. следующий раздел):


  1. Строки, не начинающиеся со знака тире, игнорируются.
  2. Каждая строка содержит один набор опций.
  3. Набор может быть представлен одной опцией в короткой (с одним тире) или в длинной форме (с двумя тире), либо разделёнными пробелом несколькими опциями в короткой или длинной форме (в произвольном порядке).
  4. Если набор содержит слова, не начинающиеся с тире, то они имеют следующее значение:
    • Слово в квадратных скобках является произвольным параметром — опции в данном наборе считаются параметеризуемыми.
    • Слово без квадратных скобок является разрешённым значением параметра — опции в данном наборе считаются параметеризуемыми и могут принимать параметр только в указанном значении. Если требуется разрешить несколько значений параметра, их необходимо привести в этой же строке через пробел (поддерживаются кавычки и экранирование пробелов).
    • Слово #NO_FILES в квадратных скобках устанавливает для данного набора опций флаг "без файлов" — программа не будет рассматривать переданные аргументы командной строки в качестве имён файлов (см. следующий раздел).

Меры безопасности


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


Учтите, что даже если модифицирующие связку ключей опции (такие как --delete-key или --import) не внесены в белый список, у клиента всё равно сохранится возможность добавлять открытые и закрытые ключи на связку, просто передавая их через стандартный ввод (обрабатываемый программой gpg по контексту). Если такие действия нежелательны, администратору сервера следует запускать сервер из-под пользователя, не имеющего прав записи в файлы связок gpg. Имейте в виду, что стандартные файлы связок всегда можно переопределить с помощью опций --no-default-keyring, --keyring и --secret-keyring.


Другая потенциальная угроза серверу заключается в риске утечки его локальных файлов. При наивной реализации пользователь мог бы попросить сервер выполнить gpg -o – --enarmor [путь к локальному файлу], и сервер, не задумываясь, передал бы содержимое этого файла через STDIN. Чтобы этого избежать, сервер проверяет, чтобы число аргументов командной строки, в которых могут содержаться имена файлов, равнялось количеству файлов, полученных от клиента в пакете запроса. (Все эти сложности необходимы, т.к. если бы сервер просто отказывался выполнять запросы, включающие пути к существующим локальным файлам, возникала бы утечка информации о содержимом файловой системы сервера.) В то же время, для такого механизма необходима корректная настройка белого списка сервера в плане обозначения опций с параметрами: если опция может принимать параметры, её набор должен содержать указатель параметра (произвольный или разрешённый), в противном случае сервер может оказаться уязвим к описанной атаке.


Обратите внимание, что ряд консольных опций gpg (в частности, --list-keys, --list-sigs и др.) могут принимать произвольное количество нефайловых аргументов. Для учёта такого случая набор опций в белом списке может содержать специальное ключевого слово [#NO_FILES]. Если клиентский запрос будет включать опцию из такого набора, сервер удалит из запроса опции -o/--output и не станет возвращать клиенту какие бы то ни было файлы.


Получаемые от клиента файлы (которые могут содержать открытый текст секретных данных) сервер сохраняет во временную директорию. По умолчанию это системная директория для временных файлов (как правило, /tmp), но если такая директория по какой-либо причине является небезопасной, её путь можно переопределить с помощью переменной окружения TEMP или опции --temp при запуске сервера. (Обратите внимание, что файлы сохраняются не непосредственно в temp-директорию, а во временные подкаталоги в ней, создаваемые с правами доступа 0700.)


Ни клиент, ни сервер не осуществляют семантический разбор аргументов командной строки gpg (иными словами, не понимают значение передаваемых команд и опций). По этой причине клиент исходит из того, что параметр любой опции или замыкающий аргумент, совпадающий с именем существующего локального файла на стороне клиента, является файлом, предназначенным для обработки программой gpg, и оптимистически пересылает его серверу. Учтите, что клиент безальтернативно сохраняет все файлы, полученные от сервера (конечно, если имеет право на запись в конкретный файл), не спрашивая пользователя о перезаписи существующих файлов.


Клиент имеет возможность вызвать у сервера отказ в обслуживании, посылая ему чрезмерно объёмные запросы. Для предотвращения такого сценария следует использовать параметры ограничения ресурсов сервера: size_limit, threads и queue. Первый ограничивает объём клиентского пакета запроса и, как следствие, объём потребляемой памяти. Второй устанавливает ограничение на число процессорных потоков, выделяемых на обработку запросов (каждый запрос обрабатывается одним потоком). Учтите, что максимальный объём ОЗУ, требуемый серверу, составляет примерно S * T * 2, где S — лимит на объём пакета и T — число потоков (т.е. при заданных по умолчанию S=1 ГБ и T=2 максимальный объём ОЗУ может составить 4 ГБ). Наконец, значение queue — это количество запросов, которые могут встать в очередь на обработку. Если это значение выше числа потоков, то оставшиеся запросы будут ожидать завершения активных. Ожидающие запросы не потребляют дополнительных ресурсов, за исключением открытого сокета для соединения.


Аутентификация/шифрование канала связи лежит за пределами данного приложения. Для организации безопасной клиент-серверной коммуникации пользователь может использовать SSH- или VPN-туннелирование.

Технические сведения


Коммуникационный протокол представляет собой простейший двухшаговый запрос-ответ. Формат пакета для обоих направлений показан ниже.

[len_p] | [len_j] | JSON([ver], [type], [fields], [files_meta]) | [binary]

  • len_p (8 bytes): Общая длина пакета.
  • len_i (8 bytes): Длина JSON-пакета.
  • ver (str): Версия приложения.
  • type (str): Идентификатор пакета.
  • fields (list): Произвольный набор полей.
  • files_meta (dict): Отображение путь_файла->длина_файла.
  • binary (bytes): Конкатенированное содержимое файлов.

Значение порта сервера по умолчанию (29797) было получено в Python следующим образом:

int.from_bytes(b'gpgremote', 'big') % 2 ** 16

Проблемы и ограничения


  • Интерактивный ввод парольных фраз не поддерживается, сервер может работать только с незащищёнными закрытыми ключами. В качестве альтрнативы клиент может вызывать gpg с опциями --passphrase или --passphrase-file.

  • Передача файловых дескрипторов и иные продвинутые виды IPC для взаимодействия с вызываемым процессом gpg не поддерживаются.

  • Переменные окружения клиента не передаются вызываемому процессу gpg. Если требуется вызов gpg с определённым окружением (к примеру, LANG), необходимо запустить сервер GPG Remote с данными переменными.

  • При использовании GnuPG 2.x, операции с защищёнными закрытыми ключами приведут к вызову диалога gpg-agent (Pinentry), который заблокирует процесс gpg, не давая ему корректно завершиться. Если клиент и сервер GPG Remote запускаются на одной системе, то такое поведение может быть даже желательным (однако убедитесь, что значения conn_timeout у клиента и gpg_timeout у сервера достаточно велики, чтобы пользователь успел ввести парольную фразу), в противном случае администратору сервера следует предпринять меры для отключения gpg-agent на стороне сервера.

Планы


  • Снизить потребление памяти.

  • Взаимодействие с gpg-agent для поддержки защищённых ключей.

  • Идея: экстренный пароль для удалённой остановки сервера / уничтожения закрытых ключей.