Tự động hóa ssh-copy-id


29

Tôi có một số máy chủ tùy ý có cùng kết hợp người dùng / pass. Tôi muốn viết một kịch bản (mà tôi gọi một lần) để

ssh-copy-id user@myserver

được gọi cho mỗi máy chủ. Vì tất cả họ đều có cùng một người dùng / vượt qua nên điều này thật dễ dàng nhưng ssh-copy-idmuốn tôi nhập mật khẩu riêng biệt mỗi lần làm thất bại mục đích của tập lệnh của tôi. Không có tùy chọn để nhập mật khẩu, tức là ssh-copy-id -p mypassword user@myserver.

Làm cách nào tôi có thể viết một tập lệnh tự động điền vào trường mật khẩu khi ssh-copy-idyêu cầu?


Tại sao bạn sử dụng nhận dạng người dùng / vượt qua thay vì nhận dạng người dùng / khóa công khai?
kagali-san

16
bởi vì tôi đang sử dụng tập lệnh này để thiết lập người dùng / khóa công khai.
devin

Câu trả lời:


27

Hãy nhìn vào sshpass . Đặt mật khẩu của bạn vào một tệp văn bản và làm một cái gì đó như thế này:

$ sshpass -f password.txt ssh-copy-id user@yourserver

Nó không hoạt động trên Centos7 chỉ chạy mà không có lỗi và không có khóa trên máy chủ từ xa
ImranRazaKhan

19

Bạn có thể sử dụng mong đợi để nghe lời nhắc mật khẩu và gửi mật khẩu của bạn:

#!/usr/bin/expect -f
spawn ssh-copy-id $argv
expect "password:"
send "YOUR_PASSWORD\n"
expect eof

Lưu tập lệnh, làm cho nó có thể thực thi được và gọi nó như sau: ./login.expect user@myserver


Bạn có cần một phiên bản bash mới hơn để sử dụng spawnkhông? Vì lý do tôi không thể kiểm soát Tôi bị mắc kẹt với bash v3.2.
devin

Phiên bản Bash không quan trọng. Tôi đã thử nghiệm với kỳ vọng 5.44.1.15, nhưng tôi đã sử dụng tương tự với các phiên bản cũ hơn mong đợi. Bạn đang gặp khó khăn khi sử dụng kịch bản?
MonkeeSage

spawn: command not found
devin

spawnlà một từ khóa mong đợi (xem hướng dẫn mong đợi (1)). Âm thanh như kịch bản đang được hiểu là vỏ hơn là mong đợi. Bạn có mong đợi cài đặt? Điều gì xảy ra nếu bạn chạy trực tiếp mong đợi:expect -f login.expect user@myserver
MonkeeSage

1
@Envek Tôi vừa mới thêm cái này nhưng thật tuyệt khi thấy bình luận cuối cùng là một câu hỏi trực tiếp cho điều mà tôi sẽ viết. Sử dụng dòng này thay thế:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Steven Lu

3

Câu trả lời của quanta khá hay, nhưng nó đòi hỏi bạn phải đặt mật khẩu của mình vào một tệp văn bản.

Từ trang người đàn ông "sshpass":

Nếu không có tùy chọn nào được đưa ra, sshpass đọc mật khẩu từ đầu vào tiêu chuẩn.

Vì vậy, những gì bạn có thể làm là nắm bắt mật khẩu một lần trong khi tập lệnh, lưu trữ nó trong một biến, lặp lại mật khẩu và đường ống để sshpass làm đầu vào.

Tôi làm điều này mọi lúc và nó hoạt động tốt. Thí dụ: 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


2

Đây là một vấn đề với ssh-copy-id; nó cũng thêm một khóa mỗi khi bạn chạy nó. Nếu bạn đang tự động hóa quy trình, tệp ủy quyền của bạn sẽ nhanh chóng bị lộn xộn với các khóa trùng lặp. Đây là một chương trình Python tránh cả hai vấn đề. Nó chạy từ máy chủ điều khiển và đặt các khóa từ một máy chủ từ xa vào một máy chủ từ xa khác.

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'

ssh-copy-id của tôi đã làm điều đó rồi: CẢNH BÁO: Tất cả các khóa đã bị bỏ qua vì chúng đã tồn tại trên hệ thống từ xa. Đây có phải là nỗ lực của bạn để đánh cắp chìa khóa? :)
Mihai Stanescu

2

Thay vì nhập mật khẩu của bạn nhiều lần, bạn có thể sử dụng pssh-Achuyển đổi mật khẩu để nhắc nhập một lần, sau đó cung cấp mật khẩu cho tất cả các máy chủ trong danh sách.

LƯU Ý:ssh-copy-id Tuy nhiên, sử dụng phương pháp này không cho phép bạn sử dụng , do đó, bạn sẽ cần cuộn phương thức của riêng mình để nối thêm tệp khóa quán rượu SSH vào tệp tài khoản từ xa ~/.ssh/authorized_keys.

Thí dụ

Đây là một ví dụ thực hiện công việc:

$ 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

Kịch bản trên thường có cấu trúc như vậy:

$ cat <pubkey> | pssh -h <ip file> -l <remote user> -A -I -i '...cmds to add pubkey...'

psshChi tiết cấp cao

  • cat <pubkey> xuất tệp khóa công khai sang pssh
  • psshsử dụng -Ichuyển đổi để nhập dữ liệu qua STDIN
  • -l <remote user> là tài khoản của máy chủ từ xa (chúng tôi giả sử bạn có cùng tên người dùng trên các máy chủ trong tệp IP)
  • -Anói psshđể yêu cầu mật khẩu của bạn và sau đó tái sử dụng nó cho tất cả các máy chủ mà nó kết nối tới
  • -iyêu psshcầu gửi bất kỳ đầu ra nào đến STDOUT thay vì lưu trữ nó trong các tệp (hành vi mặc định của nó)
  • '...cmds to add pubkey...'- đây là phần khó nhất trong những gì đang diễn ra, vì vậy tôi sẽ tự mình giải quyết vấn đề này (xem bên dưới)

Các lệnh đang được chạy trên các máy chủ từ xa

Đây là các lệnh psshsẽ chạy trên mỗi máy chủ:

'                                         \
  umask 077;                              \
  mkdir -p ~/.ssh;                        \
  afile=~/.ssh/authorized_keys;           \
  cat - >> $afile;                        \
  sort -u $afile -o $afile                \
'
Theo thứ tự:
  • đặt ô của người dùng từ xa thành 077, điều này là để bất kỳ thư mục hoặc tệp nào chúng tôi sẽ tạo, sẽ có quyền của họ được đặt tương ứng như vậy:

    $ 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
    
  • tạo thư mục ~/.sshvà bỏ qua cảnh báo chúng tôi nếu nó đã ở đó

  • đặt một biến, $afilevới đường dẫn đến tệp ủy quyền
  • cat - >> $afile - lấy đầu vào từ STDIN và nối vào tệp ủy quyền
  • sort -u $afile -o $afile - sắp xếp duy nhất tệp ủy quyền_key và lưu nó

LƯU Ý: Bit cuối cùng đó là để xử lý trường hợp bạn chạy nhiều lần trên các máy chủ tương tự. Điều này sẽ loại bỏ pubkey của bạn khỏi bị nối thêm nhiều lần.

Chú ý các dấu hiệu đơn!

Cũng đặc biệt chú ý đến thực tế là tất cả các lệnh này được lồng bên trong các dấu ngoặc đơn. Điều đó rất quan trọng, vì chúng tôi không muốn $afileđược đánh giá cho đến khi nó thực thi trên máy chủ từ xa.

'               \
   ..cmds...    \
'

Tôi đã mở rộng ở trên để dễ đọc hơn ở đây, nhưng tôi thường chạy tất cả trên một dòng như vậy:

$ 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'

Tài liệu khen thưởng

Bằng cách sử dụng, psshbạn có thể từ bỏ việc xây dựng các tệp và cung cấp nội dung động bằng cách sử dụng -h <(...some command...)hoặc bạn có thể tạo danh sách IP bằng psshcác công tắc khác , -H "ip1 ip2 ip3".

Ví dụ:

$ cat .... | pssh -h <(grep -A1 dp15 ~/.ssh/config | grep -vE -- '#|--') ...

Ở trên có thể được sử dụng để trích xuất một danh sách IP từ ~/.ssh/configtệp của tôi . Tất nhiên bạn cũng có thể sử dụng printfđể tạo nội dung động:

$ cat .... | pssh -h <(printf "%s\n" srv0{0..9}) ....

Ví dụ:

$ printf "%s\n" srv0{0..9}
srv00
srv01
srv02
srv03
srv04
srv05
srv06
srv07
srv08
srv09

Bạn cũng có thể sử dụng seqđể tạo các chuỗi số được định dạng quá!

Tài liệu tham khảo & công cụ tương tự pssh

Nếu bạn không muốn sử dụng psshnhư tôi đã làm ở trên, có một số tùy chọn khác có sẵn.


Ansible's authorized_key_moduledường như không hoạt động cho máy mới. Tôi phải ssh-copy-id xxx trước, vì vậy tôi đang tìm cách chỉ sử dụng ansible add ssh-key cho máy mới, có ý kiến ​​gì không?
Mithril

@mithril - Nghe có vẻ như là một lỗi, tôi muốn hỏi trên các diễn đàn Ansible về nó.
slm

1

Một trong những công cụ SSH song song (cl clustersh, mssh, pssh) có thể phù hợp với bạn.

Chẳng hạn, sử dụng cssh để đăng nhập vào tất cả các máy và tự thêm khóa.


1
Tôi đã có một bộ công cụ tùy chỉnh để làm mọi thứ tôi cần, ngoại trừ việc sao chép khóa đó.
devin

Chính xác thì nên sử dụng một công cụ này để thực hiện một nhiệm vụ còn thiếu. Mặc dù nếu điều này sẽ là một điều đang diễn ra, kịch bản mà MonkeeSage đã đăng (được điều chỉnh để đọc mật khẩu từ stdin và hoạt động trên nhiều máy chủ) có lẽ sẽ là lựa chọn tốt nhất của bạn.
MikeyB

0

Vài điều có thể phù hợp với dự luật:

Như đã đề cập trong các câu trả lời khác, sshpass có thể là giải pháp dễ nhất.


0

Tôi muốn nhấn mạnh đến mức độ tồi tệ của một ý tưởng:

  1. Sử dụng mật khẩu được mã hóa cứng trong tập lệnh của bạn
  2. Sử dụng cùng một mật khẩu trên TẤT CẢ các máy chủ của bạn ... như ... tại sao!?
  3. KHÔNG sử dụng SSH public_key + xác thực mật khẩu nếu bạn khăng khăng việc này
  4. Lưu mật khẩu vào một tệp văn bản

Đây là một triển khai an toàn hơn một chút ...

#!/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 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.