init
This commit is contained in:
commit
acae46d111
3 changed files with 133 additions and 0 deletions
11
pyproject.toml
Normal file
11
pyproject.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[project]
|
||||
name = 'django-webshell'
|
||||
description = 'Django Channels based SSH client'
|
||||
version = '0.0.0'
|
||||
|
||||
dependencies = [
|
||||
'asyncssh>=2',
|
||||
'channels>=4',
|
||||
'channels_redis>=4',
|
||||
'django>=4.2',
|
||||
]
|
||||
0
webshell/__init__.py
Normal file
0
webshell/__init__.py
Normal file
122
webshell/consumers.py
Normal file
122
webshell/consumers.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import asyncio
|
||||
import asyncssh
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
||||
from channels.consumer import AsyncConsumer
|
||||
from channels.layers import get_channel_layer
|
||||
|
||||
|
||||
class WebShellSocketConsumer(AsyncJsonWebsocketConsumer):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.conn = None
|
||||
|
||||
async def connect(self):
|
||||
super().connect()
|
||||
|
||||
if self.scope['session'].session_key is None:
|
||||
await self.scope['session'].acreate()
|
||||
await self.scope['session'].asave()
|
||||
|
||||
await self.accept()
|
||||
|
||||
print('accepted connection as', self.scope['session'].session_key)
|
||||
|
||||
async def receive_json(self, data):
|
||||
data['owner'] = self.scope['session'].session_key
|
||||
|
||||
await self.channel_layer.group_add(data['owner'], self.channel_name)
|
||||
await self.channel_layer.send('terminal', data)
|
||||
|
||||
async def notify(self, event):
|
||||
await self.send_json({
|
||||
'type': 'stdout',
|
||||
'stdout': event['data']
|
||||
})
|
||||
|
||||
|
||||
class WebShellClient(asyncssh.SSHClient):
|
||||
pass
|
||||
|
||||
|
||||
class WebShellClientSession(asyncssh.SSHClientSession):
|
||||
def data_received(self, data, datatype):
|
||||
channel = get_channel_layer()
|
||||
|
||||
asyncio.ensure_future(channel.group_send(self.channel_name, {
|
||||
'type': 'notify',
|
||||
'data': data,
|
||||
}))
|
||||
|
||||
def eof_received(self):
|
||||
print('ssh connection done')
|
||||
|
||||
|
||||
class WebShellWorker(AsyncConsumer):
|
||||
def __init__(self):
|
||||
self.sessions = {}
|
||||
|
||||
async def ssh(self, event):
|
||||
host = event['host']
|
||||
user = event['username']
|
||||
pw = event['password']
|
||||
|
||||
if not host:
|
||||
await self.send_json({
|
||||
'error': 'no IP provided'
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
if not user:
|
||||
await self.send_json({
|
||||
'error': 'no username provided'
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
if not pw:
|
||||
await self.send_json({
|
||||
'error': 'no password provided'
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
# TODO
|
||||
# Track these on Consumer
|
||||
|
||||
if not event['owner'] in self.sessions:
|
||||
print('creating new session')
|
||||
|
||||
conn, client = await asyncssh.create_connection(
|
||||
WebShellClient,
|
||||
host,
|
||||
username=user,
|
||||
password=pw,
|
||||
known_hosts=None,
|
||||
options=asyncssh.SSHClientConnectionOptions(
|
||||
#server_host_key_algs='ssh-rsa'
|
||||
)
|
||||
)
|
||||
|
||||
chan, session = await conn.create_session(
|
||||
WebShellClientSession,
|
||||
env={
|
||||
'channel_name': event['owner'],
|
||||
},
|
||||
term_type='xterm'
|
||||
)
|
||||
|
||||
session.channel_name = event['owner']
|
||||
session.worker = self
|
||||
|
||||
self.sessions[event['owner']] = {
|
||||
'connection': conn,
|
||||
'client': client,
|
||||
'channel': chan,
|
||||
'session': session,
|
||||
}
|
||||
|
||||
async def key(self, event):
|
||||
self.sessions[event['owner']]['channel'].write(event['key'])
|
||||
Loading…
Add table
Reference in a new issue