2022-11-23 19:18:40 +00:00
|
|
|
from sshim import *
|
|
|
|
import paramiko
|
|
|
|
import os
|
2022-11-24 19:59:21 +00:00
|
|
|
import six
|
|
|
|
import codecs
|
2022-11-25 15:41:02 +00:00
|
|
|
import uuid
|
|
|
|
import lxd_interface
|
|
|
|
import threading
|
|
|
|
import logging
|
2022-11-26 23:06:01 +00:00
|
|
|
import time
|
2022-11-25 15:41:02 +00:00
|
|
|
|
2022-11-26 22:24:31 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2022-11-24 19:59:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def expect(self, line, echo=True) -> str:
|
|
|
|
"""
|
|
|
|
Expect a line of input from the user. If this has the `match` method, it will call it on the input and return
|
|
|
|
the result, otherwise it will use the equality operator, ==. Notably, if a regular expression is passed in
|
|
|
|
its match method will be called and the matchdata returned. This allows you to use matching groups to pull
|
|
|
|
out interesting data and operate on it.
|
|
|
|
If ``echo`` is set to False, the server will not echo the input back to the client.
|
|
|
|
"""
|
|
|
|
buffer = six.BytesIO()
|
|
|
|
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
byte = self.fileobj.read(1)
|
|
|
|
|
|
|
|
if not byte or byte == '\x04':
|
|
|
|
raise EOFError()
|
|
|
|
elif byte == b'\t':
|
|
|
|
pass
|
|
|
|
elif byte == b'\x7f':
|
|
|
|
if buffer.tell() > 0:
|
|
|
|
self.sendall('\b \b')
|
|
|
|
buffer.truncate(buffer.tell() - 1)
|
|
|
|
elif byte == b'\x1b' and self.fileobj.read(1) == b'[':
|
|
|
|
command = self.fileobj.read(1)
|
|
|
|
if hasattr(self.delegate, 'cursor'):
|
|
|
|
self.delegate.cursor(command)
|
|
|
|
elif byte in (b'\r', b'\n'):
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
buffer.write(byte)
|
|
|
|
if echo:
|
|
|
|
self.sendall(byte)
|
|
|
|
|
|
|
|
if echo:
|
|
|
|
self.sendall('\r\n')
|
|
|
|
|
|
|
|
return codecs.decode(buffer.getvalue(), self.encoding)
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
raise
|
2022-11-23 19:18:40 +00:00
|
|
|
|
|
|
|
|
2022-11-25 15:41:02 +00:00
|
|
|
def check_channel_shell_request(self, channel):
|
|
|
|
logger.debug(channel)
|
|
|
|
Runner(self, channel).start()
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2022-11-23 19:18:40 +00:00
|
|
|
def check_auth_none(self, username):
|
2022-11-24 19:59:21 +00:00
|
|
|
if username == os.environ["ssh-username"]:
|
|
|
|
return paramiko.AUTH_PARTIALLY_SUCCESSFUL
|
|
|
|
return paramiko.AUTH_FAILED
|
2022-11-23 19:18:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
def check_auth_password(self, username, password):
|
2022-11-26 22:24:31 +00:00
|
|
|
logger.debug(os.environ["ssh-username"])
|
2022-11-23 19:18:40 +00:00
|
|
|
if username == os.environ["ssh-username"] and password == os.environ["ssh-password"]:
|
|
|
|
return paramiko.AUTH_SUCCESSFUL
|
|
|
|
return paramiko.AUTH_FAILED
|
|
|
|
|
|
|
|
|
|
|
|
def check_auth_publickey(self, username, key):
|
|
|
|
return paramiko.AUTH_FAILED
|
|
|
|
|
|
|
|
|
2022-11-25 15:41:02 +00:00
|
|
|
class Runner(threading.Thread):
|
|
|
|
def __init__(self, client, channel: paramiko.Channel):
|
|
|
|
threading.Thread.__init__(self, name='sshim.Runner(%s)' % channel.chanid)
|
|
|
|
self.instance_name = "instance-" + str(uuid.uuid4())
|
2022-11-25 16:08:10 +00:00
|
|
|
self.instance_password = str(uuid.uuid4()) # TODO: secure password generation
|
2022-11-25 15:41:02 +00:00
|
|
|
self.daemon = True
|
|
|
|
self.client = client
|
|
|
|
self.channel = channel
|
|
|
|
self.channel.settimeout(None)
|
2022-11-26 23:06:01 +00:00
|
|
|
self.transport = None
|
2022-11-25 15:41:02 +00:00
|
|
|
|
|
|
|
def run(self) -> None:
|
2022-11-26 23:06:01 +00:00
|
|
|
vm_ip = lxd_interface.create_instance(self.instance_name, self.instance_password)['address']
|
2022-11-25 15:41:02 +00:00
|
|
|
|
2022-11-25 16:08:10 +00:00
|
|
|
with paramiko.SSHClient() as ssh_client:
|
2022-11-26 23:06:01 +00:00
|
|
|
ssh_client.set_missing_host_key_policy(paramiko.WarningPolicy)
|
|
|
|
ssh_client.connect(vm_ip, username='root', password=self.instance_password)
|
|
|
|
self.transport = ssh_client.get_transport()
|
2022-11-25 16:08:10 +00:00
|
|
|
tmp_channel = ssh_client.invoke_shell()
|
|
|
|
|
2022-11-26 23:06:01 +00:00
|
|
|
logger.debug(tmp_channel)
|
|
|
|
logger.debug(self.channel)
|
|
|
|
self.channel.in_buffer = tmp_channel.in_buffer
|
|
|
|
self.channel.in_stderr_buffer = tmp_channel.in_stderr_buffer
|
|
|
|
|
|
|
|
while True:
|
|
|
|
time.sleep(1000)
|
2022-11-25 15:41:02 +00:00
|
|
|
|
|
|
|
|
2022-11-24 19:59:21 +00:00
|
|
|
Script.expect = expect
|
2022-11-23 19:18:40 +00:00
|
|
|
|
2022-11-25 15:41:02 +00:00
|
|
|
Handler.check_channel_shell_request = check_channel_shell_request
|
2022-11-23 19:18:40 +00:00
|
|
|
Handler.check_auth_none = check_auth_none
|
|
|
|
Handler.check_auth_password = check_auth_password
|
|
|
|
Handler.check_auth_publickey = check_auth_publickey
|
2022-11-24 19:59:21 +00:00
|
|
|
Handler.enable_auth_gssapi = paramiko.server.ServerInterface.enable_auth_gssapi
|
2022-11-25 15:41:02 +00:00
|
|
|
Handler.get_allowed_auths = paramiko.server.ServerInterface.get_allowed_auths
|