Paramiko-SSH client

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction

In the context of modern DevOps and SecOps practices, programmatic access to remote servers has become a necessity. One of the most reliable and powerful ways to interact with remote hosts via Python is the Paramiko library. It provides an implementation of the SSH2 protocol, allowing connections to servers, command execution, file transfers, and building automated infrastructure management systems.

This article examines every aspect of working with Paramiko: from basic concepts to concrete implementations. We will thoroughly explore classes, methods, functions, and practical use cases in real‑world projects.

What Is Paramiko

Paramiko is a Python implementation of the SSH2 protocol, created by Robbie Walker. The name comes from “parallel” + “miko” (miko – a shrine maiden in Japanese culture). The library’s primary goal is to provide a secure data‑transfer channel between hosts with authentication via passwords or keys.

The library is written in pure Python, making it cross‑platform and easy to integrate into any project. Paramiko is widely used in tools such as Ansible, Fabric, and other automation systems.

Key Features and Benefits

Full SSH2 Protocol Implementation

Paramiko supports all core SSH2 capabilities, including:

  • Password and key authentication
  • Remote command execution
  • File transfer via SFTP
  • Creation of secure tunnels

Support for Modern Encryption Algorithms

  • RSA, DSS, ECDSA, Ed25519 keys
  • AES, 3DES, Blowfish ciphers
  • SHA‑1, SHA‑256, SHA‑512 hashing

Additional Capabilities

  • Compatibility with Python 3.6+
  • Support for jump servers (bastion hosts)
  • Ability to create custom SSH servers
  • Integration with SSH agents
  • High reliability and security

Installation and Setup

Standard Installation

pip install paramiko

Installation with Optional Dependencies

pip install paramiko[gssapi]  # For Kerberos/GSSAPI
pip install paramiko[invoke]  # For integration with Invoke

Verify Installation

import paramiko
print(paramiko.__version__)

Library Architecture

Paramiko is built on a modular architecture with a clear separation of responsibilities among its components:

Main Components

  • Transport: Low‑level SSH transport layer
  • Channel: Abstraction for data streams
  • SSHClient: High‑level interface for SSH connections
  • SFTPClient: Client for file operations via SFTP
  • ServerInterface: Interface for building SSH servers

Core Classes and Their Methods

SSHClient – Primary Interface

SSHClient is the main class for handling SSH connections. It offers a high‑level API for connecting to servers and executing commands.

Key SSHClient Methods

Method Description Parameters
connect() Establish a connection to a server hostname, port, username, password, pkey, timeout
exec_command() Execute a command command, bufsize, timeout, get_pty
invoke_shell() Create an interactive shell term, width, height, width_pixels, height_pixels
open_sftp() Initialize an SFTP session -
load_system_host_keys() Load system host keys filename
load_host_keys() Load user‑defined host keys filename
save_host_keys() Save host keys filename
set_missing_host_key_policy() Define policy for unknown hosts policy
get_transport() Retrieve the Transport object -
close() Close the connection -

Basic SSHClient Usage Example

import paramiko

# Create a client
client = paramiko.SSHClient()

# Set policy for unknown hosts
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# Connect to the server
client.connect(
    hostname='192.168.0.1',
    username='user',
    password='password',
    port=22,
    timeout=10
)

# Execute a command
stdin, stdout, stderr = client.exec_command('ls -la')

# Retrieve results
output = stdout.read().decode('utf-8')
errors = stderr.read().decode('utf-8')

print("Command output:")
print(output)

if errors:
    print("Errors:")
    print(errors)

# Close the connection
client.close()

SFTPClient – File Operations

SFTPClient provides functionality for working with files and directories on a remote server via the SFTP protocol.

Key SFTPClient Methods

Method Description Parameters
put() Upload a file to the server localpath, remotepath, callback, confirm
get() Download a file from the server remotepath, localpath, callback
putfo() Upload from a file‑like object fl, remotepath, file_size, callback, confirm
getfo() Download into a file‑like object remotepath, fl, callback
listdir() List files in a directory path
listdir_attr() List files with attributes path
mkdir() Create a directory path, mode
rmdir() Remove a directory path
remove() Delete a file path
rename() Rename a file oldpath, newpath
stat() Get file information path
lstat() Get symbolic‑link information path
chmod() Change file permissions path, mode
chown() Change file ownership path, uid, gid
utime() Update access and modification times path, times
truncate() Truncate a file path, size
readlink() Read a symbolic link path
symlink() Create a symbolic link source, dest
normalize() Normalize a path path
getcwd() Get current working directory -
chdir() Change current directory path
close() Close the SFTP session -

SFTP Usage Example

import paramiko
import os

# Create an SSH client
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('hostname', username='user', password='password')

# Open an SFTP client
sftp = ssh.open_sftp()

# Upload a file
sftp.put('local_file.txt', '/remote/path/file.txt')

# Download a file
sftp.get('/remote/path/file.txt', 'downloaded_file.txt')

# Work with directories
try:
    sftp.mkdir('/remote/new_directory')
    print("Directory created")
except IOError:
    print("Directory already exists")

# List files
files = sftp.listdir('/remote/path')
print("Files in directory:", files)

# Detailed file info
for file_attr in sftp.listdir_attr('/remote/path'):
    print(f"File: {file_attr.filename}, Size: {file_attr.st_size}")

# Close sessions
sftp.close()
ssh.close()

Transport – Low‑Level Control

Transport offers low‑level access to an SSH connection, allowing fine‑grained session management.

Key Transport Methods

Method Description Parameters
connect() Establish the transport connection hostkey, username, password, pkey, gss_host, gss_auth, gss_kex, gss_deleg_creds
open_session() Create an SSH session window_size, max_packet_size
open_sftp_client() Create an SFTP client window_size, max_packet_size
open_channel() Create a channel kind, dest_addr, src_addr, window_size, max_packet_size
is_active() Check if the transport is active -
is_authenticated() Check authentication status -
get_username() Retrieve the username -
get_server_key() Retrieve the server's host key -
close() Close the transport -
set_keepalive() Set a keep‑alive interval interval
send_ignore() Send an ignore packet bytes

Key Classes

Paramiko supports several cryptographic key types:

RSAKey – RSA Keys

Method Description Parameters
generate() Generate a new key bits, progress_func
from_private_key_file() Load a key from a file filename, password
from_private_key() Load a key from a file‑like object file_obj, password
write_private_key_file() Save a key to a file filename, password
write_private_key() Save a key to a file‑like object file_obj, password
get_fingerprint() Get the key fingerprint -

DSAKey, ECDSAKey, Ed25519Key

These classes share the same interface as RSAKey but work with their respective algorithms.

Authentication Methods

Password Authentication

client.connect(
    hostname='server.example.com',
    username='user',
    password='secure_password'
)

Key‑Based Authentication

# Load a private key from a file
key = paramiko.RSAKey.from_private_key_file('/path/to/private_key')

# Connect using the key
client.connect(
    hostname='server.example.com',
    username='user',
    pkey=key
)

Encrypted Key Authentication

# Encrypted key with a passphrase
key = paramiko.RSAKey.from_private_key_file(
    '/path/to/encrypted_key',
    password='key_password'
)

client.connect(
    hostname='server.example.com',
    username='user',
    pkey=key
)

Using an SSH Agent

# Retrieve keys from the SSH agent
agent = paramiko.Agent()
agent_keys = agent.get_keys()

if agent_keys:
    client.connect(
        hostname='server.example.com',
        username='user',
        pkey=agent_keys[0]
    )

Security Policies

AutoAddPolicy – Automatically Add Unknown Hosts

client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

RejectPolicy – Reject Unknown Hosts (default)

client.set_missing_host_key_policy(paramiko.RejectPolicy())

WarningPolicy – Warn About Unknown Hosts

client.set_missing_host_key_policy(paramiko.WarningPolicy())

Custom Policy Example

class CustomPolicy(paramiko.MissingHostKeyPolicy):
    def missing_host_key(self, client, hostname, key):
        # Custom host verification logic
        print(f"New host: {hostname}")
        return True  # Allow the connection

client.set_missing_host_key_policy(CustomPolicy())

Working with Channels

Creating an Interactive Shell

# Open a shell session
shell = client.invoke_shell()

# Send a command
shell.send('ls -la\n')

# Receive output
import time
time.sleep(1)
output = shell.recv(1024).decode()
print(output)

# Close the channel
shell.close()

Using a Pseudo‑Terminal

stdin, stdout, stderr = client.exec_command('sudo apt update', get_pty=True)

Error Handling and Exceptions

Common Exceptions

Exception Description
AuthenticationException Authentication failure
SSHException General SSH error
BadHostKeyException Invalid host key
ChannelException Channel error
PasswordRequiredException Key requires a passphrase
ProxyCommandFailure Proxy command error

Exception Handling Example

import paramiko

try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect('hostname', username='user', password='wrong_password')
    
except paramiko.AuthenticationException:
    print("Authentication error")
except paramiko.SSHException as e:
    print(f"SSH error: {e}")
except Exception as e:
    print(f"General error: {e}")
finally:
    client.close()

Logging and Debugging

Enabling Logging

import paramiko
import logging

# Basic logging configuration
logging.basicConfig(level=logging.DEBUG)
paramiko.util.log_to_file('paramiko.log')

# Advanced configuration
logger = logging.getLogger('paramiko')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler('debug.log')
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)

Practical Use Cases

Bulk Command Execution

import paramiko
import threading

def execute_command(host, username, password, command):
    try:
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(host, username=username, password=password)
        
        stdin, stdout, stderr = client.exec_command(command)
        output = stdout.read().decode()
        errors = stderr.read().decode()
        
        print(f"Host {host}:")
        print(f"Output: {output}")
        if errors:
            print(f"Errors: {errors}")
            
    except Exception as e:
        print(f"Error on host {host}: {e}")
    finally:
        client.close()

# List of servers
servers = ['192.168.1.10', '192.168.1.11', '192.168.1.12']

# Run in parallel
threads = []
for host in servers:
    t = threading.Thread(target=execute_command, args=(host, 'admin', 'password', 'uptime'))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

File Synchronization

import paramiko
import os

def sync_directory(local_dir, remote_dir, hostname, username, password):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname, username=username, password=password)
    
    sftp = ssh.open_sftp()
    
    try:
        # Create remote directory
        sftp.mkdir(remote_dir)
    except IOError:
        pass  # Directory already exists
    
    # Upload files
    for root, dirs, files in os.walk(local_dir):
        for file in files:
            local_path = os.path.join(root, file)
            relative_path = os.path.relpath(local_path, local_dir)
            remote_path = os.path.join(remote_dir, relative_path).replace('\\', '/')
            
            # Ensure remote directories exist
            remote_dir_path = os.path.dirname(remote_path)
            try:
                sftp.mkdir(remote_dir_path)
            except IOError:
                pass
            
            # Transfer file
            print(f"Uploading {local_path} -> {remote_path}")
            sftp.put(local_path, remote_path)
    
    sftp.close()
    ssh.close()

Backup System Example

import paramiko
import datetime
import os

def create_backup(servers, backup_paths, local_backup_dir):
    timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    
    for server in servers:
        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(server['host'], username=server['username'], password=server['password'])
            
            sftp = ssh.open_sftp()
            
            server_backup_dir = os.path.join(local_backup_dir, server['host'], timestamp)
            os.makedirs(server_backup_dir, exist_ok=True)
            
            for remote_path in backup_paths:
                filename = os.path.basename(remote_path)
                local_path = os.path.join(server_backup_dir, filename)
                
                print(f"Backing up: {server['host']}:{remote_path} -> {local_path}")
                sftp.get(remote_path, local_path)
            
            sftp.close()
            ssh.close()
            
        except Exception as e:
            print(f"Backup error for {server['host']}: {e}")

# Example usage
servers = [
    {'host': '192.168.1.10', 'username': 'admin', 'password': 'password'},
    {'host': '192.168.1.11', 'username': 'admin', 'password': 'password'},
]

backup_paths = ['/etc/nginx/nginx.conf', '/etc/hosts', '/etc/passwd']
create_backup(servers, backup_paths, '/local/backup/dir')

Working with Jump (Bastion) Hosts

Connecting Through an Intermediate Server

import paramiko

def connect_through_bastion(bastion_host, bastion_user, bastion_pass, 
                           target_host, target_user, target_pass):
    # Connect to the bastion host
    bastion = paramiko.SSHClient()
    bastion.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    bastion.connect(bastion_host, username=bastion_user, password=bastion_pass)
    
    # Create a tunnel
    transport = bastion.get_transport()
    dest_addr = (target_host, 22)
    local_addr = ('127.0.0.1', 0)
    channel = transport.open_channel('direct-tcpip', dest_addr, local_addr)
    
    # Connect to the target host through the tunnel
    target = paramiko.SSHClient()
    target.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    target.connect(target_host, username=target_user, password=target_pass, sock=channel)
    
    return bastion, target

# Usage
bastion, target = connect_through_bastion(
    'bastion.example.com', 'bastion_user', 'bastion_pass',
    'internal.server.com', 'target_user', 'target_pass'
)

# Execute a command on the target server
stdin, stdout, stderr = target.exec_command('hostname')
print(stdout.read().decode())

# Close connections
target.close()
bastion.close()

Creating an SSH Server

Basic SSH Server Example

import paramiko
import socket
import threading

class SSHServer(paramiko.ServerInterface):
    def __init__(self):
        self.event = threading.Event()
    
    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
    
    def check_auth_password(self, username, password):
        if username == 'admin' and password == 'password':
            return paramiko.AUTH_SUCCESSFUL
        return paramiko.AUTH_FAILED
    
    def get_allowed_auths(self, username):
        return 'password'
    
    def check_channel_shell_request(self, channel):
        self.event.set()
        return True

def start_ssh_server():
    # Generate a host key
    host_key = paramiko.RSAKey.generate(2048)
    
    # Create a listening socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('localhost', 2222))
    sock.listen(1)
    
    print('SSH server listening on port 2222')
    
    while True:
        client, addr = sock.accept()
        print(f'Connection from {addr}')
        
        transport = paramiko.Transport(client)
        transport.add_server_key(host_key)
        
        server = SSHServer()
        transport.start_server(server=server)
        
        # Wait for a shell request
        channel = transport.accept(20)
        if channel is not None:
            server.event.wait(10)
            if server.event.is_set():
                channel.send('Welcome to the SSH server!\r\n')
                channel.send('$ ')
                
                while True:
                    try:
                        data = channel.recv(1024)
                        if not data:
                            break
                        
                        command = data.decode().strip()
                        if command == 'exit':
                            break
                        
                        # Simple command handling
                        if command == 'date':
                            import datetime
                            response = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        elif command == 'whoami':
                            response = 'admin'
                        else:
                            response = f'Unknown command: {command}'
                        
                        channel.send(f'{response}\r\n$ ')
                        
                    except Exception as e:
                        print(f'Error: {e}')
                        break
                
                channel.close()
        
        transport.close()

if __name__ == '__main__':
    start_ssh_server()

Comprehensive Method and Function Reference

Key Functions in the Paramiko Module

Function Description
Transport(sock) Create a Transport object
SSHClient() Create an SSH client
AutoAddPolicy() Policy that automatically adds unknown hosts
RejectPolicy() Policy that rejects unknown hosts
WarningPolicy() Policy that warns about unknown hosts
util.log_to_file() Enable logging to a file
Agent() Create an SSH agent
RSAKey.generate() Generate an RSA key
common.o600() Set file permissions to 600

Channel‑Related Methods

Method Description
channel.send() Send data through the channel
channel.recv() Receive data from the channel
channel.sendall() Send all data
channel.recv_ready() Check if data is ready to be read
channel.send_ready() Check if the channel can accept data
channel.close() Close the channel
channel.get_pty() Request a pseudo‑terminal
channel.invoke_shell() Start a shell session
channel.exec_command() Execute a command
channel.exit_status_ready() Check if the exit status is available
channel.recv_exit_status() Retrieve the exit status

Advanced Features

Working with SSH Config Files

import paramiko
import os

# Load SSH configuration
config = paramiko.SSHConfig()
config.parse(open(os.path.expanduser('~/.ssh/config')))

# Retrieve host settings
host_config = config.lookup('myserver')

# Connect using the retrieved settings
client = paramiko.SSHClient()
client.connect(
    hostname=host_config.get('hostname'),
    username=host_config.get('user'),
    port=int(host_config.get('port', 22)),
    key_filename=host_config.get('identityfile')
)

Using a SOCKS Proxy

import paramiko
import socks
import socket

# Configure SOCKS5 proxy
socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 1080)
socket.socket = socks.socksocket

# Connect via the proxy
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('target-server.com', username='user', password='password')

Monitoring File Transfer Progress

def progress_callback(transferred, total):
    percent = (transferred / total) * 100
    print(f'Transferred: {transferred}/{total} bytes ({percent:.1f}%)')

# Use the callback during file upload
sftp.put('large_file.bin', '/remote/path/large_file.bin', callback=progress_callback)

Comparison with Alternative Libraries

Library Language SSH Support Complexity Performance
Paramiko Python Full Medium High
Netmiko Python Network devices Low Medium
Fabric Python High‑level Low High
AsyncSSH Python Asynchronous High Very high
OpenSSH CLI Full Low Very high

Performance Optimization

Reusing Connections

class SSHConnectionPool:
    def __init__(self):
        self.connections = {}
    
    def get_connection(self, host, username, password):
        key = f"{host}:{username}"
        if key not in self.connections:
            client = paramiko.SSHClient()
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            client.connect(host, username=username, password=password)
            self.connections[key] = client
        return self.connections[key]
    
    def close_all(self):
        for client in self.connections.values():
            client.close()
        self.connections.clear()

Parallel Processing

import concurrent.futures
import paramiko

def execute_on_server(server_info):
    host, username, password, command = server_info
    
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(host, username=username, password=password)
    
    stdin, stdout, stderr = client.exec_command(command)
    result = stdout.read().decode()
    client.close()
    
    return host, result

# Parallel execution across many servers
servers = [
    ('server1.com', 'user', 'pass', 'uptime'),
    ('server2.com', 'user', 'pass', 'uptime'),
    ('server3.com', 'user', 'pass', 'uptime'),
]

with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
    results = list(executor.map(execute_on_server, servers))
    
for host, result in results:
    print(f"{host}: {result}")

Frequently Asked Questions

How do I connect to a server using a key without a password?

key = paramiko.RSAKey.from_private_key_file('/path/to/private_key')
client.connect(hostname, username=username, pkey=key)

How can I run a command with sudo?

stdin, stdout, stderr = client.exec_command('sudo command', get_pty=True)
stdin.write('password\n')
stdin.flush()

How do I transfer a large file with progress feedback?

def progress(transferred, total):
    print(f'Transferred: {transferred}/{total} bytes')

sftp.put('large_file.bin', '/remote/path/file.bin', callback=progress)

How can I handle a connection timeout?

try:
    client.connect(hostname, timeout=10)
except socket.timeout:
    print("Connection timed out")

How do I check if a file exists on a remote server?

try:
    sftp.stat('/path/to/file')
    print("File exists")
except IOError:
    print("File not found")

How do I create an SSH tunnel?

import select
import socketserver

class ForwardServer(socketserver.ThreadingTCPServer):
    daemon_threads = True
    allow_reuse_address = True

# Create a tunnel to forward a local port
def forward_tunnel(local_port, remote_host, remote_port, transport):
    class SubHandler(socketserver.BaseRequestHandler):
        def handle(self):
            try:
                chan = transport.open_channel('direct-tcpip',
                                            (remote_host, remote_port),
                                            self.request.getpeername())
            except Exception:
                return
            
            while True:
                r, w, x = select.select([self.request, chan], [], [])
                if self.request in r:
                    data = self.request.recv(1024)
                    if len(data) == 0:
                        break
                    chan.send(data)
                if chan in r:
                    data = chan.recv(1024)
                    if len(data) == 0:
                        break
                    self.request.send(data)
            
            chan.close()
            self.request.close()
    
    server = ForwardServer(('', local_port), SubHandler)
    server.serve_forever()

Best Security Practices

Using Context Managers

from contextlib import contextmanager

@contextmanager
def ssh_connection(hostname, username, password):
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        client.connect(hostname, username=username, password=password)
        yield client
    finally:
        client.close()

# Usage
with ssh_connection('server.com', 'user', 'pass') as ssh:
    stdin, stdout, stderr = ssh.exec_command('ls -la')
    print(stdout.read().decode())

Validating Host Keys

import binascii
import os

def validate_host_key(hostname, key):
    # Load known hosts
    known_hosts = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
    
    if hostname in known_hosts:
        host_keys = known_hosts[hostname]
        if key.get_name() in host_keys:
            expected_key = host_keys[key.get_name()]
            if key.asbytes() == expected_key.asbytes():
                return True
    
    return False

class ValidatingPolicy(paramiko.MissingHostKeyPolicy):
    def missing_host_key(self, client, hostname, key):
        if validate_host_key(hostname, key):
            return
        raise paramiko.SSHException(f'Unknown host key for {hostname}')

Secure Credential Storage

import keyring
import getpass

def get_credentials(hostname):
    username = input('Username: ')
    password = keyring.get_password('ssh', f'{username}@{hostname}')
    
    if not password:
        password = getpass.getpass('Password: ')
        keyring.set_password('ssh', f'{username}@{hostname}', password)
    
    return username, password

username, password = get_credentials('server.com')

Conclusion

Paramiko is a powerful and flexible tool for SSH operations in Python. The library offers complete control over SSH connections, from simple command execution to complex automation scenarios. Thanks to its reliability, security, and rich feature set, Paramiko remains the de‑facto standard for Python‑based SSH interactions.

Proper use of Paramiko, combined with security best practices, enables the creation of robust automation, monitoring, and infrastructure‑management systems. The library continues to evolve with active community support, ensuring relevance and compatibility with modern requirements.

News