Как безопасно настроить 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%
).
Нужно быть осторожным, чтобы не записывать эти значения в обычный текст.