Date

vault hashicorp logo

Vault

A tool for managing secrets.

Желательно проскролить по диагонали: https://github.com/hashicorp/vault

Vault - это тулзня от хашикорпа (по сути один бинарник на Голанге), который позволяет секурно управлять секретами. Секрет, как они говорят, это что угодно, к чему нужен гибкий доступ (АПИ ключи, пароли, сертификаты). Vault предоставляет унифицированный интерфейс к каждому секрету, и дает гибкий настраиваемый доступ с очень детальным логированием (кто что прочитал, кто что хотел прочитать и не смог).

Основные фичи:

  • сторедж для секретов

  • динамическая генерация секретов

  • шифрование данных

  • ttl секрета, регенерация

  • отзыв секрета

В общем, получается что это куча удобных фичей в одном флаконе.

Решение проблем

Со стороны инфраструктуры есть куча проблем, которые на данный момент никто не решает:

 

  • пароли лежат плеин текстом в репке

Детальное описание проблемы:

Почти везде, где есть Infrastructure as Code (IaC) есть пару репо для Configuration management, где лежат настройки провижининга баз данных, менеджеров очередей, эндпоинтов других сервисов с аутентификацией, и так далее. Как правило, это не совсем секурно. Нужно бы как-то их оттуда убрать

 

  • все знают все пароли

Детальное описание проблемы:

Окей, у нас пароли в репо, и нас это не беспокоит. Но вот проблема - есть несколько команд, и их менеджат разные люди. Например, если есть DBA - зачем ему знать креды к реббиту?

 

  • при увольнении человека пароли никто не меняет

Детальное описание проблемы:

Периодически люди уходят, и очень желательно иметь одно место, в котором можно поменять пассворд, и это применится на всей инфраструктуре. Не так ли? Очень часто этот этап скипают, потому что нужно пройтись по куче мест, где захардкожен этот пароль (и не дай бог в самом апликейше), и ооочень большая вероятность где-то в одном маленьком месте забыть.

Анализ требований

Ок, если у нас будут лежать в Vault все пароли, то это должно быть: - сесурно (внезапно!), - отказоустойчиво, - гибко, - и вообще - модно.

В общем, все это примерно так и есть.

Перед внедрением

Перед внедрением нужно обязательно поднять шумиху по поводу сесурити, иначе кто потом оценит?

Внедрение

Выкачать бинарник на сервер, и на клиент (пока только себе).

Создаем очень простой конфиг на сервере: внимание - это не json, это hcl

    backend "file" {
      path = "/etc/vault/data/"
    }

    listener "tcp" {
      address = "10.10.10.10:8200"
      tls_disable = 1 
    }

tls_disable = 1 - это только в тестовых целях, конечно

Тут есть несколько нюансов. Vault умеет сторить зашифрованную информацию в файл, различные базы данных, эндпоинты и т.п. Для теста - в файл. Для прода - в какой-то постргрес в кластере и с репликацией.

Заходим куда-то, инициализируем сервер vault init

    $ vault init
    Key 1: 427cd2c310be3b84fe69372e683a790e01
    Key 2: 0e2b8f3555b42a232f7ace6fe0e68eaf02
    Key 3: 37837e5559b322d0585a6e411614695403
    Key 4: 8dd72fd7d1af254de5f82d1270fd87ab04
    Key 5: b47fdeb7dda82dbe92d88d3c860f605005
    Initial Root Token: eaf5cc32-b48f-7785-5c94-90b5ce300e9b

    Vault initialized with 5 keys and a key threshold of 3!
    ...

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

Собственно, запускаем: vault server -config server.hcl -log-level=debug

Крутяк. Сервер работает.

В консоли на клиенте

Доступ к серверу можно получить с консоли на клиенте, предварительно выкачав бинарник Vault. Для подключения к серверу нужно иметь 2 переменных окружения.

    export VAULT_ADDR=http://10.10.10.10:8200
    export VAULT_TOKEN=eaf5cc32-b48f-7785-5c94-90b5ce300e9b

VAULT_ADDR - это ендпоин для подключения к серверу, VAULT_TOKEN - это Initial Root Token.

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

    $ vault mount generic
    Successfully mounted 'generic' at 'generic'!

Отлично, теперь туда можно что-то написать:

    vault write secret/mysql/dev user=secure_user password=sdflksdncdsjn343434sddsf
    Success! Data written to: secret/mysql/dev

Иерархия ключей тут древовидная, т.е. Vault знал только про бэкенд secret, но потом создал еще подпуть mysql,туда положил dev, и уже туда положил секреты.

Прочитать их можно подобным образом:

    vault read secret/hello
    Key             Value
    lease_duration  2764800
    excited         yes
    user            mysql
    password        sdflksdncdsjn343434sddsf

Вооо. Очень круто. Это работает.

Убираем пароли из репозитория

Ansible

В общем, то, как нужно их убрать - будет зависеть от того, что именно лежит в репке. Например, самый простой кейс - это Ansible.

Ссылка почитать и посмотреть: https://github.com/jhaals/ansible-vault

В нашем случае в vars.yml нужно будет написать такое вот:

    mysql_user: "{{ lookup('vault', 'secret/mysql/dev').user }}"
    mysql_pass: "{{ lookup('vault', 'secret/mysql/dev').password }}"

При запуске Ansible заберет креды из Vault, и положит их в вариейблы, которые дальше можно юзать в плейбуках. Подобный синтаксис будет работать и в шаблонах j2.

Chef

Ссылка почитать: https://www.hashicorp.com/blog/using-hashicorp-vault-with-chef.html

В кукбуках для подключения эндпоинта нужно будет заюзать такую вещь (непосредственно в рецепте):

    require 'vault'

    mysql_vault = Vault.logical.read("secret/mysql/dev")

    mysql_user = mysql_vault.data[:user]
    mysql_password = mysql_vault.data[:password] 

Вроде не напряжно.

Аппликейшн

Конечно, можно забирать инфу из Vault прямо из апликейшна. К примеру, у нас в компании очень хорошо прижился и активно используется ChatOps, и это копипаст оттуда (при инициализации он забирает токен для слека и рандека). Посмотрите, как это работает.

Ссылка на пекедж для питончика: https://github.com/ianunruh/hvac

Подобные модули есть на большинство языков программирования.

И сам код питончика:

    from hvac import Client


    try:
        client = Client(url=environ.get('VAULT_ADDR'), token=environ.get('VAULT_TOKEN'))

        SLACK_TOKEN = client.read('secret/slack')['data']['key']
        RUNDECK_TOKEN = client.read('secret/rundeck')['data']['token']
    except Exception:
        raise Exception('Can not get slack token from vault!')

Все очень и очень просто. Апп пытается загрести токен из переменных окружения, потом забрать нужные ему данные из Vault. Если у него не получается - он падает с ошибкой 'Can not get slack token from vault!'.

Настраиваем гибкие ACL

К примеру, настроим ACL для mysql аппа:

mkdir acl && touch acl/mysql.hcl

И прописываем детально что ему можно, что ему нельзя:

    path "secret/mysql/dev" {
      policy = "write"
    }

    path "secret/mysql/uat" {
      policy = "read"
    }

    path "secret/mysql/prod" {
      policy = "read"
    }

Заимпортим полиси на сервер: vault policy-write mysql acl/mysql.hcl

И сгенерим токен с помощью этого ACL:

    vault token-create -display-name='mysql app' -ttl="100500h" -policy="mysql"

Теперь, этот токен будет видеть только то, что мы ему наконфигурили, и ничего больше. Кстати, токен автоматически заекспайрится когда придет TTL.

Увольняем сотрудника

vault token-revoke eaf5cc32-b48f-7785-5c94-90b5ce300e9

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

Самое главное

Не нужно хранить пароли в репо, для этого есть специальные штуки, например, Vault.


Comments

comments powered by Disqus