Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Большинство программных проектов используют секреты – обычно, как ключи к удаленному API или данные для доступа к внешнему ресурсу, например к базе данных. Вашему приложению необходимы эти ключи во время работы, поэтому вам нужно предоставить их при развертывании приложения или на этапе подготовки окружения.
В данной статье я покажу вам, как использовать git-crypt так, чтобы вы могли безопасно хранить секреты ваших приложений в репозиториях исходного кода, даже публичных.
Проблема с секретами приложений
Большинство проектов имеют различные ключи или данные для входа. Например, если ваше приложение располагается на Heroku, вы можете предоставить ключ API приложению Heroku, используя команды вроде этих:
$ heroku config:set API_KEY=my-sooper-sekrit-api-key
Запуская эту команду до того, как вы (снова) развернете приложение, вы передаете переменную окружения с именем API_KEY
и значением my-sooper-sekrit-api-key
. Однако продолжать отслеживать значения этих переменных вне Heroku (или где бы вы ни развертывали свое приложение) все еще проблематично.
Я всегда стараюсь настроить мои проекты так, чтобы я мог запустить развертку с нуля одной командой, без каких-либо дополнительных шагов. В нашем примере это означает, что мне нужно хранить значение my-sooper-sekrit-api-key
где-то для того, чтобы мой развертываемый код мог использовать его (в данном случае, для запуска heroku config:set…
командой выше).
Исходный код моего проекта всегда хранится на git и обычно располагается на github.com или bitbucket.com или на другом сервисе хостинга. Я могу хранить значение API_KEY в своем репозитории исходного кода, однако есть и недостатки:
- Я не могу поделиться репозиторием, и не дать доступа к моим секретам. Это значит, что репозиторий моего приложения с секретами должен быть приватным.
- Предположительно многие участники Github/bitbucket/прочего тоже получили бы доступ к моим секретам, с чем я мог бы не согласиться (в зависимости от секрета).
- Можно легко забыть о наличии секретов в приватных репозиториях, если позже решишь сделать его публичным. Это могло бы случайно раскрыть важный секрет.
Я могу хранить секреты отдельно от исходного кода приложения, но и это имеет проблемы:
- Необходим способ доставать секреты, независимо от того, где они находятся, во время или после развертывания, и дать моему коду развертки доступ до них.
- Мои секреты могут не храниться так же надежно, как исходный код. К примеру, можно хранить секреты в файлах типа
.env
на ноутбуке, и быть уверенным, что мне не придется сверяться с git репозиторием. Однако, если я потеряю этот файл (например, если ноутбук сломается\потеряется), то секрет пропадет вместе с ним.
git-crypt
Git-crypt позволяет решить эту проблему шифрованием ваших секретов, когда загружаете их в git репозиторий, и расшифровкой, когда необходимо их извлечь. С вашей точки зрения все происходит прозрачно. То есть секреты находятся в открытом виде как для вас, так и для вашего экземпляра развертывания, но никто кроме вас не сможет читать их, даже если исходный код располагается в публичном в репозитории GitHub.
Рассмотрим пример.
1. Установка git-crypt.
Инструкция для Linux, Mac, и Windows на странице установки git-crypt.
Если вы, как и я, используете Mac с установленным Homebrew, вы можете запустить:
$ brew install git-crypt
2. Создайте новый git репозиторий.
$ mkdir myproject
$ cd myproject
$ git init
$ echo "This is some text" > file.txt
$ git add file.txt
$ git commit -m "Initial commit"
Теперь у нас есть репозиторий с одним текстовым файлом.
3. Настройка репозитория для git-crypt.
$ git-crypt init
Вы должны увидеть следующее:
Generating key...
Перед тем, как мы сделаем что-либо еще, пожалуйста, запустите следующую команду:
$ git-crypt export-key ../git-crypt-key
Эта команда создает копию симметричного ключа git-crypt, который сгенерирован для этого репозитория. Поместим его в директорию на уровень выше этого репозитория, так чтобы можно было использовать один ключ в нескольких git репозиториях.
По стандарту, git-crypt хранит все созданные ключи в файле.git/git-crypt/keys/default
, так что вы можете достичь тех же результатов запустивcp .git/git-crypt/keys/default ../git-crypt-key
Файл git-crypt-key
очень важен. Это ключ, который открывает все зашифрованные файлы в нашем репозитории. Дальше мы рассмотрим как использовать его.
4. Укажите git-crypt какой файл зашифровать.
Представим, что нашему приложению нужен API ключ, и мы собираемся хранить его в файле с именем api.key
.
До того, как добавим этот файл в репозиторий, мы укажем git-crypt, что хотим, чтобы каждый раз при совершении коммита, api.key
был зашифрован.
Мы делаем это, используя файл .gitattributes
. Его можно использовать, чтобы добавить дополнительные метаданные в git репозиторий. Это не особенность git-crypt, так что файл .gitattributes может уже существовать в вашем репозитории. В таком случае просто добавьте актуальные строки, не заменяя весь файл.
У нас же .gitattributes нет, поэтому его необходимо создать. Файл должен содержать строки вида:
[file pattern] attr1=value1 attr2=value2
Для git-crypt, шаблон должен соответствовать всем файлам, которые должны быть зашифрованы. Атрибуты всегда одинаковы: filter
и diff
, которые мы установили в настройках git-crypt
.
Так, наш .gitattributes должен содержать:
api.key filter=git-crypt diff=git-crypt
Создайте такой файл, добавьте и загрузите его в свой репозиторий:
$ echo "api.key filter=git-crypt diff=git-crypt" > .gitattributes
$ git add .gitattributes
$ git commit -m "Tell git-crypt to encrypt api.key"
Я использовал конкретное имя файла api.key
в .gitattributes, но это может быть и любой шаблон, включающий в себя файлы для шифрования, так что я мог использовать, например, *.key
. В качестве альтернативы, просто добавьте строку для каждого файла, который хотите зашифровать.
Легко совершить ошибку при создании .gitattributes, если вы пытаетесь защитить сразу несколько файлов с единственным шаблоном. Поэтому я настоятельно рекомендую прочесть раздел README из git-crypt, в котором выделено несколько распространенных ошибок.
5. Добавим секрет.
Теперь, когда мы указали git-crypt, что хотим зашифровать api.key
, давайте добавим этот файл в репозиторий.
Всегда полезно проверить настройки, добавив тестовую переменную. Затем, когда подтвердится успешное шифрование, можно совершать коммит настоящего секрета.
$ echo "dummy value" > api.key
Мы еще не добавили api.key
в git, но можем проверить, что cделает git-crypt, запустив команду:
$ git-crypt status
Вывод должен быть следующим:
encrypted: api.key
not encrypted: .gitattributes
not encrypted: file.txt
Итак, даже если api.key еще не был загружен в репозиторий, такое сообщение говорит о том, что при совершении коммита git-crypt зашифрует его.
Давайте выполним добавление и коммит файла:
$ git add api.key
$ git commit -m "Added the API key file"
6. Подтвердим шифрование секрета.
Мы указали git-crypt файл для шифрования, добавили api.key
в репозиторий, однако, если мы посмотрим, то не увидим разницы:
$ cat api.key
dummy value
Причина в том, что git-crypt шифрует и дешифрует файлы прозрачно, как во время загрузки, так и во время извлечения. Поэтому api.key
выглядит как обычный текстовый файл в открытом виде.
$ file api.key
api.key: ASCII text
Один из способов подтвердить, что ваши файлы действительно зашифрованы – загрузить их в репозиторий GitHub. Когда вы будете просматривать api.key
через интерфейс GitHub, то увидите зашифрованный двоичный файл, а не текст.
Более простой способ посмотреть, как репозиторий будет выглядеть для кого-нибудь без ключа дешифрования, это запустить команду:
$ git-crypt lock
Теперь, если мы прочитаем api.key
, все будет выглядеть по другому:
$ file api.key
api.key: data
$ cat api.key
GITCRYPTROܮ7y\R*^
Вы увидите отличающийся от моего мусор, но ясно, что файл зашифрован. Это то, что будет храниться на GitHub.
Чтобы вернуться к открытому виду файла api.key
, выполните:
$ git-crypt unlock ../git-crypt-key
../git-crypt-key
— это файл, который мы сохранили ранее с помощью git-crypt export-key...
Итак, что мы узнали?
Давайте сделаем быстрый обзор того, что уже известно
- Инициализация git-crypt в git репозитории через
git-crypt init
- Использование шаблонов в
.gitattributes
, чтобы указать git-crypt какие файлы шифровать git-crypt lock
зашифрует все указанные файлы в нашем репозиторииgit-crypt unlock [путь до файла ключа]
расшифрует зашифрованные файлы
git-crypt-key
очень важен. Без него будет невозможно дешифровать какие бы то ни было зашифрованные файлы в вашем репозитории. Кто угодно, получив копию этого файла, получит доступ ко всем зашифрованным секретам в репозитории. Поэтому необходимо держать его в безопасности.
Повторное использование ключа git-crypt
Мы использовали git-crypt init
и git-crypt export-key
, чтобы создать файл git-crypt-key
. Но если бы нам нужно было использовать отдельные ключи для каждого репозитория, то это не очень-то и улучшило бы нашу систему управления секретами.
К счастью, можно довольно легко использовать один и тот же ключ git-crypt для нескольких git репозиториев.
Чтобы использовать уже существующий ключ, просто запустите git-crypt unlock
вместо git-crypt init
, когда настраиваете ваш репозиторий для git-crypt, вроде этого:
$ mkdir my-other-project # At the same directory level as `myproject`
$ cd my-other-project
$ git init
$ echo "Something" > file.txt
$ git add file.txt
$ git commit -m "initial commit"
$ git-crypt unlock ../git-crypt-key
Если вы запустите git-crypt unlock
не добавив ни одного файла в git репозиторий, то увидите сообщение вроде такого:
fatal: You are on a branch yet to be born
Error: 'git checkout' failed
git-crypt has been set up but existing encrypted files have not been decrypted
Это по-прежнему неплохо работает, но слегка сбивает с толку, поэтому я убедился, что добавил и совершил коммит как минимум одного файла, до запуска git-crypt unlock...
Повторное использование ключа git-crypt удобно, но это значит, что если кто-либо получит ваш ключ, все ваши зашифрованные секреты раскроются.
Это такой же вид компромисса, как и при использовании менеджеров паролей, вроде LastPass или 1password. Вместо того, чтобы управлять несколькими секретами (паролями), каждый со своим риском взлома, вы храните их все в одном защищенном хранилище, и используете один мастер пароль для разблокировки.
Когда НЕ стоит использовать git-crypt
Git-crypt это прекрасный способ хранить нужные вашим приложениям секреты прямо в git репозитории, рядом с исходным кодом. Однако, как и другие меры безопасности, это не всегда будет уместно или целесообразно.
Чтобы определить, является ли это решение подходящим для вашего проекта, стоит учесть пару вещей:
- git-crypt разработан для ситуаций, когда большинство файлов в вашем git репозитории могут оставаться в открытом виде, а зашифровать нужно лишь малую часть из них, в которых есть секреты. Если вам нужно зашифровать большую часть репозитория, возможно вам лучше подойдет другое решение.
- Нет простого способа отозвать доступ к секретам в репозитории, если вы уже дали кому-то ключ-файл, как и нет простого способа изменить (т.е. заменить) ключ-файл (и даже его изменение не особо помогает, если только не поменять все действующие секреты в репозитории).
- git-crypt шифрует только содержимое файлов. Таким образов, он не подходит, если так же важны метаданные вашего репозитория (т.е. имена файлов, дата изменения, сообщения коммита и т.д.).
- Некоторые GUI-приложения git могут работать с git-crypt ненадежно. (Хотя конкретный случай Atlassian SourceTree, указанный в README, уже исправлен).
Больше информации в этом разделе README git-crypt.
Лучший способ использовать git-crypt
Вместо того, чтобы управлять ключ-файлом git-crypt вручную, можно включить в проект git-crypt вместе с gpg, и тогда вы сможете использовать приватный ключ gpg для дешифровки вашего git репозитория. Это так же позволит вам добавлять различных соавторов в репозиторий без обмена секретами между группами. Однако это требует гораздо более сложной настройки, что мы оставим для другой статьи.
Переводчик: DanayDemenir
Редактор: DariaRogoz