Skip to main content

A Python library for persistent SSH agent management with automatic key handling, focusing on Windows compatibility and seamless Git integration.

Project description

persistent-ssh-agent

Python Version Nox PyPI Version Downloads Downloads Downloads License PyPI Format Maintenance Codecov

English | ไธญๆ–‡

๐Ÿ” A modern Python library for persistent SSH agent management across sessions.

๐Ÿ“š Table of Contents

โœจ Features

  • ๐Ÿ”„ Persistent SSH agent management across sessions
  • ๐Ÿ”‘ Automatic SSH key loading and caching
  • ๐ŸชŸ Windows-optimized implementation
  • ๐Ÿ”— Seamless Git integration
  • ๐ŸŒ Cross-platform compatibility (Windows, Linux, macOS)
  • ๐Ÿ“ฆ No external dependencies beyond standard SSH tools
  • ๐Ÿ”’ Secure key management and session control with AES-256 encryption
  • โšก Asynchronous operation support
  • ๐Ÿงช Complete unit test coverage with performance benchmarks
  • ๐Ÿ“ Comprehensive type hints support
  • ๐Ÿ” Support for multiple SSH key types (Ed25519, ECDSA, RSA)
  • ๐ŸŒ IPv6 support
  • ๐Ÿ“š Multi-language documentation support
  • ๐Ÿ” Enhanced SSH configuration validation
  • ๐Ÿ› ๏ธ Modern development toolchain (Poetry, Commitizen, Black)
  • ๐Ÿ”‘ Git credential helper integration for seamless Git operations
  • ๐Ÿ’ป Command-line interface with comprehensive configuration options
  • ๐Ÿง  Smart authentication strategies with automatic fallback mechanisms
  • ๐Ÿ” Comprehensive health check and diagnostic capabilities
  • ๐Ÿงน Automatic cleanup of invalid credential configurations

๐Ÿš€ Installation

pip install persistent-ssh-agent

๐Ÿ“‹ Requirements

  • Python 3.8-3.13
  • OpenSSH (ssh-agent, ssh-add) installed and available in PATH
  • Git (optional, for Git operations)

๐Ÿ“– Usage

Basic Usage

from persistent_ssh_agent import PersistentSSHAgent

# Create an instance with custom expiration time (default is 24 hours)
ssh_agent = PersistentSSHAgent(expiration_time=86400)

# Set up SSH for a specific host
if ssh_agent.setup_ssh('github.com'):
    print("โœ… SSH authentication ready!")

Advanced Configuration

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

# Create custom SSH configuration
config = SSHConfig(
    identity_file='~/.ssh/github_key',  # Optional specific identity file
    identity_passphrase='your-passphrase',  # Optional passphrase
    ssh_options={  # Optional SSH options
        'StrictHostKeyChecking': 'yes',
        'PasswordAuthentication': 'no',
        'PubkeyAuthentication': 'yes'
    }
)

# Initialize with custom config and agent reuse settings
ssh_agent = PersistentSSHAgent(
    config=config,
    expiration_time=86400,  # Optional: Set agent expiration time (default 24 hours)
    reuse_agent=True  # Optional: Control agent reuse behavior (default True)
)

# Set up SSH authentication
if ssh_agent.setup_ssh('github.com'):
    # Get Git SSH command for the host
    ssh_command = ssh_agent.get_git_ssh_command('github.com')
    if ssh_command:
        print("โœ… Git SSH command ready!")

Agent Reuse Behavior

The reuse_agent parameter controls how the SSH agent handles existing sessions:

  • When reuse_agent=True (default):

    • Attempts to reuse an existing SSH agent if available
    • Reduces the number of agent startups and key additions
    • Improves performance by avoiding unnecessary agent operations
  • When reuse_agent=False:

    • Always starts a new SSH agent session
    • Useful when you need a fresh agent state
    • May be preferred in certain security-sensitive environments

Example with agent reuse disabled:

# Always start a new agent session
ssh_agent = PersistentSSHAgent(reuse_agent=False)

Multiple Host Configuration

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

# Create configuration with common options
config = SSHConfig(
    ssh_options={
        'BatchMode': 'yes',
        'StrictHostKeyChecking': 'yes',
        'ServerAliveInterval': '60'
    }
)

# Initialize agent
agent = PersistentSSHAgent(config=config)

# Set up SSH for multiple hosts
hosts = ['github.com', 'gitlab.com', 'bitbucket.org']
for host in hosts:
    if agent.setup_ssh(host):
        print(f"โœ… SSH configured for {host}")
    else:
        print(f"โŒ Failed to configure SSH for {host}")

Global SSH Configuration

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

# Create configuration with global options
config = SSHConfig(
    # Set identity file (optional)
    identity_file='~/.ssh/id_ed25519',

    # Set global SSH options
    ssh_options={
        'StrictHostKeyChecking': 'yes',
        'PasswordAuthentication': 'no',
        'PubkeyAuthentication': 'yes',
        'BatchMode': 'yes',
        'ConnectTimeout': '30'
    }
)

# Initialize agent with global configuration
agent = PersistentSSHAgent(config=config)

Asynchronous Support

import asyncio
from persistent_ssh_agent import PersistentSSHAgent

async def setup_multiple_hosts(hosts: list[str]) -> dict[str, bool]:
    """Set up SSH for multiple hosts concurrently."""
    ssh_agent = PersistentSSHAgent()
    results = {}

    async def setup_host(host: str):
        results[host] = await ssh_agent.async_setup_ssh(host)

    await asyncio.gather(*[setup_host(host) for host in hosts])
    return results

# Usage example
async def main():
    hosts = ['github.com', 'gitlab.com', 'bitbucket.org']
    results = await setup_multiple_hosts(hosts)
    for host, success in results.items():
        print(f"{host}: {'โœ…' if success else 'โŒ'}")

asyncio.run(main())

Security Best Practices

  1. Key Management:

    • Store SSH keys in standard locations (~/.ssh/)
    • Use Ed25519 keys for better security
    • Keep private keys protected (600 permissions)
  2. Error Handling:

    try:
        ssh_agent = PersistentSSHAgent()
        success = ssh_agent.setup_ssh('github.com')
        if not success:
            print("โš ๏ธ SSH setup failed")
    except Exception as e:
        print(f"โŒ Error: {e}")
    
  3. Session Management:

    • Agent information persists across sessions
    • Automatic cleanup of expired sessions
    • Configurable expiration time
    • Multi-session concurrent management
  4. Security Features:

    • Automatic key unloading after expiration
    • Secure temporary file handling
    • Platform-specific security measures
    • Key usage tracking

๐Ÿ”ง Common Use Cases

Command Line Interface (CLI)

The library provides a command-line interface for easy configuration and testing:

# Configure SSH agent with a specific identity file
uvx persistent_ssh_agent config --identity-file ~/.ssh/id_ed25519 --prompt-passphrase

# Test SSH connection to a host
uvx persistent_ssh_agent test github.com

# List configured SSH keys
uvx persistent_ssh_agent list

# Remove a specific SSH key
uvx persistent_ssh_agent remove --name github

# Export configuration to a file
uvx persistent_ssh_agent export --output ~/.ssh/config.json

# Import configuration from a file
uvx persistent_ssh_agent import config.json

# Set up Git credentials
uvx persistent_ssh_agent git-setup --username your-username --prompt

# Test Git credentials validity
uvx persistent_ssh_agent test-credentials github.com

# Perform comprehensive health check
uvx persistent_ssh_agent health-check

# Smart authentication setup with automatic fallback
uvx persistent_ssh_agent smart-setup github.com --strategy auto

Available commands:

  • config: Configure SSH agent settings

    • --identity-file: Path to SSH identity file
    • --passphrase: SSH key passphrase (not recommended, use --prompt-passphrase instead)
    • --prompt-passphrase: Prompt for SSH key passphrase
    • --expiration: Expiration time in hours
    • --reuse-agent: Whether to reuse existing SSH agent
  • test: Test SSH connection to a host

    • hostname: Hostname to test connection with
    • --identity-file: Path to SSH identity file (overrides config)
    • --expiration: Expiration time in hours (overrides config)
    • --reuse-agent: Whether to reuse existing SSH agent (overrides config)
    • --verbose: Enable verbose output
  • list: List configured SSH keys

  • remove: Remove configured SSH keys

    • --name: Name of the key to remove
    • --all: Remove all keys
  • export: Export configuration

    • --output: Output file path
    • --include-sensitive: Include sensitive information in export
  • import: Import configuration

    • input_file: Input file path
  • git-setup: Configure Git credentials

    • --username: Git username
    • --password: Git password (not recommended, use --prompt instead)
    • --prompt: Prompt for Git credentials interactively
  • test-credentials: Test Git credentials validity

    • hostname: Git host to test (optional, tests all common hosts if not specified)
    • --username: Git username for testing
    • --password: Git password for testing
    • --timeout: Timeout in seconds for each test
  • health-check: Perform comprehensive authentication health check

    • --format: Output format (text or json)
    • --verbose: Show detailed diagnostic information
  • smart-setup: Intelligent authentication setup with automatic fallback

    • hostname: Target Git host
    • --strategy: Authentication strategy (auto, ssh_first, credentials_first, ssh_only)
    • --username: Git username (for credential-based authentication)
    • --password: Git password (for credential-based authentication)

CI/CD Pipeline Integration

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

def setup_ci_ssh():
    """Set up SSH for CI environment."""
    # Create configuration with key content
    config = SSHConfig(
        identity_content=os.environ.get('SSH_PRIVATE_KEY'),
        ssh_options={'BatchMode': 'yes'}
    )

    ssh_agent = PersistentSSHAgent(config=config)

    if ssh_agent.setup_ssh('github.com'):
        print("โœ… SSH agent started successfully")
        return True

    raise RuntimeError("Failed to start SSH agent")

Git Integration

from git import Repo
from persistent_ssh_agent import PersistentSSHAgent
import os

def clone_repo(repo_url: str, local_path: str) -> Repo:
    """Clone a repository using persistent SSH authentication."""
    ssh_agent = PersistentSSHAgent()

    # Extract hostname and set up SSH
    hostname = ssh_agent.extract_hostname(repo_url)
    if not hostname or not ssh_agent.setup_ssh(hostname):
        raise RuntimeError("Failed to set up SSH authentication")

    # Get SSH command and configure environment
    ssh_command = ssh_agent.get_git_ssh_command(hostname)
    if not ssh_command:
        raise RuntimeError("Failed to get SSH command")

    # Clone with GitPython
    env = os.environ.copy()
    env['GIT_SSH_COMMAND'] = ssh_command

    return Repo.clone_from(
        repo_url,
        local_path,
        env=env
    )

Git Credential Helper Support (Simplified)

You can now set up Git credentials in a simplified way without manual script creation:

from persistent_ssh_agent import PersistentSSHAgent

# Method 1: Set credentials directly
ssh_agent = PersistentSSHAgent()
ssh_agent.git.setup_git_credentials('your-username', 'your-password')

# Method 2: Use environment variables
import os
os.environ['GIT_USERNAME'] = 'your-username'
os.environ['GIT_PASSWORD'] = 'your-password'
ssh_agent.git.setup_git_credentials()  # Automatically reads from env vars

# Now Git commands will use these credentials

CLI Setup:

# Set credentials directly
uvx persistent_ssh_agent git-setup --username your-username --password your-password

# Interactive setup
uvx persistent_ssh_agent git-setup --prompt

# Using environment variables
export GIT_USERNAME=your-username
export GIT_PASSWORD=your-password
uvx persistent_ssh_agent git-setup

CI Environment Usage:

# In build scripts
from persistent_ssh_agent import PersistentSSHAgent

# Use context manager
with PersistentSSHAgent() as agent:
    # SSH and Git credentials are configured, ready for Git operations
    agent.setup_ssh('github.com')
    # Execute any Git commands...

๐ŸŒŸ Advanced Features

Smart Authentication Strategies

The library now includes intelligent authentication strategies that automatically select the best authentication method:

from persistent_ssh_agent import PersistentSSHAgent

# Create agent instance
agent = PersistentSSHAgent()

# Smart authentication with automatic fallback
# Tries Git credentials first, falls back to SSH if needed
success = agent.git.setup_smart_credentials('github.com', strategy='auto')

# Force SSH-only authentication
success = agent.git.setup_smart_credentials('github.com', strategy='ssh_only')

# Prefer SSH, fallback to credentials
success = agent.git.setup_smart_credentials('github.com', strategy='ssh_first')

Available Authentication Strategies:

  • auto (default): Intelligent selection based on environment and cached preferences
  • ssh_first: Try SSH authentication first, fallback to credentials
  • credentials_first: Try Git credentials first, fallback to SSH
  • ssh_only: Use only SSH key authentication
  • credentials_only: Use only Git credential authentication

Environment Variable Control:

# Force SSH authentication for all operations
export FORCE_SSH_AUTH=true

# Prefer SSH authentication (with fallback)
export PREFER_SSH_AUTH=true

# Set specific authentication strategy
export AUTH_STRATEGY=ssh_first

Health Check and Diagnostics

Comprehensive health checking capabilities for authentication systems:

from persistent_ssh_agent import PersistentSSHAgent

agent = PersistentSSHAgent()

# Perform comprehensive health check
health_status = agent.git.health_check()

print(f"Overall status: {health_status['overall']}")  # healthy, warning, or error
print(f"Git credentials: {health_status['git_credentials']['status']}")
print(f"SSH keys: {health_status['ssh_keys']['status']}")
print(f"Network connectivity: {health_status['network']['status']}")

# Get recommendations for improvement
for recommendation in health_status['recommendations']:
    print(f"๐Ÿ’ก {recommendation}")

Health Check Features:

  • Git credential validation and testing
  • SSH key availability and functionality testing
  • Network connectivity verification to Git hosts
  • Automatic recommendation generation
  • Detailed diagnostic information

Credential Management

Advanced credential management with automatic cleanup:

from persistent_ssh_agent import PersistentSSHAgent

agent = PersistentSSHAgent()

# Test credential validity for specific hosts
results = agent.git.test_credentials('github.com', username='user', password='token')
print(f"GitHub credentials valid: {results['github.com']}")

# Test all common Git hosts
all_results = agent.git.test_credentials()
for host, valid in all_results.items():
    print(f"{host}: {'โœ…' if valid else 'โŒ'}")

# Clean up invalid credential helpers
cleanup_success = agent.git.clear_invalid_credentials()
if cleanup_success:
    print("โœ… Invalid credentials cleaned up successfully")

Custom Configuration

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

# Create config instance
config = SSHConfig()

# Add global configuration
config.add_global_config({
    'AddKeysToAgent': 'yes',
    'UseKeychain': 'yes'
})

# Add host-specific configuration
config.add_host_config('*.github.com', {
    'User': 'git',
    'IdentityFile': '~/.ssh/github_ed25519',
    'PreferredAuthentications': 'publickey'
})

# Initialize agent with config
agent = PersistentSSHAgent(config=config)

Key Management

The library automatically manages SSH keys based on your SSH configuration:

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

# Use specific key
config = SSHConfig(identity_file='~/.ssh/id_ed25519')
agent = PersistentSSHAgent(config=config)

# Or let the library automatically detect and use available keys
agent = PersistentSSHAgent()
if agent.setup_ssh('github.com'):
    print("โœ… SSH key loaded and ready!")

The library supports the following key types in order of preference:

  • Ed25519 (recommended, most secure)
  • ECDSA
  • ECDSA with security key
  • Ed25519 with security key
  • RSA
  • DSA (legacy, not recommended)

SSH Configuration Validation

The library provides comprehensive SSH configuration validation with support for:

from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

# Create custom SSH configuration with validation
config = SSHConfig()

# Add host configuration with various options
config.add_host_config('github.com', {
    # Connection Settings
    'IdentityFile': '~/.ssh/github_key',
    'User': 'git',
    'Port': '22',

    # Security Settings
    'StrictHostKeyChecking': 'yes',
    'PasswordAuthentication': 'no',
    'PubkeyAuthentication': 'yes',

    # Connection Optimization
    'Compression': 'yes',
    'ConnectTimeout': '60',
    'ServerAliveInterval': '60',
    'ServerAliveCountMax': '3',

    # Proxy and Forwarding
    'ProxyCommand': 'ssh -W %h:%p bastion',
    'ForwardAgent': 'yes'
})

# Initialize with validated config
ssh_agent = PersistentSSHAgent(config=config)

Supported configuration categories:

  • Connection Settings: Port, Hostname, User, IdentityFile
  • Security Settings: StrictHostKeyChecking, BatchMode, PasswordAuthentication
  • Connection Optimization: Compression, ConnectTimeout, ServerAliveInterval
  • Proxy and Forwarding: ProxyCommand, ForwardAgent, ForwardX11
  • Environment Settings: RequestTTY, SendEnv
  • Multiplexing Options: ControlMaster, ControlPath, ControlPersist

For detailed validation rules and supported options, see SSH Configuration Validation

SSH Key Types Support

The library supports multiple SSH key types:

  • Ed25519 (recommended, most secure)
  • ECDSA
  • ECDSA with security key
  • Ed25519 with security key
  • RSA
  • DSA (legacy, not recommended)

Security Features

  1. SSH Key Management:

    • Automatic detection and loading of SSH keys (Ed25519, ECDSA, RSA)
    • Support for key content injection (useful in CI/CD)
    • Secure key file permissions handling
    • Optional passphrase support
  2. Configuration Security:

    • Strict hostname validation
    • Secure default settings
    • Support for security-focused SSH options
  3. Session Management:

    • Secure storage of agent information
    • Platform-specific security measures
    • Automatic cleanup of expired sessions
    • Cross-platform compatibility

Type Hints Support

The library provides comprehensive type hints for all public interfaces:

from typing import Optional
from persistent_ssh_agent import PersistentSSHAgent
from persistent_ssh_agent.config import SSHConfig

def setup_ssh(hostname: str, key_file: Optional[str] = None) -> bool:
    config = SSHConfig(identity_file=key_file)
    agent = PersistentSSHAgent(config=config)
    return agent.setup_ssh(hostname)

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

๐Ÿ“„ License

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

persistent_ssh_agent-0.10.1.tar.gz (47.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

persistent_ssh_agent-0.10.1-py3-none-any.whl (47.3 kB view details)

Uploaded Python 3

File details

Details for the file persistent_ssh_agent-0.10.1.tar.gz.

File metadata

  • Download URL: persistent_ssh_agent-0.10.1.tar.gz
  • Upload date:
  • Size: 47.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for persistent_ssh_agent-0.10.1.tar.gz
Algorithm Hash digest
SHA256 5ab508ad9fd561aaa6334d2396af0f6de088b6ca55fcef4ac69c1b12e9875358
MD5 ed7087b998fe1eb2fbc7b8f903e9aa67
BLAKE2b-256 8f60168941790552ed3be52c67f504f57d03ea737a0e06182a14e56cbdff5124

See more details on using hashes here.

Provenance

The following attestation bundles were made for persistent_ssh_agent-0.10.1.tar.gz:

Publisher: python-publish.yml on loonghao/persistent_ssh_agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file persistent_ssh_agent-0.10.1-py3-none-any.whl.

File metadata

File hashes

Hashes for persistent_ssh_agent-0.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fd4b0d58baf045ae557c6d64af4a6c567fe3d2cbeec9b6d68287f2d11895b21c
MD5 ff8d78e7f1b2f3b5293e85757fd29614
BLAKE2b-256 b5082bfe822da5bf247b09955e6bd65caf756fd321045203131c91be609f7c51

See more details on using hashes here.

Provenance

The following attestation bundles were made for persistent_ssh_agent-0.10.1-py3-none-any.whl:

Publisher: python-publish.yml on loonghao/persistent_ssh_agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page