DevSecOps

24 mar, 2017

Um ambiente simples usando Kubernetes e OpenShift Next Gen – Parte 04

Publicidade

Este artigo é a quarta parte de uma série sobre o básico necessário para usar o Kubernetes, caso você não tenha lido o texto anterior, recomendo lê-lo e depois voltar aqui para não ficar perdido.

Como citei antes, ainda existe um ponto de desconforto no ambiente, que é o fato das senhas e usuários estarem expostos diretamente nas configurações. O Kubernetes oferece uma solução para esse problema: os Secrets.

E agora irei mostrar como adicioná-los ao projeto.

Caso não tenha mais as fontes até o estado do artigoanterior, ou prefira acompanhar o meu andamento, pode pode pegá-los aqui: https://github.com/lucassabreu/openshift-next-gen/tree/v2; ou executar:

git clone -b v2 \
    https://github.com/lucassabreu/openshift-next-gen.git

Secrets

Existem algumas formas de criá-los e usá-los, criando diretamente de arquivos, ou usando configurações, e expô-los aos contêineres usando volumes ou variáveis de ambiente.

Para essa aplicação, vou utilizar um YAML para definir um Secret e vou modificar os Pods para alimentarem as variáveis de ambiente com eles. A estrutura básica do Secret é a seguinte:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secrets
type: Opaque
data:
  mysql-root-password: <hash base64>
  mysql-user: <hash base64>
  mysql-password: <hash base64>
  mysql-database-connection: <hash base64>

Nele, estou criando o Secret mysql-secrets e definindo quatro chaves que representam as três variáveis do MySQL e uma do servidor HTTP. No lugar do <hash base64> deve ir o conteúdo do segredo em Base 64, que pode ser gerado usando o comando echo -n “meusegredo” | base64 -w0.

Eu não gostei muito da ideia de guardar o Base 64 dentro da definição do Secret, então, fiz a seguinte modificação no meu mysql-secrets.yml:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secrets
type: Opaque
data:
  mysql-root-password: %MYSQL_ROOT_PASSWORD
  mysql-user: %MYSQL_USER
  mysql-password: %MYSQL_PASSWORD
  mysql-database-connection: %DATABASE_CONNECTION

E quando vou aplicar o Secret no Kubernetes, uso este script:

MYSQL_ROOT_PASSWORD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32})
B64_MYSQL_ROOT_PASSWORD=$(echo -n $MYSQL_ROOT_PASSWORD | base64 -w0)
B64_DATABASE_USER=$(echo -n $DATABASE_USER | base64 -w0)
B64_DATABASE_PASSWORD=$(echo -n $DATABASE_PASSWORD | base64 -w0)
B64_DATABASE_CONNECTION=$(echo -n \
    "mysql://$DATABASE_USER:$DATABASE_PASSWORD@db-service:3306/appointments" \
    | base64 -w0)

sed "\
  s|%MYSQL_ROOT_PASSWORD|$B64_MYSQL_ROOT_PASSWORD|;\
  s|%MYSQL_USER|$B64_DATABASE_USER|;\
  s|%MYSQL_PASSWORD|$B64_DATABASE_PASSWORD|;\
  s|%DATABASE_CONNECTION|$B64_DATAASE_CONNECTION|" \
  mysql-secrets.yml | oc apply -f -

Esse script cria uma senha aleatória para o root e usa duas variáveis de ambiente para definir o usuário e senha do MySQL, faz o Base 64 deles, injeta-os no arquivo via sed no Secret e aplica no Kubernetes com oc apply -f -, que irá ler a saída do sed e aplicá-la. Na hora de executar, fica assim:

$ export DATABASE_USER=appoint
$ export DATABASE_PASSWORD=123
$ ./env-set-oc.sh
secret "mysql-secrets" configured

Altero os Deployments para considerarem o Secret que criei:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: "db-deployment"
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: "db-pod"
    spec:
      containers:
        - name: "db"
          image: "lucassabreu/openshift-mysql-test"
          ports:
            - name: "mysql-port"
              containerPort: 3306
          env:
            - name: MYSQL_DATABASE
              value: appointments
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secrets
                  key: mysql-root-password
            - name: MYSQL_USER
              valueFrom:
                secretKeyRef:
                  name: mysql-secrets
                  key: mysql-user
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secrets
                  key: mysql-password
          volumeMounts:
            - name: "mysql-persistent-volume"
              mountPath: "/var/lib/mysql"
      volumes:
        - name: "mysql-persistent-volume"
          persistentVolumeClaim:
            claimName: mysql-pv-claim
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: "node-deployment"
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: "node-pod"
    spec:
      containers:
        - name: "node"
          image: "lucassabreu/openshift-app-test"
          ports:
            - name: node-port
              containerPort: 8080
              protocol: TCP
          env:
            - name: DATABASE_CONNECTION
              valueFrom:
                secretKeyRef:
                  name: mysql-secrets
                  key: mysql-database-connection

A alteração consiste de trocar a chave value das variáveis por valueFrom e apontar para as chaves corretas dentro do Secret.

Depois que aplica as mudanças, os Deployments vão identificá-las e trocar os Pods por novos. E passaram a utilizar os Secrets informado nas variáveis para eles.

Ao final dessa séria, a conclusão que posso chegar é que o Kubernetes exige um conjunto razoavelmente grande de configurações para podermos servir uma aplicação, mas são arquivos simples de se entender e muito bem documentados, o que facilitou bastante o processo, e não me fez sentir o peso dessa quantidade.