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
  • ⚡ Asynchronous operation support
  • 🧪 Complete unit test coverage
  • 📝 Type hints support

🚀 Installation

pip install persistent-ssh-agent

📋 Requirements

  • Python 3.8+
  • 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
ssh_agent = PersistentSSHAgent(config=config)

# 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!")

Multiple Host Configuration

To configure SSH for multiple hosts:

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

To set global SSH options:

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

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
    )

🌟 Advanced Features

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

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.4.0.tar.gz (18.1 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.4.0-py3-none-any.whl (15.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: persistent_ssh_agent-0.4.0.tar.gz
  • Upload date:
  • Size: 18.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.0.1 CPython/3.12.8

File hashes

Hashes for persistent_ssh_agent-0.4.0.tar.gz
Algorithm Hash digest
SHA256 6e64841ffc4da9d5a0819691d40de6eb6ea99112479d8699a1c7cb5ccf1a2a64
MD5 7b58290078e4c4982c2f1744a09de1e0
BLAKE2b-256 08fa65989ba11e7e55db4feb68df484d5ed38af9863ca44616e36ee8d530a698

See more details on using hashes here.

Provenance

The following attestation bundles were made for persistent_ssh_agent-0.4.0.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.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for persistent_ssh_agent-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ca680c48e9ff3e7e1db52a9a0b06282a0f285f9f6d7a05ebf52028625bcdb4b8
MD5 3340bb859beb0d06f7385a674cd6c2c2
BLAKE2b-256 78249b7b3237806d3ff511f7d52e1473baade48daeb60c7cc6baa37bf61af7e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for persistent_ssh_agent-0.4.0-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