What's Actually Happening

Your automation scripts or CI/CD pipelines are failing when connecting to SSH servers that display login banners. The script expects clean SSH output but receives multi-line legal disclaimers or welcome messages that break parsing logic. Some scripts even timeout waiting for input or fail to detect successful authentication.

The Error You'll See

In your automation output:

bash
ERROR: Unexpected output before command response
Expected: 'user@host:~$'
Got: '***************************************\n* WARNING: Authorized access only...  *\n***************************************'

Or using SSH in a script:

bash
#!/bin/bash
output=$(ssh user@server 'hostname')
echo "$output"

Outputs:

bash
***************************************
*    WARNING: Authorized access only    *
*    All activity is logged             *
***************************************
server-hostname

Your script only wants the hostname but gets the banner too.

Why This Happens

SSH banners are defined in sshd_config with the Banner directive. They display before authentication completes, which means:

  1. 1.The banner arrives before your command output
  2. 2.Banners can contain any characters, including ones that break parsers
  3. 3.Some libraries don't distinguish banner from command output
  4. 4.The banner is sent to stderr, but some clients mix it with stdout

Step 1: Identify Banner Configuration

Check if the server has a banner configured:

bash
ssh -v user@server 2>&1 | grep -i banner

You might see:

bash
debug1: SSH2_MSG_USERAUTH_BANNER received

On the server, check the configuration:

bash
grep -i banner /etc/ssh/sshd_config

Look for:

bash
Banner /etc/issue.net

Step 2: Suppress Banner in SSH Client

For your scripts, tell SSH to suppress the banner. There's no direct flag, but you can use LogLevel:

bash
ssh -o LogLevel=ERROR user@server 'command'

This suppresses informational messages including banners.

Step 3: Separate Banner from Output

Banners go to stderr, while command output goes to stdout. Capture them separately:

bash
#!/bin/bash
output=$(ssh user@server 'hostname' 2>/dev/null)
echo "Hostname: $output"

Or capture both separately:

bash
#!/bin/bash
output=$(ssh user@server 'hostname' 2>&1)
stderr_output=$(ssh user@server 'hostname' 2>&1 1>/dev/null)
echo "Output: $output"
echo "Stderr: $stderr_output"

Step 4: Use SSH Config for Automation

Create a dedicated SSH config for scripts:

bash
# ~/.ssh/config.automation
Host *
    LogLevel ERROR
    BatchMode yes
    StrictHostKeyChecking no

Then use it:

bash
ssh -F ~/.ssh/config.automation user@server 'command'

Step 5: Handle Banner in Python Scripts

Using Paramiko:

```python import paramiko

client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect('server', username='user')

# Banner is available separately banner = client.get_transport().get_banner() if banner: print(f"Banner: {banner.decode()}")

# Run command stdin, stdout, stderr = client.exec_command('hostname') print(stdout.read().decode()) ```

Using subprocess:

```python import subprocess

result = subprocess.run( ['ssh', '-o', 'LogLevel=ERROR', 'user@server', 'hostname'], capture_output=True, text=True ) print(result.stdout) # Clean output, no banner ```

Step 6: Disable Banner for Specific Users (Server-Side)

If you control the server, you can disable the banner for automation users:

bash
# /etc/ssh/sshd_config
Match User automation-user
    Banner none

Then restart SSH:

bash
sudo systemctl restart sshd

Step 7: Parse Around the Banner

If you must work with the banner, skip it programmatically:

bash
#!/bin/bash
output=$(ssh user@server 'echo "---MARKER---"; hostname')
hostname=$(echo "$output" | sed -n '/^---MARKER---$/,$ p' | tail -n +2)
echo "Hostname: $hostname"

Verify the Fix

Your scripts handle banners correctly when:

  1. 1.SSH command output contains only the expected data
  2. 2.No parsing errors from unexpected characters
  3. 3.CI/CD pipelines complete without banner-related failures
  4. 4.Banners are either suppressed or properly separated from command output

Test with a script that verifies clean output:

bash
#!/bin/bash
output=$(ssh -o LogLevel=ERROR user@server 'hostname')
if [[ "$output" =~ ^[a-zA-Z0-9.-]+$ ]]; then
    echo "Clean hostname: $output"
else
    echo "Unexpected characters in output"
fi