Issue
I'm trying to write an SSH server and everything is fine but the problem seems that I cant make client to execute commands on the server as normal and can't find correct way to do it since there is no mention of it in the documentation and can't see a demo example of how to make server to accept connections so I'm completely lost in this area. code is:
#!/bin/python3
import paramiko
import socket
class Ctx(paramiko.server.ServerInterface):
def get_allowed_auths(self, username): return "password,publickey"
def check_auth_publickey(self, key): return paramiko.AUTH_SUCCESSFUL
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 ("This is MY SSH Server\n\r", "EN")
def check_channel_exec_request(self, channel, command):
print(command) # Print command
self.event.set() # I dont know why this is used.
return True # return True to accept command exec request
def check_auth_password(self, username, password):
if password == "1999": return paramiko.AUTH_SUCCESSFUL
else: return paramiko.AUTH_FAILED
paramiko.util.log_to_file("demo_server.log") # setup log file
host_key = paramiko.RSAKey(filename="./rsa") # setup rsa key file that will be used during authnitication
ctx = Ctx() # create ServerInterface context object
sock = socket.socket() # Create socket object
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 5555)) # bind socket to specific Port
sock.listen(100) # Listen for TCP connections
print("***************** Listening for connection **************************")
client, addr = sock.accept() # accept TCP socket connection
print("[+]***************** Listeing for SSH connections ***************** ")
server = paramiko.Transport(client)
server.add_server_key(host_key) # Setup key
server.start_server(server=ctx) # SSH start_server
channel = server.accept(30) # Accept Auth requests
if channel is None:
print("[+] ***************** No Auth request Was made. ***************** ")
exit(1)
channel.send("[+]***************** Welcome ***************** \n\r")
while True: # This is supposed to be used to listen to commands
channel.event.wait(5) # but I'm not sure what it does actually
Solution
My previous answer is fine for executing a single command. This new version supports the following variations:
ssh ip-address -p 5555 -T
- Creates an interactive session. For now each input line is just echoed back and logged until 'quit\n' is entered.ssh ip-address -p 5555 some-command
- Executes the single commandsome-command
, but for now that consists of just echoing back the command and logging it.ssh ip-address -p 5555 exit
- shuts down the server ifSUPPORT_EXIT = True
is set in the source.
#!/usr/bin/env python
import logging
import socket
import sys
import threading
from queue import Queue
import paramiko
logging.basicConfig()
paramiko.util.log_to_file('demo_server.log', level='INFO')
logger = paramiko.util.get_logger("paramiko")
host_key = paramiko.RSAKey(filename='./rsa')
SUPPORT_EXIT = True
# input queue of requests:
in_q = Queue()
def my_processor(stdin, stdout, event):
stdout.write('This is MY SSH Server:\n\n')
for command in stdin:
if command == 'quit\n':
break
# Just log the command and send it back:
logger.info('Command = %s', command)
stdout.write(command)
# signal termination
event.set()
class Server(paramiko.ServerInterface):
def __init__(self):
self.event = threading.Event()
def check_channel_request(self, kind, chanid):
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
def check_auth_password(self, username, password):
if password == '9999':
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def get_allowed_auths(self, username):
return 'publickey,password'
def check_channel_exec_request(self, channel, command):
# This is the command we need to parse
command = command.decode() # convert to string from bytes:
if SUPPORT_EXIT and command == 'exit':
# Place None in in_q to signify time to exit:
in_q.put(None)
# We just log it and echo it back to the user:
logger.info('Command = %s', command)
channel.send(command + '\n')
self.event.set() # Command execution complete
# Show command successfully "wired up" to stdin, stdout and stderr:
# Return False if invalid command:
return True
def check_channel_shell_request(self, channel):
""" No command specified, interactive session implied """
stdout = channel.makefile('w')
stdin = channel.makefile('r')
threading.Thread(target=my_processor, args=(stdin, stdout, self.event), daemon=True).start()
# Show command successfully "wired up" to stdin, stdout and stderr:
return True
def run_server(client):
t = paramiko.Transport(client)
t.set_gss_host(socket.getfqdn(""))
t.load_server_moduli()
t.add_server_key(host_key)
server = Server()
t.start_server(server=server)
# wait for termination:
server.event.wait()
t.close()
def accept(sock):
while True:
try:
client, _ = sock.accept()
except Exception as exc:
logger.error(exc)
else:
in_q.put(client)
def listener():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 5555))
sock.listen(100)
threading.Thread(target=accept, args=(sock,), daemon=True).start()
while True:
try:
client = in_q.get()
if SUPPORT_EXIT and client is None: # exit command issued
break
threading.Thread(target=run_server, args=(client,), daemon=True).start()
except KeyboardInterrupt:
sys.exit(0)
if __name__ == '__main__':
listener()
Answered By - Booboo