Вместо раздачи открытых ключей в authorized_keys на каждом сервере серверы доверяют одному CA в Stronghold. Пользователь входит в Stronghold через OIDC, подписывает свой открытый ключ и получает сертификат на 30 минут. Отзывать доступ на серверах не нужно: сертификат истекает сам.

Цель

Настроить SSH CA в Stronghold, роль, которая разрешает пользователю входить только под своим именем, доверие к CA на серверах и получение сертификата после входа через OIDC.

Предварительные требования

Перед началом подготовьте:

  • Токен Stronghold с правами на настройку механизмов секретов, политик и Identity.
  • Настроенный метод аутентификации OIDC, который передаёт группы пользователя (в DP — метод oidc_deckhouse).
  • Серверы с OpenSSH, на которых вы можете менять sshd_config.
  • Имя учётной записи каждого пользователя на серверах. В примере оно хранится в метаданных сущности (шаг 3).

Шаг 1. Настройте SSH CA

Включите механизм секретов SSH и сгенерируйте ключ CA.

  1. Включите механизм секретов SSH:

    d8 stronghold secrets enable -path=ssh-client-signer ssh
  2. Сгенерируйте ключ CA:

    d8 stronghold write ssh-client-signer/config/ca generate_signing_key=true

Шаг 2. Настройте доверие на серверах

На каждом сервере настройте sshd на доверие сертификатам, подписанным CA Stronghold.

  1. Сохраните открытый ключ CA на каждом сервере. Эндпоинт public_key не требует аутентификации:

    curl -o /etc/ssh/trusted-user-ca-keys.pem https://stronghold.example.com/v1/ssh-client-signer/public_key
  2. Добавьте в /etc/ssh/sshd_config:

    TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
  3. Перезапустите sshd:

    sudo systemctl restart sshd

Распространяйте ключ и настройку через систему управления конфигурацией (Ansible, Puppet, Salt).

Шаг 3. Создайте роль с шаблонным именем пользователя

Роль разрешает подписывать ключ только для principal, совпадающего с именем учётной записи пользователя на серверах. Имя учётной записи подставляет шаблон Identity.

Для метода oidc_deckhouse в DP имя псевдонима сущности — это email пользователя: модуль задаёт user_claim равным email в роли deckhouse_dex_authenticated. Email не совпадает с именем учётной записи на сервере, поэтому в примере имя учётной записи хранится в ключе ssh_user метаданных сущности. Менять метаданные сущности может только токен с правами на identity/entity/*, поэтому пользователь не может сменить свой principal.

  1. Получите accessor метода аутентификации:

    OIDC_ACCESSOR=$(d8 stronghold read -field=accessor sys/auth/oidc_deckhouse)
  2. Задайте имя учётной записи на серверах для каждого пользователя. Сущность пользователя создаётся при первом входе в Stronghold:

    ENTITY_ID=$(d8 stronghold write -field=id identity/lookup/entity alias_name="<USER_EMAIL>" alias_mount_accessor="$OIDC_ACCESSOR")
    d8 stronghold write identity/entity/id/$ENTITY_ID metadata=ssh_user=<SSH_USER>

    Плейсхолдеры:

    • <USER_EMAIL> — email пользователя, он же имя псевдонима в методе oidc_deckhouse;
    • <SSH_USER> — имя учётной записи пользователя на серверах.

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

  3. Создайте роль:

    d8 stronghold write ssh-client-signer/roles/ops - <<EOF
    {
      "key_type": "ca",
      "algorithm_signer": "rsa-sha2-256",
      "allow_user_certificates": true,
      "allowed_users_template": true,
      "allowed_users": "{{identity.entity.metadata.ssh_user}}",
      "default_user_template": true,
      "default_user": "{{identity.entity.metadata.ssh_user}}",
      "allowed_extensions": "permit-pty,permit-port-forwarding",
      "default_extensions": {
        "permit-pty": ""
      },
      "ttl": "30m",
      "max_ttl": "1h"
    }
    EOF

    Если имя псевдонима уже совпадает с именем учётной записи на сервере, используйте вместо метаданных шаблон {{identity.entity.aliases.<OIDC_ACCESSOR>.name}}, где <OIDC_ACCESSOR> — accessor метода. Например, это подходит для отдельного подключения OIDC с user_claim, равным preferred_username.

Шаг 4. Выдайте права на подпись

Разрешите членам группы IdP подписывать ключи по роли.

  1. Создайте политику:

    d8 stronghold policy write ssh-ops - <<'POLICY'
    path "ssh-client-signer/sign/ops" {
      capabilities = ["update"]
    }
    POLICY
  2. Привяжите политику к группе IdP через внешнюю группу Identity:

    GROUP_ID=$(d8 stronghold write -field=id identity/group name=ops type=external policies=ssh-ops)
    
    d8 stronghold write identity/group-alias name=ops mount_accessor="$OIDC_ACCESSOR" canonical_id="$GROUP_ID"

    Здесь name=ops в group-alias — имя группы, которое IdP передаёт в claim групп.

Шаг 5. Получите сертификат и войдите на сервер

Эти действия выполняет пользователь на своей рабочей станции.

  1. Войдите в Stronghold:

    d8 stronghold login -method=oidc -path=oidc_deckhouse
  2. Если SSH-ключа нет, создайте его:

    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
  3. Подпишите открытый ключ и сохраните сертификат рядом с ключом:

    d8 stronghold write -field=signed_key ssh-client-signer/sign/ops public_key=@$HOME/.ssh/id_ed25519.pub > ~/.ssh/id_ed25519-cert.pub
  4. Подключитесь к серверу. OpenSSH автоматически использует файл *-cert.pub:

    ssh <SSH_USER>@server.example.com

    <SSH_USER> — ваше имя учётной записи на серверах из шага 3.

Повторяйте шаги 1 и 3 после истечения сертификата.

Проверка

Проверьте результат:

  1. Просмотрите содержимое сертификата:

    ssh-keygen -Lf ~/.ssh/id_ed25519-cert.pub

    В поле Principals должно быть ваше имя учётной записи на серверах, в поле Valid — интервал не длиннее 30 минут.

  2. Попробуйте подписать ключ для чужого principal — запрос должен быть отклонён:

    d8 stronghold write ssh-client-signer/sign/ops public_key=@$HOME/.ssh/id_ed25519.pub valid_principals=root
  3. Через 30 минут убедитесь, что вход с тем же сертификатом отклоняется сервером.

При ошибках входа смотрите раздел «Устранение неполадок».

Очистка

Удалите объекты, созданные в этом руководстве.

  1. Удалите внешнюю группу и её псевдоним: d8 stronghold delete identity/group/name/ops.

  2. Удалите политику и роль:

    d8 stronghold policy delete ssh-ops
    d8 stronghold delete ssh-client-signer/roles/ops
  3. Для тестового стенда отключите mount: d8 stronghold secrets disable ssh-client-signer. После этого удалите строку TrustedUserCAKeys на серверах.

Дополнительные ресурсы