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


3 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).