mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-05-07 16:05:30 +02:00
Initial commit for v2.4.3
This commit is contained in:
166
WebTerminal/CPWebSocket.py
Normal file
166
WebTerminal/CPWebSocket.py
Normal file
@@ -0,0 +1,166 @@
|
||||
import tornado.httpserver
|
||||
import tornado.websocket
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
import paramiko
|
||||
import os
|
||||
import json
|
||||
import threading as multi
|
||||
import time
|
||||
import asyncio
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
|
||||
class SSHServer(multi.Thread):
|
||||
OKGREEN = '\033[92m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
DEFAULT_PORT = 22
|
||||
|
||||
@staticmethod
|
||||
def findSSHPort():
|
||||
try:
|
||||
|
||||
sshData = ProcessUtilities.outputExecutioner('cat /etc/ssh/sshd_config').split('\n')
|
||||
|
||||
for items in sshData:
|
||||
if items.find('Port') > -1 and items[0] != '#':
|
||||
if items[0] == 0:
|
||||
pass
|
||||
else:
|
||||
SSHServer.DEFAULT_PORT = int(items.split(' ')[1])
|
||||
|
||||
logging.writeToFile('SSH Port for WebTerminal Connection: %s' % (SSHServer.DEFAULT_PORT))
|
||||
except BaseException as msg:
|
||||
logging.writeToFile('%s. [SSHServer.findSSHPort]' % (str(msg)))
|
||||
|
||||
def loadPublicKey(self):
|
||||
pubkey = '/root/.ssh/cyberpanel.pub'
|
||||
data = open(pubkey, 'r').read()
|
||||
authFile = '/root/.ssh/authorized_keys'
|
||||
|
||||
checker = 1
|
||||
|
||||
try:
|
||||
authData = open(authFile, 'r').read()
|
||||
if authData.find(data) > -1:
|
||||
checker = 0
|
||||
except:
|
||||
pass
|
||||
|
||||
if checker:
|
||||
writeToFile = open(authFile, 'a')
|
||||
writeToFile.writelines(data)
|
||||
writeToFile.close()
|
||||
|
||||
def __init__(self, websocket):
|
||||
multi.Thread.__init__(self)
|
||||
self.sshclient = paramiko.SSHClient()
|
||||
self.sshclient.load_system_host_keys()
|
||||
self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
k = paramiko.RSAKey.from_private_key_file('/root/.ssh/cyberpanel')
|
||||
|
||||
## Load Public Key
|
||||
self.loadPublicKey()
|
||||
|
||||
self.sshclient.connect('127.0.0.1', SSHServer.DEFAULT_PORT, username='root', pkey=k)
|
||||
self.shell = self.sshclient.invoke_shell(term='xterm')
|
||||
self.shell.settimeout(0)
|
||||
|
||||
self.websocket = websocket
|
||||
self.color = 0
|
||||
|
||||
def recvData(self):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
while True:
|
||||
try:
|
||||
if self.websocket.running:
|
||||
if os.path.exists(self.verifyPath) and self.filePassword == self.password:
|
||||
if self.shell.recv_ready():
|
||||
self.websocket.write_message(self.shell.recv(9000).decode("utf-8"))
|
||||
else:
|
||||
time.sleep(0.001)
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
return 0
|
||||
except BaseException as msg:
|
||||
print('%s. [recvData]' % str(msg))
|
||||
time.sleep(0.001)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.recvData()
|
||||
except BaseException as msg:
|
||||
print('%s. [SSHServer.run]' % (str(msg)))
|
||||
|
||||
class WSHandler(tornado.websocket.WebSocketHandler):
|
||||
|
||||
def open(self):
|
||||
print('connected')
|
||||
self.running = 1
|
||||
self.sh = SSHServer(self)
|
||||
self.shell = self.sh.shell
|
||||
self.sh.start()
|
||||
self.init = 1
|
||||
print('connect ok')
|
||||
|
||||
def on_message(self, message):
|
||||
try:
|
||||
print('handle message')
|
||||
data = json.loads(message)
|
||||
|
||||
if self.init:
|
||||
self.sh.verifyPath = str(data['data']['verifyPath'])
|
||||
self.sh.password = str(data['data']['password'])
|
||||
self.sh.filePassword = open(self.sh.verifyPath, 'r').read()
|
||||
self.init = 0
|
||||
else:
|
||||
if os.path.exists(self.sh.verifyPath):
|
||||
if self.sh.filePassword == self.sh.password:
|
||||
self.shell.send(str(data['data']))
|
||||
|
||||
except BaseException as msg:
|
||||
print('%s. [WebTerminalServer.handleMessage]' % (str(msg)))
|
||||
|
||||
def on_close(self):
|
||||
print('connection closed')
|
||||
|
||||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r'/', WSHandler),
|
||||
])
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pidfile = '/usr/local/CyberCP/WebTerminal/pid'
|
||||
|
||||
writeToFile = open(pidfile, 'w')
|
||||
writeToFile.write(str(os.getpid()))
|
||||
writeToFile.close()
|
||||
|
||||
# SSHServer.findSSHPort()
|
||||
#
|
||||
# http_server = tornado.httpserver.HTTPServer(application, ssl_options={
|
||||
# "certfile": "/usr/local/lscp/conf/cert.pem",
|
||||
# "keyfile": "/usr/local/lscp/conf/key.pem",
|
||||
# }, )
|
||||
#
|
||||
# ADDR = '0.0.0.0'
|
||||
# http_server.listen(5678, ADDR)
|
||||
# print('*** Websocket Server Started at %s***' % ADDR)
|
||||
#
|
||||
# import signal
|
||||
# def close_sig_handler(signal, frame):
|
||||
# http_server.stop()
|
||||
# sys.exit()
|
||||
#
|
||||
# signal.signal(signal.SIGINT, close_sig_handler)
|
||||
#
|
||||
# tornado.ioloop.IOLoop.instance().start()
|
||||
0
WebTerminal/__init__.py
Normal file
0
WebTerminal/__init__.py
Normal file
6
WebTerminal/admin.py
Normal file
6
WebTerminal/admin.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
8
WebTerminal/apps.py
Normal file
8
WebTerminal/apps.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WebterminalConfig(AppConfig):
|
||||
name = 'WebTerminal'
|
||||
12
WebTerminal/cpssh.service
Normal file
12
WebTerminal/cpssh.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description = CyberPanel SSH Websocket Daemon
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStart = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py start
|
||||
ExecStop = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py stop
|
||||
Restart = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py restart
|
||||
Restart=on-abnormal
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
0
WebTerminal/migrations/__init__.py
Normal file
0
WebTerminal/migrations/__init__.py
Normal file
6
WebTerminal/models.py
Normal file
6
WebTerminal/models.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
8
WebTerminal/requirments.txt
Normal file
8
WebTerminal/requirments.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
bcrypt==3.1.7
|
||||
cffi==1.13.1
|
||||
cryptography==3.2.1
|
||||
paramiko==2.6.0
|
||||
pycparser==2.19
|
||||
PyNaCl==1.3.0
|
||||
six==1.12.0
|
||||
websockets==9.1
|
||||
51
WebTerminal/servCTRL.py
Normal file
51
WebTerminal/servCTRL.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import subprocess
|
||||
import shlex
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
|
||||
class servCTRL:
|
||||
pidfile = '/usr/local/CyberCP/WebTerminal/pid'
|
||||
|
||||
def prepareArguments(self):
|
||||
|
||||
parser = argparse.ArgumentParser(description='CyberPanel Policy Control Parser!')
|
||||
parser.add_argument('function', help='Specific a operation to perform!')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def start(self):
|
||||
|
||||
if os.path.exists(servCTRL.pidfile):
|
||||
self.stop()
|
||||
|
||||
command = '/usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/CPWebSocket.py'
|
||||
subprocess.Popen(shlex.split(command))
|
||||
|
||||
def stop(self):
|
||||
try:
|
||||
path = servCTRL.pidfile
|
||||
command = 'kill -9 %s' % (open(path, 'r').read())
|
||||
subprocess.Popen(shlex.split(command))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
policy = servCTRL()
|
||||
args = policy.prepareArguments()
|
||||
|
||||
## Website functions
|
||||
|
||||
if args.function == "start":
|
||||
policy.start()
|
||||
elif args.function == "stop":
|
||||
policy.stop()
|
||||
elif args.function == "restart":
|
||||
policy.stop()
|
||||
policy.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
132
WebTerminal/static/WebTerminal/main.js
Normal file
132
WebTerminal/static/WebTerminal/main.js
Normal file
@@ -0,0 +1,132 @@
|
||||
var charWidth = 6.2;
|
||||
var charHeight = 15.2;
|
||||
|
||||
/**
|
||||
* for full screen
|
||||
* @returns {{w: number, h: number}}
|
||||
*/
|
||||
function getTerminalSize() {
|
||||
var width = window.innerWidth;
|
||||
var height = window.innerHeight;
|
||||
return {
|
||||
w: Math.floor(width / charWidth),
|
||||
h: Math.floor(height / charHeight)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
function openTerminal(options) {
|
||||
if (!$.isEmptyObject($('.terminal')[0])) {
|
||||
alert("Please refresh this page.");
|
||||
return
|
||||
}
|
||||
|
||||
var client = new WSSHClient();
|
||||
var term = new Terminal({cols: 120, rows: 30, screenKeys: true, useStyle: true});
|
||||
term.on('data', function (data) {
|
||||
client.sendClientData(data);
|
||||
});
|
||||
term.open();
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
$("#term").show();
|
||||
term.write('Connecting...' + '\r\n');
|
||||
|
||||
client.connect({
|
||||
onError: function (error) {
|
||||
term.write('Error connecting to backend.\r\n');
|
||||
//term.destroy();
|
||||
},
|
||||
onConnect: function () {
|
||||
client.sendInitData(options);
|
||||
term.write('connection established..\r\n');
|
||||
},
|
||||
onClose: function (e) {
|
||||
term.write("\r\nconnection closed.")
|
||||
//term.destroy();
|
||||
},
|
||||
onData: function (data) {
|
||||
term.write(data);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function store(options) {
|
||||
window.localStorage.host = options.host;
|
||||
window.localStorage.port = options.port;
|
||||
window.localStorage.username = options.username;
|
||||
window.localStorage.ispwd = options.ispwd;
|
||||
window.localStorage.secret = options.secret
|
||||
}
|
||||
|
||||
function check() {
|
||||
return validResult["host"] && validResult["port"] && validResult["username"];
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var remember = $("#remember").is(":checked");
|
||||
var options = {
|
||||
verifyPath: $("#verifyPath").text(),
|
||||
password: $("#password").text()
|
||||
};
|
||||
if (remember) {
|
||||
store(options)
|
||||
}
|
||||
openTerminal(options)
|
||||
}
|
||||
|
||||
app.controller('webTerminal', function ($scope, $http, $window) {
|
||||
|
||||
$scope.cyberpanelLoading = true;
|
||||
|
||||
connect();
|
||||
$scope.restartSSH = function (name) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
|
||||
url = "/Terminal/restart";
|
||||
|
||||
var data = {
|
||||
name: name
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success',
|
||||
text: 'Successfully restarted SSH server, refreshing the page now..',
|
||||
type: 'success'
|
||||
});
|
||||
$window.location.href = '/Terminal/';
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
5977
WebTerminal/static/WebTerminal/term.js
Normal file
5977
WebTerminal/static/WebTerminal/term.js
Normal file
File diff suppressed because it is too large
Load Diff
64
WebTerminal/static/WebTerminal/ws.js
Normal file
64
WebTerminal/static/WebTerminal/ws.js
Normal file
@@ -0,0 +1,64 @@
|
||||
function WSSHClient() {
|
||||
};
|
||||
|
||||
WSSHClient.prototype._generateEndpoint = function () {
|
||||
var protocol = 'wss://';
|
||||
|
||||
var host = window.location.host.split(':')[0];
|
||||
var endpoint = protocol + host + ':5678';
|
||||
return endpoint;
|
||||
};
|
||||
|
||||
WSSHClient.prototype.connect = function (options) {
|
||||
var endpoint = this._generateEndpoint();
|
||||
|
||||
if (window.WebSocket) {
|
||||
this._connection = new WebSocket(endpoint);
|
||||
}
|
||||
else if (window.MozWebSocket) {
|
||||
this._connection = MozWebSocket(endpoint);
|
||||
}
|
||||
else {
|
||||
options.onError('WebSocket Not Supported');
|
||||
return;
|
||||
}
|
||||
|
||||
this._connection.onerror = function (evt) {
|
||||
options.onError(evt);
|
||||
};
|
||||
|
||||
this._connection.onopen = function () {
|
||||
options.onConnect();
|
||||
};
|
||||
|
||||
this._connection.onmessage = function (evt) {
|
||||
var data = evt.data.toString()
|
||||
options.onData(data);
|
||||
};
|
||||
|
||||
|
||||
this._connection.onclose = function (evt) {
|
||||
options.onClose(evt);
|
||||
};
|
||||
};
|
||||
|
||||
WSSHClient.prototype.send = function (data) {
|
||||
this._connection.send(JSON.stringify(data));
|
||||
};
|
||||
|
||||
WSSHClient.prototype.sendInitData = function (options) {
|
||||
var data = {
|
||||
hostname: options.host,
|
||||
port: options.port,
|
||||
username: options.username,
|
||||
ispwd: options.ispwd,
|
||||
secret: options.secret
|
||||
};
|
||||
this._connection.send(JSON.stringify({"tp": "init", "data": options}))
|
||||
}
|
||||
|
||||
WSSHClient.prototype.sendClientData = function (data) {
|
||||
this._connection.send(JSON.stringify({"tp": "client", "data": data, 'verifyPath': $("#verifyPath").text(), 'password': $("#password").text()}))
|
||||
}
|
||||
|
||||
var client = new WSSHClient();
|
||||
BIN
WebTerminal/static/images/loading.gif
Normal file
BIN
WebTerminal/static/images/loading.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
51
WebTerminal/templates/WebTerminal/WebTerminal.html
Normal file
51
WebTerminal/templates/WebTerminal/WebTerminal.html
Normal file
@@ -0,0 +1,51 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Terminal - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Terminal" %} - <a target="_blank" href="https://cyberpanel.net/docs/web-terminal/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Web Terminal Docs" %}</span></a></h2>
|
||||
<p>{% trans "Execute your terminal commands." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="webTerminal" class="row">
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="content-box-header">
|
||||
{% trans "Web Terminal" %} -
|
||||
<button ng-click="restartSSH()" class="btn btn-alt btn-hover btn-blue-alt mx-5 my-10">
|
||||
<span>{% trans "Reboot SSH Server" %}</span>
|
||||
<i class="glyph-icon icon-arrow-right"></i>
|
||||
</button>
|
||||
<img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="col-md-12">
|
||||
|
||||
<form class="form-horizontal bordered-row panel-body">
|
||||
<div class="form-group">
|
||||
<div id="term" class="col-sm-12">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div style="display: none" id="verifyPath">{{ verifyPath }}</div>
|
||||
<div style="display: none" id="password">{{ password }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
6
WebTerminal/tests.py
Normal file
6
WebTerminal/tests.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
7
WebTerminal/urls.py
Normal file
7
WebTerminal/urls.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.terminal, name='terminal'),
|
||||
path('restart', views.restart, name='restart'),
|
||||
]
|
||||
69
WebTerminal/views.py
Normal file
69
WebTerminal/views.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.shortcuts import render, redirect, HttpResponse
|
||||
from plogical.acl import ACLManager
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
from loginSystem.views import loadLoginPage
|
||||
from random import randint
|
||||
import os
|
||||
|
||||
from plogical.httpProc import httpProc
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
from plogical.firewallUtilities import FirewallUtilities
|
||||
from firewall.models import FirewallRules
|
||||
import json
|
||||
import plogical.randomPassword
|
||||
|
||||
# Create your views here.
|
||||
|
||||
def terminal(request):
|
||||
password = plogical.randomPassword.generate_pass()
|
||||
|
||||
verifyPath = "/home/cyberpanel/" + str(randint(100000, 999999))
|
||||
writeToFile = open(verifyPath, 'w')
|
||||
writeToFile.write(password)
|
||||
writeToFile.close()
|
||||
|
||||
## setting up ssh server
|
||||
path = '/etc/systemd/system/cpssh.service'
|
||||
curPath = '/usr/local/CyberCP/WebTerminal/cpssh.service'
|
||||
|
||||
if not os.path.exists(path):
|
||||
command = 'mv %s %s' % (curPath, path)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'systemctl start cpssh'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
FirewallUtilities.addRule('tcp', '5678', '0.0.0.0/0')
|
||||
|
||||
newFWRule = FirewallRules(name='terminal', proto='tcp', port='5678', ipAddress='0.0.0.0/0')
|
||||
newFWRule.save()
|
||||
|
||||
proc = httpProc(request, 'WebTerminal/WebTerminal.html',
|
||||
{'verifyPath': verifyPath, 'password': password}, 'admin')
|
||||
return proc.render()
|
||||
|
||||
def restart(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
command = 'systemctl restart cpssh'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
Reference in New Issue
Block a user