docker1000 / ssh_vnc_start.py
zhuhai111's picture
Upload ssh_vnc_start.py
60683c8 verified
#!/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)