杰瑞科技汇

Python paramiko模块如何实现SSH远程连接?

Paramiko 是一个强大的、纯 Python 实现的 SSHv2 协议库,它允许你用 Python 代码来连接远程服务器,并执行命令、上传下载文件、设置隧道等,就像是你在终端里手动操作一样,它不依赖于任何外部的 SSH 客户端或库(如 OpenSSH),非常方便。

Python paramiko模块如何实现SSH远程连接?-图1
(图片来源网络,侵删)

核心功能

Paramiko 主要提供了以下几个核心功能:

  1. 执行远程命令:这是最基本的功能,连接到服务器后,可以执行任何 Shell 命令并获取其输出。
  2. 文件传输 (SFTP)Paramiko 内置了 SFTP (SSH File Transfer Protocol) 客户端,可以方便地在本地和远程服务器之间上传或下载文件和目录。
  3. SSH 端口转发:可以创建安全的隧道,将本地端口映射到远程服务器,或者反之,这对于访问远程数据库、服务等非常有用。
  4. 处理密钥认证:支持通过密码和 SSH 密钥对(公钥/私钥)进行身份验证。

安装

在使用之前,你需要先安装 paramiko,可以通过 pip 轻松安装:

pip install paramiko

基本用法

执行远程命令

这是最常见的用法,基本流程是:创建一个 SSHClient 对象 -> 连接服务器 -> 执行命令 -> 关闭连接。

import paramiko
# --- 1. 创建 SSHClient 对象 ---
ssh = paramiko.SSHClient()
# --- 2. 自动添加主机密钥 (第一次连接时需要) ---
# 在生产环境中,更安全的做法是使用 known_hosts 文件
# ssh.load_system_host_keys()
# 或者手动指定主机密钥
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 不推荐生产环境使用
ssh.set_missing_host_key_policy(paramiko.RejectPolicy()) # 更安全,会拒绝未知主机
try:
    # --- 3. 连接服务器 ---
    # 使用密码认证
    hostname = "your_server_ip"
    port = 22
    username = "your_username"
    password = "your_password"
    ssh.connect(hostname, port=port, username=username, password=password)
    print("连接成功!")
    # --- 4. 执行命令 ---
    command = "ls -l /home"
    stdin, stdout, stderr = ssh.exec_command(command)
    # --- 5. 获取命令输出 ---
    # stdout 是标准输出
    output = stdout.read().decode('utf-8')
    # stderr 是标准错误
    error = stderr.read().decode('utf-8')
    if error:
        print(f"命令执行出错: {error}")
    else:
        print(f"命令执行结果:\n{output}")
except paramiko.AuthenticationException:
    print("认证失败,请检查用户名和密码。")
except paramiko.SSHException as e:
    print(f"SSH连接或命令执行出错: {e}")
except Exception as e:
    print(f"发生未知错误: {e}")
finally:
    # --- 6. 关闭连接 ---
    if ssh:
        ssh.close()
        print("连接已关闭。")

代码解析:

Python paramiko模块如何实现SSH远程连接?-图2
(图片来源网络,侵删)
  • SSHClient(): 创建客户端实例。
  • set_missing_host_key_policy(): 第一次连接到一台新服务器时,服务器会发送它的公钥(主机密钥),你需要决定如何处理这个密钥。
    • AutoAddPolicy: 自动接受并保存。方便但不够安全,可能会受到中间人攻击。
    • RejectPolicy: 拒绝连接。更安全,需要你提前知道并验证服务器的公钥。
  • connect(): 建立连接,可以通过 password 传密码,也可以通过 pkey 传私钥对象。
  • exec_command(): 执行命令,它返回三个文件类对象:stdin (标准输入), stdout (标准输出), stderr (标准错误)。
  • stdout.read(): 读取命令的输出,返回的是字节流,通常需要用 .decode('utf-8') 解码成字符串。

使用 SSH 密钥对认证

使用密钥对比密码更安全、更自动化。

前提:你已经有一对公钥和私钥(通常在 ~/.ssh/ 目录下,id_rsa 是私钥,id_rsa.pub 是公钥),并且公钥已经通过 ssh-copy-id user@host 命令添加到了远程服务器的 ~/.ssh/authorized_keys 文件中。

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
    # --- 1. 指定私钥文件路径 ---
    private_key_path = '/home/user/.ssh/id_rsa'
    # --- 2. 加载私钥 ---
    # 如果你的私钥有密码,需要用 password 参数指定
    private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
    ssh.connect(hostname="your_server_ip", 
                username="your_username", 
                pkey=private_key,
                # password="your_private_key_password" # 如果私钥有密码
                )
    print("密钥认证连接成功!")
    stdin, stdout, stderr = ssh.exec_command('echo "Hello from Paramiko!"')
    print(stdout.read().decode('utf-8'))
except Exception as e:
    print(f"连接或执行命令出错: {e}")
finally:
    ssh.close()

文件传输 (SFTP)

ParamikoSFTPClient 使得文件传输变得非常简单。

import paramiko
import os
# 假设已经通过上面的方式建立了 ssh 连接
# ssh = paramiko.SSHClient()
# ssh.connect(...)
# ... (连接代码同上)
# 从 SSHClient 实例获取 SFTP 客户端
sftp = ssh.open_sftp()
try:
    # --- 上传文件 ---
    local_path = 'local_file.txt'
    remote_path = '/home/your_username/remote_file.txt'
    print(f"正在上传 {local_path} 到 {remote_path}...")
    sftp.put(local_path, remote_path)
    print("上传成功!")
    # --- 下载文件 ---
    # local_download_path = 'downloaded_file.txt'
    # remote_download_path = '/home/your_username/remote_file.txt'
    # print(f"正在下载 {remote_download_path} 到 {local_download_path}...")
    # sftp.get(remote_download_path, local_download_path)
    # print("下载成功!")
    # --- 创建目录 ---
    # remote_dir = '/home/your_username/new_dir'
    # try:
    #     sftp.mkdir(remote_dir)
    #     print(f"目录 {remote_dir} 创建成功!")
    # except IOError:
    #     print(f"目录 {remote_dir} 已存在。")
    # --- 列出远程目录内容 ---
    # dir_list = sftp.listdir('/home/your_username')
    # print(f"远程目录内容: {dir_list}")
finally:
    # 关闭 SFTP 和 SSH 连接
    sftp.close()
    ssh.close()
    print("SFTP 和 SSH 连接已关闭。")

SFTP 常用方法:

  • sftp.put(local_path, remote_path): 上传文件。
  • sftp.get(remote_path, local_path): 下载文件。
  • sftp.listdir(path): 列出指定目录下的文件和文件夹名(列表)。
  • sftp.listdir_attr(path): 列出指定目录下的文件和文件夹的属性(包含权限、大小、时间等)。
  • sftp.mkdir(path): 创建目录。
  • sftp.remove(path): 删除文件。
  • sftp.rmdir(path): 删除空目录。
  • sftp.stat(path): 获取文件/目录的属性信息。

SSH 端口转发

Paramiko 可以创建本地、远程或动态的端口转发。

示例:本地端口转发 将你本地的 localhost:8888 端口的所有流量,通过 SSH 服务器 your_server_ip 转发到目标服务器 target_server_ip:3306(远程的 MySQL 数据库)。

import paramiko
import socket
# --- 1. 建立 SSH 连接 ---
ssh_transport = paramiko.Transport(('your_server_ip', 22))
# 使用密码或密钥进行认证
# ssh_transport.connect(username='your_username', password='your_password')
# ... (或使用 pkey 认证)
# --- 2. 设置端口转发 ---
# 参数: (本地端口, 目标主机, 目标端口)
local_port = 8888
target_host = 'target_server_ip'
target_port = 3306
# 创建一个本地端口转发通道
channel = ssh_transport.open_port_fwd('', local_port)
# --- 3. 将本地端口绑定到目标 ---
# 这个循环会阻塞,保持转发通道开启
try:
    print(f"正在开启本地端口转发: 127.0.0.1:{local_port} -> {target_host}:{target_port}")
    # 使用 socket 来处理连接
    # 这部分代码相对复杂,通常会用更高级的封装
    # 这里简化说明原理
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('', local_port))
    server_socket.listen(1)
    while True:
        client_socket, addr = server_socket.accept()
        print(f"收到来自 {addr} 的连接")
        # 将客户端的连接转发到远程服务器
        # ... (这里需要处理数据流转发,略)
except KeyboardInterrupt:
    print("端口转发已停止。")
finally:
    channel.close()
    ssh_transport.close()

注意:端口转发的实现细节相对复杂,上面的例子展示了基本原理,在实际应用中,可能会使用 paramikoChannel 对象进行更精细的数据流控制。


最佳实践与注意事项

  1. 主机密钥验证永远不要在生产环境中使用 AutoAddPolicy,你应该提前获取服务器的公钥(ssh-keyscan your_server_ip),然后在代码中加载它。

    # 安全的主机密钥验证方式
    host_key = paramiko.RSAKey(data=b'...') # 这里放你获取到的服务器公钥
    ssh = paramiko.SSHClient()
    ssh.get_host_keys().add('your_server_ip', 'ssh-rsa', host_key)
    ssh.connect(...)
  2. 使用上下文管理器 (with 语句):为了确保连接和资源(如 SFTP 客户端)总是被正确关闭,可以封装一个上下文管理器,幸运的是,paramikoSSHClientSFTPClient 都支持 with 语句。

    # 使用 with 语句,自动管理连接
    with paramiko.SSHClient() as ssh:
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect("your_server_ip", username="user", password="pass")
        with ssh.open_sftp() as sftp:
            sftp.put('local.txt', 'remote.txt')
            print("文件传输完成,连接已自动关闭。")
    # 连接在这里自动关闭
  3. 处理长时间运行的命令exec_command 会等待命令执行完毕,对于长时间运行的进程(如 tail -f),它可能会阻塞,这时,可以考虑使用 invoke_shell 方法,它交互式地打开一个 Shell 会话,你可以像在终端里一样持续地发送和接收数据。

  4. 错误处理paramiko 会抛出多种异常,如 AuthenticationException, SSHException, BadHostKeyException 等,使用 try...except 块来捕获这些异常,可以让你的代码更健壮。


功能 核心方法/对象 描述
执行命令 SSHClient.exec_command() 执行单条命令,获取输出和错误。
文件传输 SSHClient.open_sftp() + SFTPClient 获取 SFTP 客户端,用于上传下载文件 (put, get)。
端口转发 Transport.open_port_fwd() 创建安全的 SSH 隧道。
认证方式 password 参数 (密码)
pkey 参数 (密钥对象)
两种主要的身份验证方式。
安全实践 set_missing_host_key_policy(paramiko.RejectPolicy()) 严格验证服务器主机密钥,防止中间人攻击。

paramiko 是 Python 自动化运维、远程管理、部署脚本等场景下的必备神器,掌握它能极大地提升你的工作效率。

分享:
扫描分享到社交APP
上一篇
下一篇