Unverified Commit 08b15983 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #29 from jumpserver/dev

Merge to master
parents 9698e4f8 c1288ecd
......@@ -150,10 +150,10 @@ class Coco:
for s in self.sessions:
if not s.stop_evt.is_set():
continue
if s.date_finished is None:
if s.date_end is None:
self.remove_session(s)
continue
delta = datetime.datetime.now() - s.date_finished
delta = datetime.datetime.now() - s.date_end
if delta > datetime.timedelta(seconds=interval*5):
self.remove_session(s)
time.sleep(interval)
......
......@@ -26,7 +26,6 @@ class BaseWebSocketHandler:
def prepare(self, request):
# self.app = self.settings["app"]
child, parent = socket.socketpair()
if request.headers.getlist("X-Forwarded-For"):
remote_ip = request.headers.getlist("X-Forwarded-For")[0]
else:
......@@ -36,10 +35,6 @@ class BaseWebSocketHandler:
self.clients[request.sid]["request"].meta = {"width": self.clients[request.sid]["cols"],
"height": self.clients[request.sid]["rows"]}
# self.request.__dict__.update(request.__dict__)
self.clients[request.sid]["client"] = Client(parent, self.clients[request.sid]["request"])
self.clients[request.sid]["proxy"] = WSProxy(self, child, self.clients[request.sid]["room"])
self.app.clients.append(self.clients[request.sid]["client"])
self.clients[request.sid]["forwarder"] = ProxyServer(self.app, self.clients[request.sid]["client"])
def check_origin(self, origin):
return True
......@@ -64,9 +59,10 @@ class SSHws(Namespace, BaseWebSocketHandler):
"cols": int(request.cookies.get('cols', 80)),
"rows": int(request.cookies.get('rows', 24)),
"room": room,
"chan": None,
"proxy": None,
"client": None,
# "chan": dict(),
"proxy": dict(),
"client": dict(),
"forwarder": dict(),
"request": None,
}
self.rooms[room] = {
......@@ -80,18 +76,31 @@ class SSHws(Namespace, BaseWebSocketHandler):
self.prepare(request)
def on_data(self, message):
if self.clients[request.sid]["proxy"]:
self.clients[request.sid]["proxy"].send({"data": message})
if message['room'] and self.clients[request.sid]["proxy"][message['room']]:
self.clients[request.sid]["proxy"][message['room']].send({"data": message['data']})
def on_host(self, message):
# 此处获取主机的信息
uuid = message.get('uuid', None)
connection = str(uuid.uuid4())
assetID = message.get('uuid', None)
userid = message.get('userid', None)
if uuid and userid:
asset = self.app.service.get_asset(uuid)
self.emit('room', {'room': connection, 'secret': message['secret']})
if assetID and userid:
asset = self.app.service.get_asset(assetID)
system_user = self.app.service.get_system_user(userid)
if system_user:
self.socketio.start_background_task(self.clients[request.sid]["forwarder"].proxy, asset, system_user)
child, parent = socket.socketpair()
self.clients[request.sid]["client"][connection] = Client(parent, self.clients[request.sid]["request"])
self.clients[request.sid]["proxy"][connection] = WSProxy(self, child, self.clients[request.sid]["room"],
connection)
self.app.clients.append(self.clients[request.sid]["client"][connection])
self.clients[request.sid]["forwarder"][connection] = ProxyServer(self.app,
self.clients[request.sid]["client"][connection])
self.socketio.start_background_task(self.clients[request.sid]["forwarder"][connection].proxy, asset,
system_user)
# self.forwarder.proxy(self.asset, system_user)
else:
self.on_disconnect()
......@@ -125,13 +134,21 @@ class SSHws(Namespace, BaseWebSocketHandler):
def on_disconnect(self):
self.on_leave(self.clients[request.sid]["room"])
try:
# todo: there maybe have bug
self.clients[request.sid]["proxy"].close()
del self.clients[request.sid]
except:
pass
# self.ssh.close()
pass
def on_logout(self, connection):
print("logout", connection)
if connection:
self.clients[request.sid]["proxy"][connection].close()
del self.clients[request.sid]["proxy"][connection]
del self.clients[request.sid]["forwarder"][connection]
self.clients[request.sid]["client"][connection].close()
del self.clients[request.sid]["client"][connection]
class HttpServer:
# prepare may be rewrite it
......@@ -155,7 +172,7 @@ class HttpServer:
port = self.app.config["HTTPD_PORT"]
print('Starting websocket server at {}:{}'.format(host, port))
self.socketio.on_namespace(SSHws('/ssh').app(self.app))
self.socketio.init_app(self.flask)
self.socketio.init_app(self.flask, async_mode="threading")
self.socketio.run(self.flask, port=port, host=host)
def shutdown(self):
......
......@@ -28,7 +28,7 @@ class InteractiveServer:
self.client = client
self.request = client.request
self.assets = None
self.search_result = None
self._search_result = None
self.asset_groups = None
self.get_user_assets_async()
self.get_user_asset_groups_async()
......@@ -37,6 +37,18 @@ class InteractiveServer:
def app(self):
return self._app()
@property
def search_result(self):
if self._search_result:
return self._search_result
else:
return None
@search_result.setter
def search_result(self, value):
value = self.filter_system_users(value)
self._search_result = value
def display_banner(self):
self.client.send(char.CLEAR_CHAR)
logo_path = os.path.join(self.app.root_path, "logo.txt")
......@@ -173,12 +185,17 @@ class InteractiveServer:
line = header + '{0.comment:%s}' % (comment_length//2) # comment中可能有中文
header += "{0.comment:%s}" % comment_length
self.client.send(title(header.format(fake_group, "ID")))
for index, group in enumerate(self.asset_groups):
for index, group in enumerate(self.asset_groups, 1):
self.client.send(wr(line.format(group, index)))
self.client.send(wr(_("Total: {}").format(len(self.asset_groups)), before=1))
def display_group_assets(self, _id):
self.search_result = self.asset_groups[_id].assets_granted
if _id > len(self.asset_groups) or _id <= 0:
self.client.send(wr(warning("Not match group, select again")))
self.display_asset_groups()
return
self.search_result = self.asset_groups[_id-1].assets_granted
self.display_search_result()
def display_search_result(self):
......@@ -214,14 +231,13 @@ class InteractiveServer:
def filter_system_users(assets):
for asset in assets:
system_users_granted = asset.system_users_granted
high_priority = max([s.priority for s in system_users_granted])
high_priority = max([s.priority for s in system_users_granted]) if system_users_granted else 1
system_users_cleaned = [s for s in system_users_granted if s.priority == high_priority]
asset.system_users_granted = system_users_cleaned
return assets
def get_user_assets(self):
assets = self.app.service.get_user_assets(self.client.user)
self.assets = self.filter_system_users(assets)
self.assets = self.app.service.get_user_assets(self.client.user)
logger.debug("Get user {} assets total: {}".format(self.client.user, len(self.assets)))
def get_user_assets_async(self):
......
......@@ -186,7 +186,7 @@ class WSProxy:
```
"""
def __init__(self, ws, child, room):
def __init__(self, ws, child, room, connection):
"""
:param ws: websocket instance or handler, have write_message method
:param child: sock child pair
......@@ -196,6 +196,7 @@ class WSProxy:
self.stop_event = threading.Event()
self.room = room
self.auto_forward()
self.connection = connection
def send(self, msg):
"""
......@@ -215,7 +216,7 @@ class WSProxy:
data = self.child.recv(BUF_SIZE)
if len(data) == 0:
self.close()
self.ws.emit("data", data.decode("utf-8"), room=self.room)
self.ws.emit("data", {'data': data.decode("utf-8"), 'room': self.connection}, room=self.room)
def auto_forward(self):
thread = threading.Thread(target=self.forward, args=())
......@@ -225,4 +226,3 @@ class WSProxy:
def close(self):
self.stop_event.set()
self.child.close()
self.ws.on_disconnect()
......@@ -7,12 +7,13 @@ import threading
import logging
import time
import weakref
import paramiko
from paramiko.ssh_exception import SSHException
from .session import Session
from .models import Server
from .utils import wrap_with_line_feed as wr, wrap_with_warning as warning
from .utils import wrap_with_line_feed as wr, wrap_with_warning as warning, \
get_private_key_fingerprint
logger = logging.getLogger(__file__)
......@@ -69,7 +70,6 @@ class ProxyServer:
def get_server_conn(self, asset, system_user):
logger.info("Connect to {}".format(asset.hostname))
if not self.validate_permission(asset, system_user):
# self.client.send(warning(_('No permission')))
self.client.send(warning('No permission'))
return None
self.get_system_user_auth(system_user)
......@@ -93,16 +93,22 @@ class ProxyServer:
timeout=TIMEOUT, compress=True, auth_timeout=10,
look_for_keys=False
)
except paramiko.AuthenticationException:
except (paramiko.AuthenticationException, paramiko.BadAuthenticationType):
admins = self.app.config['ADMINS'] or 'administrator'
self.client.send(warning(wr(
"Authenticate with server failed, contact {}".format(admins),
before=1, after=0
)))
key_fingerprint = system_user.private_key.get_hex() if system_user.private_key else None
password_short = "None"
key_fingerprint = "None"
if system_user.password:
password_short = system_user.password[:5] + (len(system_user.password)-5) * '*'
if system_user.private_key:
key_fingerprint = get_private_key_fingerprint(system_user.private_key)
logger.error("Connect {}@{}:{} auth failed, password: {}, key: {}".format(
system_user.username, asset.ip, asset.port,
system_user.password, key_fingerprint,
password_short, key_fingerprint,
))
return None
except socket.error as e:
......@@ -124,7 +130,10 @@ class ProxyServer:
width = self.request.meta.get('width', 80)
height = self.request.meta.get('height', 24)
logger.debug("Change win size: %s - %s" % (width, height))
self.server.chan.resize_pty(width=width, height=height)
try:
self.server.chan.resize_pty(width=width, height=height)
except SSHException:
break
def watch_win_size_change_async(self):
thread = threading.Thread(target=self.watch_win_size_change)
......
......@@ -9,6 +9,8 @@ import threading
import paramiko
import sys
import time
from .utils import ssh_key_gen
from .interface import SSHInterface
from .interactive import InteractiveServer
......@@ -48,13 +50,13 @@ class SSHServer:
try:
sock, addr = self.sock.accept()
logger.info("Get ssh request from {}: {}".format(addr[0], addr[1]))
thread = threading.Thread(target=self.handle, args=(sock, addr))
thread = threading.Thread(target=self.handle_connection, args=(sock, addr))
thread.daemon = True
thread.start()
except Exception as e:
logger.error("Start SSH server error: {}".format(e))
def handle(self, sock, addr):
def handle_connection(self, sock, addr):
transport = paramiko.Transport(sock, gss_kex=False)
try:
transport.load_server_moduli()
......@@ -73,17 +75,23 @@ class SSHServer:
logger.warning("Handle EOF Error")
return
chan = transport.accept(10)
if chan is None:
logger.warning("No ssh channel get")
return
while True:
chan = transport.accept()
if chan is None:
continue
server.event.wait(5)
if not server.event.is_set():
logger.warning("Client not request a valid request, exiting")
return
server.event.wait(5)
if not server.event.is_set():
logger.warning("Client not request a valid request, exiting")
return
t = threading.Thread(target=self.handle_chan, args=(chan, request))
t.daemon = True
t.start()
def handle_chan(self, chan, request):
client = Client(chan, request)
print(chan)
print(request)
self.app.add_client(client)
self.dispatch(client)
......
......@@ -14,6 +14,7 @@ import time
import datetime
import gettext
from io import StringIO
from binascii import hexlify
import paramiko
import pyte
......@@ -26,16 +27,15 @@ from .exception import NoAppException
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def ssh_key_string_to_obj(text):
key_f = StringIO(text)
def ssh_key_string_to_obj(text, password=None):
key = None
try:
key = paramiko.RSAKey.from_private_key(key_f)
key = paramiko.RSAKey.from_private_key(StringIO(text), password=password)
except paramiko.SSHException:
pass
try:
key = paramiko.DSSKey.from_private_key(key_f)
key = paramiko.DSSKey.from_private_key(StringIO(text), password=password)
except paramiko.SSHException:
pass
return key
......@@ -357,6 +357,11 @@ def _gettext():
return gettext.gettext
def get_private_key_fingerprint(key):
line = hexlify(key.get_fingerprint())
return b':'.join([line[i:i+2] for i in range(0, len(line), 2)])
def make_message():
os.makedirs(os.path.join(BASE_DIR, "locale", "zh_CN"))
pass
......
___
|_ |
| |_ _ _ __ ___ _ __ ___ ___ _ ____ _____ _ __
| | | | | '_ ` _ \| '_ \/ __|/ _ \ '__\ \ / / _ \ '__|
/\__/ / |_| | | | | | | |_) \__ \ __/ | \ V / __/ |
\____/ \__,_|_| |_| |_| .__/|___/\___|_| \_/ \___|_|
| |
|_|
......@@ -28,4 +28,4 @@ tornado==4.5.2
urllib3==1.22
wcwidth==0.1.7
werkzeug==0.12.2
jumpserver-python-sdk==0.0.22
jumpserver-python-sdk==0.0.23
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