Of course! Using paramiko for SCP (Secure Copy Protocol) in Python is a common task. While paramiko is primarily an SSHv2 protocol library, it includes a built-in SCP client implementation.
Here's a comprehensive guide covering everything from basic usage to more advanced scenarios and best practices.
What is Paramiko?
Paramiko is a pure Python (3.6+) implementation of the SSHv2 protocol. It provides a client for connecting to an SSH server and performing various operations, like executing commands, transferring files, and setting up tunnels. Its built-in SCP client is a convenient wrapper for secure file transfers.
Method 1: The Easy Way (Using Paramiko's Built-in SCP Client)
Paramiko has a high-level SCPClient class that makes file transfers very straightforward. This is the recommended approach for most use cases.
Step 1: Install Paramiko
If you don't have it installed, open your terminal or command prompt and run:
pip install paramiko
Step 2: Basic SCP Upload (Local to Remote)
This example shows how to copy a local file to a remote server.
import paramiko
import os
# --- Configuration ---
hostname = 'your.server.com'
port = 22
username = 'your_username'
password = 'your_password' # Or use key-based auth
local_path = '/path/to/your/local_file.txt'
remote_path = '/path/to/remote_file.txt' # Can be a filename or a full path
# --- Create SSH Client ---
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Note: This is insecure for production
try:
# --- Connect to the Server ---
ssh.connect(hostname, port=port, username=username, password=password)
# --- Create SCP Client ---
# The 'sftp' argument is recommended as it's more robust
with paramiko.SCPClient(ssh.get_transport(), sanitize=lambda x: x) as scp:
print(f"Uploading {local_path} to {remote_path}...")
scp.put(local_path, remote_path)
print("Upload complete!")
except paramiko.AuthenticationException:
print("Authentication failed, please verify your credentials.")
except paramiko.SSHException as e:
print(f"Unable to establish SSH connection: {e}")
except Exception as e:
print(f"An error occurred: {e}")
finally:
# --- Close the SSH Connection ---
ssh.close()
Step 3: Basic SCP Download (Remote to Local)
To download a file from the server to your local machine, simply use the get() method.
import paramiko
# --- Configuration ---
hostname = 'your.server.com'
port = 22
username = 'your_username'
password = 'your_password'
remote_path = '/path/to/remote_file.txt'
local_path = '/path/to/your/local_download.txt' # The destination path
# --- Create SSH Client ---
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# --- Connect to the Server ---
ssh.connect(hostname, port=port, username=username, password=password)
# --- Create SCP Client ---
with paramiko.SCPClient(ssh.get_transport(), sanitize=lambda x: x) as scp:
print(f"Downloading {remote_path} to {local_path}...")
scp.get(remote_path, local_path)
print("Download complete!")
except paramiko.AuthenticationException:
print("Authentication failed, please verify your credentials.")
except paramiko.SSHException as e:
print(f"Unable to establish SSH connection: {e}")
except Exception as e:
print(f"An error occurred: {e}")
finally:
# --- Close the SSH Connection ---
ssh.close()
Method 2: The More Robust Way (Using SFTP)
For more complex file operations (like listing directories, renaming files, or handling directories recursively), SFTP (SSH File Transfer Protocol) is a better choice. It's a more powerful and feature-rich protocol than SCP. Paramiko has a great SFTP client built-in.
The put() and get() methods of SFTP are very similar to SCP's.
import paramiko
import os
# --- Configuration ---
hostname = 'your.server.com'
port = 22
username = 'your_username'
password = 'your_password'
local_dir = '/path/to/local_dir'
remote_dir = '/path/to/remote_dir'
# --- Create SSH Client ---
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# --- Connect to the Server ---
ssh.connect(hostname, port=port, username=username, password=password)
# --- Create SFTP Client from the SSH transport ---
sftp = ssh.open_sftp()
# --- Example: Upload a directory recursively ---
print(f"Uploading directory {local_dir} to {remote_dir}...")
# To upload a directory, you need a helper function or loop
# Here's a simple example for a single file
sftp.put(os.path.join(local_dir, 'my_file.txt'), os.path.join(remote_dir, 'my_file.txt'))
print("Directory upload complete (for the example file).")
# --- Example: Download a file ---
# sftp.get('/remote/path/to/file', '/local/path/to/file')
# --- Close the SFTP session ---
sftp.close()
except paramiko.AuthenticationException:
print("Authentication failed, please verify your credentials.")
except paramiko.SSHException as e:
print(f"Unable to establish SSH connection: {e}")
except Exception as e:
print(f"An error occurred: {e}")
finally:
# --- Close the SSH Connection ---
ssh.close()
Advanced Topics & Best Practices
Key-Based Authentication (Recommended)
Using SSH keys is more secure than passwords. You'll need your private key file (e.g., id_rsa).
import paramiko
# --- Configuration ---
hostname = 'your.server.com'
port = 22
username = 'your_username'
private_key_path = '/home/user/.ssh/id_rsa' # Path to your private key
remote_path = '/path/to/remote_file.txt'
local_path = '/path/to/your/local_file.txt'
# --- Create SSH Client ---
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# --- Load the private key ---
# You can use a password-protected key with passphrase=True
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
# --- Connect using the key ---
ssh.connect(hostname, port=port, username=username, pkey=private_key)
# --- Use SCP or SFTP ---
with paramiko.SCPClient(ssh.get_transport()) as scp:
scp.put(local_path, remote_path)
print("File uploaded successfully using key authentication.")
except paramiko.AuthenticationException:
print("Authentication failed. Check your key or username.")
except paramiko.SSHException as e:
print(f"SSH connection error: {e}")
except FileNotFoundError:
print(f"Private key file not found at {private_key_path}")
except Exception as e:
print(f"An error occurred: {e}")
finally:
ssh.close()
Handling Directories Recursively
Neither SCPClient.put() nor SFTPClient.put() can recursively upload a directory by default. You need to write a helper function.
import os
def upload_directory(local_dir, remote_dir, sftp):
"""
Recursively uploads a directory to a remote path using SFTP.
"""
# Create the remote directory if it doesn't exist
try:
sftp.stat(remote_dir)
except IOError:
sftp.mkdir(remote_dir)
for item in os.listdir(local_dir):
local_path = os.path.join(local_dir, item)
remote_path = os.path.join(remote_dir, item)
if os.path.isfile(local_path):
print(f"Uploading file: {local_path} to {remote_path}")
sftp.put(local_path, remote_path)
elif os.path.isdir(local_path):
print(f"Entering directory: {local_path}")
upload_directory(local_path, remote_path, sftp)
# --- Usage within the SFTP try/except block ---
# sftp = ssh.open_sftp()
# upload_directory('/path/to/local_dir', '/path/to/remote_dir', sftp)
# sftp.close()
Security Warning: AutoAddPolicy
The line ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) is convenient for development but insecure for production. It automatically adds the host key of the server to your local known_hosts file, making you vulnerable to Man-in-the-Middle (MITM) attacks.
The secure way is to manually add the host key or use WarningPolicy:
# --- Better, but still not perfect for production --- ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) # This will warn you if the host key changes, but will still allow the connection. # --- The most secure way (Recommended for Production) --- # 1. Get the host key from your server first (e.g., using `ssh-keyscan your.server.com`) # 2. Add it to your ~/.ssh/known_hosts file manually. # 3. Paramiko will then trust it automatically. # If you can't do that, you can load it from a file: # host_key = paramiko.RSAKey(filename='/path/to/known_hosts') # ssh.get_host_keys().add(hostname, 'ssh-rsa', host_key)
Summary: SCP vs. SFTP with Paramiko
| Feature | paramiko.SCPClient |
paramiko.SFTPClient |
|---|---|---|
| Purpose | Simple, fast file copying. | General-purpose file management. |
| Operations | put(), get() |
put(), get(), listdir(), remove(), rename(), mkdir(), etc. |
| Directory Handling | No built-in recursive support. | No built-in recursive support, but easier to implement with loops. |
| Performance | Generally faster for simple transfers. | Slightly more overhead, but very efficient. |
| Use Case | Quick, simple scripts where you just need to copy a file or two. | Complex scripts requiring file manipulation, directory traversal, or status checks. |
Final Recommendation:
- For a quick and simple file transfer, use
paramiko.SCPClient. It's clean and easy. - For any complex operation (directories, multiple files, checking if a file exists), use
paramiko.SFTPClient. It's more powerful and flexible.
