Skip to content

A fully configurable SSH server Docker container designed specifically for integration testing, development, and SSH client validation. Built with security best practices and complete runtime configurability.

License

Notifications You must be signed in to change notification settings

billchurch/ssh_test

Repository files navigation

SSH Test Server

Build and Push Integration Tests

A fully configurable SSH server Docker container designed specifically for integration testing, development, and SSH client validation. Built with security best practices and complete runtime configurability.

Available in two optimized variants:

  • Debian-based (ghcr.io/billchurch/ssh_test:debian): 118MB - Maximum compatibility with full GNU toolchain
  • Alpine-based (ghcr.io/billchurch/ssh_test:alpine): 13.8MB - Ultra-minimal footprint for resource-constrained environments

Features

  • πŸ”§ Fully Configurable: All SSH settings configurable via environment variables
  • πŸ” Multiple Auth Methods: Password, public key, and keyboard-interactive authentication
  • πŸ—οΈ Multi-Architecture: Supports AMD64 and ARM64 architectures
  • πŸ›‘οΈ Security Focused: Configurable security hardening options
  • πŸ› Debug Support: Adjustable SSH debug levels with proper logging
  • πŸ“Š CI/CD Ready: Automated builds, testing, and container registry publishing
  • πŸ§ͺ Testing Tools: Comprehensive test scripts and integration tests included

Quick Start

Choosing Your Image

Choose the image variant that best fits your needs:

# Alpine - Ultra-minimal (13.8MB)
docker pull ghcr.io/billchurch/ssh_test:alpine

# Debian - Full compatibility (118MB, default)
docker pull ghcr.io/billchurch/ssh_test:debian
# or 
docker pull ghcr.io/billchurch/ssh_test:latest  # same as debian

Using Docker

# Basic password authentication (Alpine - minimal)
docker run -d --name ssh-test-alpine \
  -p 2225:22 \
  -e SSH_USER=testuser \
  -e SSH_PASSWORD=testpass123 \
  ghcr.io/billchurch/ssh_test:alpine

# Basic password authentication (Debian - full compatibility)
docker run -d --name ssh-test-debian \
  -p 2224:22 \
  -e SSH_USER=testuser \
  -e SSH_PASSWORD=testpass123 \
  ghcr.io/billchurch/ssh_test:debian

# Test the connections
ssh -p 2225 testuser@localhost  # Alpine
ssh -p 2224 testuser@localhost  # Debian

Using Docker Compose

# Clone the repository
git clone https://github.com/billchurch/ssh-test-server.git
cd ssh-test-server/examples

# Run Alpine version (minimal)
docker-compose --profile alpine up -d

# Run Debian version (full compatibility)
docker-compose --profile debian up -d

# Run both versions simultaneously
docker-compose --profile all up -d

# Traditional profiles still available
docker-compose --profile basic up -d        # Basic password auth
docker-compose --profile pubkey up -d       # Public key auth only
docker-compose --profile hardened up -d     # Security hardened

Configuration

All configuration is done through environment variables:

Basic Configuration

Variable Default Description
SSH_USER testuser SSH username to create
SSH_PASSWORD (empty) Password for the SSH user
SSH_PORT 22 SSH server port
SSH_DEBUG_LEVEL 0 Debug level (0=none, 1=verbose, 2=debug, 3=debug3)

Authentication Configuration

Variable Default Description
SSH_PERMIT_PASSWORD_AUTH yes Enable password authentication
SSH_PERMIT_PUBKEY_AUTH yes Enable public key authentication
SSH_CHALLENGE_RESPONSE_AUTH no Enable keyboard-interactive auth
SSH_AUTHORIZED_KEYS (empty) SSH public keys (newline separated)
SSH_AUTH_METHODS any Specific auth methods to require

Security Configuration

Variable Default Description
SSH_PERMIT_ROOT_LOGIN no Allow root login
SSH_PERMIT_EMPTY_PASSWORDS no Allow empty passwords
SSH_MAX_AUTH_TRIES 6 Maximum authentication attempts
SSH_LOGIN_GRACE_TIME 120 Login grace time in seconds
SSH_USE_DNS no Perform DNS lookups

Forwarding Configuration

Variable Default Description
SSH_X11_FORWARDING no Enable X11 forwarding
SSH_AGENT_FORWARDING no Enable SSH agent forwarding
SSH_TCP_FORWARDING no Enable TCP forwarding

SSH Agent Configuration

The SSH test server includes comprehensive SSH agent support for testing agent-based authentication and forwarding scenarios.

Variable Default Description
SSH_AGENT_START no Start internal SSH agent in container
SSH_AGENT_KEYS (empty) Base64-encoded private keys to load into agent (newline-separated)
SSH_AGENT_SOCKET_PATH /tmp/ssh-agent.sock Custom path for SSH agent socket

SSH Agent Features

  • Internal Agent: Start an SSH agent inside the container with SSH_AGENT_START=yes
  • Key Loading: Automatically load private keys into the agent using SSH_AGENT_KEYS
  • Agent Forwarding: Allow client agents to be forwarded with SSH_AGENT_FORWARDING=yes
  • Custom Socket: Use custom socket paths for testing specific scenarios
  • Environment Setup: Automatically configures user environment for agent access

SSH Agent Security Notes

⚠️ Important: The SSH_AGENT_KEYS variable contains sensitive private key data. In production or CI/CD environments:

  • Load keys from secure environment variables or secrets management
  • Never commit private keys to version control
  • Use temporary keys for testing when possible
  • Consider using key passphrases for additional security

Advanced Configuration

Variable Default Description
SSH_HOST_KEYS (auto-generated) Custom host keys (base64 encoded)
SSH_CUSTOM_CONFIG (empty) Additional sshd_config directives
SSH_USE_PAM no Use PAM for authentication

SSH_CUSTOM_CONFIG Examples

The SSH_CUSTOM_CONFIG environment variable allows you to add any additional SSH configuration directives that aren't explicitly handled by other environment variables. Multiple directives can be stacked using newlines.

# Allow specific environment variables from SSH client
docker run -d \
  -p 2244:22 \
  -e SSH_USER=testuser \
  -e SSH_PASSWORD=testpassword \
  -e SSH_AUTHORIZED_KEYS="$(cat ./test-keys/*.pub)" \
  -e SSH_DEBUG_LEVEL=3 \
  -e SSH_PERMIT_PASSWORD_AUTH=yes \
  -e SSH_PERMIT_PUBKEY_AUTH=yes \
  -e SSH_CUSTOM_CONFIG=$'PermitUserEnvironment yes\nAcceptEnv FOO' \
  ghcr.io/billchurch/ssh_test:alpine

# Multiple custom configurations
docker run -d \
  -e SSH_CUSTOM_CONFIG=$'MaxSessions 10\nClientAliveInterval 30\nClientAliveCountMax 3' \
  ghcr.io/billchurch/ssh_test:alpine

Usage Examples

Password Authentication Only

docker run -d --name ssh-password-test \
  -p 2224:22 \
  -e SSH_USER=testuser \
  -e SSH_PASSWORD=secure123 \
  -e SSH_PERMIT_PASSWORD_AUTH=yes \
  -e SSH_PERMIT_PUBKEY_AUTH=no \
  ghcr.io/billchurch/ssh_test:latest

Public Key Authentication Only

# Generate a test key
ssh-keygen -t ed25519 -f test_key -N ""

# Start container with public key
docker run -d --name ssh-key-test \
  -p 2224:22 \
  -e SSH_USER=keyuser \
  -e "SSH_AUTHORIZED_KEYS=$(cat test_key.pub)" \
  -e SSH_PERMIT_PASSWORD_AUTH=no \
  -e SSH_PERMIT_PUBKEY_AUTH=yes \
  ghcr.io/billchurch/ssh_test:latest

# Connect with the key
ssh -i test_key -p 2224 keyuser@localhost

Note: When no password is provided (SSH_PASSWORD not set), the user account is automatically unlocked by setting a dummy password hash (*) to enable SSH key authentication while still preventing password logins.

Security Hardened Configuration

docker run -d --name ssh-secure-test \
  -p 2224:22 \
  -e SSH_USER=secureuser \
  -e SSH_PASSWORD=VerySecureP@ss123 \
  -e SSH_PERMIT_ROOT_LOGIN=no \
  -e SSH_MAX_AUTH_TRIES=3 \
  -e SSH_LOGIN_GRACE_TIME=60 \
  -e SSH_USE_DNS=no \
  -e SSH_X11_FORWARDING=no \
  -e SSH_AGENT_FORWARDING=no \
  -e SSH_TCP_FORWARDING=no \
  ghcr.io/billchurch/ssh_test:latest

Debug Mode

docker run -d --name ssh-debug-test \
  -p 2224:22 \
  -e SSH_USER=debuguser \
  -e SSH_PASSWORD=debugpass \
  -e SSH_DEBUG_LEVEL=3 \
  ghcr.io/billchurch/ssh_test:latest

# View debug logs
docker logs -f ssh-debug-test

SSH Agent Examples

Basic Agent with Forwarding

docker run -d --name ssh-agent-test \
  -p 2224:22 \
  -e SSH_USER=agentuser \
  -e SSH_PASSWORD=agentpass \
  -e SSH_AGENT_START=yes \
  -e SSH_AGENT_FORWARDING=yes \
  ghcr.io/billchurch/ssh_test:latest

Agent with Preloaded Keys

# Generate a test key
ssh-keygen -t ed25519 -f test_agent_key -N "" -C "test@example.com"

# Encode the private key for the container
KEY_DATA=$(base64 -i test_agent_key | tr -d '\n')

# Run container with agent and key
docker run -d --name ssh-agent-keys \
  -p 2224:22 \
  -e SSH_USER=keyuser \
  -e SSH_AUTHORIZED_KEYS="$(cat test_agent_key.pub)" \
  -e SSH_PERMIT_PASSWORD_AUTH=no \
  -e SSH_AGENT_START=yes \
  -e SSH_AGENT_KEYS="${KEY_DATA}" \
  ghcr.io/billchurch/ssh_test:latest

# Test SSH connection using the agent
ssh -p 2224 keyuser@localhost

# Cleanup
rm -f test_agent_key test_agent_key.pub

Agent Forwarding Only (No Internal Agent)

# For testing client agent forwarding
docker run -d --name ssh-forwarding-test \
  -p 2224:22 \
  -e SSH_USER=forwarduser \
  -e SSH_PASSWORD=forwardpass \
  -e SSH_AGENT_FORWARDING=yes \
  -e SSH_AGENT_START=no \
  ghcr.io/billchurch/ssh_test:latest

# Connect with agent forwarding enabled
ssh -A -p 2224 forwarduser@localhost

Testing Tools

The project includes comprehensive testing tools:

Basic Connection Test

# Test password authentication
./scripts/test-connection.sh --host localhost --port 2224 --user testuser --password testpass123

# Test public key authentication
./scripts/test-connection.sh --host localhost --port 2224 --user testuser --key ~/.ssh/id_rsa

# Verbose mode
./scripts/test-connection.sh --host localhost --port 2224 --user testuser --password testpass123 --verbose

Authentication Methods Test

# Test against a running container
./scripts/test-auth-methods.sh --container ssh-test-server --user testuser

# Generate test keys and run comprehensive tests
./scripts/test-auth-methods.sh --container ssh-test-server --user testuser --generate-keys --cleanup-keys

Integration Test Suite

# Run full integration tests
./tests/integration/run-tests.sh

# Run tests with custom image
./tests/integration/run-tests.sh --image your-custom-image:latest

# Run tests in parallel with reporting
./tests/integration/run-tests.sh --parallel --report test-results.txt

Development

Building the Images

# Build both variants
make build-all

# Build specific variants
make build-debian    # Debian-based image
make build-alpine    # Alpine-based image

# Build for local testing
make build-dev       # Development version (Debian)

# Build multi-architecture (requires buildx)
docker buildx build --platform linux/amd64,linux/arm64 -f docker/Dockerfile -t ssh-test-server:multi .

Running Tests

# Test both image variants
make test-all

# Test specific variants
make test-debian     # Test Debian image
make test-alpine     # Test Alpine image

# Run connection tests
make test-connection-debian   # Test Debian container connection
make test-connection-alpine   # Test Alpine container connection

# Run authentication tests  
make test-auth-debian        # Test Debian auth methods
make test-auth-alpine        # Test Alpine auth methods

# Compare image sizes
make compare-sizes

# Full integration test with build
make integration-test        # Build both + test both

Contributing

We welcome contributions! This project uses automated releases and conventional commit messages.

Quick Start:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Make changes following our contribution guidelines
  4. Use conventional commit messages: feat: add new feature
  5. Run tests: ./tests/integration/run-tests.sh
  6. Submit a pull request

Important: Please read our CONTRIBUTING.md for detailed guidelines on:

  • Conventional commit message format
  • Development workflow
  • Testing requirements
  • Release process
  • Code style guidelines

CI/CD Pipeline

The project uses GitHub Actions for:

  • Multi-architecture builds for AMD64 and ARM64
  • Automated testing with comprehensive integration tests
  • Security scanning with Trivy vulnerability scanner
  • Container publishing to GitHub Container Registry
  • SBOM generation for supply chain security

GitHub Container Registry

Images are automatically published to GitHub Container Registry:

# Latest stable release (Debian)
docker pull ghcr.io/billchurch/ssh_test:latest

# Specific version (Debian)
docker pull ghcr.io/billchurch/ssh_test:v1.0.2

# Specific version with variant
docker pull ghcr.io/billchurch/ssh_test:v1.0.2-alpine
docker pull ghcr.io/billchurch/ssh_test:v1.0.2-debian

# Latest variant tags
docker pull ghcr.io/billchurch/ssh_test:alpine
docker pull ghcr.io/billchurch/ssh_test:debian

# Development builds
docker pull ghcr.io/billchurch/ssh_test:main

Use Cases

Integration Testing

Perfect for testing SSH clients, automation tools, and deployment scripts:

# Start test environment
docker-compose --profile basic up -d

# Run your SSH client tests
pytest tests/ssh_client_tests.py

# Cleanup
docker-compose --profile basic down

WebSSH Testing

Ideal for testing web-based SSH clients like WebSSH2:

# Start SSH test server
docker run -d --name webssh-test \
  -p 4444:22 \
  -e SSH_USER=webuser \
  -e SSH_PASSWORD=webpass123 \
  -e SSH_DEBUG_LEVEL=2 \
  ghcr.io/billchurch/ssh_test:latest

# Configure your WebSSH client to connect to localhost:4444

Development Environment

Use as a consistent SSH target for development:

# Development setup with volume mounts
docker run -d --name dev-ssh \
  -p 2224:22 \
  -e SSH_USER=developer \
  -e SSH_PASSWORD=devpass \
  -v ./workspace:/home/developer/workspace \
  ghcr.io/billchurch/ssh_test:latest

Troubleshooting

Common Issues

Container fails to start:

# Check container logs
docker logs container-name

# Verify environment variables
docker inspect container-name | jq '.Config.Env'

SSH connection refused:

# Test port connectivity
nc -zv localhost 2224

# Check if SSH service is running
docker exec container-name ps aux | grep sshd

Authentication failures:

# Enable debug mode
docker run ... -e SSH_DEBUG_LEVEL=3 ...

# Check authentication configuration
docker exec container-name cat /etc/ssh/sshd_config

SSH Public Key Authentication Issues:

When using public key authentication without setting a password (SSH_PASSWORD not provided), the container automatically unlocks the user account to allow key-based authentication. This is necessary because:

  1. Linux locks user accounts that have no password set (marked with ! in /etc/shadow)
  2. SSH daemon refuses authentication to locked accounts, even with valid SSH keys
  3. The entrypoint script sets a dummy password hash (*) which:
    • Unlocks the account for SSH key authentication
    • Still prevents password-based login (no valid password can match *)

If you experience issues with key authentication:

# Check if user account is locked
docker exec container-name grep username /etc/shadow
# If you see '!' or '!!' in the password field, the account is locked

# Manually unlock (if needed)
docker exec container-name usermod -p '*' username

# Verify SSH key is properly set
docker exec container-name cat /home/username/.ssh/authorized_keys

Debug Mode

Enable maximum debug output:

docker run -d --name ssh-debug \
  -p 2224:22 \
  -e SSH_USER=testuser \
  -e SSH_PASSWORD=testpass \
  -e SSH_DEBUG_LEVEL=3 \
  ghcr.io/billchurch/ssh_test:latest

# Watch debug logs in real-time
docker logs -f ssh-debug

Security Considerations

While designed for testing, security best practices are followed:

  • Non-root user execution when possible
  • Configurable authentication restrictions
  • No default passwords in production images
  • Regular security updates via automated rebuilds
  • Minimal attack surface with optimized Debian slim base

Note: This container is designed for testing environments. Do not expose it directly to the internet without additional security measures.

Choosing Between Alpine and Debian

Use Alpine When:

  • Size matters: Ultra-minimal 13.8MB footprint
  • Resource-constrained environments: Kubernetes pods, edge computing
  • Fast deployment: Quicker download and startup times
  • Security: Smaller attack surface with minimal components
  • Simple SSH testing: Basic SSH client validation

Use Debian When:

  • Maximum compatibility: Full GNU toolchain and libraries
  • Complex applications: Applications requiring specific libraries
  • Legacy systems: Compatibility with older SSH clients
  • Development: More debugging tools and utilities available
  • Production-like testing: Closer to typical server environments

Performance Comparison:

Metric Alpine Debian
Image Size 13.8MB 118MB
Download Time ~2 seconds ~15 seconds
Memory Usage ~8MB ~20MB
Startup Time ~1 second ~2 seconds
Compatibility Good Excellent

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

See CHANGELOG.md for version history and changes.

Support

  • πŸ› Issues: Report bugs and feature requests on GitHub Issues
  • πŸ“– Documentation: Comprehensive documentation in this README and code comments
  • πŸ’¬ Discussions: Community support via GitHub Discussions

Acknowledgments

  • Debian variant: Built with Debian bookworm-slim for optimal size and compatibility (118MB)
  • Alpine variant: Built with Alpine Linux for ultra-minimal footprint (13.8MB)
  • Uses OpenSSH for robust SSH implementation
  • Optimized Docker images with smart OS detection and adaptive configuration
  • Dual-architecture support with 88% size reduction possible via Alpine
  • Inspired by the need for better SSH integration testing tools

About

A fully configurable SSH server Docker container designed specifically for integration testing, development, and SSH client validation. Built with security best practices and complete runtime configurability.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages