Двухфакторная аутентификации ssh при использовании пароля или публичного ключа

Одноразовые пароли, при своей кажущейся простоте, весьма эффективны и все больше набирают популярность. И действительно, даже сложный постоянный пароль можно взломать или перехватить. Реализовать двухфакторную аутентификацию можно, например, посредством sms или приложения поддерживающего стандарты TOTP или HOTP, разработанные сообществом OATH. Рассмотрим применение последнего к авторизации по ssh в связке с Google Authenticator.

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

root@linux:~# apt-get install libpam-google-authenticator libqrencode3 ruby rubygems
root@linux:~# gem install rotp

Сгенерируем ключ для авторизации суперпользователя без пароля

root@linux:~# ssh-keygen -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
b9:2d:95:8b:b1:94:c9:61:75:71:7d:5c:31:15:fb:1a root@linux
The key's randomart image is:
+--[ RSA 4096]----+
|          . o..*B|
|         . . .  *|
|        o      ..|
|       o = .    .|
|        S o   E .|
|       . B .   o |
|        = o   .  |
|         .       |
|                 |
+-----------------+

И разрешаем аутентификацию по этому ключу

root@linux:~# cat .ssh/id_rsa.pub > .ssh/authorized_keys

К аутентификации по публичному ключу вернемся позже, сначала поговорим про парольную аутентификацию.

Приведем конфигурационный файл /etc/ssh/sshd_config к виду

PermitRootLogin without-password

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      %h/.ssh/authorized_keys

ChallengeResponseAuthentication yes

Создаем группу google_auth и добавляем в эту группу пользователей, которые будут использовать libpam-google-authenticator.

root@linux:~# groupadd google_auth
root@linux:~# usermod -a google_auth rascal

Теперь необходимо настроить PAM, для этого добавляем в файл /etc/pam.d/sshd

# Google Two-step verification
auth [default=1 success=ignore] pam_succeed_if.so quiet user ingroup google_auth
auth required pam_google_authenticator.so

Перезапускаем ssh сервер

root@linux:~# service ssh restart

Для настройки окружения пользователя необходимо выполнить:

root@linux:~# google-authenticator

В результате работы этой утилиты вы увидите QR-код, который можно отсканировать в приложение Google Authenticator на ваш смартфон:
google-authenticator

Двухфакторная аутентификации при использовании для аутентификации пароля теперь работает, при следующей попытке зайти на сервер по ssh происходит запрос «Verification code».
А вот при авторизации при помощи публичного ключа вы этого запроса не увидите. Дело в том, что openssh при успешной авторизации с использованием публичного ключа не инициализирует PAM, а значит libpam-google-authenticator не работает.
Тем не менее, есть обходное решение в виде ruby-скрипта от moocode, сохраняем его как /usr/local/bin/two_factor_ssh

#!/usr/bin/env ruby
require 'rubygems'
require 'rotp'
 
# we'll pass in a secret to this script from the authorized_keys file
abort unless secret = ARGV[0]
 
# prompt the user for their validation code
 
STDERR.write "Enter the validation code: "
until validation_code = STDIN.gets.strip
  sleep 1
end
 
# check the validation code is correct
 
abort "Invalid" unless validation_code == ROTP::TOTP.new(secret).now.to_s
 
# user has validated so we'll give them their shell
 
Kernel.exec ENV['SSH_ORIGINAL_COMMAND'] || ENV['SHELL']
root@linux:~# chmod +x /usr/local/bin/two_factor_ssh

Добавляем в конфигурационный файл /etc/ssh/sshd_config

Match User root
    ForceCommand /usr/local/bin/two_factor_ssh QKRBILHQ6U3PSZHT

Перезапускаем ssh сервер

root@linux:~# service ssh restart