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

Это старая редакция страницы Разработки / gpgremote за 17/02/2015 18:47.


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 были доступны (на чтение и запись) только пользователю, из-под которого запускается сервер.


Если требуется поддержка удалённого ввода парольных фраз (при запуске клиента и сервера на отдельных системах), сделайте следующее:


  1. Убедитесь, что на клиенте установлено стандартное приложение pinentry.
  2. Установите на клиентскую и серверную системы библиотеку pyassuan.
  3. Корректно настройте и запустите gpg-agent на серверной системе. При запуске gpg-agent укажите в опции --pinentry-program путь к pinentry.py из дистрибутива GPG Remote (для дополнительной информации см. man gpg-agent).

Для поддержки "тревожных" команд (см. одноимённый раздел ниже) установите на серверной системе Python-модуль pbkdf2.

Настройка


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


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

Белый список


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


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

"Тревожные" команды


Существует возможность настройки произвольного количества так называемых "тревожных", или экстренных, команд. Эти команды (в сущности, shell-команды любого вида) исполняются на стороне сервера, если в диалог pinentry введена определённая парольная фраза. (Обратите внимание: для поддержки данной функции необходимо использовать pinentry.py из состава GPG Remote. См. выше раздел "Установка".)


Правила для "тревожных" команд задаются специальными пунктами в конфигурационном файле сервера. Имя правила должно быть уникальным и начинаться с префикса panic_. Значение правила должно состоять из защитного токена и следующей далее через пробел shell-команды или нескольких команд в обычном виде (иными словами, без кавычек, экранирования и т.п.). Защитный токен представляет собой crypt(3)-совместимый PBKDF2-хэш от парольной фразы, которая должна вызывать срабатывание правила. Чтобы получить такой токен, запустите сервер с опцией --gen-token и введите требуемые данные.


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


Обратите внимание: сработавшее "тревожное" правило само по себе не приводит к остановке каких-либо компонентов GPG Remote, а если парольная фраза данного правила совпадает с парольной фразой закрытого ключа, используемого для данной операции, выполнение этой операции будет продолжено в обычном порядке. Более того, учтите, что gpg-agent считывает закрытый ключ в память до того, как вызывает pinentry, поэтому использование команд rm/wipe для экстренного стирания связок ключей не приведёт к немедленному уничтожению ключа — для этого также необходимо убить серверный процесс GPG Remote сигналом SIGKILL, чтобы не дать ему отправить результат работы gpg обратно клиенту. С этой целью можете использовать в настройке "тревожных" команд переменную окружения GPG_REMOTE_PID, содержащую ID серверного процесса.

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


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


В качестве модели угрозы и основного вектора атаки рассматривается удалённый противник на стороне клиента (например, скомпрометированное сетевое приложение), осуществляющий утечку закрытых ключей 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 — это количество запросов, которые могут встать в очередь на обработку. Если это значение выше числа потоков, то оставшиеся запросы будут ожидать завершения активных. Ожидающие запросы не потребляют дополнительных ресурсов, за исключением открытого сокета для соединения.


При использовании удалённого ввода парольных фраз, они никогда не попадают в память долгоживущего серверного процесса GPG Remote. Тем не менее, введённая парольная фраза остаётся в памяти клиентского модуля вплоть до завершения вызова gpg, и на протяжении этого времени она остаётся уязвима к своппингу ОЗУ. Убедитесь, чтобы swap-раздел ОС был зашифрован, отключен или примите иные адекватные меры предосторожности.

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


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

[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): Конкатенированное содержимое файлов (если есть).

Удалённый ввод парольных фраз реализован на основе специализированного приложения-прокладки pinentry и выполняется по следующему протоколу (участник pinentry в нижеследующем описании — это специализированное приложение pinentry, если не указано иное):


  1. server>gpg-agent: устанавливает в переменной окружения PINENTRY_USER_DATA (передаваемой через весь стек вызова gpg > gpg-agent > pinentry) сведения для установления IPC-канала.
  2. gpg-agent>pinentry: вызывает pinentry, передавая переменную окружения PINENTRY_USER_DATA и инициализирует Assuan-протокол.
  3. server>pinentry: передаёт по IPC-каналу (UNIX-сокету) открытый сетевой сокет клиентского соединения непосредственно процессу pinentry.
  4. pinentry>client: по предоставленному сетевому соединению посылает клиенту необходимые данные (текстовые строки и опции запуска, полученные от gpg-agent на этапе 2) для вызова стандартного приложения pinentry.
  5. client: вызывает стандартное приложение pinentry и получает пользовательский ответ (в виде ответа Assuan-протокола).
  6. client>pinentry: пересылает пользовательский ответ.
  7. pinentry: выполняет "тревожные" команды, если введённая парольная фраза вызвала срабатывание каких-либо из них.
  8. pinentry>gpg-agent: воспроизводит пользовательский ответ в начатом на этапе 2 Assuan-протоколе.

При вводе неверной парольной фразы цикл 2-8 повторяется по запросу gpg-agent.


Эффективная энтропия токена безопасности "тревожных" правил ограничена 192 битами.


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

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

(В то же время, было замечено, что при такой процедуре значение имеют только последние байты b'te'.)

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


  • Интерактивные консольные операции (такие как генерация и редактирование ключа и т.п.) не поддерживаются.

  • Клиент не поддерживает чтение ввода из TTY, данные могут быть переданы только через пайп в STDIN.

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

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

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

Планы


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