Статьи

Управление секретами в Docker Swarm Clusters

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

Секрет (по крайней мере, с точки зрения Докера) — это блог данных. Типичным вариантом использования может быть сертификат, закрытые ключи SSH, пароли и т. Д. Секреты должны оставаться в секрете, что означает, что они не должны храниться в незашифрованном виде или передаваться по сети.

С учетом всего сказанного давайте посмотрим на них в действии и продолжим наше обсуждение на практических примерах.

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

Поскольку одного узла более чем достаточно для демонстрации секретов Docker, мы начнем с создания кластера Swarm с одним узлом на основе Docker Machines.

Примечание для пользователей Windows

Рекомендуется запускать все примеры из Git Bash (устанавливается через Docker Toolbox, а также из Git ). Таким образом, команды, которые вы увидите в этой статье, будут такими же, как те, которые должны выполняться в OS X или любом дистрибутиве Linux .

1
2
3
4
5
6
7
8
docker-machine create \
    -d virtualbox \
    swarm
 
eval $(docker-machine env swarm)
 
docker swarm init \
  --advertise-addr $(docker-machine ip swarm)

Мы создали узел Docker Machine под названием swarm и использовали его для инициализации кластера.

Теперь мы можем создать секрет.

Примечание для пользователей Windows

Для монтирования (секрет также является монтированием), используемого в следующей команде для работы, вы должны запретить Git Bash изменять пути файловой системы. Установите эту переменную среды.

export MSYS_NO_PATHCONV=1

Формат команды, которая создает секрет, выглядит следующим образом (пожалуйста, не запускайте его).

1
docker secret create [OPTIONS] SECRET file|-

secret create команда secret create ожидает файл, который содержит секрет. Тем не менее, создание файла с незашифрованным секретом не имеет смысла иметь секреты в первую очередь. Каждый может прочитать этот файл. Мы можем удалить файл после отправки его в Docker, но это приведет только к ненужным шагам. Вместо этого мы будем использовать - это позволит нам передавать стандартный вывод.

1
2
echo "I like candy" \
    | docker secret create my_secret -

Команда, которую мы только что выполнили, создала секрет с именем my_secret . Эта информация была отправлена ​​на удаленный Docker Engine с использованием соединения TLS. Если бы у нас был больший кластер с несколькими менеджерами, секрет был бы воспроизведен среди всех.

Мы можем проверить вновь созданный секрет.

1
docker secret inspect my_secret

Вывод следующий.

01
02
03
04
05
06
07
08
09
10
11
12
13
[
    {
        "ID": "9iqwc8zb7xum7krgm183t4mym",
        "Version": {
            "Index": 11
        },
        "CreatedAt": "2017-02-20T23:00:48.983267019Z",
        "UpdatedAt": "2017-02-20T23:00:48.983267019Z",
        "Spec": {
            "Name": "my_secret"
        }
    }
]

Ценность секрета скрыта. Даже если злоумышленник получит доступ к Docker Engine, секрет все равно будет недоступен. По правде говоря, в таком случае наши опасения были бы намного больше, чем защита секрета Докера, но я оставлю это обсуждение в другой раз.

Теперь, когда мы зашифровали секрет и сохранили его в менеджерах Swarm, нам следует изучить способы его использования в наших сервисах.

Секреты потребления

Новый аргумент --secret был добавлен в команду docker service create . Если секрет прикреплен, он будет доступен в виде файла в каталоге /run/secrets всех контейнерах, которые образуют службу.

Давайте посмотрим на это в действии.

1
2
3
4
docker service create --name test \
    --secret my_secret \
    --restart-condition none \
    alpine cat /run/secrets/my_secret

Мы создали сервис под названием test и прикрепили секрет my_secret . Сервис основан на alpine и будет выводить содержание секрета. Так как это однократная команда, которая быстро завершится, мы установили для --restart-condition значение none . В противном случае служба будет прервана через мгновение после ее создания, Swarm перенесет ее, только чтобы снова увидеть, как она завершается, и так далее. Мы бы вошли в бесконечный цикл.

Давайте посмотрим на журналы.

1
docker logs $(docker container ps -qa)

Вывод следующий.

1
I like candy

Секрет доступен в файле /run/secrets/my_secret внутри контейнера.

Прежде чем мы начнем обсуждать более реальный пример, давайте удалим службу и секрет, который мы создали.

1
2
3
docker service rm test
 
docker secret rm my_secret

Реальный пример использования секретов

Проект Docker Flow Proxy предоставляет статистику, которая должна быть зарезервирована только для внутреннего использования. Поэтому он должен быть защищен с помощью имени пользователя и пароля. До Docker v1.13 подобные ситуации разрешались бы, позволяя пользователям указывать имя пользователя и пароль через переменные среды. Docker Flow Proxy не является исключением и имеет переменные окружения STATS_USER и STATS_PASS .

Команда, которая создаст службу с пользовательским именем пользователя и паролем, будет выглядеть следующим образом.

01
02
03
04
05
06
07
08
09
10
11
docker network create --driver overlay proxy
 
docker service create --name proxy \
    -p 80:80 \
    -p 443:443 \
    -p 8080:8080 \
    -e STATS_USER=my-user \
    -e STATS_PASS=my-pass \
    --network proxy \
    -e MODE=swarm \
    vfarcic/docker-flow-proxy

Хотя это защитит страницу статистики от обычных пользователей, она все равно оставит ее доступной для всех, кто способен осмотреть службу. Простой пример заключается в следующем.

1
docker service inspect proxy --pretty

Соответствующая часть вывода выглядит следующим образом.

1
2
3
4
5
...
ContainerSpec:
 Image:     vfarcic/docker-flow-proxy:latest@sha256:b1014afa9706413818903671086e484d98db669576b83727801637d1a3323910
 Env:       STATS_USER=my-user STATS_PASS=my-pass MODE=swarm
...

Тот же результат, который не раскрывает конфиденциальную информацию, может быть достигнут с помощью следующих команд.

01
02
03
04
05
06
07
08
09
10
echo "secret-user" \
    | docker secret create dfp_stats_user -
 
echo "secret-pass" \
    | docker secret create dfp_stats_pass -
 
docker service update \
    --secret-add dfp_stats_user \
    --secret-add dfp_stats_pass \
    proxy

Мы создали два dfp_stats_user ( dfp_stats_user и dfp_stats_pass ) и обновили наш сервис. Отныне эти секреты будут доступны внутри сервисных контейнеров в виде файлов /run/secrets/dfp_stats_user и /run/secrets/dfp_stats_pass . Если секрет назван так же, как переменная окружения, в нижнем регистре и имеет префикс dpf_ , он будет использоваться вместо этого.

Если вы еще раз осмотрите контейнер, вы заметите, что секретов нет.

Мы могли бы остановиться здесь. В конце концов, больше нечего сказать о секретах Докера. Однако мы привыкли к стекам Docker, и было бы здорово, если бы секреты работали в новом формате YAML Compose.

Прежде чем мы продолжим, давайте удалим proxy сервис.

1
docker service rm proxy

Использование секретов с Docker Compose

В соответствии с миссией иметь одинаковые функции, доступные во всех поддерживаемых вариантах, Docker представил секреты в формате Compose YAML версии 3.1.

Мы продолжим использовать Docker Flow Proxy, чтобы продемонстрировать, как работают секреты внутри файлов Compose.

1
2
3
curl -o dfp.yml \
    https://raw.githubusercontent.com/vfarcic/
docker-flow-stacks/master/proxy/docker-flow-proxy-secrets.yml

Мы загрузили стек docker-flow-proxy-secrets.yml из хранилища vfarcic / docker-flow-stacks .

Соответствующие части определения стека следующие.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: "3.1"
 
...
 
services:
 
  proxy:
    image: vfarcic/docker-flow-proxy:${TAG:-latest}
    ports:
      - 80:80
      - 443:443
    networks:
      - proxy
    environment:
      - LISTENER_ADDRESS=swarm-listener
      - MODE=swarm
    secrets:
      - dfp_stats_user
      - dfp_stats_pass
    deploy:
      replicas: 3
 
...
 
secrets:
  dfp_stats_user:
    external: true
  dfp_stats_pass:
    external: true

Версия формата 3.1. proxy сервис имеет два секрета. Наконец, есть отдельный раздел secrets который определяет секреты как внешние объекты. Альтернативой будет указать внутренние секреты. Пример будет следующим.

01
02
03
04
05
06
07
08
09
10
secrets:
    dfp_stats_user:
        external: true
    dfp_stats_pass:
        external: true
secrets:
  dfp_stats_user:
    file: ./dfp_stats_user.txt
  dfp_stats_pass:
    file: ./dfp_stats_pass.txt

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

Давайте запустим стек и проверим, работает ли он.

1
docker stack deploy -c dfp.yml proxy

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

1
2
3
4
5
curl -o go-demo.yml \
    https://raw.githubusercontent.com/vfarcic/
go-demo/master/docker-compose-stack.yml
 
docker stack deploy -c go-demo.yml go-demo

Пожалуйста, подождите несколько секунд, пока не запустятся сервисы из стека go-demo . Вы можете проверить их статус, выполнив docker stack ps go-demo . Вы можете увидеть реплики go-demo_main в состоянии go-demo_main . Не паникуйте. Они будут продолжать работать только после go-demo_db .

Теперь мы можем, наконец, подтвердить, что прокси настроен на использование секретов для аутентификации.

1
2
curl -u secret-user:secret-pass \
    "http://$(docker-machine ip swarm)/admin?stats;csv;norefresh"

Оно работает! Всего за один дополнительный шаг ( docker service create ) мы сделали нашу систему более защищенной.

Распространенные способы использования секретов

Пока секреты не были введены, обычным способом передачи информации в контейнеры были переменные среды. Хотя это и впредь будет предпочтительным способом для неконфиденциальной информации, часть настроек также должна включать в себя секреты. Оба должны быть объединены. Вопрос в том, какой метод выбрать и когда.

Очевидным вариантом использования секретов Docker являются секреты. Это было очевидно, не так ли? Если есть часть информации, которая должна оставаться невидимой для всех, кроме определенных контейнеров, она должна быть предоставлена ​​через секреты Docker. Обычно используемый шаблон позволяет указывать ту же информацию, что и для переменной среды, и для секрета. В случае, если оба набора, секреты должны иметь приоритет. Вы уже видели этот шаблон через Docker Flow Proxy . Каждый фрагмент информации, который может быть указан с помощью переменных среды, также может быть указан как секрет.

В некоторых случаях вы не сможете изменить код своего сервиса и адаптировать его для использования секретов. Возможно, дело не в способностях, а в отсутствии желания модифицировать ваш код. Если вы попадете в последний случай, я пока не буду объяснять, почему код должен постоянно подвергаться рефакторингу, и представлю, что у вас есть для этого очень веские причины. В любом случае решение, как правило, заключается в создании сценария-оболочки, который преобразует секреты во все, что нужно вашей службе, а затем вызывает службу. Поместите этот скрипт в качестве инструкции CMD в Dockerfile, и все готово. Секреты остаются секретами, и вы не будете уволены с рефакторинга вашего кода. Для некоторых это последнее предложение звучит глупо, но компании нередко считают рефакторинг пустой тратой времени.

Какой должен быть секрет? Никто не может действительно ответить на этот вопрос для вас, так как он отличается от одной организации к другой. Примерами могут служить имена пользователей и пароли, ключи SSH, сертификаты SSL и т. Д. Если вы не хотите, чтобы об этом узнали другие, сделайте это секретом.

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

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

Я хотел бы получить ваш отзыв. Считаете ли вы, что секреты будут полезны, и если вы это сделаете, каков будет ваш вариант использования? Пожалуйста, присоединяйтесь к slack.devops20toolkit.com/ канал Slack и дайте мне знать.

Что теперь?

Удалите виртуальную машину Docker Machine и начните применять секреты к своему кластеру Swarm. Больше сказать нечего (пока).

1
docker-machine rm -f swarm

Если вам понравилась эта статья, вас может заинтересовать book. В отличие от предыдущего заголовка серии ( ), в котором подробно рассматриваются некоторые из последних практик и инструментов DevOps, эта книга полностью посвящена Docker Swarm, а также процессам и инструментам нам может понадобиться создавать, тестировать, развертывать и отслеживать службы, работающие внутри кластера.

Вы можете получить копию с Amazon.com (и других сайтов по всему миру) или LeanPub . Он также доступен в виде

Попробуйте книгу и дайте мне знать, что вы думаете.

Ссылка: Управление секретами в Docker Swarm Clusters от нашего партнера по JCG Виктора Фарчича в блоге технологических бесед .