zhuhai111 commited on
Commit
60683c8
·
verified ·
1 Parent(s): b98e10a

Upload ssh_vnc_start.py

Browse files
Files changed (1) hide show
  1. ssh_vnc_start.py +272 -0
ssh_vnc_start.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import subprocess
3
+ import threading
4
+ import time
5
+ import os
6
+ import sys
7
+ import logging
8
+ import traceback
9
+ import socket
10
+ import select
11
+ import fcntl
12
+
13
+ # 设置日志
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format='%(asctime)s - %(levelname)s - %(message)s'
17
+ )
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # 打印启动信息
21
+ logger.info("=" * 50)
22
+ logger.info("VNC SSH反向隧道服务启动")
23
+ logger.info(f"当前工作目录: {os.getcwd()}")
24
+ logger.info(f"Python版本: {sys.version}")
25
+ logger.info("=" * 50)
26
+
27
+ # 隧道配置
28
+ SSH_SERVER = "www.ip188.net"
29
+ SSH_PORT = 30001
30
+ SSH_USERNAME = "aauser"
31
+ SSH_PASSWORD = "aa123456"
32
+ LOCAL_PORT = 5901 # 本地VNC端口
33
+ REMOTE_PORT = 13698 # 远程暴露端口
34
+
35
+ def read_nonblocking(pipe):
36
+ """非阻塞读取管道内容"""
37
+ output = ""
38
+ if pipe:
39
+ # 设置非阻塞模式
40
+ flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
41
+ fcntl.fcntl(pipe, fcntl.F_SETFL, flags | os.O_NONBLOCK)
42
+
43
+ # 使用select检查是否有数据可读
44
+ reads, _, _ = select.select([pipe], [], [], 0)
45
+ if pipe in reads:
46
+ try:
47
+ data = pipe.read()
48
+ if data:
49
+ output = data.decode('utf-8', errors='replace')
50
+ except (OSError, IOError) as e:
51
+ logger.error(f"读取管道错误: {e}")
52
+ return output
53
+
54
+ def create_ssh_tunnel_paramiko():
55
+ """使用paramiko库创建SSH反向隧道"""
56
+ try:
57
+ import paramiko
58
+ logger.info("使用paramiko库创建SSH反向隧道")
59
+
60
+ while True:
61
+ try:
62
+ # 创建SSH客户端
63
+ ssh_client = paramiko.SSHClient()
64
+ ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
65
+ logger.info(f"连接到SSH服务器 {SSH_SERVER}:{SSH_PORT}...")
66
+
67
+ ssh_client.connect(
68
+ SSH_SERVER,
69
+ port=SSH_PORT,
70
+ username=SSH_USERNAME,
71
+ password=SSH_PASSWORD,
72
+ timeout=60,
73
+ allow_agent=False,
74
+ look_for_keys=False
75
+ )
76
+
77
+ # 保活设置
78
+ transport = ssh_client.get_transport()
79
+ transport.set_keepalive(60)
80
+
81
+ # 设置远程转发
82
+ logger.info(f"建立反向隧道 localhost:{LOCAL_PORT} -> {REMOTE_PORT}")
83
+ transport.request_port_forward("", REMOTE_PORT)
84
+
85
+ # 处理转发连接的类
86
+ class ForwardServer(threading.Thread):
87
+ def __init__(self, transport):
88
+ threading.Thread.__init__(self)
89
+ self.transport = transport
90
+ self.daemon = True
91
+
92
+ def run(self):
93
+ logger.info("SSH VNC隧道已建立,等待连接...")
94
+
95
+ try:
96
+ while transport.is_active():
97
+ try:
98
+ chan = transport.accept(60)
99
+ if chan is None:
100
+ continue
101
+
102
+ # 为每个连接创建线程处理
103
+ thr = threading.Thread(target=self.handler, args=(chan,))
104
+ thr.daemon = True
105
+ thr.start()
106
+ except Exception as e:
107
+ if transport.is_active():
108
+ logger.error(f"处理隧道连接时出错: {e}")
109
+ else:
110
+ break
111
+
112
+ logger.info("隧道传输已关闭")
113
+ except Exception as e:
114
+ logger.error(f"转发服务器错误: {e}")
115
+
116
+ def handler(self, chan):
117
+ try:
118
+ sock = socket.socket()
119
+ try:
120
+ sock.connect(('127.0.0.1', LOCAL_PORT))
121
+ except ConnectionRefusedError:
122
+ logger.error(f"连接本地VNC端口 {LOCAL_PORT} 被拒绝")
123
+ chan.close()
124
+ return
125
+
126
+ logger.info(f"处理转发连接: {REMOTE_PORT} -> localhost:{LOCAL_PORT}")
127
+
128
+ # 在两个方向上转发数据
129
+ while True:
130
+ r, w, x = select.select([sock, chan], [], [])
131
+ if sock in r:
132
+ data = sock.recv(1024)
133
+ if len(data) == 0:
134
+ break
135
+ chan.send(data)
136
+ if chan in r:
137
+ data = chan.recv(1024)
138
+ if len(data) == 0:
139
+ break
140
+ sock.send(data)
141
+ except Exception as e:
142
+ logger.error(f"处理VNC连接时出错: {e}")
143
+ finally:
144
+ try:
145
+ sock.close()
146
+ chan.close()
147
+ except:
148
+ pass
149
+
150
+ # 启动转发服务器
151
+ forward_server = ForwardServer(transport)
152
+ forward_server.start()
153
+
154
+ # 保持连接
155
+ while transport.is_active():
156
+ time.sleep(60)
157
+ logger.info("SSH VNC隧道保持活跃状态")
158
+
159
+ logger.info("SSH连接已关闭,准备重新连接")
160
+
161
+ except Exception as e:
162
+ logger.error(f"SSH VNC隧道错误: {e}")
163
+ logger.error(traceback.format_exc())
164
+ time.sleep(10) # 等待10秒后重试
165
+
166
+ except ImportError:
167
+ logger.error("未找到paramiko库,尝试安装...")
168
+ try:
169
+ subprocess.run([sys.executable, "-m", "pip", "install", "paramiko"], check=True)
170
+ logger.info("paramiko库安装成功,重新尝试创建隧道")
171
+ create_ssh_tunnel_paramiko() # 递归调用自己
172
+ except Exception as install_error:
173
+ logger.error(f"安装paramiko失败: {install_error}")
174
+ create_ssh_tunnel_command() # 回退到命令行方式
175
+
176
+ def create_ssh_tunnel_command():
177
+ """使用ssh命令创建反向隧道"""
178
+ logger.info("使用ssh命令创建VNC反向隧道")
179
+
180
+ while True:
181
+ try:
182
+ # 检查ssh命令是否可用
183
+ ssh_check = subprocess.run(["which", "ssh"], capture_output=True, text=True)
184
+ if ssh_check.returncode != 0:
185
+ logger.error("未找到ssh命令,请确保已安装openssh-client")
186
+ time.sleep(30)
187
+ continue
188
+
189
+ # 创建ssh命令
190
+ ssh_cmd = [
191
+ "ssh",
192
+ "-o", "StrictHostKeyChecking=no",
193
+ "-o", "UserKnownHostsFile=/dev/null",
194
+ "-N",
195
+ "-R", f"{REMOTE_PORT}:localhost:{LOCAL_PORT}",
196
+ "-p", str(SSH_PORT),
197
+ f"{SSH_USERNAME}@{SSH_SERVER}"
198
+ ]
199
+
200
+ # 使用sshpass或expect自动提供密码
201
+ use_sshpass = False
202
+ sshpass_check = subprocess.run(["which", "sshpass"], capture_output=True, text=True)
203
+ if sshpass_check.returncode == 0:
204
+ use_sshpass = True
205
+ ssh_cmd = ["sshpass", "-p", SSH_PASSWORD] + ssh_cmd
206
+ logger.info("使用sshpass提供密码")
207
+
208
+ logger.info(f"执行SSH命令: {' '.join(ssh_cmd if not use_sshpass else ['sshpass', '-p', '********'] + ssh_cmd[3:])}")
209
+
210
+ # 执行SSH命令
211
+ process = subprocess.Popen(
212
+ ssh_cmd,
213
+ stdout=subprocess.PIPE,
214
+ stderr=subprocess.PIPE,
215
+ stdin=subprocess.PIPE if not use_sshpass else None
216
+ )
217
+
218
+ # 如果不使用sshpass,手动提供密码
219
+ if not use_sshpass:
220
+ try:
221
+ process.stdin.write(f"{SSH_PASSWORD}\n".encode())
222
+ process.stdin.flush()
223
+ except:
224
+ logger.error("无法提供密码")
225
+
226
+ logger.info("SSH VNC隧道已启动")
227
+
228
+ # 监控进程
229
+ while process.poll() is None:
230
+ # 读取输出
231
+ stdout_data = read_nonblocking(process.stdout)
232
+ stderr_data = read_nonblocking(process.stderr)
233
+
234
+ if stdout_data:
235
+ logger.info(f"SSH输出: {stdout_data.strip()}")
236
+ if stderr_data:
237
+ logger.error(f"SSH错误: {stderr_data.strip()}")
238
+
239
+ time.sleep(5)
240
+
241
+ # 进程已结束
242
+ exit_code = process.poll()
243
+ logger.info(f"SSH VNC隧道进程退出,退出码: {exit_code}")
244
+
245
+ # 重新启动
246
+ time.sleep(10)
247
+
248
+ except Exception as e:
249
+ logger.error(f"创建SSH VNC隧道时出错: {e}")
250
+ logger.error(traceback.format_exc())
251
+ time.sleep(10)
252
+
253
+ def main():
254
+ """主函数"""
255
+ logger.info("启动SSH VNC反向隧道服务")
256
+
257
+ # 首先尝试使用paramiko库
258
+ try:
259
+ # 检查是否安装了paramiko
260
+ import paramiko
261
+ create_ssh_tunnel_paramiko()
262
+ except ImportError:
263
+ logger.warning("未找到paramiko库,尝试使用ssh命令")
264
+ create_ssh_tunnel_command()
265
+
266
+ if __name__ == "__main__":
267
+ try:
268
+ main()
269
+ except Exception as e:
270
+ logger.error(f"主程序发生错误: {e}")
271
+ logger.error(traceback.format_exc())
272
+ sys.exit(1)