Как безопасно настроить CI-сервер для цифровой подписи двоичных файлов?

Существует много сайтов, которые объясняют, как запустить signtool.exe в файле сертификата .pfx, который сводится к следующему:

signtool.exe sign /f mycert.pfx /p mypassword /t http://timestamp.server.com \
  /d "My description"   file1.exe file2.exe

У меня есть непрерывная интеграция процесса CI (с использованием TeamCity), которая, как и большинство процессов CI, выполняет все: проверяет исходный код, компилирует, выписывает все .exe, пакеты в установщик и подписывает установщик .exe. В настоящее время существует 3 агента сборки, которые работают с идентичными виртуальными машинами, и любой из них может запускать этот процесс.

Небезопасная реализация

Чтобы сделать это сегодня, я делаю пару Bad Things (TM) в отношении безопасности: файл .pfx находится в исходном управлении, а пароль для него находится в сборке script (также в исходном управлении). Это означает, что любые разработчики, имеющие доступ к репозиторию исходного кода, могут взять файл pfx и делать любые гнусные вещи, которые им бы хотелось. (Мы - относительно небольшой магазин для разработчиков и доверяем всем, у кого есть доступ, но ясно, что это все еще не хорошо).

Конечная безопасная реализация

Все, что я могу найти о том, чтобы делать это "правильно", заключается в том, что вы:

  • Сохраняйте pfx и пароль на каком-либо защищенном носителе (например, зашифрованный USB-накопитель с разблокировкой на основе пальцев) и, возможно, не вместе
  • Назначьте только пару людей, чтобы иметь доступ к файлам подписи.
  • Подписывать окончательные сборки на не связанной, выделенной машине, которая хранится в заблокированном хранилище, пока вам не нужно будет вывести ее для этой церемонии подписания кода.

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

Я уверен, что некоторые люди пропускают шаги и просто подписывают файлы с сертификатом, хранящимся в их личной системе, но это все еще не очень хорошо.

Он также несовместим с подписями файлов, которые затем используются в установщике (который также создается сервером сборки) - и это важно, когда у вас установлен установленный .exe, у которого есть приглашение UAC для доступа администратора доступ.

Ближний грунт?

Я гораздо больше обеспокоен тем, что вы не представляете страшное "ненадежное приложение" для пользователей UAC, чем доказывать, что это моя компания. В то же время хранение личного ключа и пароля в репозитории исходного кода, который имеет каждый разработчик (плюс QA и высокотехнологичная техническая поддержка), явно не является хорошей практикой безопасности.

Я бы хотел, чтобы сервер CI все еще подписывался во время процесса сборки, как сегодня, но без пароля (или части секретного ключа сертификата) для всех, у кого есть доступ к репозиторию исходного кода.

Есть ли способ как-то защитить пароль от сборки или безопасности? Должен ли я указывать signtool для использования хранилища сертификатов (и как это сделать, с 3 агентами сборки и сборкой, выполняемой как неинтерактивная учетная запись пользователя)? Что-то другое?

Ответ 1

В итоге я сделал очень похожий подход к тому, что предложил @GiulioVlan, но с некоторыми изменениями.

Задача MSBuild

Я создал новую задачу MSBuild, которая выполняет signtool.exe. Эта задача служит двум основным целям:

  • Он скрывает пароль от отображения
  • Он может повторить попытку с сервером timestamp при сбоях.
  • Это позволяет легко вызвать

Источник: https://gist.github.com/gregmac/4cfacea5aaf702365724

Это специально отображает все результаты и запускает его через функцию sanitizer, заменяя пароль на все *.

Я не знаю, как можно подвергать цензуре регулярные команды MSBuild, поэтому, если вы передадите пароль в командной строке непосредственно в файл signtool.exe, используя его, будет отображаться пароль - следовательно, необходимость в этой задаче (помимо других преимуществ).

Пароль в реестре

Я обсуждал несколько способов хранения пароля "вне диапазона" и в итоге решил использовать реестр. Легко получить доступ к MSBuild, он достаточно прост в управлении вручную, и если у пользователей нет RDP и удаленного доступа к реестру на компьютер, он фактически достаточно безопасен (может ли кто-нибудь сказать иначе?). Предположительно, есть способы защитить его, используя причудливые объекты GPO, но это за пределами длины, о которой я хочу поехать.

Это можно легко прочитать с помощью msbuild:

$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\1 Company [email protected])

И легко управлять через regedit:

enter image description here

Почему не в другом месте?

  • В сборке script: он отображается всем с исходным кодом
  • Зашифрованный/запутанный/скрытый в контроле источника: если кто-то получает копию источника, они все равно могут понять это.
  • Переменные среды. В веб-интерфейсе Teamcity существует подробная страница для каждого агента сборки, которая фактически отображает все переменные среды и их значения. Доступ к этой странице может быть ограничен, но это означает, что некоторые другие функции также ограничены.
  • Файл на сервере сборки: возможно, но кажется немного более вероятным, что он непреднамеренно сделал доступным через общий доступ к файлам или что-то.

Вызов из MSBuild

В теге:

<Import Project="signtool.msbuild.tasks"/>

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

Затем, в зависимости от того, какую цель вы хотите использовать для подписания:

<SignTool  SignFiles="file1.exe;file2.exe" 
   PfxFile="cert.pfx" 
   PfxPassword="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\1 Company [email protected])"
   TimestampServer="http://timestamp.comodoca.com/authenticode;http://timestamp.verisign.com/scripts/timstamp.dll" />

Пока это хорошо работает.

Ответ 2

Один из распространенных методов - оставить ключи и сертификаты в Control Version, но защитить их паролем или парольной фразой. Пароль сохраняется в переменных среды, локальных для машины, к которым можно легко получить доступ из сценариев (например, %PASSWORD_FOR_CERTIFICATES%).

Нужно быть осторожным, чтобы не записывать эти значения в обычный текст.