eFusion 08 апреля 2020

Как правильно общаться с удаленным сервером через SSH

О правилах безопасного SSH-подключения и способах его улучшения с примерами для систем на основе RHEL/CentOS и Ubuntu.

SSH далек от совершенства, но он был разработан с учетом безопасности, и на протяжении многих лет было написано огромное количество инструментов для облегчения его использования. Кроме того, большинство популярного софта комплектуются SSH. Он поддерживается практически во всех архитектурах и дистрибутивах, начиная от Raspberry Pi и заканчивая массивными суперкомпьютерными кластерами.

Сертификаты

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

Следующий уровень секьюрности после ключей – SSH-сертификаты, которые поддерживаются с OpenSSH 5.4, выпущенного ещё в 2010 году.

С помощью SSH-сертификатов вы создаете центр сертификации (CA), а затем используете его для выдачи и криптографической подписи, которая аутентифицирует пользователей. Вы можете создать пару ключей, например, с помощью команды ssh-keygen:

        $ ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in host_ca.
Your public key has been saved in host_ca.pub.
The key fingerprint is:
SHA256:tltbnMalWg+skhm+VlGLd2xHiVPozyuOPl34WypdEO0 host_ca
The key's randomart image is:
+---[RSA 4096]----+
|              +o.|
|            .+..o|
|           o.o.+ |
|          o o.= E|
|        S  o o=o |
|       ....+ = +.|
|       ..=. %.o.o|
|        *o Oo=.+.|
|       .oo=ooo+..|
+----[SHA256]-----+

$ ls -l
total 8
-rw-------. 1 gus gus 3381 Mar 19 14:30 host_ca
-rw-r--r--. 1 gus gus  737 Mar 19 14:30 host_ca.pub
    

Файл host_ca является закрытым ключом центра сертификации хоста и должен храниться в защищенном месте. Не выдавайте его никому, не копируйте куда-либо и убедитесь, что как можно меньше людей имеют к нему доступ. В идеале он «живет» на машине, которая не допускает прямого доступа, и все сертификаты выдаются автоматически.

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

Вот так создается user_ca:

        $ ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca
    

Файл user_ca – закрытый ключ пользователя, который должен быть защищен, как и закрытый ключ хоста.

Выдача сертификатов хостам

Создадим новый ключ и подпишем с помощью центра сертификации.

ssh_host_rsa_key-cert.pub содержит подписанный сертификат хоста.
        $ ssh-keygen -f ssh_host_rsa_key -N '' -b 4096 -t rsa

$ ls -l
-rw------- 1 palpatine palpatine 3247 Mar 17 14:49 ssh_host_rsa_key
-rw-r--r-- 1 palpatine palpatine  764 Mar 17 14:49 ssh_host_rsa_key.pub

$ ssh-keygen -s host_ca -I host.example.com -h -n host.example.com -V +52w ssh_host_rsa_key.pub
Enter passphrase: # the passphrase used for the host CA
Signed host key ssh_host_rsa_key-cert.pub: id "host.example.com" serial 0 for host.example.com valid from 2020-03-16T15:00:00 to 2021-03-15T15:01:37

$ ls -l
-rw------- 1 palpatine palpatine 3247 Mar 17 14:49 ssh_host_rsa_key
-rw-r--r-- 1 palpatine palpatine 2369 Mar 17 14:50 ssh_host_rsa_key-cert.pub
-rw-r--r-- 1 palpatine palpatine  764 Mar 17 14:49 ssh_host_rsa_key.pub
    

Вот описание используемых флагов:

  • -s host_ca: указывает имя файла закрытого CA-ключа, используемого для подписи.
  • -I host.example.com: буквенно-цифровая строка, идентифицирующая сервер. При необходимости значение можно использовать для отзыва сертификата.
  • -h: указывает, что сертификат выдается хосту, а не юзеру.
  • -n host.example.com: указывает разделенный запятыми список участников, для проверки подлинности которых будет действителен сертификат.
  • -V +52w: срок действия сертификата. В данном случае 52 недели (один год). По умолчанию сертификаты действительны вечно. Указание периода истечения срока действия сертификатов хоста настоятельно рекомендуется для ротации и замены сертификатов.

Настройка SSH для использования сертификатов

Нужно указать серверу, чтобы он использовал новый сертификат. Скопируйте три сгенерированных файла на сервер в каталог /etc/ssh, установите разрешения и добавьте в файл /etc/ssh/sshd_config следующую строку:

        HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
    

Перезагрузите sshd с помощью systemctl restart sshd. Теперь сервер настроен на выдачу сертификата. Чтобы локальный ssh-клиент мог автоматически доверять хосту на основе удостоверения сертификата, нужно добавить открытый ключ CA в known_hosts. Вы можете сделать это, добавив в начало файла host_ca.pub следующее: @cert-authority *.example.com, а затем и в ~/.ssh/known_hosts:

        @cert-authority *.example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDwiOso0Q4W+KKQ4OrZZ1o1X7g3yWcmAJtySILZSwo1GXBKgurV4jmmBN5RsHetl98QiJq64e8oKX1vGR251afalWu0w/iW9jL0isZrPrmDg/p6Cb6yKnreFEaDFocDhoiIcbUiImIWcp9PJXFOK1Lu8afdeKWJA2f6cC4lnAEq4sA/Phg4xfKMQZUFG5sQ/Gj1StjIXi2RYCQBHFDzzNm0Q5uB4hUsAYNqbnaiTI/pRtuknsgl97xK9P+rQiNfBfPQhsGeyJzT6Tup/KKlxarjkMOlFX2MUMaAj/cDrBSzvSrfOwzkqyzYGHzQhST/lWQZr4OddRszGPO4W5bRQzddUG8iC7M6U4llUxrb/H5QOkVyvnx4Dw76MA97tiZItSGzRPblU4S6HMmCVpZTwva4LLmMEEIk1lW5HcbB6AWAc0dFE0KBuusgJp9MlFkt7mZkSqnim8wdQApal+E3p13d0QZSH3b6eB3cbBcbpNmYqnmBFrNSKkEpQ8OwBnFvjjdYB7AXqQqrcqHUqfwkX8B27chDn2dwyWb3AdPMg1+j3wtVrwVqO9caeeQ1310CNHIFhIRTqnp2ECFGCCy+EDSFNZM+JStQoNO5rMOvZmecbp35XH/UJ5IHOkh9wE5TBYIeFRUYoc2jHNAuP2FM4LbEagGtP8L5gSCTXNRM1EX2gQ== host_ca
    

Значение *.example.com указывает на то, что этому сертификату следует доверять для идентификации любого хоста, к которому вы подключаетесь и который имеет домен *.example.com. Это разделенный запятыми список имен хостов для сертификата: host1, host2, host3 или 1.2.3.4, 1.2.3.5 в зависимости от обстоятельств.

Как только закончите настройку, удалите все старые записи для host.example.com в ~/.ssh/known_hosts и запустите подключение. Правильность создания сертификата проверяется так:

        $ ssh -vv host.example.com 2>&1 | grep "Server host certificate"
debug1: Server host certificate: ssh-rsa-cert-v01@openssh.com SHA256:dWi6L8k3Jvf7NAtyzd9LmFuEkygWR69tZC1NaZJ3iF4, serial 0 ID "host.example.com" CA ssh-rsa SHA256:8gVhYAAW9r2BWBwh7uXsx2yHSCjY5OPo/X3erqQi6jg valid from 2020-03-17T11:49:00 to 2021-03-16T11:50:21
debug2: Server host certificate hostname: host.example.com
    

На этом этапе вы можете продолжить выдачу сертификатов всех хостов, используя центр сертификации. Польза от этого следующая: вам больше не нужно полагаться на небезопасную модель Trust On First Use (TOFU) для новых хостов, и если вы когда-нибудь повторно развернете сервер и, следовательно, измените ключ хоста, новый хост автоматически представит подписанный сертификат без ужасного предупреждения: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

Выдача сертификатов пользователям (для аутентификации пользователей на хостах)

В этом примере мы создадим новый ключ пользователя и подпишем с помощью центра сертификации.

        $ ssh-keygen -f user-key -b 4096 -t rsa

$ ls -l
-rw-r--r--. 1 tatooine tatooine 737 Mar 19 16:33 user-key.pub
-rw-------. 1 tatooine tatooine 3369 Mar 19 16:33 user-key

$ ssh-keygen -s user_ca -I gus@gravitational.com -n ec2-user,gus -V +1d user-key.pub
Enter passphrase: # the passphrase used for the user CA
Signed user key user-key-cert.pub: id "gus@gravitational.com" serial 0 for ec2-user,gus valid from 2020-03-19T16:33:00 to 2020-03-20T16:34:54

$ ls -l
-rw-------. 1 tatooine tatooine 3369 Mar 19 16:33 user-key
-rw-r--r--. 1 tatooine tatooine 2534 Mar 19 16:34 user-key-cert.pub
-rw-r--r--. 1 tatooine tatooine 737 Mar 19 16:33 user-key.pub
    

user-key-cert.pub содержит подписанный сертификат пользователя. Для входа в систему понадобится как этот, так и приватный ключ. Вот описание используемых флагов:

  • -s user_ca: указывает приватный ключ CA, используемый для подписи.
  • -I tatooine@proglib.io: буквенно-цифровая строка, которая будет видна в логе SSH. Для точной идентификации пользователя рекомендуем использовать адрес электронной почты или логин.
  • -n palpatine, tatooine: указывает разделенный запятыми список участников, для проверки подлинности которых будет действителен сертификат. В примере мы предоставляем сертификат доступа как для palpatine, так и для tatooine.
  • -V +1d: указывает срок действия сертификата, в данном случае +1d означает 1 день.

Если нужно увидеть параметры подписи сертификата, используйте ssh-keygen -L:

        $ ssh-keygen -L -f user-key-cert.pub
user-key-cert.pub:
        Type: ssh-rsa-cert-v01@openssh.com user certificate
        Public key: RSA-CERT SHA256:egWNu5cUZaqwm76zoyTtktac2jxKktj30Oi/ydrOqZ8
        Signing CA: RSA SHA256:tltbnMalWg+skhm+VlGLd2xHiVPozyuOPl34WypdEO0 (using ssh-rsa)
        Key ID: "palpatine@proglib.io"
        Serial: 0
        Valid: from 2020-03-19T16:33:00 to 2020-03-20T16:34:54
        Principals: 
                tatooine
                palpatine
        Critical Options: (none)
        Extensions: 
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc
    

Настройка SSH для проверки подлинности

После того как сертификат подписан, нужно сообщить серверу, что можно доверять пользователским сертификатам. Копируем user_ca.pub в директорию /etc/ssh, поправляем права и добавляем в конфиг /etc/ssh/sshd_config строку:

        TrustedUserCAKeys /etc/ssh/user_ca.pub
    

Как раньше, перезагружаем sshd с помощью systemctl restart sshd. Теперь сервер настроен на пропуск всех, кто представляет сертификат, выданный центром сертификации при подключении. Если сертификат хранится в том же каталоге, что и ваш приватый ключ (указанный с флагом -i, например ssh -i /home/tatooine/user-key palpatine @proglib.io), он будет автоматически использоваться при подключении к серверам.

Проcмотр логов

Если вы посмотрите в журнал sshd сервера, запустив journalctl -u sshd, то увидите имя используемого для аутентификации сертификата, а также данные центра сертификации:

        sshd[14543]: Accepted publickey for tatooine from 1.2.3.4 port 53734 ssh2: RSA-CERT ID palpatine@proglib.io (serial 0) CA RSA SHA256:tltbnMalWg+skhm+VlGLd2xHiVPozyuOPl34WypdEO0
    

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

        sshd[14612]: error: key_cert_check_authority: invalid certificate
sshd[14612]: error: Certificate invalid: name is not a listed principal
    

Если срок действия сертификата истек, вы увидите следующую ошибку:

        sshd[14240]: error: key_cert_check_authority: invalid certificate
sshd[14240]: error: Certificate invalid: expired
    

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

Взгляните на man ssh-keygen для новых идей.

Использование SSH Bastion Host

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

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

Вот пример принудительного использования SSH-доступа к любому хосту в домене example.com, который будет перенаправлен через bastion-хост, в bastion.example.com:

        Host *.example.com
    ProxyJump bastion.example.com
    IdentityFile ~/user-key

Host bastion.example.com
    ProxyJump none
    IdentityFile ~/user-key
    

Вместе с bastion для своих подключений вы можете использовать iptables (или другой *nix-вый файервол на ваш выбор) на серверах за bastion-ом, для блокировки входящих SSH-соединений. Например, вот так:

        $ iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
$ iptables -A INPUT -p tcp --dport 22 -s <public IP of the bastion> -j ACCEPT
$ iptables -A INPUT -p tcp --dport 22 -j DROP
    

Хорошая идея оставить второй SSH-сеанс активным – если что-то пойдет не так, вы должны иметь возможность исправить ситуацию через установленное соединение.

Двухфакторная аутентификация

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

Давайте установим подключаемый модуль аутентификации google-authenticator, который потребует от пользователей на своем телефоне ввести код из приложения Google Authenticator. Вы можете скачать приложение для iOS и Android.

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

Установка google-authenticator

В системах на базе RHEL/CentOS можно установить модуль google-authenticator из репозитория EPEL:

        $ sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm # for RHEL/CentOS 7, change for other versions
$ sudo yum install google-authenticator
    

Для систем на базе Debian / Ubuntu решение доступно в виде пакета libpam-google-authenticator:

        $ sudo apt-get install libpam-google-authenticator
    

Модуль имеет множество опций. В интересах экономии времени мы используем некоторые значения по умолчанию: запретить использование одного и того же токена дважды, выдавать временные коды и ограничивать пользователя максимум тремя входами в систему каждые 30 секунд. Настроим это все одной командой:

        $ google-authenticator -d -f -t -r 3 -R 30 -W
    

Вы также можете запустить google-authenticator без флагов и настроить его в интерактивном режиме.

Выполнение предоставит QR-код, который пользователь может сканировать с помощью приложения.

Если вам потребуется изменить какие-либо настройки в будущем, или полностью удалить функциональность, конфигурация будет сохранена в разделе ~/.google_authenticator.

Настройка PAM для двухфакторной аутентификации

Чтобы заставить систему использовать коды OTP (one-time password), нужно отредактировать конфиг PAM sshd. Добавьте следующую строку в конец файла /etc/pam.d/sshd:

        auth required pam_google_authenticator.so nullok
    

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

В завершение, измените методы аутентификации по умолчанию, чтобы SSH не запрашивал у пользователей пароль, если они не представляют двухфакторный токен. Эти изменения также вносятся в файл /etc/pam.d/sshd:

  • В RHEL/CentOS закомментируйте auth substack password-auth, добавив # в начало строки: #auth substack password-auth.
  • В Debian/Ubuntu, закомментируйте @include common-auth: #@include common-auth.
  • Сохранитесь.

Настройка SSH для двухфакторной аутентификации

Для того чтобы сервер требовал использования такой аутентификации, внесем несколько изменений в /etc/ssh/sshd_config:

  • изменим ChallengeResponseAuthentication no на ChallengeResponseAuthentication yes, чтобы разрешить использование pam.
  • установим список допустимых методов аутентификации, добавив следующую строку в конец файла (или отредактировав строку, если она уже существует): AuthenticationMethods publickey,keyboard-interactive

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

Тестируем

Подключаемся к bastion-у и видим приглашение:

        $ ssh bastion.example.com
Verification code:
    

Вводим код, представленный приложением Google Authenticator. Если вы проверите журнал sshd и если все настроено верно, перед вами будет следующее:

        Mar 23 16:51:13 ip-172-31-33-142.ec2.internal sshd[29340]: Accepted keyboard-interactive/pam for gus from 1.2.3.4 port 42622 ssh2
    

Заключение

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

Источники

РУБРИКИ В СТАТЬЕ

МЕРОПРИЯТИЯ

Комментарии 0

ВАКАНСИИ

Unity Developer
Москва, по итогам собеседования
Tableau developer
по итогам собеседования
Unity Developer
по итогам собеседования
Programmer Unity
Краснодар, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

BUG