Потому что ваши ключи и секреты должны быть именно такими - секретными

Здравствуйте и добро пожаловать! Мы продолжаем путешествие Kubernetes в двух словах. В одном из предыдущих блогов мы видели, как настраивать приложения Kubernetes с помощью ConfigMapobject. В этом посте мы рассмотрим Kubernetes Secrets и то, как их можно использовать для хранения конфиденциальных данных конфигурации, которые необходимо безопасно обрабатывать (например, учетные данные базы данных, ключи API и т. Д.).

Как обычно, это будет основано на примерах, и вы узнаете:

  • как создать Secrets (CLI, yaml и т. д.) и
  • различные способы их использования в ваших приложениях (переменные env, объемы и т. д.)

Код (и YAML) доступен на GitHub.

Эта часть разделена на два логических раздела: способы создания Secrets и методы использования Secrets в ваших приложениях.

Предпосылки

Чтобы просмотреть примеры в этом посте, все, что вам нужно, это кластер Minikube и инструмент командной строки kubectl для доступа к кластеру.

Установите Minikube как одноузловой кластер Kubernetes на виртуальной машине на вашем компьютере. На Mac вы можете просто:

curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64 \
  && chmod +x minikube
sudo mv minikube /usr/local/bin

Установите kubectl для взаимодействия с кластером Minikube. На Mac вы можете:

curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl

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

Давайте посмотрим на методы, с помощью которых вы можете создать Secret.

Используйте раздел data

Можно создать Secret вместе с данными конфигурации, хранящимися в виде пар "ключ-значение" в разделе data определения.

apiVersion: v1
kind: Secret
metadata:
  name: service-apikey
data:
  apikey: Zm9vYmFy

Secret содержит данные "ключ-значение", представляющие конфиденциальную информацию, где apikey является ключом, а значение представляет собой строку в кодировке base64.

Чтобы создать этот Secret в Kubernetes:

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/secret-data.yaml

Чтобы упростить задачу, на файл YAML ссылаются непосредственно из репозитория GitHub, но вы также можете загрузить файл на свой локальный компьютер и использовать его таким же образом.

Чтобы подтвердить, что Secret был создан:

kubectl get secret/service-apikey -o yaml

Вы получите ответ YAML, подобный следующему:

apiVersion: v1
data:
  apikey: Zm9vYmFy
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"apikey":"Zm9vYmFy"},"kind":"Secret","metadata":{"annotations":{},"name":"service-apikey","namespace":"default"}}
  creationTimestamp: "2019-12-17T11:11:27Z"
  name: service-apikey
  namespace: default
  resourceVersion: "113009"
  selfLink: /api/v1/namespaces/default/secrets/service-apikey
  uid: 671b547c-3316-4916-b6dc-be2b551b974e
type: Opaque

Получение Secret сведений с помощью kubectl get не раскрывает их содержимое.

Обратите внимание, что apikey: Zm9vYmFy был тем, что мы предоставили в манифесте YAML. Вы можете проверить текстовую форму, расшифровав ее:

echo 'Zm9vYmFy' | base64 --decode
//foobar

Используйте раздел stringData

Атрибут data, используемый в приведенном выше примере, используется для сохранения информации в кодировке base64. Если вы хотите безопасно хранить текстовые данные, вы можете использовать stringDatasection. Вот пример:

apiVersion: v1
kind: Secret
metadata:
  name: plaintext-secret
stringData:
  foo: bar
  mac: cheese

Значения foo и mac передаются как обычный текст. Создайте этот Secret и подтвердите:

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/secret-plaintext.yaml
kubectl get secret/plaintext-secret -o yaml

Вот часть data ответа YAML. Фактические данные хранятся в кодированном формате base64.

data:
  foo: YmFy

Если вы декодируете данные, вы можете подтвердить, что они соответствуют исходному текстовому вводу (bar), который мы предоставили.

echo 'YmFy' | base64 --decode
//bar

Обратите внимание, что секция thedata не принимает атрибут обычного текста. Попытка сделать это приведет к ошибке, подобной этой: illegal base64 data at input byte 8

Содержимое файла

Вы также можете предоставить содержимое всего файла в качестве входных данных для раздела stringData. Вот как это может выглядеть:

apiVersion: v1
kind: Secret
metadata:
  name: secret-in-a-file
stringData:
  app-config.yaml: |-
    hello: world
    john: doe

Создать это Secret

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/secret-file.yaml

Этот результирующий Secret будет содержать ключ с именем app-config.yaml, а его содержимое (значение) будет base64 кодировкой предоставленных данных.

Как обычно, вы подтверждаете это в Kubernetes, а также декодируете содержимое.

kubectl get secret/secret-in-a-file -o yaml
echo '<"data" content in yaml response> | base64 --decode

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

Использование kubectl

Вы можете использовать команду kubectl create secret для создания Secret объектов

Использование --from-literal

Вы можете использовать текстовые данные для создания Secret с помощью интерфейса командной строки (в Kubernetes они будут храниться в кодированном формате base64):

kubectl create secret generic redis-credentials --from-literal=user=poweruser --from-literal=password='f0ob@r'

Использование --from-file

kubectl create secret generic topsecret --from-file=api_keys.txt

Это создаст секрет (topsecret) с

  • ключ с тем же именем файла, то есть api_keys.txt в данном случае
  • и значение как содержимое файла

Из файлов в каталоге

Вы можете просто указать на каталог, и все файлы в нем будут использоваться для создания Secret.

kubectl create secret generic topsecrets --from-file=/home/credentials/

Вы получите

  • несколько ключей, которые будут такими же, как имя отдельного файла
  • значением будет содержимое соответствующего файла

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

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

Переменные среды

Вы можете использовать данные Secret как переменные среды в Pod (как и ConfigMap). Вот пример:

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
    - name: nginx
      image: nginx
      env:
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: service-apikey
              key: apikey

Мы используем ключ apikey из Secret service-apikey и убеждаемся, что его значение доступно как переменная среды API_KEY внутри Pod.

Создайте Pod (при условии, что у вас есть Secret, созданный из предыдущего примера) и подтвердите.

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/pod-secret-env.yaml
kubectl get pods -w

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

kubectl exec pod1 -- env | grep API_KEY

Вы должны получить такой ответ - API_KEY=foobar.

Вместо ссылки на отдельные записи в Secret, вы можете использовать envFrom для удобного использования всех записей в качестве переменных среды в Pod. Вот как вы можете это использовать:

apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  containers:
    - name: nginx
      image: nginx
      envFrom:
        - secretRef:
            name: plaintext-secret

Мы имеем в виду plaintext-secret Secret using envFrom.secretRef. Чтобы создать этот Pod:

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/pod-secret-envFrom.yaml
kubectl get pods -w

Подождите, пока Pod перейдет в состояние Running, а затем подтвердите наличие переменных среды.

kubectl exec pod2 -- env | grep foo
//foo=bar
kubectl exec pod2 -- env | grep mac
//mac=cheese

Это подтверждает, что и foo, и mac были добавлены как переменные среды в the Pod вместе с их декодированными значениями, то есть bar и cheese соответственно.

Объемы

Вы можете смонтировать Secrets как Volume в Pod. Например:

apiVersion: v1
kind: Pod
metadata:
  name: pod3
spec:
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - name: apikey-config-volume
          mountPath: /secret
          readOnly: true
  volumes:
    - name: apikey-config-volume
      secret:
        secretName: service-apikey

Том apikey-config-volume относится к service-apikey Secret. Чтобы создать это Pod:

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/pod-secret-volume.yaml
kubectl get pods -w

Подождите, пока Pod перейдет в состояние Running. Затем выполните следующую команду:

kubectl exec pod3 -- cat /secret/apikey
//foobar

Это подтверждает, что ключ apikey в секрете service-apikey был смонтирован как файл (с именем apikey) в каталоге /secret (как указано в Pod). Содержимое файла - это не что иное, как секретное значение, в данном случае foobar.

Использование imagePullSecrets

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

На самом деле есть три типа Secrets:

  • generic - используется для хранения пар ключ-значение, как мы уже видели в примерах.
  • tls - хранить информацию о паре общедоступных и закрытых ключей как Secrets
  • docker-registry - учетные данные для аутентификации в реестре Docker.

Способ использования этой техники очень прост:

  • Используйте тип docker-registry Secret для хранения личных учетных данных реестра Docker в Kubernetes.
  • А затем imagePullSecrets (в модуле) для ссылки на Secret, содержащий учетные данные реестра Docker.

Всегда помогает пример:

apiVersion: v1
kind: Pod
metadata:
  name: pod4
spec:
  containers:
    - name: privateapp
      image: abhirockzz/test-private-repo:latest
      command: ["/bin/sh"]
      args: ["-c", "while true; do date; sleep 5;done"]
  imagePullSecrets:
    - name: docker-repo-secret

Посмотрите, как imagePullSecrets.name относится к Secret под названием docker-repo-secret. Создадим это.

Но перед этим убедитесь, что у вас есть частный реестр Docker. Я использовал DockerHub, но вы можете выбрать любой другой.

Начните с создания Secret (с именем docker-repo-secret), содержащего ваши учетные данные Docker, с помощью kubectl create secret docker-registrycommand.

kubectl create secret docker-registry docker-repo-secret --docker-server=DOCKER_REG_SERVER --docker-username=DOCKER_REG_USERNAME --docker-password=DOCKER_REG_PASSWORD --docker-email=DOCKER_REG_EMAIL

Для Docker Hub:

kubectl create secret docker-registry docker-repo-secret --docker-server=https://index.docker.io/v1/ --docker-username=foobarbaz --docker-password=t0ps3cr3t [email protected]
kubectl get secret/docker-repo-secret -o yaml

https://index.docker.io/v1/ - сервер реестра Docker Hub

Для проверки мы будем использовать изображение busybox и tagit

docker pull busybox
docker tag busybox [DOCKER_REG]/[DOCKER_PRIVATE_REPO]:[IMAGE_TAG]
e.g. 
docker tag busybox abhirockzz/test-private-repo:latest

… И push это

docker push [DOCKER_REG]/[DOCKER_PRIVATE_REPO]:[IMAGE_TAG]
e.g. 
docker push abhirockzz/test-private-repo:latest

Когда частное репо будет готово, вы можете создать Pod, который будет извлекать образ из частного репо, используя учетные данные реестра, предоставленные ему через Secret

kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/secrets/pod-secret-docker.yaml
kubectl get pods -w

Подождите, пока Pod перейдет в состояние Running.

Если вы видите ошибку ErrImagePull, это означает, что может возникнуть проблема с аутентификацией в реестре Docker. Чтобы получить подробную информацию, используйте: kubectl describe pod/pod4

Чтобы убедиться, что модуль работает нормально: kubectl logs -f pod4

Поскольку образ busybox сам по себе ничего не делает, мы выполняем: while true; do date; sleep 5;done (как предусмотрено в спецификации Pod). В результате вы должны увидеть журналы (печатаемые каждые 5 секунд).

Tue Dec 17 14:17:34 UTC 2019
Tue Dec 17 14:17:39 UTC 2019
Tue Dec 17 14:17:44 UTC 2019
Tue Dec 17 14:18:49 UTC 2019

Все хорошо! Это означает, что Pod смог извлечь ваше изображение из частного репозитория Docker, используя учетные данные Docker, которые были введены в Pod с помощью imagePullSecrets, который сам ссылался на Secret

Хорошо знать

Вот (не исчерпывающий) список вещей, которые вы должны помнить при использовании Secrets:

  • Secret должен быть создан перед любым Pod, который захочет его использовать.
  • Secrets применимы в namespace, т.е. они могут использоваться только Pods в том же namespace
  • Pod не запустится, если есть ссылка на несуществующий ключ в Secret (с использованием secretKeyRef)
  • 1 МБ - это максимальный размер для физических лиц Secrets.

Вот и все, что касается этого выпуска серии статей о Kubernetes в двух словах. Следите за новостями!

Если вы заинтересованы в изучении Kubernetes и контейнеров с помощью Azure, просто создайте бесплатную учетную запись и приступайте. Хорошей отправной точкой является использование кратких руководств, руководств и примеров кода в документации, чтобы ознакомиться с сервисом. Я также настоятельно рекомендую пройти 50-дневный курс обучения Kubernetes. Опытные пользователи могут захотеть сослаться на Лучшие практики Kubernetes или посмотреть некоторые из видео для демонстраций, основных функций и технических сессий.

Я очень надеюсь, что вам понравилась эта статья и вы ее чему-то научились. 🙌