You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用纯Python实现无需Session Manager Plugin的AWS SSM SSH数据传输与端口转发

纯Python实现AWS SSM Session Manager SSH连接与端口转发思路

核心原理

SSM Session Manager本质是通过WebSocket连接与AWS SSM服务通信,再由SSM Agent在目标实例上转发流量。纯Python实现的关键是跳过官方Session Manager Plugin,直接调用SSM API创建会话、建立WebSocket通道,再在该通道上封装SSH协议并实现端口转发。


具体实现步骤

1. 调用SSM API创建会话

使用AWS SDK for Python(boto3)调用start_session接口,获取会话的WebSocket连接地址和身份令牌。需要指定目标实例ID和用于SSH的SSM文档(AWS-StartSSHSession)。

import boto3

# 初始化SSM客户端(需配置AWS凭证,可通过环境变量、~/.aws/credentials等方式)
ssm_client = boto3.client('ssm', region_name='你的AWS区域')

# 创建SSM会话
session_response = ssm_client.start_session(
    Target='i-xxxxxxxxx',  # 目标EC2实例ID
    DocumentName='AWS-StartSSHSession'
)

# 提取WebSocket连接所需参数
stream_url = session_response['StreamUrl']
session_token = session_response['TokenValue']

2. 建立WebSocket连接

用websocket-client库创建WebSocket连接,请求头中携带会话令牌进行身份验证。这个连接将作为SSH流量的传输通道。

import websocket

class SSMSocket:
    """封装WebSocket连接为socket兼容接口,供paramiko调用"""
    def __init__(self, stream_url, token):
        self.ws = websocket.create_connection(
            stream_url,
            header={"X-Session-Token": token}
        )
    
    def send(self, data):
        self.ws.send_binary(data)
        return len(data)
    
    def recv(self, bufsize):
        frame = self.ws.recv_frame()
        return frame.data if frame else b''
    
    def close(self):
        self.ws.close()

# 初始化SSM Socket
ssm_socket = SSMSocket(stream_url, session_token)

3. 在WebSocket通道上建立SSH连接

使用paramiko库(Python SSH实现),将自定义的SSM Socket传入Transport类,完成SSH身份验证(支持密码或密钥)。

import paramiko

# 初始化SSH Transport
transport = paramiko.Transport(ssm_socket)
transport.start_client()

# 身份验证(二选一)
# 方式1:密码验证
transport.auth_password(username='ec2-user', password='你的实例密码')
# 方式2:密钥验证
# private_key = paramiko.RSAKey.from_private_key_file('/path/to/your/key.pem')
# transport.auth_publickey(username='ec2-user', key=private_key)

4. 实现本地端口转发

通过监听本地端口,将本地连接的流量通过SSH会话转发到目标实例的指定端口。使用多线程处理双向数据流,确保转发的实时性。

import threading
from socket import socket, AF_INET, SOCK_STREAM

def forward_local_port(local_port, remote_port, transport):
    """将本地端口转发到目标实例的指定端口"""
    def handle_local_connection(local_sock):
        # 在SSH会话上创建TCP通道
        remote_sock = transport.open_channel(
            'direct-tcpip',
            ('localhost', remote_port),  # 目标实例的地址和端口
            local_sock.getpeername()
        )
        if not remote_sock:
            local_sock.close()
            return
        
        # 双向转发数据
        def forward(src, dst):
            while True:
                data = src.recv(1024)
                if not data:
                    break
                dst.send(data)
            src.close()
            dst.close()
        
        threading.Thread(target=forward, args=(local_sock, remote_sock)).start()
        threading.Thread(target=forward, args=(remote_sock, local_sock)).start()
    
    # 启动本地端口监听
    server_sock = socket(AF_INET, SOCK_STREAM)
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind(('localhost', local_port))
    server_sock.listen(5)
    print(f"本地端口 {local_port} 已转发到目标实例端口 {remote_port}")
    
    while True:
        local_sock, addr = server_sock.accept()
        threading.Thread(target=handle_local_connection, args=(local_sock,)).start()

# 启动端口转发(示例:本地8080转发到目标实例80端口)
threading.Thread(
    target=forward_local_port,
    args=(8080, 80, transport)
).start()

5. 资源清理

捕获中断信号,关闭SSH连接和WebSocket通道:

try:
    while True:
        pass  # 保持主进程运行
except KeyboardInterrupt:
    print("正在关闭连接...")
    transport.close()
    ssm_socket.close()

关键注意事项

  • IAM权限:执行代码的AWS身份需具备ssm:StartSession权限,目标实例需关联包含SSM访问权限的IAM角色,且SSM Agent已安装并正常运行。
  • 依赖安装:需要提前安装依赖包:
    pip install boto3 websocket-client paramiko
    
  • 异步优化:如果需要更高性能,可使用websockets(异步WebSocket库)和asyncssh(异步SSH库)替代同步实现。

内容的提问来源于stack exchange,提问作者Shuzheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 20:17:25