#!/usr/bin/env python3 import subprocess import threading import time import os import sys import logging import traceback import socket import select import fcntl # 设置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # 打印启动信息 logger.info("=" * 50) logger.info("VNC SSH反向隧道服务启动") logger.info(f"当前工作目录: {os.getcwd()}") logger.info(f"Python版本: {sys.version}") logger.info("=" * 50) # 隧道配置 SSH_SERVER = "www.ip188.net" SSH_PORT = 30001 SSH_USERNAME = "aauser" SSH_PASSWORD = "aa123456" LOCAL_PORT = 5901 # 本地VNC端口 REMOTE_PORT = 13698 # 远程暴露端口 def read_nonblocking(pipe): """非阻塞读取管道内容""" output = "" if pipe: # 设置非阻塞模式 flags = fcntl.fcntl(pipe, fcntl.F_GETFL) fcntl.fcntl(pipe, fcntl.F_SETFL, flags | os.O_NONBLOCK) # 使用select检查是否有数据可读 reads, _, _ = select.select([pipe], [], [], 0) if pipe in reads: try: data = pipe.read() if data: output = data.decode('utf-8', errors='replace') except (OSError, IOError) as e: logger.error(f"读取管道错误: {e}") return output def create_ssh_tunnel_paramiko(): """使用paramiko库创建SSH反向隧道""" try: import paramiko logger.info("使用paramiko库创建SSH反向隧道") while True: try: # 创建SSH客户端 ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) logger.info(f"连接到SSH服务器 {SSH_SERVER}:{SSH_PORT}...") ssh_client.connect( SSH_SERVER, port=SSH_PORT, username=SSH_USERNAME, password=SSH_PASSWORD, timeout=60, allow_agent=False, look_for_keys=False ) # 保活设置 transport = ssh_client.get_transport() transport.set_keepalive(60) # 设置远程转发 logger.info(f"建立反向隧道 localhost:{LOCAL_PORT} -> {REMOTE_PORT}") transport.request_port_forward("", REMOTE_PORT) # 处理转发连接的类 class ForwardServer(threading.Thread): def __init__(self, transport): threading.Thread.__init__(self) self.transport = transport self.daemon = True def run(self): logger.info("SSH VNC隧道已建立,等待连接...") try: while transport.is_active(): try: chan = transport.accept(60) if chan is None: continue # 为每个连接创建线程处理 thr = threading.Thread(target=self.handler, args=(chan,)) thr.daemon = True thr.start() except Exception as e: if transport.is_active(): logger.error(f"处理隧道连接时出错: {e}") else: break logger.info("隧道传输已关闭") except Exception as e: logger.error(f"转发服务器错误: {e}") def handler(self, chan): try: sock = socket.socket() try: sock.connect(('127.0.0.1', LOCAL_PORT)) except ConnectionRefusedError: logger.error(f"连接本地VNC端口 {LOCAL_PORT} 被拒绝") chan.close() return logger.info(f"处理转发连接: {REMOTE_PORT} -> localhost:{LOCAL_PORT}") # 在两个方向上转发数据 while True: r, w, x = select.select([sock, chan], [], []) if sock in r: data = sock.recv(1024) if len(data) == 0: break chan.send(data) if chan in r: data = chan.recv(1024) if len(data) == 0: break sock.send(data) except Exception as e: logger.error(f"处理VNC连接时出错: {e}") finally: try: sock.close() chan.close() except: pass # 启动转发服务器 forward_server = ForwardServer(transport) forward_server.start() # 保持连接 while transport.is_active(): time.sleep(60) logger.info("SSH VNC隧道保持活跃状态") logger.info("SSH连接已关闭,准备重新连接") except Exception as e: logger.error(f"SSH VNC隧道错误: {e}") logger.error(traceback.format_exc()) time.sleep(10) # 等待10秒后重试 except ImportError: logger.error("未找到paramiko库,尝试安装...") try: subprocess.run([sys.executable, "-m", "pip", "install", "paramiko"], check=True) logger.info("paramiko库安装成功,重新尝试创建隧道") create_ssh_tunnel_paramiko() # 递归调用自己 except Exception as install_error: logger.error(f"安装paramiko失败: {install_error}") create_ssh_tunnel_command() # 回退到命令行方式 def create_ssh_tunnel_command(): """使用ssh命令创建反向隧道""" logger.info("使用ssh命令创建VNC反向隧道") while True: try: # 检查ssh命令是否可用 ssh_check = subprocess.run(["which", "ssh"], capture_output=True, text=True) if ssh_check.returncode != 0: logger.error("未找到ssh命令,请确保已安装openssh-client") time.sleep(30) continue # 创建ssh命令 ssh_cmd = [ "ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-N", "-R", f"{REMOTE_PORT}:localhost:{LOCAL_PORT}", "-p", str(SSH_PORT), f"{SSH_USERNAME}@{SSH_SERVER}" ] # 使用sshpass或expect自动提供密码 use_sshpass = False sshpass_check = subprocess.run(["which", "sshpass"], capture_output=True, text=True) if sshpass_check.returncode == 0: use_sshpass = True ssh_cmd = ["sshpass", "-p", SSH_PASSWORD] + ssh_cmd logger.info("使用sshpass提供密码") logger.info(f"执行SSH命令: {' '.join(ssh_cmd if not use_sshpass else ['sshpass', '-p', '********'] + ssh_cmd[3:])}") # 执行SSH命令 process = subprocess.Popen( ssh_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE if not use_sshpass else None ) # 如果不使用sshpass,手动提供密码 if not use_sshpass: try: process.stdin.write(f"{SSH_PASSWORD}\n".encode()) process.stdin.flush() except: logger.error("无法提供密码") logger.info("SSH VNC隧道已启动") # 监控进程 while process.poll() is None: # 读取输出 stdout_data = read_nonblocking(process.stdout) stderr_data = read_nonblocking(process.stderr) if stdout_data: logger.info(f"SSH输出: {stdout_data.strip()}") if stderr_data: logger.error(f"SSH错误: {stderr_data.strip()}") time.sleep(5) # 进程已结束 exit_code = process.poll() logger.info(f"SSH VNC隧道进程退出,退出码: {exit_code}") # 重新启动 time.sleep(10) except Exception as e: logger.error(f"创建SSH VNC隧道时出错: {e}") logger.error(traceback.format_exc()) time.sleep(10) def main(): """主函数""" logger.info("启动SSH VNC反向隧道服务") # 首先尝试使用paramiko库 try: # 检查是否安装了paramiko import paramiko create_ssh_tunnel_paramiko() except ImportError: logger.warning("未找到paramiko库,尝试使用ssh命令") create_ssh_tunnel_command() if __name__ == "__main__": try: main() except Exception as e: logger.error(f"主程序发生错误: {e}") logger.error(traceback.format_exc()) sys.exit(1)