Vault 101 - Aplicando Segurança na sua Infraestrutura como Código

Vault é um projeto criado pela Hashicorp amplamente utilizado para armazenar informações de maneira segura, podendo ser tokens de acesso, senhas, certificados, chaves SSH dentre diversas outras informações.
O Vault disponibiliza o acesso a estes dados através de Interface Gráfica, Linha de Comando ou Chamada HTTP em sua API.
Primeiros Passos
Primeiramente precisamos instalar o client do vault na nossa workstation, para isto vamos até a página de Download do Vault para fazer download do binário de acordo com o sistema operacional que estivermos utilizando.
No linux podemos efetuar o download e instalação através do comando:
$ cd /tmp
$ curl https://releases.hashicorp.com/vault/1.4.0/vault_1.4.0_linux_amd64.zip -o vault.zip
$ unzip vault.zip
$ sudo mv vault /usr/local/bin
Ao executar o comando vault --version
deve ser exibida a versão do client do vault, informando que a instalação foi efetuada com sucesso.
Podemos também instalar o recurso de autocomplete (atualmente funcionando apenas nos terminais bash, zsh e fish) que irá facilitar nosso trabalho com Vault através do comando vault -autocomplete-install
.
obs.: O Autocomplete funcionará assim que o terminal for reiniciado.
Vault Server
Para armazenar os dados do vault precisaremos de um servidor, para isto iremos utilizar um ubuntu server através do vagrant, você pode utilizar qualquer máquina virtual que quiser, basta seguir para a próxima seção caso não queira utilizar o vagrant.
Caso você queira conhecer o vagrant sugiro que leia o post Vagrant 101 do blog.
Vagrantfile para o vault server
Vamos criar um diretório para armazenar o conteúdo do Vagrantfile e popula-lo com as configurações para subir o ambiente.
Também é possível rodar o vault server no modo dev (localmente) através do comando vault server -dev
a utilização do mesmo não é indicada em ambientes de produção.
$ mkdir ~/vault
$ cd ~/vault
$ vim Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box_check_update = false
config.vm.boot_timeout = 600
config.vm.define "vault" do |machine|
machine.vm.box = "ubuntu/bionic64"
machine.vm.hostname = "vault.caiodelgado.example"
machine.vm.network "private_network", ip: "10.10.10.10"
machine.vm.provider "virtualbox" do |vb|
vb.name = "vault"
vb.memory = "1024"
vb.cpus = "1"
vb.customize ["modifyvm", :id, "--groups", "/iac"]
end
end
end
$ vagrant up
Adicione a entrada do vault no arquivo hosts para que o acesso ao mesmo fique mais fácil.
$ sudo sh -c "echo '10.10.10.10 vault.caiodelgado.example' >> /etc/hosts"
Vamos acessar o servidor para instalar o vault server.
$ cd ~/vault
$ vagrant ssh
Agora que estamos conectados no servidor podemos instalar o Vault.
$ sudo su -
$ cd /tmp
$ curl https://releases.hashicorp.com/vault/1.4.0/vault_1.4.0_linux_amd64.zip -o vault.zip
$ unzip vault.zip
$ mv vault /usr/local/bin
Precisamos também dar ao Vault o acesso ao syscall mlock (previne a memória de ser jogada para o swap) sem rodar o processo como root e criar o usuario vault para rodar o serviço.
$ setcap cap_ipc_lock=+ep /usr/local/bin/vault
$ useradd --system --home /etc/vault.d --shell /bin/false vault
Agora podemos criar os arquivos de configuração responsáveis por parametrizar o vault server.
$ mkdir -p /etc/vault.d
$ touch /etc/vault.d/config.hcl
$ chown -R vault:vault /etc/vault.d
$ chmod 600 /etc/vault.d/config.hcl
$ vim /etc/vault.d/config.hcl
storage "mysql" {
username = "vaultuser"
password = "caiodelgado.dev"
database = "vault"
}
listener "tcp" {
address = "10.10.10.10:8200"
tls_disable = 1
}
A configuração para os storage backends está disponível na Documentação Oficial
Criando o Banco de Dados
Como configuramos nosso storage backend para o mysql precisamos fazer a instalação do mesmo, iremos utilizar o MariaDB que é a versão open source do mysql e criar um usuário e o banco de dados
$ sudo apt update
$ sudo apt install mariadb-client mariadb-server -y
$ mysql -u root
> CREATE DATABASE vault;
> CREATE USER 'vaultuser'@'localhost' IDENTIFIED BY 'caiodelgado.dev';
> GRANT ALL PRIVILEGES ON vault.* TO 'vaultuser'@'localhost';
Em ambientes de produção lembre-se de executar o mysql_secure_installation para aumentar a segurança do banco de dados.
Configurando o systemd
Para adicionar o vault como serviço do sistema, iremos criar seu arquivo de unidade.
vim /etc/systemd/system/vault.service
[Unit]
Description="HashiCorp Vault - A tool for managing secrets"
Documentation=https://www.vaultproject.io/docs/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/vault.d/config.hcl
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
Capabilities=CAP_IPC_LOCK+ep
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/local/bin/vault server -config=/etc/vault.d/config.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
StartLimitInterval=60
StartLimitIntervalSec=60
StartLimitBurst=3
LimitNOFILE=65536
LimitMEMLOCK=infinity
[Install]
WantedBy=multi-user.target
Agora podemos habilitar, iniciar e verificar se o vault server está rodando.
$ systemctl enable vault
$ systemctl start vault
$ systemctl status vault
Inicializando o servidor
Agora que já parametrizamos o Banco de Dados e o Vault Server, podemos inicializar o servidor através do comando vault operator init
$ vault operator init
Output:
Unseal Key 1: F9Yo6vPumyQfuhTlTqcgkJhG3s02oeKJ6hNmkDKzItDE
Unseal Key 2: tuRLZjNOIv2DPO+TD3P3h9Gv1QQSL8rsFhjAe5q4CheH
Unseal Key 3: qA1TZ5J9iE2V3R6y/5DJh0i29MPTnGLsF/hvia2sOVAS
Unseal Key 4: GkDqxi6PbUj11bpQRJgJS9Fqebv0rxa+nYZCWcuZi0xG
Unseal Key 5: yMNs+Qh+LvOxav6ChiG3/uXhRQWRh1CY1vh1vUXqLtlt
Initial Root Token: s.EZE7QYMjfKz6zf5sDHHk6KkL
Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.
Vault does not store the generated master key. Without at least 3 key to
reconstruct the master key, Vault will remain permanently sealed!
It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.
Após inicializar o vault dois dados muito importantes são exibidos, a unseal key
e a initial root token
.
ATENÇÃO: Esta é a unica vez que estes dados são exbidos pelo Vault, guarde as unseal keys
em um local seguro, de preferencia cada chave em um local diferente, uma vez que estas chaves serão responsáveis pelo desbloqueio do cofre.
Todo servidor do Vault inicializa em sealed state
(estado lacrado/selado). Neste ponto o Vault consegue acessar o storage fisicamente porém não consegue desencriptar os dados. Precisaremos das unseal keys
para ensinar o Vault a descriptografar os dados.
O processo deunseal
deve ser realizado a cada momento que o vault iniciar e pode ser realizado pela CLI ou via API. Note que a mensagemVault initialized with 5 key shares and a key threshold of 3
foi exibida na incialização, o que significa que são necessárias 3 das 5 chaves para o processo deunseal
.
Destravando o Cofre
Para destravar o cofre (unseal the vault
) podemos executar o comando vault operator unseal
e digitar uma das chaves.

Note que o Unseal Progress
é listado como 1/3
ou seja, digitamos uma de 3 chaves necessárias para destravar o cofre e o mesmo encontra-se em estado de Sealed
. Repita o passo mais duas vezes com chaves diferentes para destravar o cofre.

Uma vez que o cofre for destravado, a mensagem de exibição será Sealed false
o que significa que podemos começar a utilizar nosso vault.

Finalmente podemos autenticar com o usuário root criado inicialmente. para isto execute o comando vault login
somado do token informado no processo do vault operator init
.

Criando secrets no Vault
Agora que configuramos nosso servidor podemos voltar a nossa estação de trabalho e parametrizar o vault para acessar nosso server, para facilitar este processo iremos criar um script para parametrizar nosso client
$ cd ~/vault/
$ vim configurevault.sh
#!/bin/bash
export VAULT_ADDR='http://vault.caiodelgado.example:8200'
export VAULT_TOKEN='s.EZE7QYMjfKz6zf5sDHHk6KkL'
$ chmod +x configurevault.sh
$ source configurevault.sh
Execute vault status
para verificar se o vault foi configurado corretamente.

Uma vez configuradas as variáveis de ambiente, podemos interagir com nosso vault server, efetue o login através do comando vault login $VAULT_TOKEN
.

O Vault trabalha, por padrão com secrets em formato kv
(key-value
). Para habilitar um secret utilizamos o comando vault secrets enable <path>
ou vault secrets enable -path=<path> <secret_type>
Existem outras Engines de secrets como Chaves SSH, Tokens AWS ou até mesmo plugins customizados, podemos verificar todos os suportados através da Documentação Oficial.
Inicialmente iremos trabalhar com secrets do tipo kv
, para isto, crie o nosso path de armazenamento e liste os secrets disponíveis.
$ vault secrets enable -path=blog -description='Segredos do Blog' kv
$ vault secrets list

Para criar um secret em um determinado path, podemos utilizar o comando vault kv put <path>/<secret-name> <key>=<value>
Crie o primeiro secret no nosso vault path blog
$ vault kv put blog/caiodelgado address=caiodelgado.dev
Uma mensagem de sucesso informa que o secret foi criado.
Success! Data written to: blog/caiodelgado
Utilize o comando vault kv list <path>
para verificar as chaves existentes no storage.

Para acessar um secret utilizamos o comando vault kv get <path>/<secret>
.

Um secret pode possuir diversos atributos key-value
bem como subdiretórios, vamos adicionar mais alguns atributos ao nosso path:
$ vault kv put blog/caiodelgado/data owner='Caio Delgado' twitter='caiodelgadonew'
Listando o nosso path conseguimos verificar que existem agora um secret chamado caiodelgado
bem como um sub-path caiodelgado/

Para verificar o conteúdo do subpath basta informar o mesmo no comando para listagem.

Mesmo que não seja informada a/
final o vault entende que ao utilizar o comandolist
você quer listar um path
Verifique os valores do secret data

Este post é o primeiro de uma série de posts onde utilizaremos o Vault.
Nos próximos posts iremos habilitar e utilizar sua interface de usuario e também integraremos o Vault com o Terraform para provisionar recursos na AWS de forma segura.
O código deste post encontra-se no repositório: https://github.com/caiodelgadonew/blog-vault-101
Ficamos por aqui com esse post e nos vemos em uma próxima!