В мире, где каждая учетная запись требует от нас еще одного пароля, и каждый облачный сервис, такой как AWS, зависит от надежности этих ключей, менеджеры паролей вроде Bitwarden выступают как спасители. Они не просто хранят наши ключи и пароли, но и делают их управление значительно удобнее. Однако, даже с таким мощным инструментом, как Bitwarden, мы сталкиваемся с ограничением: он не способен автоматически обновлять и менять используемые в облачных сервисах ключи и пароли. Итак, что делать, когда ручное обновление ключей и паролей становится скучной и малоэффективной задачей? В этой статье мы исследуем, как можно объединить удобство использования Bitwarden с эффективными методами автоматизации для управления учетными данными AWS. Представьте себе – больше нет монотонного ввода паролей и обновлений ключей вручную. Но для этого придется немного постараться. Что ж, начнем...

А почему, собственно, Bitwarden?

Выбирать инструмент для управления паролями и ключами AWS – это всегда непросто. Но после некоторых раздумий я остановился на Bitwarden, и вот почему он стал моим выбором:

  • Поддержка любых устройств: Bitwarden работает на всех платформах, которые я использую – это делает его идеальным решением для моих целей. У него есть нативные десктопные клиенты под Windows, Linux и MacOS. Есть родные мобильные приложения под iOS и Android. Есть отдельное приложение для Apple Watch. Есть command-line клиенты под любые платформы. Есть браузерные расширения для Chrome, Safari, Firefox и кучи других браузеров. И наконец есть просто веб-версия, доступная с любого устройства, где есть интернет и доступ к любому браузеру.

  • Простота и удобство использования: Несмотря на высокий уровень безопасности, Bitwarden остается интуитивно понятным и легким в использовании, что делает его доступным для всех. Особенно приятно наличие почти мгновенной безопасной синхронизации данных с централизованным облачным хранилищем. Так, что секрет, добавленный на одном клиенте, почти моментально становится доступен и на остальных. Для параноиков - можно не использовать родное облачное хранилище от самого сервиса Bitwarden, а использовать self-hosted установку, храня все секреты там, где хочется лично вам. Хоть на том же самом устройстве.

  • Двухфакторная авторизация и TOTP: Наличие поддержки двухфакторной авторизации и TOTP представляет собой важную функцию, позволяющую генерировать временные одноразовые пароли (TOTP) прямо внутри приложения. Это значительно упрощает процесс двухфакторной аутентификации, позволяя хранить и быстро использовать TOTP для различных веб-сайтов и приложений. В частности, Bitwarden можно использовать как Virtual MFA для авторизации в AWS.

  • Открытое API и открытый исходный код: Наличие открытого API и командного интерфейса в Bitwarden позволяет мне интегрировать данный сервис в различные сценарии и автоматизировать процессы управления паролями и ключами. Открытый исходный код облегчает аудит безопасности и позволяет легче расширять и улучшать сервис, реализуя новые возможности, в том числе альтернативные клиенты и даже сервер. Например, есть полностью переписанная на Rust серверная часть с полной поддержкой родного API и даже фич, которые в родном приложении доступны лишь для обладателей Premium-подписки.

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

Добавляем данные IAM пользователя AWS в Bitwarden

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

Для начала убедимся, что все необходимые данные для логина в AWS уже занесены в Bitwarden. При входе в консоль AWS обычно требуется ввести имя пользователя IAM и его пароль. Также важно сохранить в Bitwarden номер вашего AWS аккаунта или его псевдоним (alias), который необходим для доступа. Это обеспечит, что у вас всегда под рукой будут все данные для быстрого и безопасного доступа к вашему AWS аккаунту.

Форма логина в AWS Console

Давайте добавим соответствующие данные в Bitwarden, используя мобильный или десктопный клиент. При использовании браузерного расширения Bitwarden сам предлагает сохранить заполненные данные логина, создав новую запись, но мы не будем полагаться на это и попробуем для начала все настроить вручную.

Добавление логина в Bitwarden

Задаем имя пользователя в поле Username, а пароль - в поле Password. Поле Name служит просто названием карточки логина и может быть использовано для быстрого поиска среди других логинов. Если у вас в аккаунте AWS включена многофакторная аутентификация и Bitwarden добавлен в качестве соответствующего устройства Virtual MFA для вашей учетной записи, то в графе TOTP появится соответствующее значение вида otpauth://totp/Amazon%20Web%20Services:... Добавить соответствующие данные в карточку логина можно с помощью мобильного приложения Bitwarden, отсканировав QR-код, который создает сам Amazon при добавлении соответствующего устройства MFA.

Также весьма полезно будет добавить дополнительное поле account с идентификатором AWS аккаунта в графе CUSTOM FIELDS, если мы не хотим каждый раз вводить или копировать его в форму логина вручную.

Поле account в разделе CUSTOM FIELDS

В поле Website можно добавить соответствующий адрес URL, тогда при использовании клиента или браузерного расширения Bitwarden сможет автоматически заполнять форму логина нужными данными:

Список адресов в разделе Website

Настраиваем CLI

Итак, у нас уже есть сохраненные данные IAM пользователя для логина в AWS Console. Но мы не хотим каждый раз вручную проделывать эту операцию, после чего также вручную обновлять сохраненные данные в Bitwarden. Для автоматизации данного процесса нам понадобятся следующие инструменты:

  • POSIX Shell (Bash, Zsh и т.п.) Для Windows тоже можно сделать вариант под cmd или powershell, но при наличии работающего WSL мне это представляется излишним.

  • AWS CLI

  • Bitwarden command-line interface (CLI)

  • jq

Не буду детально останавливаться на установке и настройке данных инструментов, обозначу лишь наиболее значимые моменты.

Для автоматизации действий с помощью Bitwarden CLI нам понадобится настроить авторизацию. Это можно делать различными способами. Наиболее удобный для обычного пользователя - использование персонального ключа API, который можно создать в профиле Bitwarden. После чего нужно в консоли выполнить команду

bw login --apikey

и ввести в ответ на запрос соответствующие данные client_id и client_secret. Либо можно задать соответствующие переменные окружения BW_CLIENTID и BW_CLIENTSECRET.

Для работы с данными нам нужен не только успешный логин, но и unlock хранилища секретов. Это можно сделать вручную с помощью команды

bw unlock

Для разблокировки потребуется также ввести мастер-пароль, дающий доступ к зашифрованным данным. Это стоит делать только непосредственно при работе с данными. По окончании работы следует выполнить команду bw lock.

При успешном выполнении процедуры unlock будет создан временный сессионный ключ, который нам нужно будет использовать при выполнении любых операций с данными. Нам нужно задать его в виде переменной окружения для выполнения дальнейших команд:

export BW_SESSION="5PBYGU+5yt3RHcCjoeJKx/wByU34vokGRZjXpSH7Ylo8w=="

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

Тогда выполнить автоматическую разблокировку можно простым выполнением команды

bw unlock --passwordenv BW_PASSWORD

После выполнения Bitwarden вернет нам в стандартном выводе значение переменной BW_SESSION в виде готовой для исполнения команды. Если мы хотим полностью автоматизировать этот процесс, можно выполнить разблокировку следующим образом:

$(bw unlock --passwordenv BW_PASSWORD | awk '/export/ {print $2, $3}')

Так мы сразу разблокируем хранилище секретов и назначим правильное значение переменной BW_SESSION без дополнительных ручных действий. После этого можно выполнить команду

bw sync

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

Теперь можно заняться автоматизацией конкретных задач.

Меняем пароль IAM пользователя AWS

Возьмем для примера созданную нами чуть ранее запись с логином и паролем для пользователя john.doe и попробуем реализовать механизм изменения его пароля с автоматическим обновлением соответствующей записи в Bitwarden.

Для начала попробуем найти, как выглядит структура данных внутри записи при обращении к ней с помощью Bitwarden CLI. Чтобы вывести на экран данные записи для нашего логина с именем my_aws_account, используем следующую команду:

> bw get item my_aws_account --pretty
{
  "passwordHistory": null,
  "revisionDate": "2024-01-01T00:01:29.890Z",
  "creationDate": "2024-01-01T00:01:29.890Z",
  "deletedDate": null,
  "object": "item",
  "id": "32d1547c-5d14-37f2-7c2a-a0ef315dca6f",
  "organizationId": null,
  "folderId": "3f0a8c4a-9b5c-4f4d-9f9c-9c6a00a8f9e5",
  "type": 1,
  "reprompt": 0,
  "name": "my_aws_account",
  "notes": null,
  "favorite": false,
  "fields": [
    {
      "name": "account",
      "value": "123412341234",
      "type": 0,
      "linkedId": null
    }
  ],
  "login": {
    "fido2Credentials": [],
    "uris": [
      {
        "match": null,
        "uri": "https://eu-north-1.signin.aws.amazon.com/"
      }
    ],
    "username": "john.doe",
    "password": "My_R@nd0m_$tr1ng",
    "totp": null,
    "passwordRevisionDate": null
  },
  "collectionIds": []
}

Мы видим, что у записи есть множество разных атрибутов. Но нас интересуют самые основные. Это id, login.username и login.password. Для поиска записей можно использовать значение поля name, но этот параметр не обязательно будет уникальным, поэтому поиск может вернуть несколько разных записей. Чтобы обратиться к одной конкретной записи, лучше использовать ее уникальный идентификатор из поля id. Присвоим его значение конкретной переменной для удобства:

BW_ITEM_ID="32d1547c-5d14-37f2-7c2a-a0ef315dca6f"

Теперь получим старый пароль из записи Bitwarden, используя ее ID:

OLD_PASSWORD=$(bw get password "$BW_ITEM_ID")

Сгенерируем новый случайный с помощью команды bw generate:

NEW_PASSWORD=$(bw generate --length 16 --uppercase --lowercase --number --special)

Здесь в качестве параметров для генерации нового пароля мы указываем его длину (16 символов), обязательное наличие символов в верхнем регистре (uppercase), символов в нижнем регистре (lowercase), наличие цифр (number) и спецсимволов (special). В итоге в качестве значений переменной NEW_PASSWORD мы получим случайную строку вида 9@8NfuGZ!&5pJoy%.

Теперь, используя старый пароль, мы можем задать нашей учетной записи новый с помощью команды aws iam change-password. Для выполнения данной команды нам потребуется также настроенный и работающий AWS CLI. Не будем здесь останавливаться подробно на деталях настройки данного инструмента. Нужно только отметить, что если вы используете различные аккаунты AWS, то скорее всего у вас настроены разные профили для доступа к командной строке с разными ключами для соответствующих аккаунтов. Поэтому при вызове команды нам также понадобится указать соответствующий профиль AWS в виде переменной AWS_PROFILE. Если у вас нет разных профилей в настройках AWS CLI, либо вы используете другой источник ключей для доступа к AWS (например, переменные окружения), можете не указывать этот параметр. У нас получилось следующая команда:

aws iam change-password \
    --old-password "$OLD_PASSWORD" \
    --new-password "$NEW_PASSWORD" \
    --profile "$AWS_PROFILE" && \
    echo "Password changed successfully for profile '$AWS_PROFILE'." || { 
      echo "Failed to change password for profile '$AWS_PROFILE'."
      return 1
    }

Если доступ к AWS CLI настроен правильно, переменная OLD_PASSWORD соответствует значению старого пароля, а значение переменной NEW_PASSWORD соответствует требованиям к паролю для учетной записи AWS, то команда выполнится успешно и на экран выведется соответствующее сообщение. Если же по какой-то причине команда не завершится успешно, об этом тоже появится соответствующее сообщение.

Если все прошло успешно, то значит, новый пароль успешно задан и нам осталось лишь отредактировать нашу запись для учетной записи john.doe в Bitwarden, чтобы обновить значение пароля для данной записи. Чтобы сделать это из командной строки, нам придется воспользоваться также утилитой jq для работы с JSON. Итоговая команда по обновлению пароля в соответствующей записи Bitwarden будет выглядеть так:

bw get item "$BW_ITEM_ID" | jq --arg password "$NEW_PASSWORD" '.login.password = $password' | bw encode | bw edit item "$BW_ITEM_ID" >/dev/null

Если не перенаправить вывод итоговой команды в /dev/null, то на экран будет выведен итоговый JSON для нашей записи со всеми секретами, что не всегда полезно при работе в консоли. Поэтому для автоматизированных задач лучше отключить данный вывод.

В итоге мы с помощью Bitwarden CLI, AWS CLI и jq смогли сгенерировать новый пароль для выбранной нами учетной записи пользователя в AWS, а также смогли обновить соответствующую запись в нашем хранилище секретов Bitwarden.

Давайте теперь объединим данные команды в рамках одной общей функции, которую мы сможем вызывать одной командой из консоли. Получим примерно следующее:

rotate_aws_password() {
  local AWS_PROFILE=my_profile
  local BW_ITEM_ID="32d1547c-5d14-37f2-7c2a-a0ef315dca6f"
  
  # Fetch the old AWS console password from Bitwarden
  OLD_PASSWORD=$(bw get password "$BW_ITEM_ID")

  # Generate new random password using Bitwarden CLI
  NEW_PASSWORD=$(bw generate --length 16 --uppercase --lowercase --number --special)
  
  # Use AWS CLI to change the password with the specified profile
  aws iam change-password \
    --old-password "$OLD_PASSWORD" \
    --new-password "$NEW_PASSWORD" \
    --profile "$AWS_PROFILE" && \
    echo "Password changed successfully for profile '$AWS_PROFILE'." || { 
      echo "Failed to change password for profile '$AWS_PROFILE'."
      return 1
    }
  
  # Update the password in Bitwarden
  bw get item "$BW_ITEM_ID" | jq --arg password "$NEW_PASSWORD" '.login.password = $password' | bw encode | bw edit item "$BW_ITEM_ID" >/dev/null

  # Clear the entered passwords from the shell environment for security
  unset OLD_PASSWORD NEW_PASSWORD
}

Если добавить данный код в профиль вашего окружения для командного интерпретатора (.bashrc для Bash, .zshrc для Zsh и т.п.), то всю последовательность описанных выше действий можно запустить всего лишь одной командой:

> rotate_aws_password
Password changed successfully for profile 'my_profile'.

Можно проверить запись в соответствующей карточке Bitwarden и убедиться, что пароль для учетной записи john.doe действительно обновился.

Заключение

Мы рассмотрели пример автоматизации смены пароля для учетной записи AWS с использованием простых шелл-скриптов, а также с автоматическим обновлением сохраненных паролей в хранилище Bitwarden. Аналогичным образом можно автоматизировать ротацию ключей доступа к AWS CLI, получение временных токенов для доступа к AWS с использованием MFA и т.п. Если будет соответствующий запрос, попробую осветить данные моменты в следующих статьях.

Надеюсь, эта статья окажется полезной для упрощения вашей работы с AWS и Bitwarden. Я буду рад обсудить ваши идеи и предложения по дальнейшему улучшению автоматизации управления паролями. Буду рад любым отзывам в комментариях. Всего доброго.