Last active
May 4, 2026 17:08
-
-
Save vxgmichel/f5e30b571307cf43eb20d0947958b8c4 to your computer and use it in GitHub Desktop.
Test SSH public key authentication with different version of openssh client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -eu | |
| # Constants and defaults | |
| USER="root" | |
| PASSWORD="" | |
| VERSIONS=("7.2_p2-r5" "7.9_p1-r6" "8.8_p1-r1" "9.9_p2-r0" "10.2_p1-r0") | |
| KEY_TYPES=("rsa" "ed25519") | |
| SSH_OPTIONS="-o StrictHostKeyChecking=no" | |
| print_help() { | |
| cat <<'EOF' | |
| Usage: | |
| test-ssh-public-key-auth.sh <host> | |
| Description: | |
| Runs an SSH public key authentication matrix test with: | |
| - OpenSSH client versions: 7.2_p2-r5, 7.9_p1-r6, 8.8_p1-r1, 9.9_p2-r0, 10.2_p1-r0 | |
| - Key types: rsa, ed25519 | |
| Requires Docker to be installed. | |
| EOF | |
| } | |
| silent_unless_fail() { | |
| local err | |
| # Run the command passed as arguments, capture stderr, discard stdout | |
| if ! err=$("$@" 2>/dev/stdout >/dev/null); then | |
| echo -e "\b\b\b: ❌\n$err" >&2 | |
| return 1 | |
| fi | |
| } | |
| status() { | |
| # Rewrite a single terminal status line for step-by-step progress. | |
| printf "\r\033[K$CURRENT_PREFIX %s" "$1" | |
| } | |
| build_prefix() { | |
| local host="$1" | |
| local version="$2" | |
| local key_type="$3" | |
| local version_fixed | |
| local key_type_fixed | |
| C_RESET=$'\e[0m' | |
| C_DIM=$'\e[2m' | |
| C_HOST_LABEL=$'\e[36m' | |
| C_HOST_VALUE=$'\e[1;96m' | |
| C_VER_LABEL=$'\e[33m' | |
| C_VER_VALUE=$'\e[1;93m' | |
| C_KEY_LABEL=$'\e[32m' | |
| C_KEY_VALUE=$'\e[1;92m' | |
| printf -v version_fixed "%-10.10s" "$version" | |
| printf -v key_type_fixed "%-7.7s" "$key_type" | |
| local prefix="${C_DIM}[${C_RESET}" | |
| prefix+="${C_HOST_LABEL}host${C_RESET}:${C_HOST_VALUE}${host}${C_RESET} " | |
| prefix+="${C_DIM}|${C_RESET} " | |
| prefix+="${C_VER_LABEL}ver${C_RESET}:${C_VER_VALUE}${version_fixed}${C_RESET} " | |
| prefix+="${C_DIM}|${C_RESET} " | |
| prefix+="${C_KEY_LABEL}key${C_RESET}:${C_KEY_VALUE}${key_type_fixed}${C_RESET}" | |
| prefix+="${C_DIM}]${C_RESET}" | |
| printf "%s" "$prefix" | |
| } | |
| run_case() { | |
| local version="$1" | |
| local key_type="$2" | |
| local image="sig9/alpine-openssh-client:$version" | |
| local key_name="regression_testing_${version}_${key_type}" | |
| local comment="$(uuidgen)@testing-${version}-${key_type}" | |
| local cleanup="sed -i /$comment\$/d ~/.ssh/authorized_keys" | |
| CURRENT_PREFIX="$(build_prefix "$HOST" "$version" "$key_type")" | |
| status "Pulling Docker image $image..." | |
| if ! silent_unless_fail docker pull "$image"; then | |
| return 1 | |
| fi | |
| status "Testing SSH connection to $USER@$HOST using $image" | |
| rm -f "$key_name" "$key_name.pub" | |
| status "Generating SSH key pair..." | |
| if ! silent_unless_fail docker run --rm -v "$(pwd):/data" "$image" -c "cd /data && ssh-keygen -t $key_type -f $key_name -C '$comment' -N ''"; then | |
| rm -f "$key_name" "$key_name.pub" | |
| return 1 | |
| fi | |
| status "Copying public key to $USER@$HOST..." | |
| if ! silent_unless_fail docker run --rm -v "$(pwd):/data" "$image" -c "mkdir -p /root/.ssh && sshpass -p '$PASSWORD' ssh-copy-id -i /data/$key_name.pub $SSH_OPTIONS '$USER@$HOST'"; then | |
| rm -f "$key_name" "$key_name.pub" | |
| return 1 | |
| fi | |
| status "Verifying SSH connection..." | |
| if ! silent_unless_fail docker run --rm -v "$(pwd):/data" "$image" -c "ssh $SSH_OPTIONS -i /data/$key_name '$USER@$HOST' 'echo hello'"; then | |
| rm -f "$key_name" "$key_name.pub" | |
| return 1 | |
| fi | |
| status "SSH connection successful, cleaning up..." | |
| if ! silent_unless_fail docker run --rm -v "$(pwd):/data" "$image" -c "ssh $SSH_OPTIONS -i /data/$key_name '$USER@$HOST' '$cleanup'"; then | |
| rm -f "$key_name" "$key_name.pub" | |
| return 1 | |
| fi | |
| rm -f "$key_name" "$key_name.pub" | |
| status "PASS ✅" | |
| printf "\n" | |
| return 0 | |
| } | |
| # Parse arguments | |
| if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then | |
| print_help | |
| exit 0 | |
| fi | |
| if [ "$#" -ne 1 ]; then | |
| echo "Expected exactly one argument: <host>" >&2 | |
| print_help | |
| exit 1 | |
| fi | |
| HOST="$1" | |
| # Prompt for password | |
| if [ -z "$PASSWORD" ]; then | |
| read -s -p $'Enter \e[1m'"$USER@$HOST"$'\e[0m password: ' PASSWORD | |
| echo "" | |
| fi | |
| # Run the matrix of tests | |
| TOTAL=0 | |
| PASSED=0 | |
| FAILED=0 | |
| for VERSION in "${VERSIONS[@]}"; do | |
| for KEY_TYPE in "${KEY_TYPES[@]}"; do | |
| TOTAL=$((TOTAL + 1)) | |
| if run_case "$VERSION" "$KEY_TYPE"; then | |
| PASSED=$((PASSED + 1)) | |
| else | |
| FAILED=$((FAILED + 1)) | |
| fi | |
| done | |
| done | |
| printf "\e[1mMatrix run complete\e[0m total: %d \e[32mpassed: %d\e[0m \e[31mfailed: %d\e[0m\n" "$TOTAL" "$PASSED" "$FAILED" | |
| if [ "$FAILED" -gt 0 ]; then | |
| exit 1 | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment