original in en Erdal Mutlu
en to ru Eugene S. Saenko
Erdal - один из турецких редакторов LF. В настоящее время он работает системным администратором в Linotype Library. Будучи большим фанатом Linux с университетских лет, он любит работать и заниматься разработками в этой среде.
Чтобы воспользоваться этой статьей необходимо понимание основ программирования в оболочке. Информацию о программировании в оболочке см. в статье Кати и Гвидо Сочер (Katja and Guido Socher) в LinuxFocus Shell Programming. Вам, также, потребуется знание утилит ssh, таких как ssh-keygen, ssh-add, ssh, scp или sftp. Имеется свободная реализация протокола SSH под Linux: OpenSSH, содержащая все эти инструменты. Имеются и страницы руководства (man pages).
scp /path/to/the/file/file1 user@remote_host:/remotedir/newfile
В этом примере файл file1 копируется из локального каталога на удаленный компьютер (remote_host может быть или IP или имя удаленного компьютера) в каталог /remotedir под новым именем newfile. Выдается приглашение к аутентификации под именем 'user'. В случае удачной аутентификации и если пользователь имеет соответствующие права, файл будет скопирован. Можно не задавать новое имя файла. В этом случае файл будет скопирован под старым именем. Короче, это означает, что при копировании файл можно переименовать.scp user@remote_host:/remotedir/file /path/to/local/folder/newfile
Имеется, также, очень удобный вариант команды scp. Указав ключ '-r' Вы можете копировать каталоги рекурсивно.scp -r user@remote_host:/remotedir .
Эта команда копирует каталог 'remotedir' и все его подкаталоги и файлы с удаленного компьютера в текущий рабочий каталог под тем же именем.Примечание: Предполагается, что на удаленном компьютере выполняется демон sshd.
ssh [email protected] df -H
Это очень похоже на синтаксис удаленной регистрации. Различия начинаются после имени компьютера. Выдается команда (в этом примере 'df -H') для исполнения на удаленном компьютере. Вывод команды будет отображен на Вашем терминале.ssh-keygen -b 1024 -t dsa
У Вас будет запрошено имя приватного ключа. Имя публичного ключа обычно то же, что и приватного ключа с добавкой '.pub'. Здесь '-b 1024' это количество бит создаваемого ключа. Если это значение не указано, будет использовано значение по умолчанию. '-t dsa' задает тип создаваемого ключа. Возможные значения: 'rsa1' для протокола версии 1 и 'rsa' или 'dsa' для протокола версии 2. Я бы рекомендовал использование версии 2 протокола SSH. Но если у Вас есть старые серверы, поддерживающие только протокол версии 1, Вам надо указать '-t rsa1' для создания другой пары ключей. Указав '-1' или '-2' Вы можете заставить ssh протокол версии 1 или 2 соответственно.
Чтобы воспользоваться ключом, Вам надо установить свой публичный ключ на удаленный компьютер.
Содержимое файла публичного ключа должно быть скопировано или добавлено в файл
$HOME/.ssh/authorized_keys или $HOME/.ssh/authorized_keys2. Будьте внимательны и не
смешивайте ключи для различных версий протокола. Для протокола версии 1 используется
authorized_keys, а для протокола версии 2 используется authorized_keys2. Если Вы правильно
установили свой публичный ключ, в первый же раз, когда Вы подключитесь к этому компьютеру,
будет запрошена Ваша парольная фраза (passphrase), а в случае отказа, будет запрошен пароль
удаленного пользователя. Вы можете ограничить подключение к Вашей системе только
использованием аутентификации по публичному ключу, отредактировав конфигурационный файл sshd.
Имя файла
/etc/ssh/sshd_config а наименование параметра, который Вам надо изменить
'PasswordAuthentication'. Замените этот параметр на no (PasswordAuthentication no) и перезапустите sshd.
До этого момента все хорошо. У нас есть безопасный способ копирования и выполнения команд на удаленных системах. Но автоматизируя некоторые задачи мы не должны вводить ни паролей, ни парольных фраз. Иначе мы ничего не сможем автоматизировать. Конечно можно было бы вписать в каждый скрипт требуемые пароли, но это не очень хорошая идея. Лучше поручить агенту ключей (key-agent) взять на себя заботу о наших парольных фразах. ssh-agent - это программа, предназначенная для хранения приватных ключей, используемых для аутентификации по публичному ключу. Надо запустить агент ключей:
ssh-agent $BASH
и добавить Ваши приватные ключи, которые следует использоватьssh-add .ssh/id_dsa
илиssh-add .ssh/identity
id_dsa - это файл приватного ключа DSA, а identity - файл приватного ключа RSA1. Это - имена файлов по умолчанию, данные при генерации ключей с помощью ssh-keygen. Конечно, у Вас будет запрошена парольная фраза, прежде чем ssh-add добавит Ваш ключ к агенту ключей. Список добавленных ключей можно получить с помощью команды:ssh-add -l
Теперь при подключении к серверу, имеющему Ваш ключ в авторизованном файле, Вам не придется вводить вообще ничего! О процессе аутентификации позаботится ssh-agent.
При использовании ssh-agent описанным выше способом, им можно пользоваться только с терминала, в котором он был запущен. Чтобы использовать ssh-agent с любого открываемого Вами терминала придется потрудиться немного больше. Я написал такой небольшой скрипт для запуска агента:
#!/bin/sh # # Erdal mutlu # # Запуск ssh-agent для использования в пакетных заданиях. agent_info_file=~/.ssh/agent_info if [ -f $agent_info_file ]; then echo "Agent info file : $agent_info_file exists." echo "make sure that no ssh-agent is running and then delete this file." exit 1 fi ssh-agent | head -2 > $agent_info_file chmod 600 $agent_info_file exit 0
Приведенный выше скрипт проверяет наличие файла agent_info в домашнем каталоге пользователя, в котором обычно расположены ssh файлы пользователя. В нашем случае это каталог '.ssh/'. Если файл существует, пользователю выдается предупреждение о существовании файла и краткая инструкция о том, что нужно предпринять. Если у пользователя не запущен ssh-agent, он должен удалить файл agent_info и перезапустить скрипт. Скрипт запускает ssh-agent и включает первые две строки в файл agent_info. Эта информация, в дальнейшем, будет использоваться утилитами ssh. Следующая строка используется для изменения прав доступа к файлу таким образом, что только владелец файла может читать его и писать в него.
Если агент запущен и выполняется Вы можете добавлять к нему свои ключи. Но перед этим Вы должны выполнить файл agent_info, чтобы утилиты ssh знали, где расположен Ваш агент:
source ~/.ssh/agent_info или . ~/.ssh/agent_info
И добавить свои ключи с помощью ssh-add. Можно добавить в файл .bashrc несколько строк так, чтобы каждый раз при открытии нового терминала выполнялся файл agent_info:
if [ -f .ssh/agent_info ]; then . .ssh/agent_info fi
ВНИМАНИЕ: Вы должны обезопасить компьютер, на котором используете ssh-agent и скрипт автоматизации, который я здесь опишу. Иначе, если кто-нибудь получит доступ к Вашей учетной записи, он получит доступ ко всем серверам, с которыми Вы общаетесь с помощью ключей ssh. За все приходится платить!
Теперь пора объяснить, как мы собираемся автоматизировать некоторые из задач системного администратора. Идея состоит в том, чтобы выполнить некоторое множество команд на определенном множестве хостов и скопировать некоторые файлы на или с этих хостов. Это то, чем часто приходится заниматься системным администраторам. Вот этот скрипт:
#!/bin/sh # Установка с использованием Secure SHELL и агента SSH # Erdal MUTLU # 11.03.2001 ################################################################## # Функции ################################################################## ### Копирование файлов с одного компьютера на другой copy_files() { if [ $files_file != "files_empty.txt" ];then cat $files_file | grep -v "#" | while read -r line do direction=`echo ${line} | cut -d " " -f 1` file1=`echo ${line} | cut -d " " -f 2` file2=`echo ${line} | cut -d " " -f 3` case ${direction} in "l2r") : ### С локального компьютера на удаленный echo "$file1 --> ${host}:${file2}" scp $file1 root@${host}:${file2} ;; "r2l") : ### С удаленного компьютера на локальный echo "${host}:${file2} --> localhost:${file2}" scp root@${host}:${file1} ${file2} ;; *) echo "Неизвестно направление копирования : ${direction}" echo "Должно быть local или remote." ;; esac done fi } ### Выполнение команд на удаленных компьютерах execute_commands() { if [ $commands_file != "commands_empty.txt" ];then cat $commands_file | grep -v "#" | while read -r line do command_str="${line}" echo "Выполняю $command_str ..." ssh -x -a root@${host} ${command_str} & wait $! echo "Выполнено $command_str OK." done fi } ### Функция-оболочка (wrapper function) для функций execute_commands и copy_files doit() { cat $host_file | grep -v "#" | while read -r host do echo "host=$host processing..." case "${mode}" in "1") copy_files execute_commands ;; "2") execute_commands copy_files ;; *) echo "$0 : Неизвестный режим : ${mode}" ;; esac echo "host=$host ok." echo "------------------------------------------------------------------" done } ################################################################## ### Программа начинается здесь ################################################################## if [ $# -ne 4 ]; then echo "Использование : $0 mode host_file files_file commands_file" echo "" echo "режим 1 или 2 " echo " 1 : сначала копировать файлы, потом выполнять команды." echo " 2 : сначала выполнять команды, потом копировать файлы." echo "Если файл files.txt называется files_empty.txt то он не обрабатывается." echo "Если файл commands.txt называется commands_empty.txt то он не обрабатывается." exit fi mode=$1 host_file=$2 files_file=$3 commands_file=$4 agent_info_file=~/.ssh/agent_info if [ -f $agent_info_file ]; then . $agent_info_file fi if [ ! -f $host_file ]; then echo "Файл хостов : $host_file не существует!" exit 1 fi if [ $files_file != "files_empty.txt" -a ! -f $files_file ]; then echo "Файл файлов : $files_file не существует!" exit 1 fi if [ $commands_file != "commands_empty.txt" -a ! -f $commands_file ]; then echo "Файл команд : $commands_file не существует!" exit 1 fi #### Все выполняется здесь doit
Давайте запишем скрипт ainstal.sh (automated installation -- автоматизированная установка) и попытаемся выполнить его без параметров. Мы получим сообщение:
./ainstall.sh
Использование : ./ainstall.sh mode host_file files_file commands_file режим 1 или 2 1 : сначала копировать файлы, потом выполнять команды. 2 : сначала выполнять команды, потом копировать файлы. Если файл files.txt называется files_empty.txt то он не обрабатывается. Если файл commands.txt называется commands_empty.txt то он не обрабатывается. |
В соответствии с этим сообщением, если Вы не хотите выполнять команды, дайте аргументу commands.txt имя commands_empty.txt, а если не хотите пересылать никаких файлов, дайте аргументу files_file имя files_empty.txt. Иногда, ведь, нужно только выполнить несколько команд, а в других случаях только переслать несколько файлов.
Перед тем, как дать построчное объяснение скрипта, дадим пример его использования:
Предположим, Вы добавили в сеть вторичный DNS сервер и хотели бы добавить его в файл
/etc/resolv.conf. Для простоты предположим, что на всех Ваших компьютерах одинаковый
файл resolv.conf. В этом случае единственное, что Вам надо сделать, это скопировать новый
файл resolv.conf на все компьютеры.
Первое, что Вам потребуется - это список хостов. Запишем их все в файл hosts.txt. Формат
этого файла такой, что каждая строка содержит имя или IP только одного хоста. Вот пример:
############################################################################# #### Каждая строка содержит одно имя или IP адрес хоста. Строки, #### начинающиеся со знака # или его содержащие игнорируются. ############################################################################# helvetica.fonts.de optima.fonts.de zaphino vectora #10.10.10.162 10.10.10.106 193.103.125.43 10.53.103.120 |
Как видно из примера, можно указывать полностью квалифицированные имена или только часть, определяющую имя компьютера. Кроме того, Вам потребуется файл, в котором будут записаны имена файлов, которые надо передать. Возможны два типа передачи:
Файлы, которые надо передать, перечислены в другом файле. Давайте назовем этот файл files_file.txt. Формат files_file.txt такой: Каждая строка содержит информацию о копировании только одного файла. Возможны два направления копирования: l2r (local to remote - с локального на удаленный) и r2l (remote to local - с удаленного на локальный). l2r - это когда файл копируется с локального хоста на удаленный. r2l - когда файл с удаленного хоста копируется на локальный. После ключевого слова направления следуют два имени файла. Поля разделяются пробелами или табуляциями. Первый файл копируется во второй в соответствии с ключевым словом направления. Имя файла для удаленного хоста должно быть полностью квалифицированным, иначе файл будет скопирован в домашний каталог пользователя root. Вот наш files_file.txt :
############################################################################ # Структура файла такова : # - Значения полей : l2r (localhost to remote - с локального хоста на удаленный) # и r2l (remote computer to local - с удаленного компьютера на локальный). # r2l file1 file2 # означает копирование файла file1 с удаленного (хосты перечислены в # файле hosts.txt) компьютера на локальный под именем file2. # l2r file1 file2 # означает копирование файла file1 с локального хоста на # удаленный (хосты перечислены в файле hosts.txt) компьютер под # именем file2 # file1 и file2 - это файлы на соответствующих хостах. # # Примечание: порядок указания local и remote указывает направление # процесса копирования. ############################################################################ l2r resolv.conf /etc/resolv.conf |
Как видите, я включил в файл описание его структуры. Обычно я включаю это описание в каждый файл files_file.txt, который использую. Это простое, но хорошее решение для документирования. В этом примере мы хотим скопировать файл resolv.conf на удаленный компьютер под именем /etc/resolv.conf. В демонстрационных целях после копирования файла на целевые компьютеры я добавил команды смены владельца и группы файла и отображения содержимого /etc/resolv.conf. Команды, которые надо выполнить, помещаются в отдельный файл. Давайте назовем его commands_file.txt. Вот наш файл commands_file.txt:
########################################################################### # Структура этого файла : Каждая строка содержит команду, которую надо # выполнить. Каждая команда рассматривается отдельно. ########################################################################### chown root.root /etc/resolv.conf chmod 644 /etc/resolv.conf cat /etc/resolv.conf |
Файл команд содержит команды, которые должны быть выполнены на каждом компьютере, перечисленном в файле hosts.txt. Команды выполняются последовательно, это значит, что сначала выполняется первая команда, затем вторая и так далее.
Хорошо, теперь у нас есть все файлы, необходимые для этого простого примера. Единственное, что осталось, это указать режим, который определяет, какой из двух файлов: commands_file.txt или files_file.txt должен обрабатываться в первую очередь. Можно передать файлы, перечисленные в файле files_file.txt file, а затем выполнить все команды на целевом компьютере, это режим 1. Или наоборот, выполнить команды, а затем передать файлы, это режим 2. Теперь можно выполнить скрипт с необходимыми аргументами, вот так:
./ainstall.sh 1 hosts.txt files_file.txt commands_file.txt
Небольшой совет: обычно имя файла files.txt я начинаю с files_ и добавляю короткое описательное имя, например, files_resolvconf.txt. Подобным же образом я называю hosts.txt и commands.txt.
Теперь пришло время поговорить подробнее о самом скрипте. При запуске программа проверяет количество аргументов и, если оно не равно четырем, выдает сообщение об использовании. При правильном количестве аргументов их значения присваиваются соответствующим переменным. Затем, если существует файл '~/.ssh/agent_info', он выполняется. Этот файл содержит информацию о запущенном у Вас агенте ssh. Если Вы не пользуетесь агентом, Вам придется ввести пароль или парольную фразу вручную, что означает никакой автоматики:). Затем каждый файл (hosts, files и commands) проверяется на существование. Есть, также, специальная проверка на files_empty.txt и commands_empty.txt. Если Вы указали такое имя, то проверка на существование не нужна. Я изменил эту часть скрипта при написании этой статьи. Раньше было только:
if [ -f $host_file -a -f $files_file -a -f $commands_file ]; then echo "$host_file $files_file $commands_file" doit else echo "$host_file или $files_file или $commands_file не существует" exit fi
В этом случае у меня должны были быть файлы с именами: files_empty.txt и commands_empty.txt.
Но с этим не было проблем, поскольку я всегда работал только в одном каталоге.
В конце производится вызов функции 'doit'. Эта функция управляет всем. В этой функции имеется
цикл с командами 'cat' и 'while', который для каждого из хостов, перечисленных в
'$hosts_file', вызывает, в соответствии с 'mode', функции copy_files и execute_commands.
Таким образом обрабатывается каждый хост в задании. Переменная 'host' содержит имя или IP
адрес текущего хоста.
Давайте рассмотрим функцию copy_files. Эта функция сначала проверяет, имеет ли файл
файлов имя 'files_empty.txt'. Если да, то ничего не делается. Если нет, то в каждой строке
'$files_file' переменные 'direction', 'file1' и 'file2' содержат направление копирования,
имена первого и второго файла, соответственно. Копирование производится с помощью scp в
соответствии с переменной 'direction'.
И, наконец, давайте посмотрим, как работает функция execute_commands. Эта функция сначала
проверяет, имеет ли файл команд имя 'commands_empty.txt'. Если да, то ничего не делается.
Если нет, то каждая из команд в '$commands_file' выполняется с помощью ssh на удаленном
компьютере в фоновом режиме. После вызова команды ssh вызывается команда wait с параметром
'$!'. Эта команда обеспечивает то, что все команды выполняются строго друг за другом.
Вместо параметра '$!' подставляется идентификатор процесса последней команды, выполненной в
фоновом режиме.
Вот так-то. Просто, не правда ли?
Здесь представлено более сложное использование этого скрипта. Идея состоит в том, чтобы сделать резервные копии конфигурационных файлов всех Ваших компьютеров или серверов. С этой целью я написал небольшой скрипт, использующий ainstall.sh :
#!/bin/sh server_dir=${HOME}/erdal/sh/ServerBackups if [ ! -d $server_dir ]; then echo "Каталог : $server_dir не существует." exit 1 fi cd $server_dir servers=ll_servers.txt prog=${HOME}/erdal/sh/einstall_sa.sh cat $servers | grep -v "#" | while read -r host do echo $host > host.txt $prog 1 host.txt files_empty.txt servers/${host}/commands_make_backup.txt $prog 1 host.txt files_getbackup.txt commands_empty.txt mv -f backup.tgz servers/${host}/backup/`date +%Y%m%d`.tgz rm -f host.txt done exit 0
У Вас должен быть создан специальный каталог под именем servers. В этом каталоге должны быть два файла: files_getbackup.txt и ll_servers.txt. Вот файл 'files_getbackup.txt' :
r2l /root/backup.tgz backup.tgz
'll_servers.txt' содержит имена или IP хостов, которые необходимо резервировать. Каждому хосту, перечисленному в файле 'll_servers.txt', должен соответствовать каталог с таким же именем, в котором должен быть файл commands_make_backups.txt, содержащий команды для создания архива /root/backup.tgz из конфигурационных файлов этого хоста. И каталог под именем backup. Все резервные файлы этого хоста будут храниться в этом каталоге. Файл ll_servers.txt содержит:
fileserver dbserver 10.10.10.1 appserver |
структура подкаталогов каталога '$servers' должна быть такой:
servers |-- files_getbackup.txt |-- ll_servers.txt |-- make_server_backups.sh |-- 10.10.10.1 | |-- backup | `-- commands_make_backup.txt |-- appserver | |-- backup | `-- commands_make_backup.txt |-- dbserver | |-- backup | `-- commands_make_backup.txt |-- fileserver |-- backup `-- commands_make_backup.txt |
А вот несколько примеров файлов commands_make_backups.txt:
tar cfz /root/backup.tgz /etc/samba /etc/atalk /etc/named.conf /var/named/zones
Этот файл commands_make_backup.txt используется для резервирования конфигурационных файлов samba, atalk и nameserver и файлов зон.
tar cfz /root/backup.tgz /etc/httpd /usr/local/apache
Этот файл commands_make_backup.txt используется для резервирования конфигурации и файлов сервера apache.
tar cfz /root/backup.tgz /etc/squid /etc/named.conf
Этот файл commands_make_backup.txt используется для резервирования конфигурации прокси-сервера squid и вторичного сервера DNS.
С помощью этого скрипта Вы можете, приспособив файлы commands_make_backup.txt к своим требованиям, делать резервные копии конфигурации своих серверов.