I've been working with this multi-threaded SSH server that I made and I've encountered an issue. I need a way to get the credentials each channel used for authentication. For example, in handle
after I accept a channel
from the transport
, I'd like to know what credentials that channel used to authenticate itself to the server, something like channel.get_credentials() -> (username, password)
. Reading the docs, I couldn't find a way, but hopefully someone out there knows something I don't.
import paramiko
import socket
import threading
class Server(paramiko.server.ServerInterface):
def get_allowed_auths(self, username):
return "password"
def check_channel_request(self, kind, channelID):
return paramiko.OPEN_SUCCEEDED
def check_channel_shell_request(self, channel):
return True
def check_channel_pty_request(self, c, t, w, h, p, ph, m):
return True
def get_banner(self):
return ("Paramiko SSH Server v1.0\n\r", "EN")
def check_auth_password(self, username, password):
print(f"[*] Auth request with credentials {username}:{password}")
return paramiko.AUTH_SUCCESSFUL
def handle(conn, addr):
print("[*] Handler waiting for SSH connection...")
transport = paramiko.Transport(conn)
transport.add_server_key(host_key)
transport.start_server(server=server)
channel = transport.accept(30)
if channel:
print("[*] SSH connection recieved")
channel.send("Hi :)\r\n")
print(f"[>] {channel.recv(1024)}")
channel.close()
host_key = paramiko.RSAKey.generate(2048)
server = Server()
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 5555))
sock.listen(100)
print("[*] Socket listening")
while True:
conn, addr = sock.accept()
print(f"[*] Connection from {addr[0]}:{addr[1]}, starting handler...")
handler = threading.Thread(target=handle, args=(conn, addr))
handler.start()
print("[*] Started handler...")
I believe you will have to remember the credentials in your own code. I doubt Paramiko remembers it for you. How would it anyway? SSH authentication might be complex. Theoretically it might require multiple factors.
Imo, the idea of the Paramiko server API is that there's not a single global ServerInterface
, but each connection has its own instance. And in that instance you can store the login identity.
class Server(paramiko.server.ServerInterface):
def check_auth_password(self, username, password):
print(f"[*] Auth request with credentials {username}:{password}")
self.username = username
return paramiko.AUTH_SUCCESSFUL
server = Server()
transport.start_server(server=server)
channel = transport.accept(30)
print(f"Connection by {server.username}...")