LaurVas

Как случайно не выключить сервер по ssh

   linux

Однажды я набрал sudo poweroff не в той консоли и погасил какой-то сервер вместо домашнего компьютера. После этого я задумался, что неплохо было бы иметь защитный механизм от невнимательности. Возникла идея простой проверки: если poweroff или reboot был запущен по ssh, то предупредить пользователя что он собирается выключить/перезагрузить сервер и потребовать подтверждение.

Получился маленький скрипт:

/usr/local/sbin/poweroff
#!/bin/bash

action=$(basename $0)
if [[ -n "$SSH_CLIENT" || -n "$SSH2_CLIENT" ]]; then
  read -p "You are going to $action $(hostname) over ssh. Are you sure (y/N)? " -r
  if [[ $REPLY =~ ^[Yy]$ ]]; then
    /sbin/$action
  fi
else
  /sbin/$action
fi

Пути к poweroff и reboot отличаются в разных дистрибутивах, поэтому в Arch Linux надо заменить /sbin/$action на /usr/bin/$action.

Дистрибутив Система инициализации Каталог
Arch Systemd /usr/bin
Debian Wheezy System V /sbin
Debian Jessie Systemd /sbin
Gentoo OpenRC /sbin

Чтобы при запуске poweroff в консоли вместо стандартного бинаря вызывался наш скрипт, его надо положить в каталог, который прописан в PATH раньше, чем каталог с оригинальным файлом. Смотрим PATH у рута:

root@archlinux# echo $PATH | tr ':' '\n'
/usr/local/sbin
/usr/local/bin
/usr/bin
/usr/lib/jvm/default/bin
/usr/bin/site_perl
/usr/bin/vendor_perl
/usr/bin/core_perl
root@debian-wheezy# echo $PATH | tr ':' '\n'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
root@debian-jessie# echo $PATH | tr ':' '\n'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
root@gentoo# echo $PATH | tr ':' '\n'
/sbin
/bin
/usr/sbin
/usr/bin

Я положил скрипт в /usr/local/sbin. Чтобы reboot тоже работал, просто делаем симлинк:

# cd /usr/local/sbin
# ln -svf poweroff reboot

Отлично, вот только запуск через sudo не работает. Потому что по умолчанию sudo не передаёт переменные окружения, которые нам нужны (SSH_CLIENT и SSH2_CLIENT). Чтобы скрипт корректно работал при запуске через sudo, надо поправить конфиг. Не делайте это напрямую, используйте команду visudo.

/etc/sudoers
Defaults env_keep += "SSH_CLIENT"
Defaults env_keep += "SSH2_CLIENT"

Проверяем что всё работает как положено:

$ su
# poweroff

$ sudo poweroff

$ sudo -s
# poweroff

$ sudo su
# poweroff

Если запросить логиновый шелл рута (su - или su -l), то переменная окружения SSH_CLIENT или SSH2_CLIENT пропадает и скрипт предупреждение не выдаёт. Я не знаю как это можно обойти.

Будет ли спасён сервер благодаря этому скрипту? Запасаемся попкорном и ждём когда кто-нибудь по невнимательности жахнет sudo poweroff не в той консоли. По закону Мерфи это рано или поздно произойдёт.