Commit 1bc15e57 authored by ibuler's avatar ibuler

完成webtermial demo

parent 618aed32
......@@ -8,4 +8,5 @@ urlpatterns = patterns('',
url(r'^history/$', log_history),
url(r'^log_kill/', log_kill),
url(r'^record/$', log_record),
url(r'web_terminal/$', web_terminal),
)
\ No newline at end of file
......@@ -105,4 +105,6 @@ def log_record(request):
return HttpResponse('无日志记录, 请查看日志处理脚本是否开启!')
def web_terminal(request):
return render_to_response('jlog/web_terminal.html', locals())
......@@ -16,7 +16,30 @@ import tornado.gen
from tornado.websocket import WebSocketClosedError
from tornado.options import define, options
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier, TornadoAsyncNotifier, ThreadedNotifier
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY
from gevent import monkey
monkey.patch_all()
import gevent
from gevent.socket import wait_read, wait_write
from gevent.select import select
from gevent.event import Event
import paramiko
from paramiko import PasswordRequiredException
from paramiko.dsskey import DSSKey
from paramiko.rsakey import RSAKey
from paramiko.ssh_exception import SSHException
import socket
try:
import simplejson as json
except ImportError:
import json
from StringIO import StringIO
define("port", default=8080, help="run on the given port", type=int)
define("host", default='0.0.0.0', help="run port on", type=str)
......@@ -80,6 +103,7 @@ class Application(tornado.web.Application):
handlers = [
(r'/', MainHandler),
(r'/monitor', MonitorHandler),
(r'/terminal', WebTerminalHandler),
]
setting = {
......@@ -145,6 +169,68 @@ class MainHandler(tornado.web.RequestHandler):
self.render('log_watch.html')
class WebTerminalHandler(tornado.websocket.WebSocketHandler):
tasks = []
def __init__(self, *args, **kwargs):
self.chan = None
self.ssh = None
super(WebTerminalHandler, self).__init__(*args, **kwargs)
def check_origin(self, origin):
return True
def open(self):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
self.ssh.connect('127.0.0.1', 22, 'root', 'redhat')
except:
self.write_message(json.loads({'data': 'Connect server Error'}))
self.close()
self.chan = self.ssh.invoke_shell(term='xterm')
WebTerminalHandler.tasks.append(threading.Thread(target=self._forward_outbound))
for t in WebTerminalHandler.tasks:
if t.is_alive():
continue
t.setDaemon(True)
t.start()
def on_message(self, message):
data = json.loads(message)
if not data:
return
if 'resize' in data:
self.chan.resize_pty(
data['resize'].get('width', 80),
data['resize'].get('height', 24))
if 'data' in data:
self.chan.send(data['data'])
def on_close(self):
self.write_message(json.dumps({'data': 'close websocket'}))
def _forward_outbound(self):
""" Forward outbound traffic (ssh -> websockets) """
try:
data = ''
while True:
wait_read(self.chan.fileno())
recv = self.chan.recv(1024)
if not len(recv):
return
data += recv
try:
self.write_message(json.dumps({'data': data}))
data = ''
except UnicodeDecodeError:
pass
finally:
self.close()
if __name__ == '__main__':
tornado.options.parse_command_line()
app = Application()
......
#!/usr/bin/env python
__author__ = 'liuzheng'
import sys
import os
import re
from tornado.options import options, define, parse_command_line
import tornado.wsgi
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.ioloop
import tornado.websocket
import pty
import io
import struct
import string
import random
import fcntl
import termios
import tornado.process
import tornado.options
import signal
import utils
import getpass
from connect import Jtty
define('port', type=int, default=8000)
ioloop = tornado.ioloop.IOLoop.instance()
class Terminal(tornado.websocket.WebSocketHandler):
terminals = set()
def pty(self):
# Make a "unique" id in 4 bytes
self.uid = ''.join(
random.choice(
string.ascii_lowercase + string.ascii_uppercase +
string.digits)
for _ in range(4))
self.pid, self.fd = pty.fork()
# print "pid:",self.pid,"fd",self.fd,"uid",self.uid
if self.pid == 0:
# print "Login as", self.user
os.execv("/usr/bin/ssh", [self.user, "localhost"])
# self.callee = utils.User(name=self.user)
# self.determine_user()
self.shell()
else:
self.communicate()
def determine_user(self):
if self.callee is None:
# If callee is now known and we have unsecure connection
user = self.user
try:
self.callee = utils.User(name=user)
except Exception:
# self.log.debug("Can't switch to user %s" % user, exc_info=True)
self.callee = utils.User(name='nobody')
assert self.callee is not None
def shell(self):
try:
os.chdir(self.path or self.callee.dir)
except Exception:
pass
env = os.environ
# If local and local user is the same as login user
# We set the env of the user from the browser
# Usefull when running as root
if self.caller == self.callee:
env.update(self.socket.env)
env["TERM"] = "xterm-256color"
env["COLORTERM"] = "butterfly"
env["HOME"] = self.callee.dir
# print(self.callee.dir)
env["LOCATION"] = "http%s://%s:%d/" % (
"s" if not True else "",
"localhost", 8001)
env["PATH"] = '%s:%s' % (os.path.abspath(os.path.join(
os.path.dirname(__file__), 'bin')), env.get("PATH"))
try:
tty = os.ttyname(0).replace('/dev/', '')
except Exception:
tty = ''
if self.caller != self.callee:
try:
os.chown(os.ttyname(0), self.callee.uid, -1)
except Exception:
pass
utils.add_user_info(
self.uid,
tty, os.getpid(),
self.callee.name, self.request.headers['Host'])
if os.path.exists('/usr/bin/su'):
args = ['/usr/bin/su']
else:
args = ['/bin/su']
if sys.platform == 'linux':
args.append('-p')
if tornado.options.options.shell:
args.append('-s')
args.append(tornado.options.options.shell)
args.append(self.callee.name)
os.execvpe(args[0], args, env)
def communicate(self):
fcntl.fcntl(self.fd, fcntl.F_SETFL, os.O_NONBLOCK)
self.reader = io.open(
self.fd,
'rb',
buffering=0,
closefd=False
)
self.writer = io.open(
self.fd,
'wt',
encoding='utf-8',
closefd=False
)
ioloop.add_handler(
self.fd, self.shellHandle, ioloop.READ | ioloop.ERROR)
def allow_draft76(self):
# for iOS 5.0 Safari
return True
def check_origin(self, origin):
return True
def open(self):
print "term socket open"
self.fd = None
self.closed = False
self.socket = utils.Socket(self.ws_connection.stream.socket)
self.set_nodelay(True)
self.path = None
self.user = getpass.getuser()
self.caller = self.callee = None
# self.user = "liuzheng"
self.callee = None
Terminal.terminals.add(self)
self.pty()
print self.fd
def on_message(self, msg):
print "on_message ", msg
if not hasattr(self, 'writer'):
self.on_close()
self.close()
return
if msg[0] == 'C': # Change screen
c, r = map(int, msg[1:].split(','))
s = struct.pack("HHHH", r, c, 0, 0)
fcntl.ioctl(self.fd, termios.TIOCSWINSZ, s)
elif msg[0] == 'R': # Run shell
self.writer.write(msg[1:])
self.writer.flush()
def shellHandle(self, f, events):
if events & ioloop.READ:
try:
read = self.reader.read()
except IOError:
read = ''
if read and len(read) != 0 and self.ws_connection:
self.write_message(read.decode('utf-8', 'replace'))
else:
events = ioloop.ERROR
if events & ioloop.ERROR:
self.on_close()
self.close()
def on_close(self):
print "term close", self.uid
if self.closed:
return
self.closed = True
if getattr(self, 'pid', 0) == 0:
return
utils.rm_user_info(self.uid, self.pid)
try:
ioloop.remove_handler(self.fd)
except Exception:
pass
try:
os.close(self.fd)
except Exception:
pass
try:
os.kill(self.pid, signal.SIGKILL)
os.waitpid(self.pid, 0)
except Exception:
pass
Terminal.terminals.remove(self)
class Index(tornado.web.RequestHandler):
def get(self):
self.render('templates/terminal.html')
def main():
sys.path.append('./jumpserver') # path to your project if needed
parse_command_line()
tornado_app = tornado.web.Application(
[
('/ws/terminal', Terminal),
('/ws/Terminal', Index),
])
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
This diff is collapsed.
This diff is collapsed.
/*
WSSH Javascript Client
Usage:
var client = new WSSHClient();
client.connect({
// Connection and authentication parameters
username: 'root',
hostname: 'localhost',
authentication_method: 'password', // can either be password or private_key
password: 'secretpassword', // do not provide when using private_key
key_passphrase: 'secretpassphrase', // *may* be provided if the private_key is encrypted
// Callbacks
onError: function(error) {
// Called upon an error
console.error(error);
},
onConnect: function() {
// Called after a successful connection to the server
console.debug('Connected!');
client.send('ls\n'); // You can send data back to the server by using WSSHClient.send()
},
onClose: function() {
// Called when the remote closes the connection
console.debug('Connection Reset By Peer');
},
onData: function(data) {
// Called when data is received from the server
console.debug('Received: ' + data);
}
});
*/
function WSSHClient() {
};
WSSHClient.prototype._generateEndpoint = function(options) {
console.log(options);
if (window.location.protocol == 'https:') {
var protocol = 'wss://';
} else {
var protocol = 'ws://';
}
var endpoint = protocol + window.location.host + ':8080' + '/terminal';
return endpoint;
};
WSSHClient.prototype.connect = function(options) {
var endpoint = this._generateEndpoint(options);
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.onopen = function() {
options.onConnect();
};
this._connection.onmessage = function (evt) {
var data = JSON.parse(evt.data.toString());
if (data.error !== undefined) {
options.onError(data.error);
}
else {
options.onData(data.data);
}
};
this._connection.onclose = function(evt) {
options.onClose();
};
};
WSSHClient.prototype.send = function(data) {
this._connection.send(JSON.stringify({'data': data}));
};
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment