Задача управления секретами одна из самых неприятных в IT. Само существование секретов уже неприятно, потому надо специально прикладывать усилия, чтобы у кого-то не работало (например, чтобы анонимный пользователь не мог прочитать секрет).

Когда кто-то прикладывает осмысленные усилия, появляются баги. А баги с секретами особо плохие, потому что это секреты, и смотреть на вывод нельзя. Всё равно, что USB-A втыкать вслепую, только возможных неправильных позиций больше.

В индустрии, по мере наработки практик, появилось множество систем управления секретами: с собственными серверами (hashicorp vault), 'as a service' (их ещё называют KMS, key management system), аппаратные (токены и TPM), самописные скрипты на gpg и т.д.

Среди всего этого множества я хочу выделить Mozilla Sops, и, как мне кажется, это один из лучших инструментов. Предупреждая возражения: я говорю про инструмент, а не решение. SOPS не заменяет KMS и не претендует на отмену Hashicorp'ового vault'а.

На Хабре уже был перевод про sops с точки зрения IT-директора, весьма убедительная статья, после которой я и занялся sops всерьёз. Если вы ту статью не читали, очень рекомендую начать с неё, чтобы получить заряд мотивации.

В этой статье я расскажу про техническую часть.

Модель хранения секретов

Sops использует два понятия:

  • секрет - информация, которая хранится в зашифрованных файлах и расшифровывается в нужный момент.

  • мастер-ключ - то, с помощью чего расшифровывают секреты. В случае GPG это приватный ключ. Для одного файла с секретами может использоваться несколько мастер-ключей.

Sops использует внешнего поставщика "мастер-ключей" для хранения секретов. Среди них pgp/gpg (для локальной разработки), Hashicorp Vault (для интеграции в существующие системы под его управлением), KMS в Amazon'е, Google'е и Microsoft'е, и многие другие. В этой статье я буду фокусироваться на сценариях с pgp (в форме gpg), как наиболее удобных для знакомства и для локальной работы. Соответственно, ожидается, что у вас уже есть приватный ключ в GPG keyring'е.

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

Для шифрования достаточно иметь публичные ключи всех участников (а для просмотра - хотя бы один приватный). Если приватного ключа нет, получаются "слепые секреты" (создающий новый секрет не может его потом прочитать). Поддерживается система разделения мастер-ключа (алгоритм Шамира, когда для открытия секрета надо более одного участника).

Шифрованные файлы планируется хранить в гите и они адаптированы к читаемости в diff'е при изменении.

Киллер-фича sops (для меня) — одновременная поддержка и частичного шифрования файлов, и их rekeying (смену ключей). Это функция, которой отчаянно не хватает в ansible-vault.

Частичное шифрование

Частичное шифрование поддерживается для файлов со структурой вида key/value:

  • env-файлы (SECRET=hide)

  • yaml и json-файлы, содержащие в top-level словарь (например, secret: hide)

При частичном шифровании в файле остаются незашифрованными ключи (имена), т.е. mysecret: hide превращается в mysecret: "ENC[AES256_GCM,data:...(далее неразборчиво).

Глядя в pull request вы видите какие секреты были поменяны в файле, но не видите на какие значения. С небольшой инструментацией git diff начинает показывать diff в plain-text (если у вас есть ключ для расшифровки), то есть вы видите, что, например mysecret: hide поменялся на mysecret: HIDE.

Для остальных файлов доступно полнофайловое шифрование.

При шифровании (или выполнении rekeying) sops читает свой конфигурационный файл (в текущем каталоге или в каталогах выше, .sops.yaml), берёт оттуда все требуемые публичные ключи и шифрует данные каждым из них. Если конфига нет, sops можно вызывать с переменными среды окружения, с параметрами командной строки; хотя моё наблюдение, что конфиг - самая удобная опция.

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

Доступ к секрету

sops предлагает несколько опций:

  • Расшифровка файла на stdout. (обычный режим -d)

  • Расшифровка файла in place; -d -i

  • Открытие расшифрованной копии на редактирование (для интерактивной работы с файлом), это режим по-умолчанию при запуске sops myfile.yaml.

  • Запуск указанной программы с путём к временному файлу (fifo или обычному файлу), который удаляется по завершению программы; sops exec-file.

  • Запуск программы с выставлением переменных окружения из шифрованного файла; sops exec-env

  • Извлечение значения по имени ключа из шифрованного файла (когда нужен один секрет из множества); --extract в сочетании с другими опциями.

Весьма интересной особенностью является возможность выставления переменных среды окружения из yaml-файлов, что делает их редактирование куда приятнее, чем редактирование самих env-файлов. В yaml доступны всякие вкусные конструкции с многострочным представлением вместо борьбы за эскейпинг третьей вложенности кавычек в обычном env'е.

Создание секрета

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

При редактировании (или создании с нуля) шифрованного файла sops запускается с именем файла без остальных параметров (например, sops foobar.sops.yaml). Если у файла уже есть содержимое, оно расшифровывается (или нет, в зависимости от наличия приватного ключа) и показывается в редакторе. После сохранения и выхода из редактора файл шифруется обратно; причём шифруется всеми публичными ключами согласно конфига или командной строки. Если при открытии файла нет, в редактор загружается пример возможных данных с указанием того, что будет шифровано, а что нет.

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

При шифровании существующего файла (например, приватного ключа от сертификата) указывается опция -e для шифрования в stdout, или -e -i для перезаписи файла шифрованной версией.

Rekeying

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

В sops рекеинг выполняется простой командой sops updatekeys для каждого файла. Дальше он спрашивает, действительно ли убрать/добавить ключ, показывая их оттиски (fingerprints), в соответствии с конфигом .sops.yaml. Поменяли конфиг, sops его учитывает. Не меняли конфиг — сообщает, что менять нечего. До выполнения какой-то операции с файлами секретов конфиг игнорируется (т.е. смена конфига не приведёт автоматически к перешифровке всех файлов).

Аудит

Поддержка аудита есть. Sops может сообщать о всех случаях доступа к секретам. /etc/sops/audit.yaml, хотя я эту функцию не пробовал.

Применение на сервере

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

Идея проста: один из ключей при шифровании файла секретов — KMS. Люди используют GPG, а сервер, в момент запуска приложения, использует sops и KMS для расшифровки значений на лету и передачи их в приложение. В таком режиме доступ к секретам имеют те, кому положено, и сервер только если он запущен где положено. Если кто-то утащит бэкап или запустит сервер вне ожидаемого места, то вместо секрета он получит либо ошибку запуска, либо (если будет сильно стараться) ENC[AES256_GCMinyourface.

Применение в CI

Точнее, в Github Actions, как примере для других CI'ев. При запуске workflow мы всё-таки должны иметь один секрет из самого github'а — ключ для расшифровки, но все последующие мы можем либо добавить в перменную окружения:

echo '::add-mask::$(sops exec-file .secrets.sops "cat {} | cut -d = -f 2-")'
sops exec-file .secrets.sops "cat {} >> $GITHUB_ENV"

Первая строчка маскирует все секреты, вторая добавляет их в окружение всех последующих job.
Запуск с расшифрованным файлом секретов виден в первой строчке, где файл (точнее, именованный FIFO) передаётся как аргумент в командой строке в форме {}.

Getting started

Одной из проблем с внедрением системам управления секретами является боль при отладке этого процесса. Пройдя этот процесс, могу дать несколько советов:

  • Отлаживайте процесс установки sops отдельно от всего. Это вполне себе задача (например, есть отдельный GH Action для установки mdgreenwald/mozilla-sops-action@v1.1.0). Её можно реализовывать без привлечения секретов, т.е. комфортно.

  • Разберитесь с мастер-ключами. Если это pgp, вам нужно "прикрутить" появление ключа в keyring'е в процессе (для GH Actions есть crazy-max/ghaction-import-gpg@v4). Так же вам нужно синхронизироваться со всеми участниками команды — все должны завести себе gpg-ключи и иметь ключи коллег в keyring'е. Возня с KMS доставит вам отдельный класс развлечений вне контекста SOPS.

  • Напишите .sops.yaml . Сам файл тривиальный, но он содержит в себе полиси: чьи ключи какие файлы должны шифровать.

  • Начните с одного маленького секрета. Я выбрал секрет для запуска стейджинга. Вероятнее всего, в процессе вам придётся преодолеть борьбу с пробелами, переводами строк и ещё чем-то невидимым.

  • Настройте git diff, плагины для редактора и т.д.

  • Только после получения look-n-feel от первого внедрения, начинайте трогать что-то большее. Возможно, стоит делать это в режиме "один секрет" (или файл секретов) за раз.

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

Заключение

Sops выглядит как добротная unix-утилита. Она делает что нужно, как нужно, и очень хорошо. У неё минимальное количество полиси и максимальное количество пользы.

Комментарии (10)


  1. bm13kk
    01.12.2021 12:41

    А есть хорошие английские обзорные статьи по теме, чтобы кинуть заказчику на изучение?


    1. amarao Автор
      01.12.2021 12:49
      +1

      Во-первых перевод, на который я ссылался, он в оригинале на английском. (https://oteemo.com/hashicorp-vault-is-overhyped-and-mozilla-sops-with-kms-and-git-is-massively-underrated/)

      Во-вторых сама документация SOPS'а довольно прямолинейна: https://github.com/mozilla/sops/blob/master/README.rst


  1. saboteur_kiev
    01.12.2021 17:16

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

    Удобство sops заключается в том, что там есть чуть более удобная интеграция с aws и готовые ключи для, допустим, редактирования файла ?


    1. amarao Автор
      01.12.2021 18:22
      +1

      Если вы просто шифруете строку, то openssl удобнее. Sops даёт несколько вещей:

      1. Поддержку списка пользователей для шифрования "для всех них".

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

      В целом, поддержка KMS не так важна, как мне кажется (вы можете в openssl передавать секрет из kms в пару строчек), а самое важное - простая и ясная модель работы, рассчитанная на команду.


  1. UnclShura
    02.12.2021 17:57

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

    При использовании подобных тулзов надо очень хорошо понимать что именно вы делаете. Как именно задается мастер ключ? Через какой канал? Как защищен ввод пароля если он есть (имеется ввиде защита от кейлогеров в том числе и аппаратных)? Как долго мастер ключ хранится в памяти в открытом виде? Как оргинизвана смена мастер ключа? Что происходит при компрометации чего-либо на любом уровне? Как это обеспечено в смысле невозможности обойти процедуру смены? В конце концов какой источник энтропии был использован при генерации ключей/паролей (если пароль генерится)? Он точно был аппаратный? А что там с качеством по rfc4086?

    Я к тому, что "туториалы" по таким вещам больше вреда приносят чем пользы.


    1. amarao Автор
      02.12.2021 18:27
      +2

      Спасибо за замечания.

      sops рашифровывает не в файл, а в fifo (хотя ему можно сказать "в файл" если очень надо). fifo гарантирует, что данные на диск не сохраняются в расшифрованном виде.

      Насчёт "не хранить ключи в открытом виде в памяти" - а где их хранить? Вот у вас nginx читает приватный ключ от сертификата, и что он с ним делает? Складывает в регистры процессора? В /dev/null? В TMP? Вообще говоря, управление секретами вне оперативной памяти находится целиком вне зоны компетенции sops, и если вы мне покажете, где на современном линуксе, кроме оперативной памяти, можно хранить ключ к расшифровке блочного устройства, я буду очень благодарен.

      Остальные вопросы, касающиеся политик ротации ключей и т.д. целиком оставляются на усмотрение пользователя и к работе самого sops'а не относятся, это утилита, а не платформа или фреймворк.

      Критику качества таториала я от вас восприму более серьёзно после ответа про место нахождения приватного ключа вне оперативной памяти у nginx, dmcrypt и (например) токен докера для доступа к hub'у.


      1. UnclShura
        02.12.2021 18:58

        Во-первых вы не поняли. Перед подобного вида туториалами обязательно объяснять что "не знаешь зачем - не лезь". Знаешь зачем? Сначала составь модель от кого ты защищаешься и сколько ты готов потратить удобства/денег на это? Только когда ты точно знаешь как и от кого ты собираешься защищаться - можно дальше читать или не читать, когда понятно что тул в принципе не способен обеспечить требуемый уровень безопасности.

        Насчет того где хранить: можно-ли хранить ключ в памяти? Да можно. Но при этом злоумышленник не должен иметь возможности ни удаленного доступа ни физического. Ну или вы можете с таким-же успехом хранить ключи в открытом виде - безопасности оно не прибавит не убавит. Где еще хранить? TPM, Smartcard, HSM. Т.е. все крипторафические операции вынесены за пределы компьютера (CPU & RAM). Повторюсь - речь идет именно о мастер ключе, компрометация которого автоматически означает компрометацию всех прошлых и настоящих секретов. Ключи уровнем ниже хранятся в соответствии с назначением (вы-же не станете доверять сертификатом выданным CA у которого ключ в памяти побывал, но при этом пароль до базы иногда можно и в памяти пошифровать).

        Может я конечно слишком строго про мастер ключ... Если просто стоит задача "заткнуть рот аудиту", то и так сойдет. Но если прямо вот про хранилище уровня большой компании, то хранить мастер не в HSM это как-то...


        1. amarao Автор
          02.12.2021 22:27
          +2

          1. Вы совершенно правы про то, что безопасность должна начинаться с модели угроз. Однако, я не пишу статьи по безопасности. Я пишу статью с обзором возможностей утилиты. Ровно так же инструкция по пользованию эндоскопом не заменяет медицинского образования врача. Но ровно так же наличие медицинского знания у врача не отменяет пользы от наличия "таториала" по использованию эндоскопа. Есть инструмент и есть его описание. Будет ли правильным в каждом руководстве по каждому инструменту начинать с модели угроз и CRISC.

          2. Ваше требование хранить мастер-ключи только на аппаратных средствах сильно отличается от существующих практик в индустрии. Вы можете одеться в белые доспехи и громить еретиков, но 90% gpg-ключей, использующихся для подписи пакетов для ftpmasters в debian (а через него и в Ubuntu), хранятся в обычных keyring'ах обычных компьютеров, и имеют из средств защиты, максимум, passphrase.


          1. ggo
            03.12.2021 10:09

            Извиняюсь, что вмешиваюсь в ваш диалог.

            А что за мастер ключ? Там же вроде у каждого пользователя свой ключ.

            Мастер ключ, насколько я понимаю, есть у Vault, а здесь - полная децентрализация в случае использования gpg.


            1. amarao Автор
              03.12.2021 12:43

              Этот вопрос был не очень хорошо описан, я обновил пост. В контексте sops "мастер-ключи", это то, чем расшифровывают секреты. Мастер-ключ - это то, что прислал KMS, либо приватные ключи gpg пользователей. Грубо говоря, когда sops шифрует, он берёт публичные ключи всех мастер-ключей (из .sops.yaml) и ими шифрует секрет. А при расшифровке ищет приватный мастер-ключ (хоть какой-то) и им расшифровывает.