While ClusterSSH and pssh are popular for managing multiple servers, they fall short when dealing with SSH key authentication scenarios. Many modern server environments require private key authentication with passphrases for security compliance.
Here are three powerful alternatives that properly handle SSH key authentication:
1. tmux + SSH Config
The simplest solution combines tmux with proper SSH configuration:
# ~/.ssh/config
Host *.example.com
User admin
IdentityFile ~/.ssh/production_key
AddKeysToAgent yes
# Terminal command
tmux new-session -s cluster "ssh server1" \; \
split-window -v "ssh server2" \; \
split-window -h "ssh server3" \; \
select-pane -t 0
2. Ansible Ad-Hoc Commands
Ansible provides excellent parallel execution with key support:
# Install ansible
pip install ansible
# Create inventory file
[webservers]
web1.example.com
web2.example.com
web3.example.com
# Run command on all servers
ansible webservers -i inventory.ini -m shell -a "uptime" \
--private-key=~/.ssh/production_key --ask-pass
3. Terminator + SSH Agent
For GUI lovers, Terminator with proper SSH agent setup works wonders:
# Start SSH agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/production_key
# Configure Terminator layouts
# (GUI configuration available via right-click)
For complete control, here's a Python script using Paramiko:
import paramiko
from multiprocessing import Pool
servers = ['server1', 'server2', 'server3']
key_path = '/path/to/private_key'
passphrase = 'your_passphrase'
def run_command(server):
key = paramiko.RSAKey.from_private_key_file(key_path, password=passphrase)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(server, username='admin', pkey=key)
stdin, stdout, stderr = client.exec_command('hostname')
print(f"{server}: {stdout.read().decode()}")
client.close()
if __name__ == '__main__':
with Pool(len(servers)) as p:
p.map(run_command, servers)
Regardless of tool choice, proper key management is essential:
- Use ssh-agent to cache decrypted keys
- Configure key passphrase timeout (ssh-add -t 3600)
- Limit key permissions (chmod 600 ~/.ssh/*)
- Consider using a PKI solution like HashiCorp Vault
When managing hundreds of servers:
- Batch connections in groups of 20-50
- Use connection pooling where available
- Consider asynchronous I/O (asyncssh library)
- Monitor SSH session limits on target servers
When administering multiple Linux servers, tools like ClusterSSH and parallel-ssh (pssh) have been popular choices. However, they often fall short when dealing with key-based authentication scenarios. The main pain points include:
- No native support for SSH private key passphrases
- Limited session management capabilities
- No proper terminal multiplexing
- Lack of modern authentication methods
Here are three powerful alternatives that properly handle SSH key authentication:
1. tmux + ClusterSSH Hybrid Approach
Combine tmux's multiplexing with SSH config management:
# ~/.ssh/config example
Host prod-*
User admin
IdentityFile ~/.ssh/production_key
AddKeysToAgent yes
# tmux script to launch multiple sessions
tmux new-session -s cluster -d
tmux split-window -h
tmux send-keys "ssh prod-web01" C-m
tmux split-window -v
tmux send-keys "ssh prod-db01" C-m
tmux attach-session -t cluster
2. Ansible Ad-Hoc Commands
For quick parallel execution without full playbooks:
ansible all -i "prod-web01,prod-db01," -m shell -a "uptime" \
--private-key=~/.ssh/production_key --become --ask-become-pass
3. Terminator + SSH Config
Terminator's layout system with predefined SSH connections:
# Install terminator
sudo apt install terminator
# Sample terminator config (~/.config/terminator/config)
[layouts]
[[cluster]]
[[[child0]]]
type = Terminal
profile = default
command = ssh -i ~/.ssh/production_key admin@prod-web01
[[[child1]]]
type = Terminal
profile = default
command = ssh -i ~/.ssh/production_key admin@prod-db01
For power users needing more control, here's a Python script using paramiko:
import paramiko
from threading import Thread
servers = [
{"host": "prod-web01", "key": "~/.ssh/production_key"},
{"host": "prod-db01", "key": "~/.ssh/production_key"}
]
def ssh_session(host, key):
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect(hostname=host,
username='admin',
key_filename=key,
passphrase='your_passphrase_here')
stdin, stdout, stderr = client.exec_command('hostname')
print(f"{host}: {stdout.read().decode()}")
threads = []
for server in servers:
t = Thread(target=ssh_session, args=(server['host'], server['key']))
threads.append(t)
t.start()
for t in threads:
t.join()
When working with private keys across multiple sessions:
- Always use ssh-agent with timeout settings
- Consider using hardware security modules for production keys
- Implement proper session logging and auditing
- Use different keys for different server tiers