Большая часть рабочей нагрузки в наши дни возложена на виртуальные машины, но вы не можете просто подойти к виртуальному серверу, нажать кнопку питания и войти в систему, как это было в прошлом. Вы должны войти в виртуальную машину через SSH, что означает, что вам нужно подключиться к серверу через удаленный рабочий стол, затем запустить командную строку на виртуальной машине и запустить программу, которую вы хотите запустить. Это сложная задача, особенно если вы никогда не использовали SSH и никогда не работали с командной строкой. Однако, если вы сможете настроить это, то сможете сделать это.
(С) Балабоба, искусственный интеллект Яндекса.
Для доступа к виртуальному серверу у компании 4vps имеется система подключения VNC (Virtual Network Computing), работающая через браузер. Она использует протокол Remote Frame Bufer (RFB), передавая данные в графическом режиме, даже когда взаимодействие происходит в командной строке и потому не отличается особо быстрым откликом.
В связи с этим в режиме командной строки предпочтительнее использовать для управления своим сервером SSH (Secure Shell) - протоколом прикладного уровня, разработанным для подключения к удалённому устройству и туннелирования TCP-соединений.
В Linux для работы с SSH используется набор программ OpenSSH, реализующий такие функции как аутентификация по открытому ключу и генерация этих ключей, шифрование сеансов связи, удалённое управление, безопасное копирование файлов, создание SOCKS-прокси серверов и защищённых туннелей (на 2-м и 3-м уровнях модели OSI).
Подключимся к нашему серверу по VNC и выполним действия, необходимые для установки и настройки SSH.
Для начала обновим локальную базу пакетов менеджера pacman и синхронизируем ей с репозиториями:
pacman -Sy
Установим OpenSSH:
pacman -S openssh
Отредактируем файл sshd_config:
mcedit /etc/ssh/sshd_config
Находим, раскомментируем (убираем знак "#") и приводим к соответствующему виду строки:
#Номер порта, который служба sshd будет прослушивать для входящих подключений
Port 22
#Указание, разрешена ли авторизация суперпользователя root
PermitRootLogin no
#Указание, разрешена ли аутентификация по открытому ключу
PubKeyAuthentication yes
#Путь к файлу, где будут храниться открытые ключи по которым пользователям разрешено проходить аутентификацию
AutorizedKeysFile %h/.ssh/autorized_keys
#Проверка подлинности по паролю, установка "no" позволяет пользователям проходить аутентификации только с использованием криптографии с открытым ключом.
PasswordAuthentication yes
#Разрешена ли аутентификация с использованием клавиатуры, "yes" позволяет пользователям проходить аутентификацию с использованием механизмов запроса-ответа
KbdInteractiveAuthentication no
#Поддержка подключаемого модуля аутентификации (PAM)
UsePAM yes
#Должно ли отображаться сообщение Linux Message of the Day (MOTD) перед запросом на вход в систему
PrintMotd no
#Расположение подсистемы SFTP (протокол передачи файлов по SSH) на сервере
Subsystem sftp /usr/lib/ssh/sftp-server
Сохраняемся, выходим и запускаем sshd:
systemctl start sshd
Смотрим состояние sshd сервиса:
systemctl status sshd
Если возникли какие-либо ошибки, за подробностями можно обратиться к журналу, выполнив команду: journalctl -xe. Исправить недочёты и двигаться дальше по настройке SSH.
Чтобы клиентская машина могла подключиться к серверу по SSH ей нужен ключ. Если его ещё нет, то можно сгенерировать командой:
ssh-keygen -t rsa.
В результате её выполнения должны получиться два файла - публичный и секретный ключи. Обычно они помещаются в подкаталог .ssh домашнего каталога пользователя.
Переходим в него и смотрим что там есть:
cd ~/.ssh
ls -l
Передаём публичный ключ серверу:
ssh-copy-id -i .ssh/id_rsa.pub user1@193.233.18.65
Сервер попросит ввести пароль пользователя user1, который был задан во время установки системы. Если всё прошло успешно, на сервере в домашнем каталоге user1 появится подкаталог .ssh, в который будет сохранён публичный ключ, переданный с клиентской машины. Обычно он помещается в файл authorized_keys, который может содержать несколько таких ключей от разных клиентских машин.
После этого можно подключаться к своему серверу без пароля. А с целью повышения безопасности нужно снова отредактировать файл sshd_config:
mcedit /etc/ssh/sshd_config
Устанавливаем значение строчки PasswordAuthentication no
Сохраняемся, выходим и перезапускаем SSH-сервер:
systemctl restart sshd
Добавляем sshd в автозагрузку:
systemctl enable sshd
Теперь можно подключиться к серверу без ввода логина и пароля, введя лишь команду ssh:
ssh user1@193.233.18.65
При проверке статуса sshd сервера можно увидеть статистику подключений к нему. И что я вижу. Не прошло и 5 минут со времени его запуска, как какие-то субъекты помимо меня уже пытаются к нему подключиться. Конкретно в этот раз - с адресов 178.128.125.144, 206.81.31.144 и 134.122.94.223.
В Интернете существует множество сервисов, позволяющих примерно установить по IP местоположение его обладателя:
https://2ip.ru/info/178.128.15.144/
https://2ip.ru/info/206.189.137.127/
https://2ip.ru/info/134.122.94.223/
В данном случае это IP-адреса американского провайдера облачных услуг DigitalOcean. Кто бы не были пользователи его услуг, им определённо не следует подключаться к моему серверу по SSH. Чтобы статистика их попыток не мозолила лишний раз глаза, можно соответствующим образом настроить межсетевой экрана netfilter с помощью команды iptables. Используем её, чтобы ограничить число доступных для соединения портов одним tcp 22 для подключения по SSH и одним IP адресом - моего компьютера.
Сначала для цепочки INPUT устанавливается политика DROP:
iptables -P INPUT DROP
Она означает, что всех входящие пакеты будут отбрасываться (DROP) по умолчанию, если не будет задано другое правило, явно разрешающее их прохождение.
Чтобы включить ICMP-трафик (для команды ping), добавим следующую строку:
iptables -A INPUT -p icmp -j ACCEPT
Чтобы получать пакеты, которые будут отправляться с моего сервера на IP-адреса внутри собственной сети и на локальный хост (127.0.0.1), добавим такие строки:
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -s 193.233.18.65 -j ACCEPT
Поскольку все TCP-соединения начинаются c пакета, имеющего флаг SYN (synchronize, запрос соединения), то можно позволить поступление всех TCP-пакетов, которые не являются SYN-пакетами, чтобы оставить серверу возможность общаться по протоколу TCP с внешним миром:
iptables -A INPUT -p tcp '!' --syn -j ACCEPT
Здесь '!' --syn является отрицательным условием, которое означает что правило будет применяться ко всем пакетам кроме SYN.
Для того, чтобы север имел возможность работы с удалёнными серверами DNS нужно разрешить входящий трафик от серверов имён:
iptables -A INPUT -p udp --source-port 53 -s 8.8.8.8 -j ACCEPT
iptables -A INPUT -p udp --source-port 53 -s 8.8.4.4 -j ACCEPT
И наконец, можно разрешить SSH-соединение с моего IP адреса:
iptables -A INPUT -s 89.107.139.124 -p tcp --detination-port 22 -j ACCEPT
Просмотреть каике правила фильтрации пакетов в итоге получились можно с помощью команды:
iptables -nvL.
Она покажет:
- число пакетов и объём данных, соответствующих каждому правилу;
- текущее состояние цепочек INPUT, OUTPUT и FORWARD, включая правила, применяемые к каждой цепочке;
- информацию о применяемых правилах, такие как IP-адреса источника и назначения, протокол, порты и действия, выполняемые с пакетами (ACCEPT, DROP, REJECT).
Также можно воспользоваться командой iptables vL, если нужно вместо IP-адресов выводить имена.
Сохранить набор правил межсетевого экрана можно с помощью команды iptables-save:
Данные правила действуют только до перезагрузки сервера. Поэтому их ещё нужно сохранить:
iptables-save -f /etc/iptables/ssh_ipv4.rules
Также, нужно обеспечить его применение при загрузке. За это отвечает команда iptables-restore. Можно написать bash-скрипт и добавить его в автозагрузку, но в archlinux уже есть файл, правила из которого применяются автоматически: /etc/iptables/iptables.rules. Просто скопируем содержимое ssh_ipv4.rules в этот файл (исходный файл также сохраним):
cp /etc/iptables/iptables.rules /etc/iptables/iptables.rules.bak
cp /etc/iptables/ssh_ipv4.rules /etc/iptables/iptables.rules
После этого нужно перезапустить сервис iptables и проверить его статус:
systemctl restart iptables
sytemctl status iptables
Если всё в порядке, включить автозагрузку:
systemctl enable iptables
Во время работы со своим сервером по SSH я столкнулся с тем, что он довольно быстро разрывает соединение при простое с сообщением: "client_loop: send disconnect: Broken pipe". Исправить это можно подключаясь к серверу с дополнительными параметрами:
ssh -o ServerAliveInterval=60 -ServerAliveCountMax=5 user1@193.233.18.65
Параметр "-o ServerAliveInterval=60" указывает на интервал в секундах между отправкой "keep-alive" пакетов серверу SSH. Это помогает поддерживать активное соединение, предотвращая его разрыв в случае длительного бездействия.
Параметр "-o ServerAliveCountMax=5" определяет максимальное количество "keep-alive" пакетов, которые могут оставаться без ответа от сервера SSH, прежде чем соединение будет считаться разорванным. В данном случае, если сервер не ответит на 5 последовательных "keep-alive" пакетов, соединение будет считаться разорванным.
Чтобы не набирать всякий раз такую длинную команду, можно создать её псевдоним в файле ~/.bashrc:
mcedit ~/.bashrc
Поместить туда текст:
alias myssh='ssh -o ServerAliveInterval=60 -ServerAliveCountMax=5 user1@193.233.18.65'
И перечитать этот файл, чтобы изменения вступили в силу:
source ~/.bashrc
Теперь можно подключаться к своему серверу вводя короткую команду: myssh
Завершить сеанс связи можно введя команду exit в терминале клиента, либо на сервере, узнав PID процессов, подключённых к SSHD:
ps axu | grep sshd
и завершив тот, который требуется, с помощью команды:
kill -s SIGQUIT номер_PID.
В комплект OpenSSH входит программа sshfs для монтирования какого-либо каталога сервера в каталог на клиентском компьютере. Например, создадим на клиентской стороне подкаталог myserver в домашнем каталоге:
mkdir ~/myserver
Теперь смонтируем в него домашний каталог пользователя user1 виртуального сервера:
sshfs user1@193.233.18.65:./ ~/myserver
После этого можно работать с каталогом myserver как будто он часть файловой системы клиентского компьютера, всё что туда будет помещено, окажется на сервере.
Доработаем команду myssh, чтобы автоматически при подключении к серверу монтировался и домашний каталог:
mcedit ~/.bashrc
alias myssh='sshfs -o reconnect,serverAliveInterval=60,ServerAliveCountMax=5 user1@193.233.18.65:./ ~/myserver ; ssh -o ServerAliveInterval=60 -ServerAliveCountMax=5 user1@193.233.18.65'
Сохраняемся, выходим.
Снова перечитаем этот файл, чтобы изменения вступили в силу:
source ~/.bashrc
Теперь команда myssh будет работать по новому.
SSH позволяет сразу выполнять команды без подключения к терминалу сервера:
ssh user1@193.233.18.65 ls
Этот способ позволяет выполнять и составные команды, включая конвеейр и перенаправление ввода/вывода. Например можно выполнить на удалённом сервере интерпретатор bash, которому передать с локального компьютера скрипт с помощью перенаправления ввода:
ssh user1@193.233.18.65 'bash -s'< script.sh
Передать файл можно такой командой:
cat localfile | ssh user1@193.233.18.65 "cat > remotefile"
Впрочем, удобнее для этого использовать отдельную команду из пакета OpenSSH:
scp ~/testfile.txt user1@193.233.18.65:./
Ещё одной весьма польезной возможностью OpenSSH является туннелирование (Secure Shell tunnel). Это механизм, который позволяет безопасно передавать данные между двумя узлами сети через незащищённое соединение. Он используе протокол SSH для создания зашифрованного канала связи мжду клиентом и сервером.
Но прежде чем перейти к туннелям поработаем ещё немного надо безопасностью ssh-сервиса. Запретим пользователям добавлять новые открытые ключи, по которым можно будет выполнить соединение.
Для этого файлу authoized_keys установим права только на чтение для владельца:
chmod 400 /home/user1/.ssh/authorized_keys
А чтобы осложнить пользователям возможность вернуть разрешения, установим также бит неизменяемости на этой файл, а заодно и на каталог .ssh:
chattr -R +i /home/user1/.ssh
Итак, ssh-туннелирование. Эта возможность может быть полезна при подключению к интернету по WiFi в общественных местах. Если заблаговременно запустить сервер SSH в каком-нибудь безопасном месте, например дома или на работе (а в нашем случае он уже есть - на виртуальном сервере), то достаточно будет ввести команду:
ssh -v -TND 4711 user1@193.233.18.65
Параметр -T отключает выделение псевдотерминала на удалённом сервере, N указывает серверу SSH не выполнять удалённые команды после установления соединения, D определяет локальный порт, который будет проксировать все входящие соединения через SSH-туннель на удалённый сервер 193.233.18.65.
Далее нужно настроить браузер для работы через этот тоннель. Для этого жмём кнопку с тремя горизонтальными полосками в правом верхнем углу окна браузера - "Открыть меню приложения", выбираем пункт "Настройки", прокручиваем страницу настроек вниз до тех пор поа не появится пункт "Настройки сети" и нажимаем настроить. В окне "Параметры соединения" нужно установить переключатель в "Ручная настройка прокси", в поле "узел SOCKS" указать "localhost", в поле "Порт" - "4711", версия - SOCKS 5. Дальше нажать кнопку OK и перезапустить браузер.
Заходим на 2ip.ru, чтобы проверить свой IP адрес, и видим, что он поменялся на адрес сервера, потому что теперь трафик идёт через него.
Есть и другие интересные способы использования команды ssh. Напримр, с параметром -L она позволяет установить локальный прокси-сервер для перенаправления трафика через защищённое соединение SSH. Это может пригодиться для доступа к удалённым хостам, к которым нельзя подключиться напрямую. Синтаксис команды в данном случае:
ssh -L [локальный адрес]:[локальный порт]:[удаленный адрес]:[удаленный порт] [пользователь]@[удаленный хост]
Квадратные скобки для каждой части означают, что она не обязательная и её можно пропустить.
Конкретнее, чтобы установить соединение с удалённым хостом 193.233.18.65 и перенаправить порт 8080 на клиентском компьютере через SSH, можно выполнить команду:
ssh -L 127.0.0.1:8080:193.233.18.65:80 user1@193.233.18.65
После запуска этой команды все запросы, направленные на локальный порт 8080, будут перенаправлены на удаленный хост 193.233.18.65 на порт 80. Если бы там был установлен HTTP сервер, можно было бы загружать его web-страницы по адресу: http://localhost:8080
команда ssh -L позволяет перенаправлять локальный порт на удаленный порт на любом компьютере, а не только на самом SSH-сервере. Обратите внимание, что [удалённый адрес] и [удалённый хост] могут иметь одинаковое значение, а могут и отличаться.
По умолчанию SSH-туннель позволит использовать только локальный хост шлюза в качестве удаленного адреса. Другими словами, ваш локальный порт станет доступен только изнутри самого сервера шлюза. Если же нужно использовать общедоступный адрес шлюза в качестве удаленного адреса, чтобы предоставить своим локальным службам доступ к общедоступному Интернету, то SSH-сервер должен быть настроен с параметром GatewayPorts yes.
Включение этого параметра может представлять некоторые риски для безопасности, поэтому рекомендуется использовать его только в доверенных сетевых средах. В то же время он позволяет довольно удобную вещь - получать доступ к своим сервисам на домашнем компьютере, из любого места, через Интернет.
Для запуска такого "обратного" сценария (по сравнению с предыдущим примером) команду ssh надо запускать с параметром -R. Синтаксис её в этом случае такой:
ssh -N -R [remote_addr:]удаленный_порт:localhost:[локальный_порт] [пользователь]@[удаленный_IP]
Например:
ssh -N -R 193.233.18.65:8080:127.0.0.1:80 user1@193.233.18.65
Эта команда сделает доступным в Интернет веб-сервер Apache2, запущенный на моём локальном компьютере, при обращении к виртуальному серверу по его публичному адресу и порту 8080. В адресной строке браузера указываю: http://193.233.18.65:8080 и получаю стартовую страницу сервера. Но не сразу, а после того, как разрешу на сервере входящий трафик на tcp порт 8080:
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
Также можно добавить правило для перенаправления портов, чтобы в браузере писать просто адрес или имя хоста:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
Эта команда добавит правило в таблицу NAT которая перенаправит все входящие TCP-соедиения с порта 80 на порт 8080. Просмотреть список правил этой таблицы можно командой:
iptables -t nat -nvL --line-numbers
Удалить правило можно указав параметр -D, название цепочки и номер строки:
iptables -t nat -D PREROUTING 1
Если нужно сделать автоматическое включение этого туннеля при загрузке, создаём модуль сервиса systemd на клиентском компьютере:
sudo mcedit /etc/systemd/system/myssh_tun.service
Вписываем следующие строчки:
[Unit]
Description=SSH tunnel to HomePC
After=network-online.target
[Service]
ExecStart=/usr/bin/ssh -N -o "ServerAliveInterval 60" -o "ServerAliveCountMax 5" -R 193.233.18.65:8080:127.0.0.1:80 user1@193.233.18.65
User=user1
[Install]
WnatedBy=multi-user.target
Сохраняем файл, выходим, перезагружаем сервисы с помощью команды:
systemctl daemon-reload
Теперь запускаем сервис и проверяем его статус:
systemctl start myssh_tun.service systemctl status myssh_tun.service
Если всё впорядке, включаем его автозагрузку:
systemctl enable myssh_tun.service
Осталось рассмотреть ещё несколько мелочей.
Что это за сообщение в статистике подключения sshd, которое выводится при проверке его статуса:
"pam_systemd_home (sshd:account): systemd-homed is not available: Unit dbus-og.freedesktop.home1.service not found"?
А это результат попытки аутентификации пользователя посредством модуля pam_systemd_home.so, используемого сервисом systemd-homed.service.
Если посмотреть статус этого сервиса, введя команду:
systemctl status systemd-homed.service
Можно увидеть, что он деактивирован, автозагрузка выключена, но имеется параметр предустановки (preset:enabled), который означает, что сервис будет запущен при старте системы.
Обращение к модульной системе аутиентификации PAM при подключении к sshd идёт потому, что в файле конфигурации:
/etc/ssh/sshd_config
раскоментирована соответствующая строчка:
UsePAM yes
Она нужна для аутентификации по ключу. Значит её придётся оставить. А искать проблему в файлах конфигурации PAM.
Открываем файл /etc/pam.d/system-auth:
mcedit /etc/pam.d/system-auth
Находим все строчки pam_systemd_home.so и комментируем их, ставя в начале каждой символ "#".
После этого переподключаемся к серверу по ssh и смотрим статус sshd. Сообщение должно исчезнуть.
Ну и завершающие штрих на сей раз по небольшому аудиту системы.
Посмотрим список активных служб systemd на предмет чего-нибудь лишнего:
systemctl list-unit-files --type=service --state=enabled
Выведем список всех активных ssh-соединений соединений:
ss -o state established '( dport = "ssh or sport = "ssh )'
Просканируем сервер на предмет открытых портов с помощью программы nmap, запустив команду с клиентского копьютера:
nmap 193.233.18.65
На этом пока что всё по SSH, несмотря на то, что в теме на самом деле осталось ещё много нерасмотренных вещей. Наиболее впечатляющие, на мой взгляд, возможности дают туннели SSH. Думаю в будущем они смогут ещё не раз пригодиться.
Помимо меня и Балабобы Яндекса при написании статьи были использованы мледующие материалы:
1. Дэфвид Клинтон "Linux в действии" (книга)
2. Брайан Уорд "Внутреннее устройство Linux" (книга)
3. Наглядное руководство по SSH-туннелям:
https://habr.com/ru/companies/flant/articles/691388/
4. Памятка пользователям ssh:
https://habr.com/ru/articles/122445/
5. SSH back-connect:
https://aminux.wordpress.com/2012/12/26/ssh-back-connect/
6. A Visual Guide to SSH Tunnels: Local and Remote Port Forwarding:
https://iximiuz.com/en/posts/ssh-tunnels/
7. OpenSSH (Русский):
https://wiki.archlinux.org/title/OpenSSH_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)
8. Подключаемся к серверу за NAT при помощи туннеля SSH:
https://blog.kvv213.com/2018/08/podkljuchaemsja-k-serveru-za-nat-pri-pomoshhi-tunnelja-ssh-prostaja-i-ponjatnaja-instrukcija/?ysclid=lkh72lrxhd404993686
9. SSH Hardening Guides
https://www.ssh-audit.com/hardening_guides.html
Все картинки, взятые из интернета, принадлежат их авторам.