Перейти к содержанию

Нюансы и пределы

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

Кластер не проверяет отзыв

API-сервер Kubernetes при аутентификации по клиентскому сертификату проверяет цепочку и читает субъект. Ни CRL, ни OCSP он не спрашивает — такой проверки у него нет вовсе. Значит, отзыв внутри Alatyr не дотягивается до кластера: пока выданный сертификат не истёк, кластер продолжает его принимать.

Истечение — единственный контроль, который кластер применяет сам. Именно поэтому то, что он видит, живёт десять минут, а не год.

Отзыв останавливает следующую выдачу, а не текущее соединение

Формулировка здесь важна до буквы, потому что неточная напрашивается сама.

  • Новый доступ прекращается в пределах одной жизни credential. После отзыва следующий kubectl не получит ничего.
  • Уже установленные соединения продолжают работать. kubectl exec, port-forward и watch держат сессию, и TLS не разрывает её, когда сертификат под ней истёк. Оболочка kubectl exec, открытая за минуту до отзыва, останется открытой.

То есть верно «новый доступ прекращается за десять минут», а не «доступ отбирается за десять минут». Разница вылезает на первом же разборе инцидента, где спросят, сколько прожила сессия, и обещание, данное там неверными словами, хуже, чем отсутствие обещания.

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

Группы не передаются

Kubernetes берёт имя пользователя из Subject CN, а группы — из Subject O. Alatyr заполняет первое и не заполняет второе, поэтому привязки kind: Group не сработают. Привязывайте пользователей.

Это осознанное решение, а не недосмотр: содержимое субъекта на этой цели — ровно то, к чему привязывается RBAC, и оно держится минимальным намеренно.

Реестр управляет раздачей, а не приёмом

Реестр кластеров решает, о каких кластерах устройству вообще расскажут: до того, которого в его списке нет, сотрудник не дотянется, потому что не знает ни адреса, ни CA.

Чего реестр не может — помешать принять сертификат где-то ещё. В сертификате не написано имя кластера, поэтому любой кластер, доверяющий тому же корню, его аутентифицирует.

Один корень на production и staging делает staging-доступ production-доступом

Это не смягчается реестром и не смягчалось до него. Либо разводите кластеры по разным корням, либо принимайте это осознанно — но не считайте, что реестр закрыл вопрос. Раздача и приём — разные вопросы: за первый отвечает реестр, за второй — ваш --client-ca-file.

Часы на устройстве — требование, а не гигиена

Сертификат на десять минут непригоден на машине, часы которой ушли на несколько минут: кластер читает «действует с» и «действует по» и отказывает. Поэтому синхронизация времени на машинах сотрудников — настоящее требование этого сценария, а не общее пожелание.

Alatyr старается поймать это раньше кластера: при расхождении больше двух минут выдача отказывается своим отказом, с указанием величины, вместо того чтобы выдать сертификат, который откажет там, где причину не видно. Лечится это на устройстве — ни кластер, ни настройки Alatyr здесь ни при чём.

Подтверждение человеком

Ступень «Подтверждение при выдаче доступа к кластеру» закрывает ровно одну вещь и стоит того, чтобы понимать какую.

Аппаратный ключ не вытащить ни в одной схеме. Но у локального атакующего под тем же пользователем есть два пути, и второй важнее: прочитать память процесса kubectl — или просто запустить kubectl самому. Второй путь работает против любого молчаливого механизма одинаково: против петлевого прокси (порт открыт — ходи), против токена, против смарт-карты без PIN. Схема хранения ключа его не закрывает вовсе. Закрывает только вопрос человеку.

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

Чего она не даёт. Окно называет кластер и программу, которая просит доступ, — но не доказывает, что программа та, за кого себя выдаёт: имя берётся из таблицы процессов. Ценность в том, что неожиданное имя видно, а не в том, что ожидаемое что-то доказывает.

Деградации нет, и это заметно. Там, где спросить негде, поднятая ступень отказывает в выдаче, а не выдаёт молча:

  • на macOS — спрашивать при выдаче credential там пока нечем;
  • на любой машине без графического сеанса — сервер, сессия по SSH.

Умолчание — «Спросить раз в окно», поэтому на macOS это касается и установки, где ступень никто не трогал. Если доступ к кластеру нужен на macOS, ступень придётся опустить осознанно.

Ответ живёт в памяти работающего агента. Он не переживает перезапуск агента и перезагрузку машины — после них человека спросят снова. Если агент не запущен вовсе, вопрос задаётся на каждый вызов kubectl, и плагин говорит об этом строкой, а не молча.

Локальный прокси не строже плагина

Пока прокси запущен, его петлевым портом может воспользоваться любой процесс того же пользователя, и это окно длиннее десятиминутного сертификата. Режим убирает приватный ключ из памяти чужого процесса — и только это. Подробнее — «Два режима доставки».

Пустой реестр означает отсутствие доступа

Если в реестре нет ни одного включённого кластера, устройствам не поедет ни один, и kubectl скажет именно это. Пустой список здесь — ответ, а не незаполненная настройка.

Семантика выбрана так намеренно: «не знаю, что тебе положено» обязано читаться как «ничего». Обратное прочтение означало бы, что первая же ошибка чтения раздаёт доступ.

Что дальше

  • Настройка — вернуться к пошаговому порядку: кластер, админка, рабочая машина.
  • Два режима доставки — чем отличается локальный прокси и когда он оправдан.