我们目前有一个简单的 sftp 服务器,允许客户端连接并将其 chroot 到各自的 ftp 目录中。我们希望将客户端移入 lxc 容器,但不想改变它们连接到 sftp 的方式。
2、解决方案
我们想在主机上创建一个简单的服务器,根据用户名决定将连接转发到哪个容器,从而实现将 SSH 服务器的隧道路由到用户。这将允许客户端继续使用他们现有的 GUI ftp 客户端连接到 sftp 服务器,而无需更改他们的连接方式。
为了实现这一点,需要修改 paramiko 服务器以支持在客户端传输中创建直接 TCP/IP 通道,以便可以将连接转发到正确的容器。
以下是实现解决方案的示例代码:
import paramiko
import socket
import traceback
class Server(paramiko.ServerInterface):
def check_channel_request(self, kind, chanid):
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
elif kind == 'direct-tcpip':
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
if __name__ == '__main__':
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)
代码中,您需要将目标容器的 IP 地址和端口替换为 dest_addr 和 local_addr 变量的值。您还需要将主机密钥替换为 host_key 变量的值。
运行此代码后,您应该能够使用 SSH 客户端连接到主机,并根据您的用户名将连接转发到正确的容器。