Automating ssh-copy-id with Password Input for Multiple Servers


2 views

When managing multiple servers with identical credentials, manually typing passwords for each ssh-copy-id operation becomes tedious. The standard tool doesn't support command-line password input for security reasons, breaking automation workflows.

The expect utility solves this by programmatically interacting with interactive prompts. Here's a basic implementation:

#!/usr/bin/expect -f
set username "your_user"
set password "your_password"
set hosts {
    "server1.example.com"
    "server2.example.com"
    "192.168.1.100"
}

foreach host $hosts {
    spawn ssh-copy-id $username@$host
    expect {
        "password:" {
            send "$password\r"
            exp_continue
        }
        "yes/no" {
            send "yes\r"
            exp_continue
        }
        eof
    }
}

For production use, we should add error handling and logging:

#!/usr/bin/expect -f
set timeout 30
log_file ssh_copy_id.log

proc copy_key {user pass host} {
    spawn ssh-copy-id $user@$host
    expect {
        timeout {
            puts "Timeout connecting to $host"
            return 1
        }
        "password:" {
            send "$pass\r"
            exp_continue
        }
        "yes/no" {
            send "yes\r"
            exp_continue
        }
        eof
    }
    return [wait]
}

array set servers {
    web1  "192.168.1.101"
    web2  "192.168.1.102"
    db    "db.internal"
}

foreach {name host} [array get servers] {
    set result [copy_key "admin" "s3cur3P@ss" $host]
    if {[lindex $result 3] == 0} {
        puts "Successfully copied key to $name ($host)"
    } else {
        puts "Failed to copy key to $name ($host)"
    }
}

For systems without expect, we can use SSH_ASKPASS:

#!/bin/bash
USER="admin"
PASS="s3cur3P@ss"
HOSTS=("server1" "server2" "server3")

# Create simple password provider
export SSH_ASKPASS=$(mktemp)
cat > $SSH_ASKPASS <

While convenient, these methods expose passwords:

  • Passwords appear in process listings
  • Scripts containing credentials need strict permissions
  • Consider using SSH certificates or temporary credentials
  • For production, use configuration management tools like Ansible

For larger deployments, Ansible provides a cleaner solution:

---
- hosts: all
  become: false
  vars:
    ansible_user: admin
    ansible_password: "s3cur3P@ss"
    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
  tasks:
    - name: Copy SSH key
      ansible.builtin.authorized_key:
        user: "{{ ansible_user }}"
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"

When managing multiple servers with identical credentials, manually typing passwords for ssh-copy-id becomes tedious. The standard command:

ssh-copy-id user@server1
ssh-copy-id user@server2
...

requires interactive password entry for each server, defeating automation purposes.

The expect utility can automate interactive sessions. Create an expect script (auto_ssh_copy_id.exp):

#!/usr/bin/expect -f
set timeout 20
set username [lindex $argv 0]
set password [lindex $argv 1]
set hostname [lindex $argv 2]

spawn ssh-copy-id $username@$hostname
expect {
    "*yes/no*" { 
        send "yes\r"
        exp_continue
    }
    "*password*" {
        send "$password\r"
    }
}
expect eof

Execute it with:

./auto_ssh_copy_id.exp USER PASSWORD HOST

For simpler cases, sshpass provides direct password passing:

sshpass -p "your_password" ssh-copy-id user@host

Combine with a server list:

for server in $(cat server_list.txt); do
    sshpass -p "your_password" ssh-copy-id user@$server
done

For infrastructure-as-code environments, use Ansible:

- name: Deploy SSH keys
  hosts: all
  gather_facts: no
  vars:
    ansible_user: "your_user"
    ansible_password: "your_pass"
    ansible_become_pass: "your_pass"
  
  tasks:
    - name: Copy SSH key
      ansible.builtin.shell: |
        grep "{{ ansible_user }}" ~/.ssh/authorized_keys || 
        cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

While convenient, these methods expose passwords:

  • Use temporary credential files with restricted permissions
  • Clean command history afterward
  • Consider certificate-based authentication for production

For large server fleets, speed up deployment:

cat servers.txt | parallel -j 10 \
    'sshpass -p "PASSWORD" ssh-copy-id -o StrictHostKeyChecking=no user@{}'

This processes 10 servers simultaneously while suppressing host key prompts.