Проверка и безопасная ротация SSH-ключа облачного Mac

Проверка и безопасная ротация SSH-ключа облачного Mac

При первом подключении к облачному Mac терминал обычно спрашивает, следует ли доверять неизвестному узлу. При ручной работе проще всего ввести yes, а в CI — указать StrictHostKeyChecking=no, но оба варианта отключают проверку подлинности. Если DNS, адресная конфигурация или сетевой маршрут окажутся ошибочными, учётные данные и материалы сборки могут попасть на чужую машину. Безопасный подход — сначала получить отпечаток ключа узла через доверенный канал, а затем настроить рабочие станции и автоматизированные задания так, чтобы они принимали только этот ключ.

Сначала создайте независимую основу доверия

Ключ узла подтверждает, что подключение установлено именно с ожидаемым Mac. Это не тот же ключ, что закрытый ключ пользователя для входа в систему. Например, отпечаток ключа узла Ed25519 можно получить, открыв локальный терминал целевой машины через Web VNC в консоли XcodeVM:

sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub -E sha256

Сохраните полное значение SHA256:, а также идентификатор экземпляра, адрес подключения и способ получения отпечатка. Не запрашивайте это значение только через SSH-соединение, которое ещё предстоит проверить: такая проверка замкнётся сама на себя.

Затем получите на рабочей станции открытый ключ, который в данный момент предоставляет удалённый узел:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan -T 5 -t ed25519 "$MAC_HOST" > ~/.ssh/xcodevm_known_hosts.new
ssh-keygen -lf ~/.ssh/xcodevm_known_hosts.new -E sha256

Посимвольно сравните оба отпечатка. Если они совпадают, установите файл:

install -m 600 ~/.ssh/xcodevm_known_hosts.new ~/.ssh/xcodevm_known_hosts
rm -f ~/.ssh/xcodevm_known_hosts.new

ssh-keyscan позволяет получить открытый ключ, но не доказывает, что ему можно доверять. Без независимо полученного эталонного отпечатка результат сканирования нельзя сразу добавлять в рабочий файл доверенных узлов.

Изолируйте known_hosts для каждого проекта

Глобальный файл ~/.ssh/known_hosts удобен для повседневных интерактивных подключений, но для командного проекта лучше использовать отдельный файл. Так изменения можно проверять явно, а очистка личной истории одним из разработчиков не повлияет на автоматизированные задания.

Создайте псевдоним в ~/.ssh/config:

Host xcodevm-build
    HostName 192.0.2.10
    User builder
    IdentityFile ~/.ssh/xcodevm_build_ed25519
    IdentitiesOnly yes
    UserKnownHostsFile ~/.ssh/xcodevm_known_hosts
    StrictHostKeyChecking yes
    HashKnownHosts yes
    ServerAliveInterval 30
    ServerAliveCountMax 3

Параметр IdentitiesOnly yes не позволяет клиенту последовательно перебирать слишком много файлов идентификации, а StrictHostKeyChecking yes гарантирует немедленный отказ при неизвестном или изменившемся ключе. Перед подключением проверьте итоговую конфигурацию, чтобы убедиться, что другие правила не переопределили параметры псевдонима:

ssh -G xcodevm-build |
  grep -E '^(hostname|user|identityfile|userknownhostsfile|stricthostkeychecking) '

Если в одном проекте используется несколько машин, храните отдельную запись для каждой из них и указывайте идентификатор экземпляра во внутреннем журнале изменений. Не используйте один общий псевдоним для адреса, который может произвольно меняться.

Закрепите отпечаток ключа в CI

CI не должен постоянно использовать личный файл доверенных узлов одного из разработчиков. Надёжнее хранить ожидаемый отпечаток SHA256 в защищённой переменной, а в каждом задании сначала сканировать и сравнивать ключ, после чего создавать временный файл known_hosts.

set -euo pipefail

: "${MAC_HOST:?MAC_HOST is required}"
: "${EXPECTED_ED25519_SHA256:?fingerprint is required}"

scan_file="$(mktemp)"
known_hosts_file="$(mktemp)"
trap 'rm -f "$scan_file" "$known_hosts_file"' EXIT

ssh-keyscan -T 5 -t ed25519 "$MAC_HOST" > "$scan_file" 2>/dev/null
actual="$(ssh-keygen -lf "$scan_file" -E sha256 | awk '{print $2}')"

if [ "$actual" != "$EXPECTED_ED25519_SHA256" ]; then
  printf '%s
' "SSH host key verification failed" >&2
  exit 1
fi

install -m 600 "$scan_file" "$known_hosts_file"

ssh \
  -o UserKnownHostsFile="$known_hosts_file" \
  -o StrictHostKeyChecking=yes \
  -o IdentitiesOnly=yes \
  "$MAC_USER@$MAC_HOST" \
  'uname -m && sw_vers -productVersion'

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

Правильно обрабатывайте изменение ключа

Ключ узла может измениться после сброса системы, повторного создания ключей или назначения прежнего адреса другой машине. Однако это также может означать, что соединение перенаправлено на посторонний узел. При появлении сообщения REMOTE HOST IDENTIFICATION HAS CHANGED сначала остановите автоматизированные задания. Нельзя просто удалить старую запись и повторить подключение.

Безопасная ротация выполняется в следующем порядке:

  1. В консоли проверьте идентификатор экземпляра, адрес подключения и убедитесь, что последние операции соответствуют ожиданиям.
  2. Через локальный терминал Web VNC повторно получите отпечаток открытого ключа Ed25519.
  3. Попросите другого специалиста проверить старый и новый отпечатки, а также причину изменения.
  4. Просканируйте открытый ключ удалённого узла, проверьте новый отпечаток и создайте новый файл доверенных узлов.
  5. Сначала обновите тестовое задание, затем выполните подключение только для чтения и проверку окружения.
  6. После этого обновите рабочее задание и отзовите старый отпечаток.

Если записи хешированы, сначала найдите старую:

ssh-keygen -F "$MAC_HOST" -f ~/.ssh/xcodevm_known_hosts
ssh-keygen -R "$MAC_HOST" -f ~/.ssh/xcodevm_known_hosts

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

Завершите приёмку по контрольному списку

Перед вводом в эксплуатацию проверьте как минимум следующее:

Проверка Критерий соответствия
Источник отпечатка Получен из локального терминала целевой машины
Алгоритм ключа Явно закреплён Ed25519, произвольные алгоритмы не принимаются
Права доступа Для .ssh установлено 700, для файла доверенных узлов — 600
Политика клиента StrictHostKeyChecking=yes
Изоляция CI Каждое задание использует отдельный временный файл
Обработка изменений Сначала остановить задания, затем проверить и только после этого заменить ключ
Содержимое журналов Сохраняются отпечаток и этап сбоя, учётные данные не раскрываются

Также проведите негативный тест: временно укажите неправильный ожидаемый отпечаток и убедитесь, что задание завершается до фактического выполнения удалённой команды. Затем восстановите правильное значение и проверьте команды только для чтения, доступ к репозиторию и точку запуска сборки. Это докажет, что защитный механизм действительно участвует в выполнении, а не просто присутствует в конфигурационном файле.

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

Часто задаваемые вопросы

Можно ли сразу доверять ключу, полученному через ssh-keyscan?

Нет. ssh-keyscan только считывает ключ, который узел показывает в данный момент. Его SHA256-отпечаток необходимо сравнить со значением, полученным через независимый доверенный доступ к Mac.

Можно ли удалить запись known_hosts после предупреждения и подключиться заново?

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

Следует ли CI использовать личный файл known_hosts разработчика?

Нет. Для задания создают отдельный файл с правами 600, включают StrictHostKeyChecking и удаляют временный файл доверия после завершения работы.

XcodeVM облачные Mac

Выберите выделенный физический сервер под текущую нагрузку

Проверьте объём памяти, хранилище, узел и расчётный период, затем перейдите к настройке заказа. Все узлы работают 365 дней в году; фактическая доступность определяется в реальном времени через панель управления.

Выбрать план и арендовать