Как безопасно настроить 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, делает все: проверяет исходный код, компилирует, подписывает все.exes, упаковывает в установщик и подписывает установщик.exe. В настоящее время существует 3 агента сборки, работающих с одинаковыми виртуальными машинами, и любой из них может запустить этот процесс.

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

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

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

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

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

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

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

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

Середина?

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

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

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

2 ответа

Решение

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

MSBuild Task

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

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

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

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

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

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

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

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

$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\1 Company Dev@CodeSigningCertPassword)

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

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

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

Звонок из MSBuild

В теге:

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

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

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

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

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

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

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

Другие вопросы по тегам