อัตโนมัติ ssh-copy-id


29

ฉันมีเซิร์ฟเวอร์จำนวนหนึ่งที่มีการรวมกันของผู้ใช้ / รหัสผ่านเดียวกัน ฉันต้องการเขียนสคริปต์ (ที่ฉันโทรหนึ่งครั้ง) เพื่อที่

ssh-copy-id user@myserver

ถูกเรียกสำหรับแต่ละเซิร์ฟเวอร์ เนื่องจากพวกเขาทุกคนมีผู้ใช้ / รหัสผ่านที่เหมือนกันสิ่งนี้ควรจะง่าย แต่ssh-copy-idต้องการให้ฉันพิมพ์รหัสผ่านในแต่ละครั้งที่เอาชนะวัตถุประสงค์ของสคริปต์ของฉัน ssh-copy-id -p mypassword user@myserverมีตัวเลือกสำหรับการวางในรหัสผ่านไม่เป็นเช่น

ฉันจะเขียนสคริปต์ที่กรอกข้อมูลในฟิลด์รหัสผ่านโดยอัตโนมัติเมื่อถูกssh-copy-idถามได้อย่างไร


เหตุใดคุณจึงใช้การระบุผู้ใช้ / รหัสผ่านแทนการระบุผู้ใช้ / publickey
kagali-san

16
เพราะฉันใช้สคริปต์นี้เพื่อตั้งค่าผู้ใช้ / publickey
devin

คำตอบ:


27

ลองดูที่sshpass วางรหัสผ่านของคุณในไฟล์ข้อความและทำสิ่งนี้:

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

มันไม่ทำงานบน Centos7 เพียงแค่เรียกใช้โดยไม่มีข้อผิดพลาดและไม่มีกุญแจบนเซิร์ฟเวอร์ระยะไกล
ImranRazaKhan

19

คุณสามารถใช้ที่คาดหวังว่าจะฟังพร้อมท์รหัสผ่านและส่งรหัสผ่านของคุณ:

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

บันทึกสคริปต์ทำให้ใช้งานได้และเรียกมันว่า: ./login.expect user@myserver


คุณต้องการ bash รุ่นใหม่กว่านี้spawnหรือไม่ ด้วยเหตุผลที่ฉันไม่สามารถควบคุมได้ฉันติดอยู่กับ bash v3.2
devin

รุ่นทุบตีไม่ควรสำคัญ ฉันทดสอบด้วยความคาดหวัง 5.44.1.15 แต่ฉันใช้คล้ายกับเวอร์ชันที่คาดหวังรุ่นเก่ากว่า คุณประสบปัญหาในการใช้สคริปต์หรือไม่
MonkeeSage

spawn: command not found
devin

spawnเป็นคำหลักที่คาดหวัง (ดูคู่มือคาดหวัง (1)) ดูเหมือนว่าสคริปต์กำลังถูกตีความว่าเป็นเชลล์มากกว่าที่คาดไว้ คุณคาดหวังว่าจะติดตั้งหรือไม่ จะเกิดอะไรขึ้นถ้าคุณคาดหวังโดยตรง:expect -f login.expect user@myserver
MonkeeSage

1
@Envek ฉันเพิ่งจะเพิ่มสิ่งนี้ แต่ก็ดีที่เห็นว่าความคิดเห็นสุดท้ายเป็นคำถามโดยตรงสำหรับสิ่งที่ฉันจะเขียน ใช้บรรทัดนี้แทน:spawn ssh-copy-id -o StrictHostKeyChecking=no $argv
Steven Lu

3

คำตอบของ quanta นั้นค่อนข้างดี แต่คุณต้องใส่รหัสผ่านในไฟล์ข้อความ

จากหน้าคน "sshpass":

หากไม่มีตัวเลือกใด sshpass จะอ่านรหัสผ่านจากอินพุตมาตรฐาน

ดังนั้นสิ่งที่คุณสามารถทำได้คือการจับรหัสผ่านหนึ่งครั้งในระหว่างสคริปต์เก็บไว้ในตัวแปร echo รหัสผ่านและไพพ์ที่ 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


2

นี่เป็นปัญหากับ 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'

ssh-copy-id ของฉันทำอย่างนั้นแล้ว: คำเตือน: คีย์ทั้งหมดถูกข้ามไปเนื่องจากมีอยู่แล้วในระบบรีโมต นี่เป็นความพยายามของคุณที่จะขโมยกุญแจหรือไม่? :)
Mihai Stanescu

2

แทนที่จะพิมพ์รหัสผ่านของคุณหลายครั้งคุณสามารถใช้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_keys
  • cat - >> $afile - รับอินพุตจาก STDIN และผนวกเข้ากับไฟล์ authorized_keys
  • sort -u $afile -o $afile - จัดเรียงไฟล์ที่ได้รับอนุญาตไม่ซ้ำกันและบันทึกไว้

หมายเหตุ:บิตสุดท้ายนั้นคือการจัดการเคสที่คุณรันหลาย ๆ ครั้งด้านบนเทียบกับเซิร์ฟเวอร์เดียวกัน วิธีนี้จะกำจัด 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เพราะฉันได้ทำไปแล้วมีตัวเลือกอื่นให้เลือก


Ansible's authorized_key_moduleดูเหมือนจะไม่ทำงานสำหรับเครื่องใหม่ ฉันต้อง ssh-copy-id xxx ก่อนดังนั้นฉันกำลังมองหาวิธีใช้เพียงเพิ่ม ansible เพิ่ม ssh-key สำหรับเครื่องใหม่ความคิดใด ๆ
มิ ธ ริล

@mithril - ดูเหมือนข้อผิดพลาดฉันถามฟอรัม Ansible เกี่ยวกับเรื่องนี้
slm

1

หนึ่งในเครื่องมือ SSH แบบขนาน (กลุ่ม, mssh, pssh) อาจเหมาะสมสำหรับคุณ

ตัวอย่างเช่นใช้ cssh เพื่อเข้าสู่ระบบเครื่องทั้งหมดและผนวกคีย์ด้วยตัวคุณเอง


1
ฉันมีชุดเครื่องมือที่กำหนดเองสำหรับทำทุกอย่างที่ฉันต้องการยกเว้นการคัดลอกคีย์นั่นคือ
devin

เผง…ใช้เครื่องมือนี้เพื่อทำภารกิจเดียวที่ขาดหายไป แม้ว่านี่จะเป็นสิ่งที่ต่อเนื่อง แต่สคริปต์ที่ MonkeeSage โพสต์ (ดัดแปลงให้อ่านรหัสผ่านจาก stdin และทำงานบนเซิร์ฟเวอร์หลายเครื่อง) อาจเป็นทางออกที่ดีที่สุดของคุณ
MikeyB

0

สองสิ่งที่อาจเหมาะสมกับใบเสร็จ:

ดังที่กล่าวไว้ในคำตอบอื่น ๆ sshpass น่าจะเป็นทางออกที่ง่ายที่สุด


0

ฉันต้องการเน้นเพียงแค่ความคิดที่ไม่ดีพอที่จะ:

  1. ใช้รหัสผ่านที่เข้ารหัสยากในสคริปต์ของคุณ
  2. ใช้รหัสผ่านเดียวกันบนเซิร์ฟเวอร์ทั้งหมดของคุณ ... เช่น ... ทำไม!
  3. อย่าใช้การรับรองความถูกต้อง SSH public_key + รหัสผ่านหากคุณยืนยันในเรื่องนี้
  4. บันทึกรหัสผ่านลงในไฟล์ข้อความ

นี่คือการใช้งานที่มีความปลอดภัยมากกว่า ...

#!/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 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.