When working with automated scripts that connect to multiple servers via SSH, the default behavior of falling back to password authentication can cause significant issues. Consider this common scenario:
#!/bin/bash
servers=("server1" "server2" "server3" "server4")
for server in "${servers[@]}"; do
ssh user@$server "echo Connection successful to $server"
done
If server2 has public key authentication misconfigured, the script will hang at the password prompt, preventing execution on subsequent servers.
The -o PreferredAuthentications=publickey
option combined with -o PasswordAuthentication=no
solves this:
ssh -o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
user@server.example.com
This tells SSH:
- Only attempt public key authentication
- Never fall back to password authentication
- Fail immediately if public key auth isn't possible
Here's a robust implementation for production scripts:
#!/bin/bash
# Array of servers to connect to
servers=("web1.example.com" "web2.example.com" "db1.example.com")
# SSH options to enforce publickey-only
SSH_OPTS="-o PreferredAuthentications=publickey -o PasswordAuthentication=no -o ConnectTimeout=10"
for server in "${servers[@]}"; do
if ssh $SSH_OPTS "admin@$server" "echo Connection test successful"; then
echo "✅ $server: Success"
else
echo "❌ $server: Failed (exit code: $?)" >&2
# Continue to next server rather than exiting
fi
done
For permanent configuration, add these to your ~/.ssh/config
:
Host *
PreferredAuthentications publickey
PasswordAuthentication no
# Optional: Set a reasonable timeout
ConnectTimeout 15
# Important for automation
BatchMode yes
StrictHostKeyChecking accept-new
When implementing this in production scripts, consider these error handling patterns:
max_attempts=3
for server in "${servers[@]}"; do
attempt=1
while [ $attempt -le $max_attempts ]; do
if ssh $SSH_OPTS "$server" "true"; then
process_server "$server"
break
else
echo "Attempt $attempt failed for $server" >&2
((attempt++))
sleep $((attempt * 2)) # Exponential backoff
fi
done
done
This implements retry logic with exponential backoff while still maintaining the publickey-only requirement.
When automating SSH connections across multiple servers using public key authentication, encountering a server that falls back to password authentication can break your entire workflow. The script gets stuck waiting for password input, which:
- Blocks execution of subsequent connections
- Requires manual intervention to continue
- Makes automated monitoring/management impossible
The solution lies in these essential ssh
client parameters:
ssh -o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
-o BatchMode=yes \
user@host.example.com
Let's break down what each option does:
PreferredAuthentications
Explicitly tells SSH to only attempt public key auth:
-o PreferredAuthentications=publickey
PasswordAuthentication
Disables the fallback to password prompts:
-o PasswordAuthentication=no
BatchMode
Prevents all interactive prompts (including passwords):
-o BatchMode=yes
Here's how to implement this in a robust server-checking script:
#!/bin/bash
servers=("server1" "server2" "server3")
for server in "${servers[@]}"; do
if ssh -o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
-o BatchMode=yes \
-o ConnectTimeout=5 \
admin@"$server" true; then
echo "$server: SSH connection successful"
else
echo "$server: SSH connection failed (exit code $?)" >&2
fi
done
For better debugging, capture SSH's stderr output:
ssh_output=$(ssh -o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
-o BatchMode=yes \
user@host.example.com 2>&1)
if [ $? -ne 0 ]; then
echo "SSH failed with error: $ssh_output" >&2
# Additional error handling here
fi
For permanent settings, add to ~/.ssh/config
:
Host *
PreferredAuthentications publickey
PasswordAuthentication no
BatchMode yes
Or for specific hosts:
Host production-*
User admin
IdentityFile ~/.ssh/production_key
PreferredAuthentications publickey
PasswordAuthentication no
Verify with verbose output:
ssh -vvv -o PreferredAuthentications=publickey user@host.example.com
Look for these key lines in output:
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
user@host.example.com: Permission denied (publickey).