У меня есть произвольное количество серверов с одной и той же комбинацией пользователь / пароль. Я хочу написать сценарий (который я вызываю один раз), чтобы
ssh-copy-id user@myserver
вызывается для каждого сервера. Поскольку у всех один и тот же пользователь / пропуск, это должно быть легко, но ssh-copy-id
хочет, чтобы я вводил пароль отдельно каждый раз, что противоречит цели моего сценария. Нет возможности ввести пароль, т.е. ssh-copy-id -p mypassword user@myserver
.
Как мне написать скрипт, который автоматически заполняет поле пароля при ssh-copy-id
просит об этом?
Взгляни на sshpass. Поместите свой пароль в текстовый файл и сделайте что-то вроде этого:
$ sshpass -f password.txt ssh-copy-id user@yourserver
Вы можете использовать ожидание, чтобы прослушать запрос пароля и отправить свой пароль:
#!/usr/bin/expect -f
spawn ssh-copy-id $argv
expect "password:"
send "YOUR_PASSWORD\n"
expect eof
Сохраните сценарий, сделайте его исполняемым и назовите его так: ./login.expect user@myserver
Ответ Quanta довольно хорош, но он требует, чтобы вы поместили свой пароль в текстовый файл.
На странице руководства "sshpass":
Если опция не указана, sshpass считывает пароль со стандартного ввода.
Итак, что вы можете сделать, так это захватить пароль один раз во время выполнения сценария, сохранить его в переменной, отобразить пароль и передать его sshpass в качестве входных данных.
Я делаю это постоянно, и все работает нормально. Пример:
echo "Please insert the password used for ssh login on remote machine:"
read -r USERPASS
for TARGETIP in $@; do
echo "$USERPASS" | sshpass ssh-copy-id -f -i $KEYLOCATION "$USER"@"$TARGETIP"
done
Это проблема с ssh-copy-id; он также добавляет ключ каждый раз, когда вы его запускаете. Если вы автоматизируете процесс, ваш файл authorized_keys быстро забивается дублирующимися ключами. Вот программа на Python, которая позволяет избежать обеих проблем. Он запускается с управляющего сервера и помещает ключи с одного удаленного сервера на другой удаленный сервер.
import subprocess
def Remote(cmd,IP):
cmd = '''ssh root@%s '''%(IP)+cmd
lines = subprocess.check_output(cmd.split())
return '\n'.join(lines)
source = '123.456.78.90'
target = '239.234.654.123'
getkey = 'cat /root/.ssh/id_rsa.pub'
getauth = 'cat /root/.ssh/authorized_keys'
sourcekey = Remote(getkey, source).replace('\n','').strip()
authkeys = Remote(getauth, target).replace('\n','').strip()
if sourcekey not in authkeys:
keycmd=''' echo "%s" >>/root/.ssh/authorized_keys;
chmod 600 /root/.ssh/authorized_keys '''%(sourcekey) # A compound shell statement
print 'Installed key', Remote(keycmd,target)
else: print 'Does not need key'
Вместо того, чтобы вводить пароль несколько раз, вы можете использовать pssh
и это -A
переключитесь, чтобы запросить его один раз, а затем введите пароль на все серверы в списке.
НОТА: Использование этого метода не позволяет использовать ssh-copy-id
, однако вам нужно будет использовать свой собственный метод для добавления файла ключа публикации SSH в файл удаленной учетной записи. ~/.ssh/authorized_keys
файл.
Вот пример, который работает:
$ cat ~/.ssh/my_id_rsa.pub \
| pssh -h ips.txt -l remoteuser -A -I -i \
' \
umask 077; \
mkdir -p ~/.ssh; \
afile=~/.ssh/authorized_keys; \
cat - >> $afile; \
sort -u $afile -o $afile \
'
Warning: do not enter your password if anyone else has superuser
privileges or access to your account.
Password:
[1] 23:03:58 [SUCCESS] 10.252.1.1
[2] 23:03:58 [SUCCESS] 10.252.1.2
[3] 23:03:58 [SUCCESS] 10.252.1.3
[4] 23:03:58 [SUCCESS] 10.252.1.10
[5] 23:03:58 [SUCCESS] 10.252.1.5
[6] 23:03:58 [SUCCESS] 10.252.1.6
[7] 23:03:58 [SUCCESS] 10.252.1.9
[8] 23:03:59 [SUCCESS] 10.252.1.8
[9] 23:03:59 [SUCCESS] 10.252.1.7
Приведенный выше сценарий обычно имеет такую структуру:
$ cat <pubkey> | pssh -h <ip file> -l <remote user> -A -I -i '...cmds to add pubkey...'
pssh
подробностиcat <pubkey>
выводит файл открытого ключа в pssh
pssh
использует -I
переключиться на получение данных через STDIN-l <remote user>
- это учетная запись удаленного сервера (мы предполагаем, что у вас одно и то же имя пользователя на всех серверах в IP-файле)-A
говорит pssh
чтобы запросить ваш пароль, а затем повторно использовать его для всех серверов, к которым он подключается-i
говорит pssh
для отправки любого вывода в STDOUT, а не для сохранения его в файлах (поведение по умолчанию)'...cmds to add pubkey...'
- это самая сложная часть того, что происходит, поэтому я разберу сам (увидеть ниже)Это команды, которые pssh
будет работать на каждом сервере:
' \
umask 077; \
mkdir -p ~/.ssh; \
afile=~/.ssh/authorized_keys; \
cat - >> $afile; \
sort -u $afile -o $afile \
'
Для того, чтобы: установите umask удаленного пользователя на 077, это значит, что любые каталоги или файлы, которые мы собираемся создать, будут иметь соответствующие права доступа, например так:
$ ls -ld ~/.ssh ~/.ssh/authorized_keys
drwx------ 2 remoteuser remoteuser 4096 May 21 22:58 /home/remoteuser/.ssh
-rw------- 1 remoteuser remoteuser 771 May 21 23:03 /home/remoteuser/.ssh/authorized_keys
создать каталог ~/.ssh
и игнорировать предупреждение, если он уже там
$afile
, с путем к файлу authorized_keyscat - >> $afile
- взять ввод из STDIN и добавить в файл authorized_keyssort -u $afile -o $afile
- однозначно сортирует файл authorized_keys и сохраняет егоНОТА: Этот последний бит предназначен для обработки случая, когда вы запускаете вышеуказанное несколько раз на одних и тех же серверах. Это избавит вас от многократного добавления вашего pubkey.
Также обратите особое внимание на то, что все эти команды заключены в одинарные кавычки. Это важно, поскольку мы не хотим $afile
чтобы получить оценку до тех пор, пока он не будет запущен на удаленном сервере.
' \
..cmds... \
'
Я расширил приведенное выше, чтобы его было легче читать, но я обычно запускаю все в одной строке, например:
$ cat ~/.ssh/my_id_rsa.pub | pssh -h ips.txt -l remoteuser -A -I -i 'umask 077; mkdir -p ~/.ssh; afile=~/.ssh/authorized_keys; cat - >> $afile; sort -u $afile -o $afile'
Используя pssh
вы можете отказаться от создания файлов и предоставить динамический контент, используя -h <(...some command...)
или вы можете создать список IP-адресов, используя другой из pssh
переключатели, -H "ip1 ip2 ip3"
.
Например:
$ cat .... | pssh -h <(grep -A1 dp15 ~/.ssh/config | grep -vE -- '#|--') ...
Вышеупомянутое можно использовать для извлечения списка IP-адресов из моих ~/.ssh/config
файл. Конечно, вы также можете использовать printf
также для создания динамического контента:
$ cat .... | pssh -h <(printf "%s\n" srv0{0..9}) ....
Например:
$ printf "%s\n" srv0{0..9}
srv00
srv01
srv02
srv03
srv04
srv05
srv06
srv07
srv08
srv09
Вы также можете использовать seq
для генерации форматированных числовых последовательностей!
pssh
Если вы не хотите использовать pssh
как я сделал это выше, есть и другие варианты.
Вам может подойти один из параллельных инструментов SSH (clusterssh, mssh, pssh).
Например, используйте cssh для входа на все машины и добавьте ключ самостоятельно.
Пара вещей, которые могут соответствовать всем требованиям:
Как упоминалось в других ответах, sshpass, вероятно, является самым простым решением.
Я хочу подчеркнуть, насколько плохая идея:
Вот реализация, которая немного более безопасна ...
#!/usr/bin/python3
import os
import getpass
import argparse
parser = argparse.argument_parser()
parser.add_argument('-l','--login', action='store', help='username')
parser.add_argument('-p','--port', action='store', default='22', help='port')
parser.add_argument('-L','--list', action='store', help='file list of IPs')
parser.add_argument('-i','--ip-address', action='store', nargs='+', metavar='host' help='ip or list of ips')
args = parser.parse_args()
if not args.login:
print("You need a login, broski!")
return 0
if args.list:
ips = [i for i in open(args.list, 'r').readlines()]
passwd = getpass.getpass('Password: ')
for ip in ips:
cmd = 'ssh-id-copy {0}@{1} -p {2}'.format(ip,args.port,passwd)
os.system('sshpass -p ' + passwd + ' ' + cmd)
print("Key added: ", ip) # prints if successful
# ex: sshpass -p passwd ssh-id-copy login@1.1.1.1
elif args.host:
ip = args.host
cmd = 'ssh-id-copy {0}@{1} -p {2}'.format(ip,args.port,passwd)
os.system('sshpass -p ' + passwd + ' ' + cmd)
print("Key added: ", ip) # prints if successful
else:
print("No IP addresses were given to run script...")
return 0