Одноразовые пароли для доступа по ssh через HashiCorp Vault

от автора

HashiCorp Vault имеет в своём арсенале SSH secrets engine, который позволяет организовать защищённый доступ к вашим машинам по ssh, через создание клиентских сертификатов и одноразовых паролей. Про последнее – создание одноразовых паролей (OTP) – мы и поговорим в этой статье.

Как это работает?

Схема работы

Схема работы

Клиент генерирует OTP для нужного пользователя и хоста, далее обычным ssh username@host подключается к серверу. Сгенерировать ключ можно напрямую через vault, используя cli, API или веб-интерфейс, или через вашу внутреннюю систему, которая сама будет дёргать vault API и создавать OTP.

Когда SSH-сервер получает запрос на аутентификацию, он вызывает PAM (Pluggable Authentication Modules) модуль, который в процессе выполняет внешнюю команду. Этой внешней командой является vault-ssh-helper, который, в свою очередь, стучится в ваш Vault-кластер для проверки токена, отправленного клиентом. Если всё ок, то доступ предоставляется, а токен инвалидируется.

Установка и настройка

Вся настройка достаточно быстрая и состоит из двух этапов: необходимые манипуляции внутри кластера Vault и на самом host’е.

Как всё настроить подробно описано в официальной статье от HashiCorp. Инструкция ниже это по сути её перевод. Вы можете это пропустить и перейти сразу к нюансам.

Манипуляции внутри Vault’а

Включаем SSH secrets engine.

vault secrets enable ssh

Создаём роль, которая будет использована для генерации OTP ключей для клиентов. Указываем дефолтного юзера и список разрешённых. А также в cidr_list задаём список адресов, к которым будут подходить ключи.

Рекомендуется создавать по одной роли для каждого пользователя.

vault write ssh/roles/otp_role \     key_type=otp \     default_user=worker \     allowed_users=worker, worker2 \     cidr_list=10.10.10.10/32

Далее осталось создать policy к нашей роли для генерации ключей и сгенерировать access-token, привязанный к этой policy.

tee test.hcl <<EOF path "ssh/creds/otp_role" {   capabilities = ["update"] } EOF
vault policy write otp-polcy ./test.hcl

Создаём token и сохраняем его, потому что потом запросить его у vault не удастся.

vault token create -policy=otp-polcy  Key                  Value ---                  ----- token                hvs.CAESIG1_CrngaECzf6yvTDBgUZz2Lt-mYfdZXogrsiV0ulH1Gh4KHGh2cy4bPmFmN24xNVM5cnBqbFNLTUdpd1JDcTM token_accessor       n76E8Bc8P9SyPLpVZa2EoWGq token_duration       768h token_renewable      true token_policies       ["default" "otp-polcy"] identity_policies    [] policies             ["default" "otp-polcy"]

Манипуляции внутри нужной машины

Скачиваем последнюю версию vaul-ssh-helper с этой ссылки, указанной в этом репозитории.

wget https://releases.hashicorp.com/vault-ssh-helper/0.2.1/vault-ssh-helper_0.2.1_linux_amd64.zip

Распаковываем в директорию /usr/local/bin.

unzip -q vault-ssh-helper_0.2.1_linux_amd64.zip -d /usr/local/bin

Указываем владельцем root’а и устанавливаем права доступа 0755 (rwxr-xr-x).

sudo chown root:root /usr/local/bin/vault-ssh-helper
sudo chmod 0755 /usr/local/bin/vault-ssh-helper

Создаём файл с конфигурацией vault-ssh-helper в директории /etc/vault-ssh-helper.d.

sudo mkdir /etc/vault-ssh-helper.d
sudo tee /etc/vault-ssh-helper.d/config.hcl <<EOF vault_addr = "VAULT_ADDR" tls_skip_verify = false ca_path = "CA_CRT_PATH" ssh_mount_point = "ssh" allowed_roles = "*" EOF

Меняем конфигурация для PAM sshd (/etc/pam.d/sshd).

# на всякий случай бэкапим её sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.orig

Комментируем строчку include common-auth и добавляем следующие две: в них для vault-ssh-helper указываем конфиг helper’а и файл, куда будут выводиться логи.

auth requisite pam_exec.so quiet expose_authtok log=/var/log/vault-ssh.log /usr/local/bin/vault-ssh-helper -config=/etc/vault-ssh-helper.d/config.hcl auth optional pam_unix.so not_set_pass use_first_pass nodelay

Меняем конфигурацию sshd (/etc/ssh/sshd_config).

# на всякий случай бэкапим её sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig

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

KbdInteractiveAuthentication yes UsePAM yes PasswordAuthentication no

Старые версии Ubuntu используют ChallengeResponseAuthentication вместо KbdInteractiveAuthentication.

Так как вы отключаете аутентификацию по паролю, убедитесь, что возможны другие способы доступа к серверу. Например доступ по ssh ключу у root’а.

Перезапускаем sshd.

sudo systemctl restart sshd

Проверка

Авторизуемся в vault с токеном, полученным выше.

vault login $TOKEN

Генерируем OTP ключ для нужного ip-адреса (юзера не указываем – используется дефолтный).

vault write ssh/creds/otp_key_role ip=$IP_ADDRESS  Key                Value ---                ----- lease_id           ssh/creds/otp_key_role/yZaiE4bRiVUVvc6LetQDtDmS lease_duration     768h lease_renewable    false ip                 10.10.10.10 key                edcf6837-902f-43c6-9e54-c8c26ab80ff3 key_type           otp port               22 username           username

Подключаемся к серверу с полученным ключём.

ssh username@10.10.10.10

Нюансы

Чтобы лучше понимать некоторые пункты

Роль в ssh secret engine – это конфигурация для генерации SSH-учетных данных. В рамках OTP она включает в себя такие параметры, как, например: списки разрешённых ip-адресов и пользователей, а также дефолтное имя пользователя, для которого сгенерируется OTP в случае, если имя пользователя не будет передано.

Lease в Vault – это метаданные, которые содержат информацию о времени действия, возможности обновления и т.д., связанных с каждым динамическим секретом и токеном.

#1 Генерировать на одного юзера можно сколько угодно ключей

Имея токен с доступом к нужной роли ssh engine, можно создавать сколько угодно одноразовых ключей для всех пользователей и всех машин, которые указаны в роли. И живут они около месяца. Поэтому рекомендуется ограничивать конфигурацию роли одним пользователем на одной машине.

#2 Сгенерированные ключи останутся рабочими после удаления роли

Пока валиден lease созданного ключа, не важно, существует роль, которой его выдали, или нет – ключ останется валидным.

#3 Узнать, сколько сейчас неиспользованных ключей для пользователя, невозможно

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

curl -s -X LIST -H "X-Vault-Token: ${VAULT_TOKEN}" ${VAULT_ADDR}/v1/sys/leases/lookup/ssh/creds/$ROLE_NAME

Но понять, какой lease указывает на валидный ключ, а какой нет, вы не сможете.

#4 Инвалидировать ключ можно только отозвав привязанный к нему lease

Так как ключи живут даже после удаления роли, единственный способ их убить, это отозвать lease.

Можно точечно:

vault lease revoke ssh/creds/otp-role/lease-id

А можно отрубить сразу lease’ы всех ключей роли:

vault lease revoke -prefix ssh/creds/otp-role

#5 По дефолту логи использования access-token’ов в HashiCorp Vault не ведутся

Чтобы узнать какие access-token’ы, когда и для чего использовались, нужно включить запись логов обращения к API.

vault audit enable file file_path=ПУТЬ_ДО_ФАЙЛА_КУДА_БУДУТ_СОХРАНЯТЬСЯ_ЛОГИ

Файл с логами будет очень жирным.

Все token’ы и их accessor’ы в логах будут захешированы. Чтобы как-то найти строчки использования нужного вам token’а, вам придется сначала прогнать его через ту же хеш-функцию, которую использует audit. И дальше по полученному хешу искать в лог-файле.

vault write sys/audit-hash/file input="<TOKEN ACCESSOR>"

Автоматизация получение OTP

Автоматизировать этот процесс очень легко благодаря Vault API и гибкой настройке прав доступа через ACL. По сути просто создаёте пользователя или сразу token с необходимой policy, и можете использовать нужные вам роли для генерации OTP ключей.

Пример с Ansible

Самый, наверное, простой способ – это создать Ansible Playbook, запрашивающий OTP у vault напрямую модулем community.hashi_vault.vault_write.

- name: Play   hosts: localhost   gather_facts: false   tasks:     - name: Get OTP       community.hashi_vault.vault_write:         auth_method: token         url: https://vault.service         token: YOUR_TOKEN         path: ssh/creds/otp_role         data:           ip: HOST_IP       register: ssh     - name: Setting ssh vars       ansible.builtin.set_fact:         hashi_host_ip: '{{ ssh.data.data.ip }}'         hashi_host_username: '{{ ssh.data.data.username }}'         hashi_host_port: '{{ ssh.data.data.port }}'         hashi_host_key: '{{ ssh.data.data.key }}'     - name: DBG       ansible.builtin.debug:         msg:           - 'ip: {{ hashi_host_ip }}'           - 'port: {{ hashi_host_port }}'           - 'username: {{ hashi_host_username }}'           - 'password: {{ hashi_host_key }}'

Пример с использованием API запросов

curl --location 'https://vault.service/v1/ssh/creds/otp_role' \ --header 'X-Vault-Token: TOKEN' \ --header 'Content-Type: application/json' \ --data '{     "ip": "10.10.10.10" }' | jq '.data'

Спасибо за чтение! 🙂


ссылка на оригинал статьи https://habr.com/ru/articles/785052/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *