2022年 11月 3日

python ssh实现_Python之——实现SSH隧道功能

试想在如下环境中:你可以访问一台在内网中的SSH服务器,同时,你还想访问在同一个网段中的Web服务器。你不能直接访问Web服务器,但是SSH服务器可以访问Web服务器,而且这个SSH服务器上没有安装你想要使用的工具。

我们可以利用Python创建一个转发的SSH隧道来实现这些功能,具体详情参见如下代码:

# -*- coding:UTF-8 -*-

”’

文件可将SSH服务端打开的某个端口的数据流量导向到指定的另一台服务器的端口上

例如:打开命令行输入以下代码:

rforward.py 192.168.209.121 -p 8080 -r 192.168.209.122:80 –user root –password

输入ssh密码后,控制台打印如下:

—————————-控制台内容开始—————————————-

D:\Workspaces\python27\py_hacker\com\lyz\chapter2>rforward.py 192.168.209.121 -p 8080 -r 192.168.209.122:80 –user root –password

Enter SSH password:

Connecting to ssh host 192.168.209.121 …

D:\Program Files\Python27\lib\site-packages\paramiko\client.py:779: UserWarning: Unknown ssh-rsa host key for 192.168.209.121: 3bc514e5b8ad5377141030149ea79649

key.get_name(), hostname, hexlify(key.get_fingerprint()),

Now forwarding remote port 8080 to 192.168.209.122:80 …

—————————-控制台内容结束—————————————-

说明程序已经启动成功。

rforward.py 192.168.209.121 -p 8080 -r 192.168.209.122:80 –user root –password的作用是:

将访问192.168.209.121:8080的数据流量通过SSH隧道导向到192.168.209.122:80上,也就是说,打开浏览器访问http://192.168.209.121:8080会通过SSH隧道导向到http://192.168.209.122:80上。

这样,只要我们能够访问http://192.168.209.121:8080,不能直接访问http://192.168.209.122:80,通过这种方式,我们也能够访问http://192.168.209.122:80了

Created on 2017年12月19日

@author: liuyazhuang

”’

import getpass

import os

import socket

import select

import sys

import threading

from optparse import OptionParser

import paramiko

SSH_PORT = 22

DEFAULT_PORT = 4000

g_verbose = True

def handler(chan, host, port):

sock = socket.socket()

try:

sock.connect((host, port))

except Exception as e:

verbose(‘Forwarding request to %s:%d failed: %r’ % (host, port, e))

return

verbose(‘Connected! Tunnel open %r -> %r -> %r’ % (chan.origin_addr,

chan.getpeername(), (host, 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)

chan.close()

sock.close()

verbose(‘Tunnel closed from %r’ % (chan.origin_addr,))

def reverse_forward_tunnel(server_port, remote_host, remote_port, transport):

transport.request_port_forward(”, server_port)

while True:

chan = transport.accept(1000)

if chan is None:

continue

thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port))

thr.setDaemon(True)

thr.start()

def verbose(s):

if g_verbose:

print(s)

HELP = “””\

Set up a reverse forwarding tunnel across an SSH server, using paramiko. A

port on the SSH server (given with -p) is forwarded across an SSH session

back to the local machine, and out to a remote site reachable from this

network. This is similar to the openssh -R option.

“””

def get_host_port(spec, default_port):

“parse ‘hostname:22’ into a host and port, with the port optional”

args = (spec.split(‘:’, 1) + [default_port])[:2]

args[1] = int(args[1])

return args[0], args[1]

def parse_options():

global g_verbose

parser = OptionParser(usage=’usage: %prog [options] [:]’,

version=’%prog 1.0′, description=HELP)

parser.add_option(‘-q’, ‘–quiet’, action=’store_false’, dest=’verbose’, default=True,

help=’squelch all informational output’)

parser.add_option(‘-p’, ‘–remote-port’, action=’store’, type=’int’, dest=’port’,

default=DEFAULT_PORT,

help=’port on server to forward (default: %d)’ % DEFAULT_PORT)

parser.add_option(‘-u’, ‘–user’, action=’store’, type=’string’, dest=’user’,

default=getpass.getuser(),

help=’username for SSH authentication (default: %s)’ % getpass.getuser())

parser.add_option(‘-K’, ‘–key’, action=’store’, type=’string’, dest=’keyfile’,

default=None,

help=’private key file to use for SSH authentication’)

parser.add_option(”, ‘–no-key’, action=’store_false’, dest=’look_for_keys’, default=True,

help=’don\’t look for or use a private key file’)

parser.add_option(‘-P’, ‘–password’, action=’store_true’, dest=’readpass’, default=False,

help=’read password (for key or password auth) from stdin’)

parser.add_option(‘-r’, ‘–remote’, action=’store’, type=’string’, dest=’remote’, default=None, metavar=’host:port’,

help=’remote host and port to forward to’)

options, args = parser.parse_args()

if len(args) != 1:

parser.error(‘Incorrect number of arguments.’)

if options.remote is None:

parser.error(‘Remote address required (-r).’)

g_verbose = options.verbose

server_host, server_port = get_host_port(args[0], SSH_PORT)

remote_host, remote_port = get_host_port(options.remote, SSH_PORT)

return options, (server_host, server_port), (remote_host, remote_port)

def main():

options, server, remote = parse_options()

password = None

if options.readpass:

password = getpass.getpass(‘Enter SSH password: ‘)

client = paramiko.SSHClient()

client.load_system_host_keys()

client.set_missing_host_key_policy(paramiko.WarningPolicy())

verbose(‘Connecting to ssh host %s:%d …’ % (server[0], server[1]))

try:

client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile,

look_for_keys=options.look_for_keys, password=password)

except Exception as e:

print(‘*** Failed to connect to %s:%d: %r’ % (server[0], server[1], e))

sys.exit(1)

verbose(‘Now forwarding remote port %d to %s:%d …’ % (options.port, remote[0], remote[1]))

try:

reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport())

except KeyboardInterrupt:

print(‘C-c: Port forwarding stopped.’)

sys.exit(0)

if __name__ == ‘__main__’:

main()