Of course! Using Python to handle file transfers via SCP (Secure Copy Protocol) is a common task, especially for automating server administration. The paramiko library is the standard go-to for this in Python.
Here's a comprehensive guide covering everything from the basics to more advanced use cases.
What is Paramiko?
Paramiko is a pure Python (3.6+ and 2.7) implementation of the SSHv2 protocol. It provides both a client and a server-side library, allowing you to create SSH clients that can execute commands and transfer files (using SFTP or SCP) on remote servers.
The Modern Way: Using paramiko.SCPClient
The easiest and most direct way to perform an SCP transfer with Paramiko is by using its built-in SCPClient. It's a high-level wrapper that handles the SCP protocol details for you.
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 is the most common use case: copying a file from your local machine to a remote server.
import paramiko
import os
# --- Configuration ---
HOSTNAME = 'your_server_ip'
USERNAME = 'your_username'
PASSWORD = 'your_password' # Or use a key
LOCAL_PATH = '/path/to/your/local_file.txt'
REMOTE_PATH = '/path/to/your/remote_file.txt' # Can be a filename or a path
# --- Setup SSH Client ---
try:
# Create an SSH client
ssh = paramiko.SSHClient()
# Automatically add the server's host key (this is insecure, see security note below)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the server
ssh.connect(hostname=HOSTNAME, username=USERNAME, password=PASSWORD)
# --- SCP Upload ---
print(f"Uploading {LOCAL_PATH} to {REMOTE_PATH}...")
# Create an SCP client
with paramiko.SCPClient(ssh.get_transport()) as scp:
# Put the local file to the 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
if 'ssh' in locals() and ssh:
ssh.close()
Step 3: Basic SCP Download (Remote to Local)
Downloading a file from the remote server to your local machine is just as easy. You use the get() method instead of put().
import paramiko
# --- Configuration ---
HOSTNAME = 'your_server_ip'
USERNAME = 'your_username'
PASSWORD = 'your_password' # Or use a key
REMOTE_PATH = '/path/to/your/remote_file.txt'
LOCAL_PATH = '/path/to/your/local_file.txt' # Can be a filename or a path
# --- Setup SSH Client ---
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=HOSTNAME, username=USERNAME, password=PASSWORD)
# --- SCP Download ---
print(f"Downloading {REMOTE_PATH} to {LOCAL_PATH}...")
with paramiko.SCPClient(ssh.get_transport()) as scp:
# Get the remote file to the 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:
if 'ssh' in locals() and ssh:
ssh.close()
Important Security Note: AutoAddPolicy
Using ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) is convenient but insecure. It tells Paramiko to automatically accept any host key from a server for the first time, which makes you vulnerable to Man-in-the-Middle (MitM) attacks.
The secure way is to manually add the host key the first time you connect and then check for it on subsequent connections.
# A more secure way to handle host keys
import paramiko
import os
# Path to store the known hosts file
KNOWN_HOSTS_FILE = os.path.expanduser('~/.ssh/known_hosts')
# Load known hosts
known_hosts = paramiko.HostKeys(filename=KNOWN_HOSTS_FILE)
# Get the host key from the server (this happens during the first connection)
# You can get this by running `ssh-keyscan your_server_ip >> ~/.ssh/known_hosts` on your command line
# or by connecting once with a standard SSH client.
HOST_KEY = paramiko.RSAKey(data=b'...') # Paste the actual host key here
# Add the host key to your known hosts
known_hosts.add(HOSTNAME, ssh-rsa, HOST_KEY)
known_hosts.save(KNOWN_HOSTS_FILE)
# Now, create the SSH client with a strict policy
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy()) # Reject unknown hosts
ssh.load_host_keys(KNOWN_HOSTS_FILE) # Load your known hosts
# Connect
ssh.connect(hostname=HOSTNAME, username=USERNAME, password=PASSWORD)
# ... rest of your code
For most automation scripts, AutoAddPolicy is often used for simplicity, but be aware of the security trade-off.
Advanced: Using SSH Keys for Authentication
Using SSH keys is more secure and convenient than passwords. You can pass the key's path directly to the connect method.
import paramiko
# --- Configuration ---
HOSTNAME = 'your_server_ip'
USERNAME = 'your_username'
KEY_PATH = '/path/to/your/private_key' # e.g., ~/.ssh/id_rsa
REMOTE_PATH = '/path/to/your/remote_file.txt'
LOCAL_PATH = '/path/to/your/local_file.txt'
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect using the private key
# You can also specify a passphrase if your key is encrypted
ssh.connect(hostname=HOSTNAME, username=USERNAME, key_filename=KEY_PATH)
with paramiko.SCPClient(ssh.get_transport()) as scp:
# Example: Upload
scp.put(LOCAL_PATH, REMOTE_PATH)
print("Upload complete!")
# Example: Download
# scp.get(REMOTE_PATH, LOCAL_PATH)
# print("Download complete!")
except paramiko.AuthenticationException:
print("Authentication failed. Is the key authorized on the server?")
except Exception as e:
print(f"An error occurred: {e}")
finally:
if 'ssh' in locals() and ssh:
ssh.close()
Alternative: The Lower-Level SFTP Method
SCP is great for single files, but if you need to work with directories, recursive uploads/downloads, or get more fine-grained control, SFTP (SSH File Transfer Protocol) is a better choice. Paramiko's SFTPClient is more powerful.
Here's how you would do the same upload using SFTP:
import paramiko
import os
# --- Configuration ---
HOSTNAME = 'your_server_ip'
USERNAME = 'your_username'
PASSWORD = 'your_password'
LOCAL_PATH = '/path/to/your/local_file.txt'
REMOTE_PATH = '/path/to/your/remote_file.txt'
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=HOSTNAME, username=USERNAME, password=PASSWORD)
# Get an SFTP client from the active SSH transport
sftp = ssh.open_sftp()
print(f"Uploading {LOCAL_PATH} to {REMOTE_PATH} using SFTP...")
# Use sftp.put() to upload a file
sftp.put(LOCAL_PATH, REMOTE_PATH)
print("Upload complete!")
# Don't forget to close the SFTP session
sftp.close()
except Exception as e:
print(f"An error occurred: {e}")
finally:
if 'ssh' in locals() and ssh:
ssh.close()
SFTP vs. SCP: Quick Guide
| Feature | paramiko.SCPClient |
paramiko.SFTPClient |
|---|---|---|
| Use Case | Simple, fast file transfers. | Complex file operations (directories, permissions, listing). |
| Operations | put(), get() |
put(), get(), mkdir(), remove(), listdir(), stat(), etc. |
| Recursive | No, requires manual handling. | Yes, easily with sftp.put_r() and sftp.get_r(). |
| Performance | Generally faster for single files. | Slightly more overhead, but more versatile. |
| Permissions | Not preserved by default. | Preserves file permissions and attributes. |
Summary
- For quick and simple file copies, use
paramiko.SCPClient. It's straightforward and does exactly what you expect. - For managing files and directories, preserving permissions, or performing complex file operations, use
paramiko.SFTPClient. It's more powerful and flexible. - Always prefer SSH key authentication over passwords for security and convenience.
- Be mindful of the security implications of
AutoAddPolicyand useRejectPolicywith known hosts for production-level security.
