Можно ли автоматически добавить новый хост в known_hosts?

Вопрос или проблема

Вот моя ситуация: я настраиваю тестовую среду, которая из центрального клиента будет запускать ряд экземпляров виртуальных машин и затем выполнять команды на них через ssh. Виртуальные машины будут иметь ранее неиспользуемые имена хостов и IP-адреса, поэтому они не будут находиться в файле ~/.ssh/known_hosts на центральном клиенте.

Проблема, с которой я сталкиваюсь, заключается в том, что первая команда ssh, выполненная против нового виртуального экземпляра, всегда вызывает интерактивный запрос:

Аутентичность хоста ‘[имя_хоста] ([IP-адрес])’ не может быть установлена.
Отпечаток RSA ключа [отпечаток_ключа].
Вы уверены, что хотите продолжить подключение (да/нет)?

Существует ли способ обойти это и сделать так, чтобы новый хост уже был известен клиенту, возможно, используя публичный ключ, который уже встроен в образ виртуальной машины? Мне действительно хотелось бы избежать использования Expect или чего-то подобного для ответа на интерактивный запрос, если это возможно.

Установите параметр StrictHostKeyChecking в no, либо в конфигурационном файле, либо через -o:

ssh -o StrictHostKeyChecking=no [email protected]

На мой взгляд, лучший способ сделать это следующий:

ssh-keygen -R [имя_хоста]
ssh-keygen -R [ip_address]
ssh-keygen -R [имя_хоста],[ip_address]
ssh-keyscan -H [имя_хоста],[ip_address] >> ~/.ssh/known_hosts
ssh-keyscan -H [ip_address] >> ~/.ssh/known_hosts
ssh-keyscan -H [имя_хоста] >> ~/.ssh/known_hosts

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

Для ленивых:

ssh-keyscan -H <host> >> ~/.ssh/known_hosts

-H хэширует имя хоста / IP-адрес.

Как упоминалось, использование key-scan будет правильным и ненавязчивым способом сделать это.

ssh-keyscan -t rsa,dsa HOST 2>&1 | sort -u - ~/.ssh/known_hosts > ~/.ssh/tmp_hosts
mv ~/.ssh/tmp_hosts ~/.ssh/known_hosts

Вышеуказанное позволит добавить хост, ТОЛЬКО если он еще не был добавлен. Это также небезопасно для конкурентности; вы не должны выполнять фрагмент кода на одной и той же машине-происхождении более одного раза одновременно, так как файл tmp_hosts может быть перезаписан, в конечном итоге это может привести к росту файла known_hosts…

Вы можете использовать команду ssh-keyscan, чтобы получить публичный ключ и добавить его в ваш файл known_hosts.

Проверьте отпечаток каждого нового сервера/хоста. Это единственный способ аутентифицировать сервер. Без этого ваше SSH-соединение может быть подвержено атаке "человек посередине".

Не используйте старое значение StrictHostKeyChecking=no, которое никогда не проверяет подлинность сервера. Хотя значение StrictHostKeyChecking=no планируется изменить позже.

Второй вариант, но менее безопасный, это использовать StrictHostKeyChecking=accept-new, который был введен в версии 7.6 (2017-10-03) OpenSSH:

Первая "accept-new" автоматически примет
ранее не виденные ключи, но откажет в соединениях для измененных или
недопустимых хостов.

Чтобы сделать это правильно, что вам действительно нужно сделать, это собрать публичные ключи хостов виртуальных машин по мере создания и поместить их в файл в формате known_hosts. Вы можете затем использовать -o GlobalKnownHostsFile=..., указывая на этот файл, чтобы убедиться, что вы подключаетесь к хосту, к которому вы считаете, что должны подключаться. Как вы это сделаете, зависит от того, как вы настраиваете виртуальные машины, однако, чтение с виртуальной файловой системы, если возможно, или даже получение хоста для распечатки содержимого /etc/ssh/ssh_host_rsa_key.pub во время конфигурации может сработать.

Сказав это, это может быть нецелесообразно, в зависимости от того, в каком окружении вы работаете и кто ваши предполагаемые противники. Выполнение простого "сохранить при первом подключении" (через сканирование или просто во время первого "реального" соединения), как описано в нескольких других ответах выше, может быть значительно проще и все равно обеспечить некоторое количество безопасности. Однако, если вы это сделаете, я настоятельно рекомендую вам изменить файл известных хостов пользователя (-o UserKnownHostsFile=...) на файл, специфичный для этой конкретной тестовой установки; это предотвратит загрязнение вашего личного файла известных хостов тестовой информацией и упростит очистку теперь бесполезных публичных ключей, когда вы удалите ваши ВМ.

Вот как вы можете интегрировать ssh-keyscan в ваше действие:

---
# ansible playbook that adds ssh fingerprints to known_hosts
- hosts: all
  connection: local
  gather_facts: no
  tasks:
  - command: /usr/bin/ssh-keyscan -T 10 {{ ansible_host }}
    register: keyscan
  - lineinfile: name=~/.ssh/known_hosts create=yes line={{ item }}
    with_items: '{{ keyscan.results | map(attribute="stdout_lines") | list }}'

Я делаю однострочный скрипт, немного длинный, но полезный для выполнения этой задачи для хостов с несколькими IP-адресами, используя dig и bash:

(host=github.com; ssh-keyscan -H $host; for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan -H $host,$ip; ssh-keyscan -H $ip; done) 2>/dev/null >> .ssh/known_hosts

Это будет полное решение, принимающее ключ хоста в первый раз только:

#!/usr/bin/env ansible-playbook
---
- name: accept ssh fingerprint automatically for the first time
  hosts: all
  connection: local
  gather_facts: False

  tasks:
    - name: "check if known_hosts contains server's fingerprint"
      command: ssh-keygen -F {{ inventory_hostname }}
      register: keygen
      failed_when: keygen.stderr != ''
      changed_when: False

    - name: fetch remote ssh key
      command: ssh-keyscan -T5 {{ inventory_hostname }}
      register: keyscan
      failed_when: keyscan.rc != 0 or keyscan.stdout == ''
      changed_when: False
      when: keygen.rc == 1

    - name: add ssh-key to local known_hosts
      lineinfile:
        name: ~/.ssh/known_hosts
        create: yes
        line: "{{ item }}"
      when: keygen.rc == 1
      with_items: '{{ keyscan.stdout_lines|default([]) }}'

Как вы создаете эти машины? Можете ли вы запустить сценарий обновления dns? Можете ли вы присоединить IPA-домен?

FreeIPA делает это автоматически, но в основном вам нужны записи DNS SSHFP и DNSSEC в вашей зоне (freeipa предоставляет как настраиваемые параметры (dnssec отключен по умолчанию)).

Вы можете получить существующие записи SSHFP от вашего хоста, выполнив:

ssh-keygen -r jersey.jacobdevans.com
jersey.jacobdevans.com IN SSHFP 1 1 4d8589de6b1a48e148d8fc9fbb967f1b29f53ebc jersey.jacobdevans.com IN SSHFP 1 2 6503272a11ba6d7fec2518c02dfed88f3d455ac7786ee5dbd72df63307209d55
jersey.jacobdevans.com IN SSHFP 3 1 5a7a1e8ab8f25b86b63c377b303659289b895736 > jersey.jacobdevans.com IN SSHFP 3 2 1f50f790117dfedd329dbcf622a7d47551e12ff5913902c66a7da28e47de4f4b

Затем, когда вы это опубликуете, вы должны добавить VerifyHostKeyDNS yes в ваш ssh_config или ~/.ssh/config.

Если/Когда Google решит включить DNSSEC, вы можете подключиться по ssh без запроса ключа хоста.

ssh jersey.jacobdevans.com

НО мой домен пока не подписан, так что на данный момент вы увидите…

debug1: Server host key: ecdsa-sha2-nistp256 SHA256:H1D3kBF9/t0ynbz2IqfUdVHhL/WROQLGan2ijkfeT0s 

debug1: found 4 insecure fingerprints in DNS

debug1: matching host key fingerprint

found in DNS Аутентичность хоста 'jersey.jacobdevans.com (2605:6400:10:434::10)' не может быть установлена. Отпечаток ключа ECDSA - SHA256:H1D3kBF9/t0ynbz2IqfUdVHhL/WROQLGan2ijkfeT0s. Совпадающий отпечаток ключа хоста найден в DNS. Вы уверены, что хотите продолжить подключение (да/нет)? нет

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

ssh -o "StrictHostKeyChecking no" -o PasswordAuthentication=no 10.x.x.x

Это добавляет ключ в known_hosts и не запрашивает пароль.

Следующее предотвращает дублирование записей в ~/.ssh/known_hosts:

if ! grep "$(ssh-keyscan github.com 2>/dev/null)" ~/.ssh/known_hosts > /dev/null; then
    ssh-keyscan github.com >> ~/.ssh/known_hosts
fi

Вся эта

  • ssh-key-scan
  • ssh-copy-id
  • предупреждение ключа ECSDA

ситуация продолжала меня беспокоить, поэтому я выбрал

Один скрипт, чтобы править всех

Это вариант скрипта по https://askubuntu.com/a/949731/129227 с ответом Амаду Баха https://serverfault.com/a/858957/162693 в цикле.

пример вызова

./sshcheck somedomain site1 site2 site3

Скрипт будет проходить по именам сайтов и изменять файл .ssh/config и файл .ssh/known_hosts и выполнять ssh-copy-id по запросу – для последней функции просто дайте командам тестов ssh завершиться неудачно, например, нажав Enter 3 раза по запросу пароля.

скрипт sshcheck

#!/bin/bash
# WF 2017-08-25
# check ssh access to bitplan servers

#ansi colors
#http://www.csc.uvic.ca/~sae/seng265/fall04/tips/s265s047-tips/bash-using-colors.html
blue="\033[0;34m"  
red='\033[0;31m'  
green='\033[0;32m' # '\e[1;32m' слишком яркий для белого фона.
endColor="\033[0m"

#
# colored message 
#   params:
#     1: l_color - цвет сообщения
#     2: l_msg - сообщение для отображения
#
color_msg() {
  local l_color="$1"
  local l_msg="$2"
  echo -e "${l_color}$l_msg${endColor}"
}

#
# ошибка
#
#   показать сообщение об ошибке и выйти
#
#   params:
#     1: l_msg - сообщение для отображения
error() {
  local l_msg="$1"
  # использовать ansi red для ошибки
  color_msg $red "Ошибка: $l_msg" 1>&2
  exit 1
}

#
# показать использование
#
usage() {
  echo "использование: $0 домен сайты"
  exit 1 
}

#
# проверить запись known_hosts для сервера
#
checkknown() {
  local l_server="$1"
  #echo $l_server
  local l_sid="$(ssh-keyscan $l_server 2>/dev/null)" 
  #echo $l_sid
  if (! grep "$l_sid" $sknown) > /dev/null 
  then
    color_msg $blue "добавление $l_server в $sknown"
    ssh-keyscan $l_server >> $sknown 2>&1
  fi
}

#
# проверить данный сервер
#
checkserver() {
  local l_server="$1"
  grep $l_server $sconfig > /dev/null
  if [ $? -eq 1 ]
  then
    color_msg $blue "добавление $l_server в $sconfig"
    today=$(date "+%Y-%m-%d")
    echo "# добавлено $today с помощью $0"  >> $sconfig
    echo "Хост $l_server" >> $sconfig
    echo "   StrictHostKeyChecking no" >> $sconfig
    echo "   userKnownHostsFile=/dev/null" >> $sconfig
    echo "" >> $sconfig
    checkknown $l_server
  else
    color_msg $green "$l_server найден в $sconfig"
  fi
  ssh -q $l_server id > /dev/null
  if [ $? -eq 0 ]
  then
    color_msg $green "$l_server доступен через ssh"
  else
    color_msg $red "ssh к $l_server не удался" 
    color_msg $blue "должен ли я передать учетные данные ssh-copy-id на $l_server?"
    read answer
    case $answer in
      y|yes) ssh-copy-id $l_server
    esac
  fi
}

#
# проверить все серверы
#
checkservers() {
me=$(hostname -f)
for server in $(echo $* | sort)
do
  os=`uname`
  case $os in
   # Mac OS X
   Darwin*)
     pingoption=" -t1";;
    *) ;;
  esac

  pingresult=$(ping $pingoption -i0.2 -c1 $server)
  echo $pingresult | grep 100 > /dev/null
  if [ $? -eq 1 ]
  then 
    checkserver $server
    checkserver $server.$domain
  else
    color_msg $red "ping к $server не удался"
  fi
done
}

#
# проверить конфигурацию
#
checkconfig() {
#https://askubuntu.com/questions/87449/how-to-disable-strict-host-key-checking-in-ssh
  if [ -f $sconfig ]
  then
    color_msg $green "$sconfig существует"
    ls -l $sconfig
  fi
}

sconfig=~/.ssh/config
sknown=~/.ssh/known_hosts

case  $# in
  0) usage ;;
  1) usage ;;
  *) 
    domain=$1 
    shift 
    color_msg $blue "проверка конфигурации ssh для домена $domain сайты $*"
    checkconfig
    checkservers $* 
    #for server in $(echo $* | sort)
    ##do
    #  checkknown $server 
    #done
    ;;
esac

Если вы хотите проверить ключ перед тем, как добавлять его вслепую, вы можете использовать этот код:

# проверить ключи github и gitlab
# GitHub
github=SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8
ssh-keyscan github.com >> githubKey
read bit githubkey host <<< $(ssh-keygen -lf githubKey)
if [ "$githubkey" != "$github" ]
then
  echo "Отпечаток GitHub неверный"
  exit 1
fi
echo "github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" | sudo tee -a /etc/ssh/ssh_known_hosts

# GitLab
gitlab=SHA256:ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ
ssh-keyscan gitlab.com >> gitlabKey
read bit gitlabkey host <<< $(ssh-keygen -lf gitlabKey)
if [ "$githubkey" != "$github" ]
then
  echo "Отпечаток GitLab неверный"
  exit 1
fi
echo "gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9" | sudo tee -a /etc/ssh/ssh_known_hosts

Ключи GitHub и GitLab могут измениться, если они будут скомпрометированы. В этом случае проверьте самые последние здесь и там.

Замечание: Вам может потребоваться убедиться, что ключ не добавляется дважды. Для этого обратитесь к другим ответам.

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

brad@computer:~$ git clone [email protected]:viperks/viperks-api.git
Клонирование в 'viperks-api'...
Аутентичность хоста 'bitbucket.org (104.192.143.3)' не может быть установлена.
Отпечаток RSA ключа 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40.
Вы уверены, что хотите продолжить подключение (да/нет)?

Обратите внимание на отпечаток RSA ключа…

Итак, это дело SSH, это будет работать для git через SSH и вообще для SSH-связанных вещей…

brad@computer:~$ nmap bitbucket.org --script ssh-hostkey

Начало сканирования Nmap 7.01 ( https://nmap.org ) 2016-10-05 10:21 EDT
Отчет о сканировании Nmap для bitbucket.org (104.192.143.3)
Хост доступен (0.032s задержки).
Другие адреса для bitbucket.org (не сканировались): 104.192.143.2 104.192.143.1 2401:1d80:1010::150
Не показаны: 997 отфильтрованных портов
PORT    STATE SERVICE
22/tcp  open  ssh
| ssh-hostkey:
|   1024 35:ee:d7:b8:ef:d7:79:e2:c6:43:9e:ab:40:6f:50:74 (DSA)
|_  2048 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40 (RSA)
80/tcp  open  http
443/tcp open  https

Сканирование завершено: 1 IP адрес (1 хост доступен) за 42.42 секунды

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

Хорошо. Либо я был скомпрометирован в нескольких местах и машинах, которые я проверил, либо все идет нормально.

Этот ‘отпечаток’ – это всего лишь строка, сокращенная односторонним алгоритмом для удобства человека с риском того, что более одной строки будет разрешено в тот же отпечаток. Такое случается, их называют коллизиями.

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

brad@computer:~$ ssh-keyscan bitbucket.org
# bitbucket.org SSH-2.0-conker_1.0.257-ce87fba app-128
no hostkey alg
# bitbucket.org SSH-2.0-conker_1.0.257-ce87fba app-129
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
# bitbucket.org SSH-2.0-conker_1.0.257-ce87fba app-123
no hostkey alg

Таким образом, заранее у нас есть способ запросить идентификацию от оригинального хоста.

На этом этапе мы вручную так же уязвимы, как автоматически – строки совпадают, у нас есть базовые данные, создающие отпечаток, и мы могли бы запросить эти базовые данные (предотвращая коллизии) в будущем.

Теперь, чтобы использовать эту строку таким образом, чтобы предотвратить запрос подлинности хоста…

Файл known_hosts в данном случае не использует текстовые записи. Вы сможете узнать хэшированные записи, когда увидите их, они выглядят как хэши со случайными символами вместо xyz.com или 123.45.67.89.

brad@computer:~$ ssh-keyscan -t rsa -H bitbucket.org
# bitbucket.org SSH-2.0-conker_1.0.257-ce87fba app-128
|1|yr6p7i8doyLhDtrrnWDk7m9QVXk=|LuKNg9gypeDhfRo/AvLTAlxnyQw= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==

Первая строка комментария раздражающе показывается – но вы можете избавиться от нее с помощью простого перенаправления через стандартную конвенцию ">" или ">>".

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

Спасибо, что оставались со мной, вот ваше решение. Я добавляю ключ RSA bitbucket для того, чтобы взаимодействовать с моими git-репозиториями там ненавязчиво как часть рабочего процесса CI, но, что бы вы ни делали, делайте, что хотите.

#!/bin/bash
cp ~/.ssh/known_hosts ~/.ssh/known_hosts.old && echo "|1|yr6p7i8doyLhDtrrnWDk7m9QVXk=|LuKNg9gypeDhfRo/AvLTAlxnyQw= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==" >> ~/.ssh/known_hosts

Итак, вот как вы остаетесь невинным сегодня. Вы можете сделать то же самое с github, следуя аналогичным указаниям в свое время.

Я видел так много постов на stack overflow, которые говорят вам добавить ключ программным способом вслепую без какой-либо проверки. Чем больше вы проверяете ключ из разных машин в разных сетях, тем больше доверия вы можете иметь к тому, что хост – именно тот, за который он себя выдает – и это лучшее, на что вы можете надеяться с этого слоя безопасности.

НЕПРАВИЛЬНО
ssh -oStrictHostKeyChecking=no hostname [command]

НЕПРАВИЛЬНО
ssh-keyscan -t rsa -H hostname >> ~/.ssh/known_hosts

Пожалуйста, не делайте ничего из вышесказанного. Вам предоставлена возможность повысить свои шансы избежать прослушивания ваших данных через атаку "человек посередине" – воспользуйтесь этой возможностью. Разница заключается в том, чтобы буквально подтвердить, что RSA ключ, который у вас есть, является ключом добросовестного сервера, и теперь вы знаете, как получить эту информацию для их сравнения, чтобы вы могли доверять соединению. Просто помните, что больше сравнений с различных компьютеров и сетей обычно увеличивают вашу способность доверять соединению.

Вот как сделать коллекцию хостов

определите коллекцию хостов

ssh_hosts:
  - server1.domain.com
  - server2.domain.com
  - server3.domain.com
  - server4.domain.com
  - server5.domain.com
  - server6.domain.com
  - server7.domain.com
  - server8.domain.com
  - server9.domain.com

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

- command: "ssh-keyscan {{item}}"
  register: known_host_keys
  with_items: "{{ssh_hosts}}"
  tags:
    - "ssh"

- name: Добавить ssh ключи в известные хосты
  known_hosts:
    name: "{{item.item}}"
    key: "{{item.stdout}}"
    path: ~/.ssh/known_hosts
  with_items: "{{known_host_keys.results}}"

Если у вас уже есть .pub, то кто-то, рассказывающий вам о ssh-keyscan, предлагает вам риск атаки MitM.

Я настраиваю тестовую среду, которая из центрального клиента будет запускать ряд экземпляров виртуальных машин и затем выполнять команды на них через ssh.

Этот ответ из StackOverflow содержит лучший, более правильный ответ. Вы должны получить файл .pub от надежного источника – ваш системный загрузчик должен сообщить домой и предоставить файл управление системой.

#!/usr/bin/env bash

: "${pubkey=-"$2"}"
: "${host=-"$1"}"

TMP_KNOWN_HOSTS=$(mktemp)
echo "${host}" "$(cat "${pubkey}")" > "${TMP_KNOWN_HOSTS}"

ssh-keygen -H -f "${TMP_KNOWN_HOSTS}"
ssh-keygen -F "${host}" -f "${TMP_KNOWN_HOSTS}" | tee ~/.ssh/known_hosts

shred "${TMP_KNOWN_HOSTS}.old"
rm -f "${TMP_KNOWN_HOSTS}" "${TMP_KNOWN_HOSTS}.old"

Вы можете попробовать это с вашими локальными ключами хоста:

$ for pubkey in /etc/ssh/ssh_host_*.pub; do ./add_pubkey localhost "${pubkey}"; done

Чтобы автоматически принимать и запоминать отпечаток на ранее невидимом удаленном git-сервере, например, при выполнении git clone [email protected], вы можете использовать команду GIT_SSH_COMMAND вместе с ssh по -o StrictHostKeyChecking=accept-new:

GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git clone [email protected]

Современные версии SSH поддерживают сертификаты авторитета, похожие на те, которые используются для SSL/TLS, которые защищают ваше повседневное веб-серфинг. Это позволяет вам обойти механизм доверия при первом использовании, поскольку доверие уже установлено сертификатом CA. Это работает как для пользователей (авторизованные ключи), так и для хостов (известные хосты).

Обратите внимание, что это усложнит процесс, которым вы будете поднимать ВМ, так как вам понадобятся все системы, настроенные на доверие CA, и вам нужно будет, чтобы ваш CA подписал ключи хоста для ВМ, прежде чем вы сможете использовать его, чтобы пропустить TOFU.

Вот несколько ссылок на документацию об этом:

https://www.lorier.net/docs/ssh-ca.html

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/sec-creating_ssh_ca_certificate_signing-keys

https://jameshfisher.com/2018/03/16/how-to-create-an-ssh-certificate-authority/

https://smallstep.com/blog/use-ssh-certificates/

Поскольку это "ВМ", вы должны иметь возможность получить доступ к ним другими способами (например, смонтировав файловую систему) и получить ключи.

Как только вы получите публичные ключи, вы можете собрать свой "known_hosts" (конкатенировать публичные ключи).

Или другим путем: разместить известный закрытый ключ на каждом уровне, получив доступ к файловой системе ВМ.

Это решение чрезмерно для простых случаев, когда вы контролируете свое окружение, но оно не имеет смысла для атаки "человек посередине"…

Это должен быть редкий случай, но мне недавно нужно было подключить 2 контейнера docker с помощью SSH. Сначала я заставил это работать с использованием ssh-keyscan:

#!/usr/sbin/sshd
wait4ports tcp://"$1":22
if ! ssh-keygen -F "$1"; then
    ssh-keyscan "$1" > ~/.ssh/known_hosts
fi

Затем, без ssh-keyscan (предварительно сгенерировав ключи и инжектируя их с хоста):

awk -v "host=$1" '{print host, $1, $2}' \
    /etc/ssh/ssh_host_ecdsa_key.pub \
    /etc/ssh/ssh_host_ed25519_key.pub \
    /etc/ssh/ssh_host_rsa_key.pub \
    > ~/.ssh/known_hosts
/usr/sbin/sshd

Ключи хоста могут быть сгенерированы таким образом (хотя будет, возможно, проще сделать это):

docker run --rm alpine:3.15 sh -euc '
    (apk add openssh
    ssh-keygen -A
    cd /etc/ssh
    tar czf keys.tar.gz ssh_host*) >/dev/null
    cat ~/.ssh/keys.tar.gz
' > keys.tar.gz
tar xf keys.tar.gz
rm keys.tar.gz

Для всех, кто жалуется на проблемы с MITM:

Во-первых, если вы боитесь атак MITM, пересмотрите свою инфраструктуру и сетевое проектирование.

Но если у вас нет выбора, это может быть жизнеспособное решение:

На хосте, на котором работает SSHD, выполните:

ssh-keygen -l -v -E sha256 -f /etc/ssh/ssh_host_ecdsa_key.pub

На вашем клиентском хосте выполните:

ssh -o visualhostkey=yes -o FingerprintHash=sha256 [email protected]

Теперь сравните изображения, они похожи? Поздравляю, введите "да" на вашем клиентском хосте, чтобы автоматически добавить в known_hosts.

Отличаются ли они? Возможно, это атака MITM, или вы просто могли быть небрежными. Это нормальное решение, если у вас есть доступ только по паролю. Надеюсь, у никого нет вашего пароля.

Но давайте углубимся и проанализируем настоящую проблему здесь:

Проблема на самом деле физическая, а не техническая. Спросите себя, что такое первый уровень сети? Подумайте об этом.

Некоторые жизнеспособные решения для этого:

Если вы находитесь в том же физическом месте. Купите новый маршрутизатор, коммутатор и создайте локальную сеть, не подключенную к интернету или какой-либо другой неизвестной сети. Здесь вы можете использовать StrictHostKeyChecking=no, как вам угодно, никаких проблем с MITM. То же самое касается виртуальных машин, которые вы запускаете непосредственно на рабочей станции в изолированной сети. Вам не нужно беспокоиться об атаках MITM, если вы контролируете физический уровень сети.

Рассмотрите возможность покупки переключателя KVM, используйте HPE iLO, IPMI или даже Pi-KVM, если ваш сервер поддерживает это. Но даже в этом случае эти сервисы также должны находиться в изолированной сети. Вы также можете использовать Intel-ME на ноутбуках или рабочих станциях, но это совершенно другая тема.

Скопируйте отпечаток ключа вашего сервера на USB-накопитель, вернитесь к клиенту и вручную добавьте отпечаток в известные хосты с USB-накопителя. Это сложно, но вы сможете спать по ночам. Вы даже можете немного автоматизировать это с помощью скрипта. Если вы хотите быть суперумным и впечатлить надменного подчиненного сверху, автоматизируйте это с помощью "Резиновой утки" или чего-то подобного. Но не будьте хитрым. Чья-то "умность" привела нас сюда в первую очередь.

Рассмотрите возможность добавления ключей во время установки ваших клиентов. Например, в Debian вы можете использовать Preseed и настраиваемый ISO. Здесь есть множество решений.

Если у вас нет доступа к местоположению серверов, позвоните другу, которому вы доверяете, и спросите его, чтобы он подтвердил отпечаток перед тем, как вы добавите его в known_hosts. Но часто, если у вас нет физического доступа, вы, вероятно, работаете в компании, которая уже имеет безопасные решения для этой ситуации. Если нет, подумайте о смене работы или будьте умными и предложите своему начальнику лучшее решение в обмен на повышение. (не забудьте упомянуть слово "Ransomware" как минимум 5 раз в ходе этого разговора).

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

if ! ssh-keygen -F HOST; then
    ssh-keyscan HOST >> ~/.ssh/known_hosts
fi

или в случае с настраиваемым портом:

if ! ssh-keygen -F [HOST]:PORT; then
    ssh-keyscan -p PORT HOST >> ~/.ssh/known_hosts
fi

Но это уязвимо для MITM.

Я не проверял все ответы, но после прочтения некоторых из них я предлагаю скопировать запись с другого хоста, который уже знает целевой сервер, и добавить ее в .ssh/known_hosts.

echo '[KNOWN_HOST_ENTRY]' >> .ssh/known_hosts

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

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

Если вы столкнетесь с аналогичной проблемой, то посмотрите, сможете ли вы записать файл known_hosts в временное расположение. Обычно это разрешено даже в файловой системе только для чтения.

Позже в команде ssh вы можете указать ssh читать файл known_hosts из временного расположения.

ssh -o UserKnownHostsFile=/tmp/known_hosts -o StrictHostKeyChecking=no user_name@destination_server_ip

Используйте эту команду, чтобы добавить хост в ~/.ssh/known-hosts (и не добавляйте дублирующиеся) согласно указаниям здесь

например, добавляя gitlab.com:

ssh-keygen -F gitlab.com || ssh-keyscan gitlab.com >> ~/.ssh/known_hosts

Вот мой крайний случай:

Я создаю сценарий fabric 2.5 для развертывания веб-сайта на новом сайте. В какой-то момент он создаст ssh-ключ и добавит открытый ключ в gitlab, используя его API. Затем он клонирует репозиторий (содержащий исходный код веб-сайта). Команда клонирования не удалась в скрипте, и когда я зашел на сервер и вручную запустил команду, я получил запрос Аутентичность хоста 'host.com ()' не может быть установлена. Вы уверены, что хотите продолжить подключение (да/нет)?.

Сначала моим решением было найти, как автоматически это принять, но по соображениям безопасности я добавил аргумент pty=True в функцию c.run("command"), и я получил доступ к ошибке во время выполнения моего скрипта.

Ответ или решение

Как автоматизировать добавление новых хостов в known_hosts

Введение

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


Проблема и её последствия

Каждый раз, когда выполняется команда ssh к новому хосту, система запрашивает подтверждение подлинности хоста, что может создать затруднения при массовом развертывании или тестировании. Это связано с тем, что SSH использует механизм Trust on First Use (TOFU), который требует подтверждения хоста при первом подключении. Игнорирование этого вызова может привести к уязвимостям, например, к атаке "человек посередине" (Man-in-the-Middle, MITM). Поэтому важно находить эффективные и безопасные методы автоматизации.


Методы автоматизации улучшенного использования ssh

  1. Использование ssh-keyscan

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

    Пример команды для добавления:

    ssh-keyscan -H hostname >> ~/.ssh/known_hosts

    Ключ -H используется для хеширования имен хостов, что увеличивает безопасность, предотвращая вывод реальных хостов из файла.

  2. Проверка существующих записей

    Перед добавлением ключа лучше проверить, существует ли уже запись о хосте. Это позволит избежать дублирования:

    if ! ssh-keygen -F hostname; then
       ssh-keyscan -H hostname >> ~/.ssh/known_hosts
    fi
  3. Использование параметров SSH для автоматизации

    Для временного решения можно использовать параметр StrictHostKeyChecking=no, который отключает запросы на подтверждение:

    ssh -o StrictHostKeyChecking=no user@hostname

    Однако, это небезопасный метод, так как он не позволяет проверить подлинность хоста.

    Безопасная альтернатива: Используйте StrictHostKeyChecking=accept-new, чтобы автоматически принимать новые ключи, но отклонять любые измененные или неподходящие ключи.

    ssh -o StrictHostKeyChecking=accept-new user@hostname
  4. Использование SSHFP записей и DNS

    Если у вас есть доступ к DNS вашей сети, вы можете рассмотреть внедрение SSHFP (SSH Fingerprint) записей. Это обеспечит передачу информации о ключах хоста через DNS, что значительно упростит процесс подключения, особенно в больших сетях.

    Чтобы включить проверку ключей через DNS, добавьте в конфигурацию SSH следующую строку:

    VerifyHostKeyDNS yes
  5. Интеграция с инструментами автоматизации

    При использовании инструментов автоматизации, таких как Ansible, можно централизованно управлять ключами SSH для множества хостов. Например, Ansible позволяет автоматически добавлять ключи в known_hosts при каждом развертывании.

    Пример задачи в Ansible:

    - name: Ensure SSH keys are in known_hosts
     known_hosts:
       name: "{{ item }}"
       key: "{{ lookup('pipe', 'ssh-keyscan ' + item) }}"
     with_items:
       - server1
       - server2

Заключение

Автоматизация процесса добавления новых хостов в known_hosts — это важная задача, которая требует взвешенного подхода к безопасности и удобству использования. Используя подходы, описанные выше, вы сможете минимизировать интерактивные запросы и улучшить эффективность работы с SSH в облачных и виртуализированных среды. Однако всегда помните о рисках, связанных с автоматическим принятием хостов, и старайтесь проверять ключи и их подлинность, когда это возможно.

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

Капча загружается...