How to Force SSH to Fail Immediately When Public Key Authentication Fails (Prevent Password Prompts)


11 views

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:

  1. Only attempt public key authentication
  2. Never fall back to password authentication
  3. 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).