File size: 10,782 Bytes
e4859c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | #!/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) |