我想要完成的图表:
$ sftp joe@gatewayserver.horse
SSHCLIENT_JOE --------> GATEWAY_SERVER (does logic by username to determine
| socket to forward the connection to.)
|
\ 127.0.0.1:1030 CONTAINRR_SSHD_SALLY
---> 127.0.0.1:1031 CONTAINER_SSHD_JOE
127.0.0.1:1032 CONTAINER_SSHD_MRAYMOND
这似乎与我想要做的最接近: paramiko server mode port forwarding http://bitprophet.org/blog/2012/11/05/gateway-solutions/
但是我不想客户端执行ProxyCommand或请求“direct-tcpip”通道,而是希望服务器完成转发,对客户端来说是无形的。 我一直试图通过连接客户端的Transport对象并代表客户端创建direct-tcpip通道,使用paramiko服务器执行此操作,但我遇到了障碍。 我正在使用https://github.com/paramiko/paramiko/blob/master/demos/demo_server.py作为模板
# There's a ServerInterface class definition that overrides check_channel_request
# (allowing for direct-tcpip and session), and the other expected overides like
# check_auth_password, etc that I'm leaving out for brevity.
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 2200))
except Exception as e:
print('*** Bind failed: ' + str(e))
traceback.print_exc()
sys.exit(1)
try:
sock.listen(100)
print('Listening for connection ...')
client, addr = sock.accept()
except Exception as e:
print('*** Listen/accept failed: ' + str(e))
traceback.print_exc()
sys.exit(1)
print('Got a connection!')
try:
t = paramiko.Transport(client)
t.add_server_key(host_key)
server = Server()
try:
t.start_server(server=server)
except paramiko.SSHException:
print('*** SSH negotiation failed.')
sys.exit(1)
# Waiting for authentication.. returns an unwanted channel object since
# it isn't the right "kind" of channel
unwanted_chan = t.accept(20)
dest_addr = ("127.0.0.1", 1030) # target container port
local_addr = ("127.0.0.1", 1234) # Arbitrary port on gateway server
# Trying to put words in the client's mouth here.. fails
# What should I do?
print(" Attempting creation of direct-tcpip channel on client Transport")
tunnel_chan = t.open_channel("direct-tcpip", dest_addr, local_addr)
print("tunnel_chan created.")
tunnel_client = SSHClient()
tunnel_client.load_host_keys(host_key)
print("attempting connection using tunnel_chan")
tunnel_client.connect("127.0.0.1", port=1234, sock=tunnel_channel)
stdin, stdout, stderr = tunnel_client.exec_command('hostname')
print(stdout.readlines())
except Exception as e:
print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit(1)
当前输出:
Read key: bc1112352a682284d04f559b5977fb00
Listening for connection ...
Got a connection!
Auth attempt with key: 5605063f1d81253cddadc77b2a7b0273
Attempting creation of direct-tcpip channel on client Transport
*** Caught exception: <class 'paramiko.ssh_exception.ChannelException'>: (1, 'Administratively prohibited')
Traceback (most recent call last):
File "./para_server.py", line 139, in <module>
tunnel_chan = t.open_channel("direct-tcpip", dest_addr, local_addr)
File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 740, in open_channel
raise e
ChannelException: (1, 'Administratively prohibited')
我们目前有一个直接的sftp服务器,客户端连接它们,并且它们被chroot到各自的ftp目录。 我们想要将客户端移动到lxc容器中但不想改变它们连接到sftp的方式..(因为它们可能正在使用像filezilla这样的gui ftp客户端。)我也不想创建桥接接口并分配所有容器都有新的ips。因此,容器没有与主机分开的ips,它们共享相同的网络空间。 客户端容器的sshds将绑定到localhost上的不同端口。这样他们就可以拥有独特的端口,并且选择哪个端口的逻辑可以在概念上移出......物理主机上的一个简单服务器。
这更像是一种概念验证,而且是我的一般好奇心。
答案 0 :(得分:1)
Attempting creation of direct-tcpip channel on client Transport
*** Caught exception: <class '[...]'>: (1, 'Administratively prohibited')
容器ssh服务器拒绝你的direct-tcpip频道请求,因为它已被配置为拒绝这些请求。我收集这里的意图是将SFTP会话代理到正确的容器?我想在the usual fashion中配置了容器SSH服务器,只允许这些人做SFTP? SFTP会话通过会话通道,而不是直接tcpip通道。
我不是python编码器,也不能给你特定的paramiko代码,但你的中继代理应该打开一个到容器服务器的会话通道并调用“sftp”子系统。如果可能,您的中继代理只应在远程客户端请求SFTP会话时执行此操作,而不应用于其他类型的通道请求。
答案 1 :(得分:1)
正如我在上面的评论中所提到的,我对paramiko一无所知,但我可以从OpenSSH的角度对此进行评论,也许你可以将概念翻译成你需要的。
评论中提到了NAT。 NAT是在比SSH更低级别完成的事情,而不是基于SSH登录设置的东西(尽管SOCK5)。您可以在防火墙中实现它,而不是在SSH配置中实现它。 ProxyCommand的工作方式是协商SSH连接,然后将客户端交给下一跳说“在这里,与这个人协商”。它是在SSH协议内部实现的。
你可能不会完全失去运气。
标准ProxyCommand设置可能如下所示,在客户端指定目标端口:
host joecontainer
User joe
ProxyCommand ssh -x -a -q -Wlocalhost:1031 gatewayserver.horse
旧版本可能使用过Netcat:
host joecontainer
User joe
ProxyCommand ssh -x -a -q gatewayserver.horse nc localhost 1031
这里的想法是nc localhost 1031
是一个命令,它提供对SSH链中“下一跳”的SSH访问。只要该命令的结果是与SSH守护程序的连接,就可以在此处运行任何命令。
但是您希望端口选择由GATEWAY而不是客户端处理。其中有一点困难,因为SSH守护程序仅使用目标用户名来选择要读取的用户帐户的authorized_keys文件。它是重要的键,而不是用户。当服务器开始运行与用户关联的脚本或命令时,SSH协商就完成了,将连接转发到下一跳是为时已晚。
所以...你可以考虑让每个人都连接到普通用户,然后根据 SSH密钥完成端口选择。例如,这是gitolite处理用户的方式。在您的情况下,Joe和Sally都可以使用他们的DSA或RSA密钥连接到common@gatewayserver.horse
。
有趣的是,所有端口选择都在“common”用户的.ssh/authorized_keys
文件中处理。该文件看起来像这样:
command="/usr/bin/nc localhost 1030",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ... sally@office
command="/usr/bin/nc localhost 1031",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ... joe@home
您可以在the "AUTHORIZED_KEYS FILE FORMAT" section of the sshd(8) man page下阅读此内容。
要使用这种技术,我们仍然需要客户端ProxyCommand,但由于端口选择基于客户端密钥在服务器端进行,因此每个人都可以使用完全相同的ProxyCommand:
host mycontainer
ProxyCommand ssh -xaq common@gatewayserver.horse
Sally和Joe将运行ssh-keygen
来创建一个密钥对,如果他们还没有。他们会使用上面的格式向您发送您要添加到common / .ssh / authorized_keys的公钥。
当Joe使用他的密钥连接时,ssh服务器只运行与其密钥相关联的nc
命令。而且由于ProxyCommand,netcat的输出被解释为SSH的“下一个希望”。
我用sftp测试了这个(在我的最终目标上运行,类似于你的容器),它似乎对我有效。
SSH很神奇。 : - )