Python:指定 SSH-ED25519 公钥算法访问 OpenSSH-server

2024-07-20 10:59:28 浏览数 (3)

随着 OpenSSH 的不断更新和安全性的提升,最新版本的 OpenSSH 服务器(如 openssh-server-9.8p1-1)逐步减少了对某些旧公钥算法的支持,转而仅支持更安全的算法如 ssh-ed25519。这在增强安全性的同时,也给一些依赖旧公钥算法的工具和脚本带来了兼容性问题。本文将探讨如何解决 Python 脚本在这种环境下无法正常连接服务器的问题。

问题背景

在最新版本的 OpenSSH 服务器中,默认配置仅支持 ssh-ed25519 公钥算法。这导致了许多工具和脚本,尤其是基于 Python 的 Paramiko 库无法正常工作,因为它们默认使用的公钥算法可能不包括 ssh-ed25519。以下是一个常见的错误信息:

代码语言:javascript复制


Exception (client): Unable to agree on a pubkey algorithm for signing a 'ssh-rsa' key!
解决方案

要解决这个问题,我们需要显式地指定 Paramiko 使用 ssh-ed25519 算法。以下是详细步骤和代码示例。

1. 更新 Paramiko 和加密库

首先,确保 Paramiko 和其依赖库(如 Cryptography)是最新的。使用以下命令进行更新:

代码语言:javascript复制

bash
pip install --upgrade paramiko cryptography
2. 创建和配置 SSH 客户端

在 Python 脚本中,使用 Paramiko 创建 SSH 客户端,并配置其使用 ssh-ed25519 公钥算法进行连接。以下是完整的代码示例:

代码语言:javascript复制

python
import paramiko

def create_ssh_client(server, port, user, password):
    """创建SSH客户端并连接到服务器"""
    ssh = paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
    # 配置公钥算法
    ssh.connect(server, port=port, username=user, password=password, allow_agent=False, look_for_keys=False)
    
    # 获取 transport 并设置公钥算法
    transport = ssh.get_transport()
    transport.get_security_options().key_types = ['ssh-ed25519']
    
    return ssh

# 使用示例
server = '10.0.0.16'
port = 22
user = 'root'
password = ' ~qCw?Ao2i08mqTp'

ssh_client = create_ssh_client(server, port, user, password)

# 示例:上传文件
sftp = ssh_client.open_sftp()
sftp.put('upload_file.py', '/tmp/upload_file.py')
sftp.close()

ssh_client.close()
3. 验证和调试连接

为了确保连接成功,可以启用调试模式查看详细的连接过程:

代码语言:javascript复制

bash
ssh -vvv root@10.0.0.16

这将输出详细的调试信息,有助于识别和解决潜在的问题。

4. 工具开发

我们可以扩展该脚本以支持命令行参数输入主机信息、区分执行命令和上传文件的功能。下面是一个完整的解决方案,包括处理命令行参数的代码,并区分执行命令和上传文件的逻辑。

完整代码:

代码语言:javascript复制

python
import paramiko
import argparse
import sys

def create_ssh_client(server, port, user, password):
    """创建SSH客户端并连接到服务器"""
    ssh = paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
    # 配置公钥算法
    ssh.connect(server, port=port, username=user, password=password, allow_agent=False, look_for_keys=False)
    
    # 获取 transport 并设置公钥算法
    transport = ssh.get_transport()
    transport.get_security_options().key_types = ['ssh-ed25519']
    
    return ssh

def upload_file(ssh_client, local_path, remote_path):
    """上传文件到远程服务器"""
    sftp = ssh_client.open_sftp()
    sftp.put(local_path, remote_path)
    sftp.close()

def execute_command(ssh_client, command):
    """在远程服务器上执行命令"""
    stdin, stdout, stderr = ssh_client.exec_command(command)
    print(stdout.read().decode())
    print(stderr.read().decode())

def main():
    parser = argparse.ArgumentParser(description='SSH 文件上传和命令执行工具')
    parser.add_argument('server', help='服务器地址')
    parser.add_argument('port', type=int, help='服务器端口')
    parser.add_argument('user', help='用户名')
    parser.add_argument('password', help='密码')
    parser.add_argument('--upload', nargs=2, metavar=('local_path', 'remote_path'), help='上传文件')
    parser.add_argument('--command', help='执行远程命令')

    args = parser.parse_args()

    try:
        ssh_client = create_ssh_client(args.server, args.port, args.user, args.password)

        if args.upload:
            local_path, remote_path = args.upload
            upload_file(ssh_client, local_path, remote_path)
        elif args.command:
            execute_command(ssh_client, args.command)
        else:
            print("请提供 --upload 或 --command 参数以执行相应的操作。")

        ssh_client.close()
    except Exception as e:
        print(f"连接或操作过程中发生错误: {e}")
        sys.exit(1)

if __name__ == '__main__':
    main()

使用说明

上传文件:

代码语言:javascript复制

bash
python script.py <server> <port> <user> <password> --upload <local_path> <remote_path>

例如:

代码语言:javascript复制

bash
python script.py 10.0.0.16 22 root ' ~qCw?Ao2i08mqTp' --upload upload_file.py /tmp/upload_file.py

执行命令:

代码语言:javascript复制

bash
python script.py <server> <port> <user> <password> --command "<command>"

例如:

代码语言:javascript复制

bash
python script.py 10.0.0.16 22 root ' ~qCw?Ao2i08mqTp' --command "ls -l /tmp"

该脚本可以灵活地根据命令行参数来处理不同的操作需求,包括上传文件和执行命令。这样不仅增强了脚本的功能性,还提高了使用的便利性,适应了最新版本 OpenSSH 服务器的安全要求。

总结

通过上述步骤,我们能够解决由于 OpenSSH 仅支持 ssh-ed25519 公钥算法而导致的 Python 脚本无法连接的问题。确保使用最新版本的 Paramiko 和正确配置公钥算法,可以保证脚本在更安全的 SSH 环境中正常运行。

0 人点赞