From c7f3aaa654b04b4d1ee3fb90df44f17a558975c6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 20 Sep 2016 23:34:37 +0800 Subject: [PATCH 01/26] Change appname webterminal to terminal --- apps/jumpserver/settings.py | 2 +- apps/jumpserver/urls.py | 2 +- apps/{webterminal => terminal}/__init__.py | 0 apps/{webterminal => terminal}/admin.py | 0 apps/{webterminal => terminal}/apps.py | 4 ++-- apps/{webterminal => terminal}/models.py | 0 apps/{webterminal => terminal}/templates/main.html | 0 apps/{webterminal => terminal}/tests.py | 0 apps/terminal/urls.py | 10 ++++++++++ apps/{webterminal => terminal}/views.py | 0 apps/webterminal/urls.py | 11 ----------- connect.py | 7 +++++++ server.py | 7 +++++++ 13 files changed, 28 insertions(+), 15 deletions(-) rename apps/{webterminal => terminal}/__init__.py (100%) rename apps/{webterminal => terminal}/admin.py (100%) rename apps/{webterminal => terminal}/apps.py (55%) rename apps/{webterminal => terminal}/models.py (100%) rename apps/{webterminal => terminal}/templates/main.html (100%) rename apps/{webterminal => terminal}/tests.py (100%) create mode 100644 apps/terminal/urls.py rename apps/{webterminal => terminal}/views.py (100%) delete mode 100644 apps/webterminal/urls.py create mode 100644 connect.py create mode 100644 server.py diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index cb6f7670c..b9ff532fe 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -54,7 +54,7 @@ INSTALLED_APPS = [ 'users.apps.UsersConfig', 'assets.apps.AssetsConfig', 'perms.apps.PermsConfig', - 'webterminal.apps.WebterminalConfig', + 'terminal.apps.TerminalConfig', 'ops.apps.OpsConfig', 'audits.apps.AuditsConfig', 'common.apps.CommonConfig', diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index e593e60a8..21b144da8 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -25,7 +25,7 @@ urlpatterns = [ url(r'^(api/)?users/', include('users.urls')), url(r'^assets/', include('assets.urls')), url(r'^perms/', include('perms.urls')), - url(r'^terminal/', include('webterminal.urls')), + url(r'^terminal/', include('terminal.urls')), ] diff --git a/apps/webterminal/__init__.py b/apps/terminal/__init__.py similarity index 100% rename from apps/webterminal/__init__.py rename to apps/terminal/__init__.py diff --git a/apps/webterminal/admin.py b/apps/terminal/admin.py similarity index 100% rename from apps/webterminal/admin.py rename to apps/terminal/admin.py diff --git a/apps/webterminal/apps.py b/apps/terminal/apps.py similarity index 55% rename from apps/webterminal/apps.py rename to apps/terminal/apps.py index 0e6932f9a..c81fa232b 100644 --- a/apps/webterminal/apps.py +++ b/apps/terminal/apps.py @@ -3,5 +3,5 @@ from __future__ import unicode_literals from django.apps import AppConfig -class WebterminalConfig(AppConfig): - name = 'webterminal' +class TerminalConfig(AppConfig): + name = 'terminal' diff --git a/apps/webterminal/models.py b/apps/terminal/models.py similarity index 100% rename from apps/webterminal/models.py rename to apps/terminal/models.py diff --git a/apps/webterminal/templates/main.html b/apps/terminal/templates/main.html similarity index 100% rename from apps/webterminal/templates/main.html rename to apps/terminal/templates/main.html diff --git a/apps/webterminal/tests.py b/apps/terminal/tests.py similarity index 100% rename from apps/webterminal/tests.py rename to apps/terminal/tests.py diff --git a/apps/terminal/urls.py b/apps/terminal/urls.py new file mode 100644 index 000000000..6afe754bc --- /dev/null +++ b/apps/terminal/urls.py @@ -0,0 +1,10 @@ +# coding:utf-8 +from django.conf.urls import url + +import views + +app_name = 'terminal' + +urlpatterns = [ + url(r'^web-terminal$', views.TerminalView.as_view(), name='web-terminal'), +] diff --git a/apps/webterminal/views.py b/apps/terminal/views.py similarity index 100% rename from apps/webterminal/views.py rename to apps/terminal/views.py diff --git a/apps/webterminal/urls.py b/apps/webterminal/urls.py deleted file mode 100644 index b8c6af98a..000000000 --- a/apps/webterminal/urls.py +++ /dev/null @@ -1,11 +0,0 @@ -# coding:utf-8 -from django.conf.urls import url -from .views import * -from django.contrib import admin -admin.autodiscover() - -app_name = 'webterminal' - -urlpatterns = [ - url(r'^$', TerminalView.as_view(), name='webterminal'), -] \ No newline at end of file diff --git a/connect.py b/connect.py new file mode 100644 index 000000000..f93d0bec7 --- /dev/null +++ b/connect.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + + +if __name__ == '__main__': + pass diff --git a/server.py b/server.py new file mode 100644 index 000000000..f93d0bec7 --- /dev/null +++ b/server.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + + +if __name__ == '__main__': + pass From 771cf3994491b2172db5e07e7a415219e750eb88 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 21 Sep 2016 00:38:17 +0800 Subject: [PATCH 02/26] ssh server --- test_rsa.key | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test_rsa.key diff --git a/test_rsa.key b/test_rsa.key new file mode 100644 index 000000000..e69de29bb From e020aaa368ce582a61dedc1868e8bbd0668ab95b Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 21 Sep 2016 00:43:19 +0800 Subject: [PATCH 03/26] ssh server --- connect.py | 31 ++++++++++-- server.py | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++- test_rsa.key | 15 ++++++ 3 files changed, 177 insertions(+), 4 deletions(-) diff --git a/connect.py b/connect.py index f93d0bec7..51020d8f1 100644 --- a/connect.py +++ b/connect.py @@ -1,7 +1,32 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# +# + +import sys +import os + +# reload(sys) +# sys.setdefaultencoding('utf8') + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(BASE_DIR, 'apps')) + +import re +import time +import datetime +import textwrap +import getpass +import readline +import django +import paramiko +import errno +import pyte +import operator +import struct, fcntl, signal, socket, select +from io import open as copen +import uuid + + +os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' -if __name__ == '__main__': - pass diff --git a/server.py b/server.py index f93d0bec7..b2e902218 100644 --- a/server.py +++ b/server.py @@ -3,5 +3,138 @@ # +import base64 +from binascii import hexlify +import os +import socket +import sys +import threading +import traceback + +import paramiko +from paramiko.py3compat import b, u, decodebytes + + +paramiko.util.log_to_file('demo_server.log') + +host_key = paramiko.RSAKey(filename='test_rsa.key') + + +class Server(paramiko.ServerInterface): + # 'data' is the output of base64.encodestring(str(key)) + # (using the "user_rsa_key" files) + data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' + b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC' + b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' + b'UWT10hcuO4Ks8=') + good_pub_key = paramiko.RSAKey(data=decodebytes(data)) + + def __init__(self): + self.event = threading.Event() + + def check_channel_request(self, kind, chanid): + if kind == 'session': + return paramiko.OPEN_SUCCEEDED + return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + + def check_auth_password(self, username, password): + print(username, password) + if (username == 'robey') and (password == 'foo'): + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def check_auth_publickey(self, username, key): + print('Auth attempt with key: ' + u(hexlify(key.get_fingerprint()))) + if (username == 'robey') and (key == self.good_pub_key): + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def get_allowed_auths(self, username): + return 'password,publickey' + + def check_channel_shell_request(self, channel): + self.event.set() + return True + + def check_channel_pty_request(self, channel, term, width, height, pixelwidth, + pixelheight, modes): + return True + + +def handle_ssh_request(client, addr): + print('Got a connection!') + + try: + t = paramiko.Transport(client, gss_kex=False) + t.set_gss_host(socket.getfqdn("")) + try: + t.load_server_moduli() + except: + print('(Failed to load moduli -- gex will be unsupported.)') + raise + t.add_server_key(host_key) + server = Server() + try: + t.start_server(server=server) + except paramiko.SSHException: + print('*** SSH negotiation failed.') + return + + while True: + # wait for auth + chan = t.accept(20) + if chan is None: + print('*** No channel.') + return + print('Authenticated!') + + server.event.wait(10) + if not server.event.is_set(): + print('*** Client never asked for a shell.') + return + + chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') + chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') + chan.send('Happy birthday to Robot Dave!\r\n\r\n') + chan.send('Username: ') + f = chan.makefile('rU') + username = f.readline().strip('\r\n') + chan.send('\r\nI don\'t like you, ' + username + '.\r\n') + chan.close() + + except Exception as e: + print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) + traceback.print_exc() + try: + t.close() + except: + pass + sys.exit(1) + + +def run_server(): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('', 2200)) + except Exception as e: + print('*** Bind failed: ' + str(e)) + traceback.print_exc() + sys.exit(1) + + try: + sock.listen(100) + print('Listening for connection ...') + client, addr = sock.accept() + + t = threading.Thread(target=handle_ssh_request, args=(client, addr)) + t.start() + + except Exception as e: + print('*** Listen/accept failed: ' + str(e)) + traceback.print_exc() + sys.exit(1) + + if __name__ == '__main__': - pass + run_server() \ No newline at end of file diff --git a/test_rsa.key b/test_rsa.key index e69de29bb..f50e9c538 100644 --- a/test_rsa.key +++ b/test_rsa.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz +oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/ +d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB +gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0 +EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon +soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H +tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU +avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA +4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g +H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv +qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV +HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc +nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7 +-----END RSA PRIVATE KEY----- From db2d00f828b6fb42d774aca5ee3b187bdba2afdb Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 22 Sep 2016 00:37:13 +0800 Subject: [PATCH 04/26] implement a some server --- server.py | 147 +++++++++++++++++++++++++------------------------ test_server.py | 44 +++++++++++++++ 2 files changed, 119 insertions(+), 72 deletions(-) create mode 100644 test_server.py diff --git a/server.py b/server.py index b2e902218..d27a4f313 100644 --- a/server.py +++ b/server.py @@ -57,84 +57,87 @@ class Server(paramiko.ServerInterface): return True def check_channel_pty_request(self, channel, term, width, height, pixelwidth, - pixelheight, modes): + pixelheight, modes): return True -def handle_ssh_request(client, addr): - print('Got a connection!') +class SSHServer: + def __init__(self, host, port): + self.host = host + self.port = port + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind((self.host, self.port)) - try: - t = paramiko.Transport(client, gss_kex=False) - t.set_gss_host(socket.getfqdn("")) - try: - t.load_server_moduli() - except: - print('(Failed to load moduli -- gex will be unsupported.)') - raise - t.add_server_key(host_key) - server = Server() - try: - t.start_server(server=server) - except paramiko.SSHException: - print('*** SSH negotiation failed.') - return + @staticmethod + def handle_ssh_request(client, addr): + print('Got a connection!') + try: + t = paramiko.Transport(client, gss_kex=False) + t.set_gss_host(socket.getfqdn("")) + try: + t.load_server_moduli() + except: + print('(Failed to load moduli -- gex will be unsupported.)') + raise + t.add_server_key(host_key) + server = Server() + server.add_prompt(">>") + try: + t.start_server(server=server) + except paramiko.SSHException: + print('*** SSH negotiation failed.') + return + + while True: + # wait for auth + chan = t.accept(20) + if chan is None: + print('*** No channel.') + return + print('Authenticated!') + + server.event.wait(10) + if not server.event.is_set(): + print('*** Client never asked for a shell.') + return + + chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') + chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') + chan.send('Happy birthday to Robot Dave!\r\n\r\n') + chan.send('Username: ') + f = chan.makefile('rU') + username = f.readline().strip('\r\n') + chan.send('\r\nI don\'t like you, ' + username + '.\r\n') + chan.close() + + except Exception as e: + print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) + traceback.print_exc() + try: + t.close() + except: + pass + sys.exit(1) + + def listen(self): + self.sock.listen(5) while True: - # wait for auth - chan = t.accept(20) - if chan is None: - print('*** No channel.') - return - print('Authenticated!') - - server.event.wait(10) - if not server.event.is_set(): - print('*** Client never asked for a shell.') - return - - chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') - chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') - chan.send('Happy birthday to Robot Dave!\r\n\r\n') - chan.send('Username: ') - f = chan.makefile('rU') - username = f.readline().strip('\r\n') - chan.send('\r\nI don\'t like you, ' + username + '.\r\n') - chan.close() - - except Exception as e: - print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) - traceback.print_exc() - try: - t.close() - except: - pass - sys.exit(1) - - -def run_server(): - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind(('', 2200)) - except Exception as e: - print('*** Bind failed: ' + str(e)) - traceback.print_exc() - sys.exit(1) - - try: - sock.listen(100) - print('Listening for connection ...') - client, addr = sock.accept() - - t = threading.Thread(target=handle_ssh_request, args=(client, addr)) - t.start() - - except Exception as e: - print('*** Listen/accept failed: ' + str(e)) - traceback.print_exc() - sys.exit(1) + try: + client, addr = self.sock.accept() + print('Listening for connection ...') + threading.Thread(target=self.handle_ssh_request, args=(client, addr)).start() + except Exception as e: + print('*** Bind failed: ' + str(e)) + traceback.print_exc() + sys.exit(1) if __name__ == '__main__': - run_server() \ No newline at end of file + server = SSHServer('', 2200) + try: + server.listen() + except KeyboardInterrupt: + sys.exit(1) + diff --git a/test_server.py b/test_server.py new file mode 100644 index 000000000..b6314f2bc --- /dev/null +++ b/test_server.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +import socket +import sys +import threading + + +class ThreadSocket: + def __init__(self, host, port): + self.host = host + self.port = port + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind((self.host, self.port)) + + def listen(self): + self.sock.listen(5) + while True: + client, address = self.sock.accept() + client.settimeout(60) + threading.Thread(target=self.handle_client_request, args=(client, address)).start() + + def handle_client_request(self, client, address): + print("Get client: %s" % str(address)) + while True: + try: + data = client.recv(1024) + print("sleep : %s" % str(address)) + if data: + client.send(data) + else: + raise IndexError('Client has disconnected') + except: + client.close() + + +if __name__ == '__main__': + server = ThreadSocket('', 9000) + try: + server.listen() + except KeyboardInterrupt: + sys.exit(1) From f946a4bfb3c84f2240bd45147102956814abe2a4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 22 Sep 2016 23:26:44 +0800 Subject: [PATCH 05/26] finish example --- server.py | 75 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/server.py b/server.py index d27a4f313..0e348a0ec 100644 --- a/server.py +++ b/server.py @@ -6,10 +6,13 @@ import base64 from binascii import hexlify import os -import socket import sys import threading import traceback +import tty +import termios +import struct, fcntl, signal, socket, select +import errno import paramiko from paramiko.py3compat import b, u, decodebytes @@ -20,7 +23,7 @@ paramiko.util.log_to_file('demo_server.log') host_key = paramiko.RSAKey(filename='test_rsa.key') -class Server(paramiko.ServerInterface): +class SSHService(paramiko.ServerInterface): # 'data' is the output of base64.encodestring(str(key)) # (using the "user_rsa_key" files) data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' @@ -68,11 +71,19 @@ class SSHServer: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) + self.server_ssh = None + self.server_chan = None - @staticmethod - def handle_ssh_request(client, addr): + def connect(self): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(hostname='127.0.0.1', port=22, username='root', password='redhat') + self.server_ssh = ssh + self.server_chan = channel = ssh.invoke_shell(term='xterm') + return channel + + def handle_ssh_request(self, client, addr): print('Got a connection!') - try: t = paramiko.Transport(client, gss_kex=False) t.set_gss_host(socket.getfqdn("")) @@ -82,35 +93,45 @@ class SSHServer: print('(Failed to load moduli -- gex will be unsupported.)') raise t.add_server_key(host_key) - server = Server() - server.add_prompt(">>") + service = SSHService() try: - t.start_server(server=server) + t.start_server(server=service) except paramiko.SSHException: print('*** SSH negotiation failed.') return + chan = t.accept(20) + + if chan is None: + print('*** No channel.') + return + print('Authenticated!') + + chan.settimeout(100) + + chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') + chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') + chan.send('Happy birthday to Robot Dave!\r\n\r\n') + server_chan = self.connect() + if not service.event.is_set(): + print('*** Client never asked for a shell.') + return while True: - # wait for auth - chan = t.accept(20) - if chan is None: - print('*** No channel.') - return - print('Authenticated!') + r, w, e = select.select([server_chan, chan], [], []) - server.event.wait(10) - if not server.event.is_set(): - print('*** Client never asked for a shell.') - return + if chan in r: + recv_data = chan.recv(1024).decode('utf8') + print("From client: " + repr(recv_data)) + if len(recv_data) == 0: + break + server_chan.send(recv_data) - chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') - chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') - chan.send('Happy birthday to Robot Dave!\r\n\r\n') - chan.send('Username: ') - f = chan.makefile('rU') - username = f.readline().strip('\r\n') - chan.send('\r\nI don\'t like you, ' + username + '.\r\n') - chan.close() + if server_chan in r: + recv_data = server_chan.recv(1024).decode('utf8') + print("From server: " + repr(recv_data)) + if len(recv_data) == 0: + break + chan.send(recv_data) except Exception as e: print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) @@ -127,7 +148,7 @@ class SSHServer: try: client, addr = self.sock.accept() print('Listening for connection ...') - threading.Thread(target=self.handle_ssh_request, args=(client, addr)).start() + threading.Thread(target=self.handle_ssh_request, args=( client, addr)).start() except Exception as e: print('*** Bind failed: ' + str(e)) traceback.print_exc() From de0f8c24f7c33acf33256ecc4f6b34f96ecde487 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 22 Sep 2016 23:56:27 +0800 Subject: [PATCH 06/26] finish example --- server.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index 0e348a0ec..c8e01d52f 100644 --- a/server.py +++ b/server.py @@ -116,22 +116,41 @@ class SSHServer: if not service.event.is_set(): print('*** Client never asked for a shell.') return + server_data = [] + input_mode = True while True: r, w, e = select.select([server_chan, chan], [], []) + if chan in r: recv_data = chan.recv(1024).decode('utf8') - print("From client: " + repr(recv_data)) + # print("From client: " + repr(recv_data)) if len(recv_data) == 0: break server_chan.send(recv_data) if server_chan in r: recv_data = server_chan.recv(1024).decode('utf8') - print("From server: " + repr(recv_data)) + # print("From server: " + repr(recv_data)) if len(recv_data) == 0: break chan.send(recv_data) + if len(recv_data) > 20: + server_data.append('...') + else: + server_data.append(recv_data) + try: + if repr(server_data[-2]) == u'\r\n': + result = server_data.pop() + server_data.pop() + command = ''.join(server_data) + server_data = [] + print(">>> Command: %s" % command) + print(result) + except IndexError: + pass + print(server_data) + except Exception as e: print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) From ab18fe466bbbb84b3a0e6effcf7d5abcfc332b70 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 24 Sep 2016 21:47:10 +0800 Subject: [PATCH 07/26] stash --- apps/terminal/hands.py | 5 +++ apps/terminal/keys/README.md | 0 server.py => apps/terminal/server.py | 60 ++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 apps/terminal/hands.py create mode 100644 apps/terminal/keys/README.md rename server.py => apps/terminal/server.py (79%) diff --git a/apps/terminal/hands.py b/apps/terminal/hands.py new file mode 100644 index 000000000..38de26f22 --- /dev/null +++ b/apps/terminal/hands.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +from users.utils import ssh_key_gen diff --git a/apps/terminal/keys/README.md b/apps/terminal/keys/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/server.py b/apps/terminal/server.py similarity index 79% rename from server.py rename to apps/terminal/server.py index c8e01d52f..dfcea1d21 100644 --- a/server.py +++ b/apps/terminal/server.py @@ -1,22 +1,39 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # +import sys +import os +import django +BASE_DIR = os.path.dirname(__file__) +APP_DIR = os.path.abspath(os.path.dirname(BASE_DIR)) +sys.path.append(APP_DIR) + +os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' + +try: + django.setup() +except IndexError: + pass import base64 from binascii import hexlify -import os import sys import threading import traceback import tty import termios -import struct, fcntl, signal, socket, select +import struct +import fcntl +import signal +import socket +import select import errno - import paramiko from paramiko.py3compat import b, u, decodebytes +from .hands import ssh_key_gen + paramiko.util.log_to_file('demo_server.log') @@ -24,24 +41,42 @@ host_key = paramiko.RSAKey(filename='test_rsa.key') class SSHService(paramiko.ServerInterface): - # 'data' is the output of base64.encodestring(str(key)) - # (using the "user_rsa_key" files) - data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' - b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC' - b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' - b'UWT10hcuO4Ks8=') - good_pub_key = paramiko.RSAKey(data=decodebytes(data)) + # data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' + # b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC' + # b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' + # b'UWT10hcuO4Ks8=') + # good_pub_key = paramiko.RSAKey(data=decodebytes(data)) + + ssh_key_path = os.path.join(BASE_DIR, 'keys', 'ssh_host_key') + ssh_pub_key_path = ssh_key_path + '.pub' def __init__(self): self.event = threading.Event() + @classmethod + def get_host_key(cls): + if os.path.isfile(cls.ssh_pub_key_path): + with open(cls.ssh_pub_key_path) as f: + ssh_pub_key = f.read() + else: + ssh_key, ssh_pub_key = cls.host_key_gen() + return ssh_pub_key + + @classmethod + def host_key_gen(cls): + ssh_key, ssh_pub_key = ssh_key_gen() + with open(cls.ssh_key_path, 'w') as f: + with open(cls.ssh_pub_key_path, 'w') as f2: + f.write(ssh_key) + f2.write(ssh_pub_key) + return ssh_key, ssh_pub_key + def check_channel_request(self, kind, chanid): if kind == 'session': return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): - print(username, password) if (username == 'robey') and (password == 'foo'): return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_FAILED @@ -151,7 +186,6 @@ class SSHServer: pass print(server_data) - except Exception as e: print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) traceback.print_exc() @@ -167,7 +201,7 @@ class SSHServer: try: client, addr = self.sock.accept() print('Listening for connection ...') - threading.Thread(target=self.handle_ssh_request, args=( client, addr)).start() + threading.Thread(target=self.handle_ssh_request, args=(client, addr)).start() except Exception as e: print('*** Bind failed: ' + str(e)) traceback.print_exc() From cfef3744548e357cf61464b4ed2b796a167e8871 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 00:11:31 +0800 Subject: [PATCH 08/26] Update ssh server --- apps/terminal/hands.py | 3 +- apps/terminal/keys/host_rsa_key | 27 +++++++ apps/terminal/server.py | 130 +++++++++++++++++--------------- apps/users/models.py | 6 ++ apps/users/utils.py | 20 ++++- config-example.py | 4 + 6 files changed, 128 insertions(+), 62 deletions(-) create mode 100644 apps/terminal/keys/host_rsa_key diff --git a/apps/terminal/hands.py b/apps/terminal/hands.py index 38de26f22..065ff1a77 100644 --- a/apps/terminal/hands.py +++ b/apps/terminal/hands.py @@ -2,4 +2,5 @@ # -*- coding: utf-8 -*- # -from users.utils import ssh_key_gen +from users.utils import ssh_key_gen, check_user_is_valid + diff --git a/apps/terminal/keys/host_rsa_key b/apps/terminal/keys/host_rsa_key new file mode 100644 index 000000000..5b1aa9804 --- /dev/null +++ b/apps/terminal/keys/host_rsa_key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAxreMFq9tp1hb2NOIkV7PqeyfS0GTJSfW2WcXuqkGPSVtPdYw +cJRY/s5eBn0KbO6JVj9yfwXNTKdnq1ODuuJDMnhTWXq1x7VICcou/69kFdSGiAzl +wfYP6LATEMCmpFRVi5UZip7SWopLE1JEw79nYWrbhUpDOpGTRKoIxoz+Uvg0h15G +pec3faL7PXj/J6j9pis44N1PCMBY3DVFPLopVcFpElbzwScNvZGBes90JoKLsqfD +Vjc9PUPjYjck9NFR6Xy0D5Gnw9MD5o0yK1l+3sibXjFBMOxA7aNxIlMhXQGol8R7 +3HBHWo/+Bwct3w1c8cPKfdPd8jGn3eWGYxupvQIDAQABAoIBADuyWDtYaDClsrHo +mlZRjUEW/KO3B2VaGoklF1PUAzPLUo4JEnQ/nJyvkj+QwNkIr+lhFhxiudIVWGd3 +p1M1Ncqrqx5uZr2gEAwg2Q2muwJz3hZxCXTDXvQgMRoPRgCH9UsBd7LVE4xvjy42 +wMGtdnkliNz5+khWA0/VZN2A7cYukrKzPwnhEMSrzYfnRwcOvp8pDp++Yjs3ZhQL +8+sgL1UDap5p5QZSQ98qJGNwmePAlTig+2Z5HvF+zussK2N7g5AcfghQFo5vCw/L +PXYtIfBH+Tv+6s7vMBMSLpbDcAZsxR9gDVUQi252Gu/nWClCzH3Kgu5ormHSOkYO +F6/n5AECgYEA69anuf52KWwYypVA73HiUbuzOdeuc1Br+s0uzOvpFX0HaqDxo8dm +N7FtUj/WnoqFivQrsrt4LpIzKn1XPNk7wMnwIZAQHNEI8sy7LBVh3RJOP2ZC2329 +ZHWxB3EVQ8Q5MbZy/AOn92UYwz8xIb0LweGYnHZlMp+xtOhdUR0/Z90CgYEA17R8 +EOeErksBRHotrEk0jLx+rrhK0JGcpXo/Dw6AOEp936DgHlqbkUURT2ejDOSQY/dN +7i4WeFJCVfFRNMbsitWxNmAdl3NJ5C2bV+7sz+oZfo5zP/e1RYCNLVjLxLYOHQ37 +GWwAlQr6fPcIZMCaPH+xq/0WSqcP96Lu6G0VG2ECgYB1XtcKkcFszAdqiu1OPXdN +BgUkfFqtuRCEOSlZgu71aswOHRslT09n2D13+Z1uObJMfUhiIzqkss4UD10jQ1mh +kN6ZVYEvVjkF3S4pulqCE2It207avbFMFeaMtZLHrxhnzU1cbtVhIkc4pHJnQBZh +30x8Uc/7ac6fIiWPAOdVYQKBgCi8rEWhA7zK64VcMa388VC29JHYukBjj5rs2GXm +ji6TWuxV/J2e7QxlZ9yALRntPJu0g+I8j//PQTnr5jM6ckfSDbLAOjZ1DnpqZpEX +zV+CzafKDVgCVxi2K3Np9qnC3C1+i3KEpCOBvEbHfK1Sdo6AazSZCpG0tV5GRipd +F4RhAoGAUJBoemipDjFoLSD3cpKpUXHIc6eieAI1GwYiL4CVugrvj5gO2B5c5yYb +3E8VWfuEHbBg0rmZIQ0sQf2ospZha7WBNhg9WB016aHyeZTIuHchfU4y3l2Jl8Re +enz4SSi6ZR6hgbJ9XzeiI+UTcDEuUzDUy9YktREuIBmMPXm7u5s= +-----END RSA PRIVATE KEY----- diff --git a/apps/terminal/server.py b/apps/terminal/server.py index dfcea1d21..a04e1f628 100644 --- a/apps/terminal/server.py +++ b/apps/terminal/server.py @@ -3,19 +3,6 @@ # import sys import os -import django - -BASE_DIR = os.path.dirname(__file__) -APP_DIR = os.path.abspath(os.path.dirname(BASE_DIR)) -sys.path.append(APP_DIR) - -os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' - -try: - django.setup() -except IndexError: - pass - import base64 from binascii import hexlify import sys @@ -30,14 +17,24 @@ import socket import select import errno import paramiko +import django from paramiko.py3compat import b, u, decodebytes -from .hands import ssh_key_gen +BASE_DIR = os.path.abspath(os.path.dirname(__file__)) +APP_DIR = os.path.dirname(BASE_DIR) +sys.path.append(APP_DIR) +os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' +try: + django.setup() +except IndexError: + pass -paramiko.util.log_to_file('demo_server.log') +from django.conf import settings +from common.utils import get_logger +from hands import ssh_key_gen, check_user_is_valid -host_key = paramiko.RSAKey(filename='test_rsa.key') +logger = get_logger(__name__) class SSHService(paramiko.ServerInterface): @@ -46,30 +43,31 @@ class SSHService(paramiko.ServerInterface): # b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' # b'UWT10hcuO4Ks8=') # good_pub_key = paramiko.RSAKey(data=decodebytes(data)) + # host_key = paramiko.RSAKey(filename='test_rsa.key') - ssh_key_path = os.path.join(BASE_DIR, 'keys', 'ssh_host_key') - ssh_pub_key_path = ssh_key_path + '.pub' + host_key_path = os.path.join(BASE_DIR, 'keys', 'host_rsa_key') def __init__(self): self.event = threading.Event() + self.user = None + + @classmethod + def host_key(cls): + return cls.get_host_key() @classmethod def get_host_key(cls): - if os.path.isfile(cls.ssh_pub_key_path): - with open(cls.ssh_pub_key_path) as f: - ssh_pub_key = f.read() - else: - ssh_key, ssh_pub_key = cls.host_key_gen() - return ssh_pub_key + logger.debug("Get ssh server host key") + if not os.path.isfile(cls.host_key_path): + cls.host_key_gen() + return paramiko.RSAKey(filename=cls.host_key_path) @classmethod def host_key_gen(cls): + logger.debug("Generate ssh server host key") ssh_key, ssh_pub_key = ssh_key_gen() - with open(cls.ssh_key_path, 'w') as f: - with open(cls.ssh_pub_key_path, 'w') as f2: - f.write(ssh_key) - f2.write(ssh_pub_key) - return ssh_key, ssh_pub_key + with open(cls.host_key_path, 'w') as f: + f.write(ssh_key) def check_channel_request(self, kind, chanid): if kind == 'session': @@ -77,18 +75,30 @@ class SSHService(paramiko.ServerInterface): return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): - if (username == 'robey') and (password == 'foo'): + self.user = check_user_is_valid(username=username, password=password) + if self.user: + logger.info('User: %s password auth passed' % username) return paramiko.AUTH_SUCCESSFUL + else: + logger.warning('User: %s password auth failed' % username) return paramiko.AUTH_FAILED - def check_auth_publickey(self, username, key): - print('Auth attempt with key: ' + u(hexlify(key.get_fingerprint()))) - if (username == 'robey') and (key == self.good_pub_key): + def check_auth_publickey(self, username, public_key): + self.user = check_user_is_valid(username=username, public_key=public_key) + if self.user: + logger.info('User: %s public key auth passed' % username) return paramiko.AUTH_SUCCESSFUL + else: + logger.warning('User: %s public key auth failed' % username) return paramiko.AUTH_FAILED def get_allowed_auths(self, username): - return 'password,publickey' + auth_method_list = [] + if settings.CONFIG.SSH_PASSWORD_AUTH: + auth_method_list.append('password') + if settings.CONFIG.SSH_PUBLICK_KEY_AUTH: + auth_method_list.append('publickey') + return ','.join(auth_method_list) def check_channel_shell_request(self, channel): self.event.set() @@ -100,7 +110,7 @@ class SSHService(paramiko.ServerInterface): class SSHServer: - def __init__(self, host, port): + def __init__(self, host='127.0.0.1', port=2200): self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -118,58 +128,57 @@ class SSHServer: return channel def handle_ssh_request(self, client, addr): - print('Got a connection!') + logger.info("Get connection from " + str(addr)) try: - t = paramiko.Transport(client, gss_kex=False) - t.set_gss_host(socket.getfqdn("")) + transport = paramiko.Transport(client, gss_kex=False) + transport.set_gss_host(socket.getfqdn("")) try: - t.load_server_moduli() + transport.load_server_moduli() except: - print('(Failed to load moduli -- gex will be unsupported.)') + logger.warning('(Failed to load moduli -- gex will be unsupported.)') raise - t.add_server_key(host_key) + + transport.add_server_key(SSHService.get_host_key()) service = SSHService() try: - t.start_server(server=service) + transport.start_server(server=service) except paramiko.SSHException: print('*** SSH negotiation failed.') return - chan = t.accept(20) - - if chan is None: + channel = transport.accept(20) + if channel is None: print('*** No channel.') return print('Authenticated!') - chan.settimeout(100) + channel.settimeout(100) - chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') - chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') - chan.send('Happy birthday to Robot Dave!\r\n\r\n') - server_chan = self.connect() + channel.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') + channel.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') + channel.send('Happy birthday to Robot Dave!\r\n\r\n') + server_channel = self.connect() if not service.event.is_set(): print('*** Client never asked for a shell.') return server_data = [] input_mode = True while True: - r, w, e = select.select([server_chan, chan], [], []) + r, w, e = select.select([server_channel, channel], [], []) - - if chan in r: - recv_data = chan.recv(1024).decode('utf8') + if channel in r: + recv_data = channel.recv(1024).decode('utf8') # print("From client: " + repr(recv_data)) if len(recv_data) == 0: break - server_chan.send(recv_data) + server_channel.send(recv_data) - if server_chan in r: - recv_data = server_chan.recv(1024).decode('utf8') + if server_channel in r: + recv_data = server_channel.recv(1024).decode('utf8') # print("From server: " + repr(recv_data)) if len(recv_data) == 0: break - chan.send(recv_data) + channel.send(recv_data) if len(recv_data) > 20: server_data.append('...') else: @@ -190,13 +199,14 @@ class SSHServer: print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) traceback.print_exc() try: - t.close() + transport.close() except: pass sys.exit(1) def listen(self): self.sock.listen(5) + print('Start ssh server %(host)s:%(port)s' % {'host': self.host, 'port': self.port}) while True: try: client, addr = self.sock.accept() @@ -209,7 +219,7 @@ class SSHServer: if __name__ == '__main__': - server = SSHServer('', 2200) + server = SSHServer(host='', port=2200) try: server.listen() except KeyboardInterrupt: diff --git a/apps/users/models.py b/apps/users/models.py index 200d3e038..f1a2e548b 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -109,6 +109,12 @@ class User(AbstractUser): else: return True + @property + def is_valid(self): + if self.is_active and not self.is_expired: + return True + return False + @property def private_key(self): return decrypt(self._private_key) diff --git a/apps/users/utils.py b/apps/users/utils.py index a94de773a..de21a1539 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -13,7 +13,8 @@ from django.utils.translation import ugettext as _ from paramiko.rsakey import RSAKey from common.tasks import send_mail_async -from common.utils import reverse +from common.utils import reverse, get_object_or_none +from .models import User try: @@ -203,3 +204,20 @@ def validate_ssh_pk(text): return optionState(text[1:]) return startState([n.strip() for n in text.splitlines()]) + + +def check_user_is_valid(**kwargs): + password = kwargs.pop('password', None) + public_key = kwargs.pop('public_key', None) + user = get_object_or_none(User, **kwargs) + + if password and not user.check_password(password): + user = None + + if public_key and not user.public_key == public_key: + user = None + + if user and user.is_valid: + return user + + return None diff --git a/config-example.py b/config-example.py index a1087a1aa..9bae73181 100644 --- a/config-example.py +++ b/config-example.py @@ -70,6 +70,10 @@ class Config: # EMAIL_USE_TLS = False # If port is 587, set True # EMAIL_SUBJECT_PREFIX = '[Jumpserver] ' + # SSH use password or public key for auth + SSH_PASSWORD_AUTH = False + SSH_PUBLIC_KEY_AUTH = True + def __init__(self): pass From d3e9c8c9c096d4c741a09e7d7058d2f7650ff0e4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 00:21:32 +0800 Subject: [PATCH 09/26] Replace ssh server dir --- .gitignore | 1 + apps/terminal/hands.py | 1 - apps/terminal/keys/README.md | 0 apps/terminal/keys/host_rsa_key | 27 ------------------------- ssh_server/__init__.py | 7 +++++++ {apps/terminal => ssh_server}/server.py | 10 +++++---- 6 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 apps/terminal/keys/README.md delete mode 100644 apps/terminal/keys/host_rsa_key create mode 100644 ssh_server/__init__.py rename {apps/terminal => ssh_server}/server.py (95%) diff --git a/.gitignore b/.gitignore index f8166b97a..38b5a999c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ db.sqlite3 config.py migrations/ *.log +host_rsa_key diff --git a/apps/terminal/hands.py b/apps/terminal/hands.py index 065ff1a77..0133d394f 100644 --- a/apps/terminal/hands.py +++ b/apps/terminal/hands.py @@ -2,5 +2,4 @@ # -*- coding: utf-8 -*- # -from users.utils import ssh_key_gen, check_user_is_valid diff --git a/apps/terminal/keys/README.md b/apps/terminal/keys/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/terminal/keys/host_rsa_key b/apps/terminal/keys/host_rsa_key deleted file mode 100644 index 5b1aa9804..000000000 --- a/apps/terminal/keys/host_rsa_key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAxreMFq9tp1hb2NOIkV7PqeyfS0GTJSfW2WcXuqkGPSVtPdYw -cJRY/s5eBn0KbO6JVj9yfwXNTKdnq1ODuuJDMnhTWXq1x7VICcou/69kFdSGiAzl -wfYP6LATEMCmpFRVi5UZip7SWopLE1JEw79nYWrbhUpDOpGTRKoIxoz+Uvg0h15G -pec3faL7PXj/J6j9pis44N1PCMBY3DVFPLopVcFpElbzwScNvZGBes90JoKLsqfD -Vjc9PUPjYjck9NFR6Xy0D5Gnw9MD5o0yK1l+3sibXjFBMOxA7aNxIlMhXQGol8R7 -3HBHWo/+Bwct3w1c8cPKfdPd8jGn3eWGYxupvQIDAQABAoIBADuyWDtYaDClsrHo -mlZRjUEW/KO3B2VaGoklF1PUAzPLUo4JEnQ/nJyvkj+QwNkIr+lhFhxiudIVWGd3 -p1M1Ncqrqx5uZr2gEAwg2Q2muwJz3hZxCXTDXvQgMRoPRgCH9UsBd7LVE4xvjy42 -wMGtdnkliNz5+khWA0/VZN2A7cYukrKzPwnhEMSrzYfnRwcOvp8pDp++Yjs3ZhQL -8+sgL1UDap5p5QZSQ98qJGNwmePAlTig+2Z5HvF+zussK2N7g5AcfghQFo5vCw/L -PXYtIfBH+Tv+6s7vMBMSLpbDcAZsxR9gDVUQi252Gu/nWClCzH3Kgu5ormHSOkYO -F6/n5AECgYEA69anuf52KWwYypVA73HiUbuzOdeuc1Br+s0uzOvpFX0HaqDxo8dm -N7FtUj/WnoqFivQrsrt4LpIzKn1XPNk7wMnwIZAQHNEI8sy7LBVh3RJOP2ZC2329 -ZHWxB3EVQ8Q5MbZy/AOn92UYwz8xIb0LweGYnHZlMp+xtOhdUR0/Z90CgYEA17R8 -EOeErksBRHotrEk0jLx+rrhK0JGcpXo/Dw6AOEp936DgHlqbkUURT2ejDOSQY/dN -7i4WeFJCVfFRNMbsitWxNmAdl3NJ5C2bV+7sz+oZfo5zP/e1RYCNLVjLxLYOHQ37 -GWwAlQr6fPcIZMCaPH+xq/0WSqcP96Lu6G0VG2ECgYB1XtcKkcFszAdqiu1OPXdN -BgUkfFqtuRCEOSlZgu71aswOHRslT09n2D13+Z1uObJMfUhiIzqkss4UD10jQ1mh -kN6ZVYEvVjkF3S4pulqCE2It207avbFMFeaMtZLHrxhnzU1cbtVhIkc4pHJnQBZh -30x8Uc/7ac6fIiWPAOdVYQKBgCi8rEWhA7zK64VcMa388VC29JHYukBjj5rs2GXm -ji6TWuxV/J2e7QxlZ9yALRntPJu0g+I8j//PQTnr5jM6ckfSDbLAOjZ1DnpqZpEX -zV+CzafKDVgCVxi2K3Np9qnC3C1+i3KEpCOBvEbHfK1Sdo6AazSZCpG0tV5GRipd -F4RhAoGAUJBoemipDjFoLSD3cpKpUXHIc6eieAI1GwYiL4CVugrvj5gO2B5c5yYb -3E8VWfuEHbBg0rmZIQ0sQf2ospZha7WBNhg9WB016aHyeZTIuHchfU4y3l2Jl8Re -enz4SSi6ZR6hgbJ9XzeiI+UTcDEuUzDUy9YktREuIBmMPXm7u5s= ------END RSA PRIVATE KEY----- diff --git a/ssh_server/__init__.py b/ssh_server/__init__.py new file mode 100644 index 000000000..f93d0bec7 --- /dev/null +++ b/ssh_server/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + + +if __name__ == '__main__': + pass diff --git a/apps/terminal/server.py b/ssh_server/server.py similarity index 95% rename from apps/terminal/server.py rename to ssh_server/server.py index a04e1f628..e93e8761b 100644 --- a/apps/terminal/server.py +++ b/ssh_server/server.py @@ -21,7 +21,7 @@ import django from paramiko.py3compat import b, u, decodebytes BASE_DIR = os.path.abspath(os.path.dirname(__file__)) -APP_DIR = os.path.dirname(BASE_DIR) +APP_DIR = os.path.join(os.path.dirname(BASE_DIR), 'apps') sys.path.append(APP_DIR) os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' @@ -32,7 +32,7 @@ except IndexError: from django.conf import settings from common.utils import get_logger -from hands import ssh_key_gen, check_user_is_valid +from users.utils import ssh_key_gen, check_user_is_valid logger = get_logger(__name__) @@ -45,7 +45,7 @@ class SSHService(paramiko.ServerInterface): # good_pub_key = paramiko.RSAKey(data=decodebytes(data)) # host_key = paramiko.RSAKey(filename='test_rsa.key') - host_key_path = os.path.join(BASE_DIR, 'keys', 'host_rsa_key') + host_key_path = os.path.join(BASE_DIR, 'host_rsa_key') def __init__(self): self.event = threading.Event() @@ -211,7 +211,9 @@ class SSHServer: try: client, addr = self.sock.accept() print('Listening for connection ...') - threading.Thread(target=self.handle_ssh_request, args=(client, addr)).start() + t = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) + t.daemon = True + t.start() except Exception as e: print('*** Bind failed: ' + str(e)) traceback.print_exc() From 216163f4367d77a2c95820cb81fda2ea186b58b5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 11:30:02 +0800 Subject: [PATCH 10/26] stash --- config-example.py => config_example.py | 0 {ssh_server => terminal}/__init__.py | 0 terminal/config_example.py | 93 +++++++++++++++++++ terminal/logs/.gitkeep | 0 .../server.py => terminal/ssh_server.py | 17 +--- terminal/utils.py | 8 ++ terminal/web_server.py | 7 ++ 7 files changed, 113 insertions(+), 12 deletions(-) rename config-example.py => config_example.py (100%) rename {ssh_server => terminal}/__init__.py (100%) create mode 100644 terminal/config_example.py create mode 100644 terminal/logs/.gitkeep rename ssh_server/server.py => terminal/ssh_server.py (91%) create mode 100644 terminal/utils.py create mode 100644 terminal/web_server.py diff --git a/config-example.py b/config_example.py similarity index 100% rename from config-example.py rename to config_example.py diff --git a/ssh_server/__init__.py b/terminal/__init__.py similarity index 100% rename from ssh_server/__init__.py rename to terminal/__init__.py diff --git a/terminal/config_example.py b/terminal/config_example.py new file mode 100644 index 000000000..9cde69b99 --- /dev/null +++ b/terminal/config_example.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +import logging +import os + + +BASE_DIR = os.path.dirname(os.path.abspath(__name__)) +LOG_LEVEL_CHOICES = { + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, + 'critical': logging.CRITICAL +} + + +class Config: + LOG_LEVEL = '' + LOG_DIR = os.path.join(BASE_DIR, 'logs') + LOGGING = { + 'version': 1, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'main': { + 'datefmt': '%Y-%m-%d %H:%M:%S', + 'format': '%(asctime)s [%(module)s %(levelname)s] %(message)s', + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'logging.NullHandler', + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'main' + }, + 'file': { + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'formatter': 'main', + 'filename': os.path.join(PROJECT_DIR, 'logs', 'jumpserver.log') + }, + }, + 'loggers': { + 'django': { + 'handlers': ['null'], + 'propagate': False, + 'level': LOG_LEVEL, + }, + 'django.request': { + 'handlers': ['console', 'file'], + 'level': LOG_LEVEL, + 'propagate': False, + }, + 'django.server': { + 'handlers': ['console', 'file'], + 'level': LOG_LEVEL, + 'propagate': False, + }, + 'jumpserver': { + 'handlers': ['console', 'file'], + 'level': LOG_LEVEL, + }, + 'jumpserver.users.api': { + 'handlers': ['console', 'file'], + 'level': LOG_LEVEL, + }, + 'jumpserver.users.view': { + 'handlers': ['console', 'file'], + 'level': LOG_LEVEL, + } + } + } + + def __init__(self): + pass + + def __getattr__(self, item): + return None + + + +if __name__ == '__main__': + pass diff --git a/terminal/logs/.gitkeep b/terminal/logs/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/ssh_server/server.py b/terminal/ssh_server.py similarity index 91% rename from ssh_server/server.py rename to terminal/ssh_server.py index e93e8761b..7d6a6dee3 100644 --- a/ssh_server/server.py +++ b/terminal/ssh_server.py @@ -37,14 +37,7 @@ from users.utils import ssh_key_gen, check_user_is_valid logger = get_logger(__name__) -class SSHService(paramiko.ServerInterface): - # data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' - # b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC' - # b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' - # b'UWT10hcuO4Ks8=') - # good_pub_key = paramiko.RSAKey(data=decodebytes(data)) - # host_key = paramiko.RSAKey(filename='test_rsa.key') - +class SSHServerInterface(paramiko.ServerInterface): host_key_path = os.path.join(BASE_DIR, 'host_rsa_key') def __init__(self): @@ -138,10 +131,10 @@ class SSHServer: logger.warning('(Failed to load moduli -- gex will be unsupported.)') raise - transport.add_server_key(SSHService.get_host_key()) - service = SSHService() + transport.add_server_key(SSHServerInterface.get_host_key()) + ssh_interface = SSHServerInterface() try: - transport.start_server(server=service) + transport.start_server(server=ssh_interface) except paramiko.SSHException: print('*** SSH negotiation failed.') return @@ -158,7 +151,7 @@ class SSHServer: channel.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') channel.send('Happy birthday to Robot Dave!\r\n\r\n') server_channel = self.connect() - if not service.event.is_set(): + if not ssh_interface.event.is_set(): print('*** Client never asked for a shell.') return server_data = [] diff --git a/terminal/utils.py b/terminal/utils.py new file mode 100644 index 000000000..f5c5d234f --- /dev/null +++ b/terminal/utils.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +import logging + + + diff --git a/terminal/web_server.py b/terminal/web_server.py new file mode 100644 index 000000000..f93d0bec7 --- /dev/null +++ b/terminal/web_server.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + + +if __name__ == '__main__': + pass From ebb30424fa88fc3b9a7c4be5790322ed279e7471 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 19:53:55 +0800 Subject: [PATCH 11/26] Use process except thread --- terminal/ssh_config.py | 105 ++++++++++++++ ...onfig_example.py => ssh_config_example.py} | 55 +++---- terminal/ssh_server.py | 134 +++++++++++------- terminal/utils.py | 9 ++ terminal/{web_server.py => web_ssh_server.py} | 0 5 files changed, 224 insertions(+), 79 deletions(-) create mode 100644 terminal/ssh_config.py rename terminal/{config_example.py => ssh_config_example.py} (66%) rename terminal/{web_server.py => web_ssh_server.py} (100%) diff --git a/terminal/ssh_config.py b/terminal/ssh_config.py new file mode 100644 index 000000000..52a0424c1 --- /dev/null +++ b/terminal/ssh_config.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +import logging +import os + + +BASE_DIR = os.path.dirname(os.path.abspath(__name__)) + + +class Config: + SSH_HOST = '' + SSH_PORT = 2200 + LOG_LEVEL = 'INFO' + LOG_DIR = os.path.join(BASE_DIR, 'logs') + LOG_FILENAME = 'ssh_server.log' + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'main': { + 'datefmt': '%Y-%m-%d %H:%M:%S', + 'format': '%(asctime)s [%(module)s %(levelname)s] %(message)s', + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'logging.NullHandler', + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'main', + 'stream': 'ext://sys.stdout', + }, + 'file': { + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'formatter': 'main', + 'mode': 'a', + 'filename': os.path.join(LOG_DIR, LOG_FILENAME), + }, + }, + 'loggers': { + 'jumpserver': { + 'handlers': ['console', 'file'], + # 'level': LOG_LEVEL_CHOICES.get(LOG_LEVEL, None) or LOG_LEVEL_CHOICES.get('info') + 'level': LOG_LEVEL, + 'propagate': True, + }, + 'jumpserver.web_ssh_server': { + 'handlers': ['console', 'file'], + # 'level': LOG_LEVEL_CHOICES.get(LOG_LEVEL, None) or LOG_LEVEL_CHOICES.get('info') + 'level': LOG_LEVEL, + 'propagate': True, + }, + 'jumpserver.ssh_server': { + 'handlers': ['console', 'file'], + # 'level': LOG_LEVEL_CHOICES.get(LOG_LEVEL, None) or LOG_LEVEL_CHOICES.get('info') + 'level': LOG_LEVEL, + 'propagate': True, + } + } + } + + def __init__(self): + pass + + def __getattr__(self, item): + return None + + +class DevelopmentConfig(Config): + pass + + +class ProductionConfig(Config): + pass + + +class TestingConfig(Config): + pass + + +config = { + 'development': DevelopmentConfig, + 'production': ProductionConfig, + 'testing': TestingConfig, + 'default': DevelopmentConfig, +} + +env = 'default' + + +if __name__ == '__main__': + pass + diff --git a/terminal/config_example.py b/terminal/ssh_config_example.py similarity index 66% rename from terminal/config_example.py rename to terminal/ssh_config_example.py index 9cde69b99..238304fcd 100644 --- a/terminal/config_example.py +++ b/terminal/ssh_config_example.py @@ -7,20 +7,14 @@ import os BASE_DIR = os.path.dirname(os.path.abspath(__name__)) -LOG_LEVEL_CHOICES = { - 'debug': logging.DEBUG, - 'info': logging.INFO, - 'warning': logging.WARNING, - 'error': logging.ERROR, - 'critical': logging.CRITICAL -} class Config: - LOG_LEVEL = '' + LOG_LEVEL = 'INFO' LOG_DIR = os.path.join(BASE_DIR, 'logs') LOGGING = { 'version': 1, + 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' @@ -47,35 +41,23 @@ class Config: 'level': 'DEBUG', 'class': 'logging.FileHandler', 'formatter': 'main', - 'filename': os.path.join(PROJECT_DIR, 'logs', 'jumpserver.log') + 'filename': LOG_DIR, }, }, 'loggers': { - 'django': { - 'handlers': ['null'], - 'propagate': False, - 'level': LOG_LEVEL, - }, - 'django.request': { - 'handlers': ['console', 'file'], - 'level': LOG_LEVEL, - 'propagate': False, - }, - 'django.server': { - 'handlers': ['console', 'file'], - 'level': LOG_LEVEL, - 'propagate': False, - }, 'jumpserver': { 'handlers': ['console', 'file'], + # 'level': LOG_LEVEL_CHOICES.get(LOG_LEVEL, None) or LOG_LEVEL_CHOICES.get('info') 'level': LOG_LEVEL, }, - 'jumpserver.users.api': { + 'jumpserver.web_ssh_server': { 'handlers': ['console', 'file'], + # 'level': LOG_LEVEL_CHOICES.get(LOG_LEVEL, None) or LOG_LEVEL_CHOICES.get('info') 'level': LOG_LEVEL, }, - 'jumpserver.users.view': { + 'jumpserver.ssh_server': { 'handlers': ['console', 'file'], + # 'level': LOG_LEVEL_CHOICES.get(LOG_LEVEL, None) or LOG_LEVEL_CHOICES.get('info') 'level': LOG_LEVEL, } } @@ -88,6 +70,27 @@ class Config: return None +class DevelopmentConfig(Config): + pass + + +class ProductionConfig(Config): + pass + + +class TestingConfig(Config): + pass + + +config = { + 'development': DevelopmentConfig, + 'production': ProductionConfig, + 'testing': TestingConfig, + 'default': DevelopmentConfig, +} + +env = 'default' + if __name__ == '__main__': pass diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 7d6a6dee3..94a0b70e8 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -7,6 +7,7 @@ import base64 from binascii import hexlify import sys import threading +from multiprocessing import process import traceback import tty import termios @@ -31,17 +32,21 @@ except IndexError: pass from django.conf import settings -from common.utils import get_logger from users.utils import ssh_key_gen, check_user_is_valid +from utils import get_logger + logger = get_logger(__name__) class SSHServerInterface(paramiko.ServerInterface): host_key_path = os.path.join(BASE_DIR, 'host_rsa_key') + channel_pools = [] - def __init__(self): + def __init__(self, client, addr): self.event = threading.Event() + self.client = client + self.addr = addr self.user = None @classmethod @@ -70,19 +75,35 @@ class SSHServerInterface(paramiko.ServerInterface): def check_auth_password(self, username, password): self.user = check_user_is_valid(username=username, password=password) if self.user: - logger.info('User: %s password auth passed' % username) + logger.info('Accepted password for %(user)s from %(host)s port %(port)s ' % { + 'user': username, + 'host': self.addr[0], + 'port': self.addr[1], + }) return paramiko.AUTH_SUCCESSFUL else: - logger.warning('User: %s password auth failed' % username) + logger.info('Authentication password failed for %(user)s from %(host)s port %(port)s ' % { + 'user': username, + 'host': self.addr[0], + 'port': self.addr[1], + }) return paramiko.AUTH_FAILED def check_auth_publickey(self, username, public_key): self.user = check_user_is_valid(username=username, public_key=public_key) if self.user: - logger.info('User: %s public key auth passed' % username) + logger.info('Accepted public key for %(user)s from %(host)s port %(port)s ' % { + 'user': username, + 'host': self.addr[0], + 'port': self.addr[1], + }) return paramiko.AUTH_SUCCESSFUL else: - logger.warning('User: %s public key auth failed' % username) + logger.info('Authentication public key failed for %(user)s from %(host)s port %(port)s ' % { + 'user': username, + 'host': self.addr[0], + 'port': self.addr[1], + }) return paramiko.AUTH_FAILED def get_allowed_auths(self, username): @@ -95,12 +116,20 @@ class SSHServerInterface(paramiko.ServerInterface): def check_channel_shell_request(self, channel): self.event.set() + self.__class__.channel_pools.append(channel) return True def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, modes): return True + def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight): + logger.info('Change window size %s * %s' % (width, height)) + logger.info('Change length %s ' % len(self.__class__.channel_pools)) + # for channel in self.__class__.channel_pools: + # channel.send("Hello world") + return True + class SSHServer: def __init__(self, host='127.0.0.1', port=2200): @@ -110,18 +139,22 @@ class SSHServer: self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) self.server_ssh = None - self.server_chan = None + self.server_channel = None + self.client_channel = None def connect(self): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname='127.0.0.1', port=22, username='root', password='redhat') self.server_ssh = ssh - self.server_chan = channel = ssh.invoke_shell(term='xterm') + self.server_channel = channel = ssh.invoke_shell(term='xterm') return channel def handle_ssh_request(self, client, addr): - logger.info("Get connection from " + str(addr)) + logger.info("Get connection from %(host)s:%(port)s" % { + 'host': addr[0], + 'port': addr[1], + }) try: transport = paramiko.Transport(client, gss_kex=False) transport.set_gss_host(socket.getfqdn("")) @@ -132,70 +165,63 @@ class SSHServer: raise transport.add_server_key(SSHServerInterface.get_host_key()) - ssh_interface = SSHServerInterface() + ssh_interface = SSHServerInterface(client, addr) try: transport.start_server(server=ssh_interface) except paramiko.SSHException: print('*** SSH negotiation failed.') return - channel = transport.accept(20) - if channel is None: + self.client_channel = client_channel = transport.accept(20) + if client_channel is None: print('*** No channel.') return print('Authenticated!') - channel.settimeout(100) + client_channel.settimeout(100) - channel.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') - channel.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') - channel.send('Happy birthday to Robot Dave!\r\n\r\n') + client_channel.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') + client_channel.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') + client_channel.send('Happy birthday to Robot Dave!\r\n\r\n') server_channel = self.connect() if not ssh_interface.event.is_set(): print('*** Client never asked for a shell.') return - server_data = [] - input_mode = True - while True: - r, w, e = select.select([server_channel, channel], [], []) - if channel in r: - recv_data = channel.recv(1024).decode('utf8') - # print("From client: " + repr(recv_data)) - if len(recv_data) == 0: + while True: + r, w, x = select.select([client_channel, server_channel], [], []) + + if client_channel in r: + data_client = client_channel.recv(1024) + logger.info(data_client) + if len(data_client) == 0: break - server_channel.send(recv_data) + # client_channel.send(data_client) + server_channel.send(data_client) if server_channel in r: - recv_data = server_channel.recv(1024).decode('utf8') - # print("From server: " + repr(recv_data)) - if len(recv_data) == 0: + data_server = server_channel.recv(1024) + if len(data_server) == 0: break - channel.send(recv_data) - if len(recv_data) > 20: - server_data.append('...') - else: - server_data.append(recv_data) - try: - if repr(server_data[-2]) == u'\r\n': - result = server_data.pop() - server_data.pop() - command = ''.join(server_data) - server_data = [] - print(">>> Command: %s" % command) - print(result) - except IndexError: - pass - print(server_data) + client_channel.send(data_server) - except Exception as e: - print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) - traceback.print_exc() - try: - transport.close() - except: - pass - sys.exit(1) + # if len(recv_data) > 20: + # server_data.append('...') + # else: + # server_data.append(recv_data) + # try: + # if repr(server_data[-2]) == u'\r\n': + # result = server_data.pop() + # server_data.pop() + # command = ''.join(server_data) + # server_data = [] + # except IndexError: + # pass + + except Exception: + client_channel.close() + server_channel.close() + logger.info('Close with server %s from %s' % ('127.0.0.1', '127.0.0.1')) def listen(self): self.sock.listen(5) @@ -204,7 +230,9 @@ class SSHServer: try: client, addr = self.sock.accept() print('Listening for connection ...') - t = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) + # t = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) + t = process.Process(target=self.handle_ssh_request, args=(client, addr)) + t.daemon = True t.start() except Exception as e: diff --git a/terminal/utils.py b/terminal/utils.py index f5c5d234f..1aba6ba02 100644 --- a/terminal/utils.py +++ b/terminal/utils.py @@ -3,6 +3,15 @@ # import logging +from logging.config import dictConfig +from ssh_config import config, env +CONFIG_SSH_SERVER = config.get(env) + + +def get_logger(name): + dictConfig(CONFIG_SSH_SERVER.LOGGING) + return logging.getLogger('jumpserver.%s' % name) + diff --git a/terminal/web_server.py b/terminal/web_ssh_server.py similarity index 100% rename from terminal/web_server.py rename to terminal/web_ssh_server.py From 5e33c2dc6b2b1cb0cfe278d50046a7e7b22a5605 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 20:41:56 +0800 Subject: [PATCH 12/26] Update ssh config --- terminal/ssh_config.py | 4 ---- terminal/ssh_server.py | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/terminal/ssh_config.py b/terminal/ssh_config.py index 52a0424c1..a23ebbce7 100644 --- a/terminal/ssh_config.py +++ b/terminal/ssh_config.py @@ -99,7 +99,3 @@ config = { env = 'default' - -if __name__ == '__main__': - pass - diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 94a0b70e8..c0f520f1f 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -126,8 +126,8 @@ class SSHServerInterface(paramiko.ServerInterface): def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight): logger.info('Change window size %s * %s' % (width, height)) logger.info('Change length %s ' % len(self.__class__.channel_pools)) - # for channel in self.__class__.channel_pools: - # channel.send("Hello world") + for channel in self.__class__.channel_pools: + channel.send("Hello world") return True From 2c64b784870da5a27c05d45d53d0c789a58e71cb Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 21:21:25 +0800 Subject: [PATCH 13/26] Update terminal --- terminal/ssh_server.py | 57 ++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index c0f520f1f..c70152173 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -124,13 +124,42 @@ class SSHServerInterface(paramiko.ServerInterface): return True def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight): - logger.info('Change window size %s * %s' % (width, height)) - logger.info('Change length %s ' % len(self.__class__.channel_pools)) - for channel in self.__class__.channel_pools: - channel.send("Hello world") return True +class BackendServer: + def __init__(self, host, port, username): + self.host = host + self.port = port + self.username = username + self.ssh = None + self.channel = None + + def connect(self, term='xterm', width=80, height=24): + self.ssh = ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(hostname=self.host, port=self.port, username=self.username, password=self.host_password, + pkey=self.host_private_key, look_for_keys=False, allow_agent=True, compress=True) + self.channel = channel = ssh.invoke_shell(term=term, width=width, height=height) + return channel + + @property + def host_password(self): + return 'redhat' + + @property + def host_private_key(self): + return 'redhat' + + +class Navigation: + def __init__(self, username): + self.username = username + + def display(self): + pass + + class SSHServer: def __init__(self, host='127.0.0.1', port=2200): self.host = host @@ -142,13 +171,14 @@ class SSHServer: self.server_channel = None self.client_channel = None - def connect(self): - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(hostname='127.0.0.1', port=22, username='root', password='redhat') - self.server_ssh = ssh - self.server_channel = channel = ssh.invoke_shell(term='xterm') - return channel + def invoke_with_backend(self): + pass + + def display_navigation(self): + pass + + def make_client_channel(self): + pass def handle_ssh_request(self, client, addr): logger.info("Get connection from %(host)s:%(port)s" % { @@ -173,6 +203,8 @@ class SSHServer: return self.client_channel = client_channel = transport.accept(20) + # self.client_channel = client_channel = transport.open_session() + # client_channel.get_pty(term='xterm') if client_channel is None: print('*** No channel.') return @@ -219,9 +251,8 @@ class SSHServer: # pass except Exception: - client_channel.close() - server_channel.close() logger.info('Close with server %s from %s' % ('127.0.0.1', '127.0.0.1')) + sys.exit(100) def listen(self): self.sock.listen(5) From 4b7419559c7b387bddf79ee540ec32dc0dd9feb5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 23:11:09 +0800 Subject: [PATCH 14/26] Update ssh_server to some class --- terminal/ssh_server.py | 196 ++++++++++++++++++++++++----------------- terminal/utils.py | 2 + 2 files changed, 119 insertions(+), 79 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index c70152173..0b8261963 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -1,9 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# +# + +__version__ = '0.3.3' + import sys import os import base64 +import time from binascii import hexlify import sys import threading @@ -19,7 +23,6 @@ import select import errno import paramiko import django -from paramiko.py3compat import b, u, decodebytes BASE_DIR = os.path.abspath(os.path.dirname(__file__)) APP_DIR = os.path.join(os.path.dirname(BASE_DIR), 'apps') @@ -33,13 +36,13 @@ except IndexError: from django.conf import settings from users.utils import ssh_key_gen, check_user_is_valid -from utils import get_logger +from utils import get_logger, SSHServerException logger = get_logger(__name__) -class SSHServerInterface(paramiko.ServerInterface): +class SSHServer(paramiko.ServerInterface): host_key_path = os.path.join(BASE_DIR, 'host_rsa_key') channel_pools = [] @@ -47,7 +50,10 @@ class SSHServerInterface(paramiko.ServerInterface): self.event = threading.Event() self.client = client self.addr = addr + self.username = None self.user = None + self.channel_width = None + self.channel_height = None @classmethod def host_key(cls): @@ -73,34 +79,37 @@ class SSHServerInterface(paramiko.ServerInterface): return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): - self.user = check_user_is_valid(username=username, password=password) + self.user = user = check_user_is_valid(username=username, password=password) + self.username = username = user.username if self.user: - logger.info('Accepted password for %(user)s from %(host)s port %(port)s ' % { - 'user': username, + logger.info('Accepted password for %(username)s from %(host)s port %(port)s ' % { + 'username': username, 'host': self.addr[0], 'port': self.addr[1], }) return paramiko.AUTH_SUCCESSFUL else: - logger.info('Authentication password failed for %(user)s from %(host)s port %(port)s ' % { - 'user': username, + logger.info('Authentication password failed for %(username)s from %(host)s port %(port)s ' % { + 'username': username, 'host': self.addr[0], 'port': self.addr[1], }) return paramiko.AUTH_FAILED def check_auth_publickey(self, username, public_key): - self.user = check_user_is_valid(username=username, public_key=public_key) + self.user = user = check_user_is_valid(username=username, public_key=public_key) + self.username = username = user.username + if self.user: - logger.info('Accepted public key for %(user)s from %(host)s port %(port)s ' % { - 'user': username, + logger.info('Accepted public key for %(username)s from %(host)s port %(port)s ' % { + 'username': username, 'host': self.addr[0], 'port': self.addr[1], }) return paramiko.AUTH_SUCCESSFUL else: - logger.info('Authentication public key failed for %(user)s from %(host)s port %(port)s ' % { - 'user': username, + logger.info('Authentication public key failed for %(username)s from %(host)s port %(port)s ' % { + 'username': username, 'host': self.addr[0], 'port': self.addr[1], }) @@ -135,12 +144,18 @@ class BackendServer: self.ssh = None self.channel = None - def connect(self, term='xterm', width=80, height=24): + def connect(self, term='xterm', width=80, height=24, timeout=10): self.ssh = ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname=self.host, port=self.port, username=self.username, password=self.host_password, - pkey=self.host_private_key, look_for_keys=False, allow_agent=True, compress=True) + pkey=self.host_private_key, look_for_keys=False, allow_agent=True, compress=True, timeout=timeout) self.channel = channel = ssh.invoke_shell(term=term, width=width, height=height) + logger.info('Connect %(username)s@%(host)s:%(port)s successfully' % { + 'username': self.username, + 'host': self.host, + 'port': self.port, + }) + channel.settimeout(100) return channel @property @@ -149,90 +164,108 @@ class BackendServer: @property def host_private_key(self): - return 'redhat' + return None class Navigation: - def __init__(self, username): + def __init__(self, username, client_channel): self.username = username + self.client_channel = client_channel + + def display_banner(self): + client_channel = self.client_channel + client_channel.send('\r\n\r\n\t\tWelcome to use Jumpserver open source system !\r\n\r\n') + client_channel.send('If use find some bug please contact us \r\n') + # client_channel.send(self.username) def display(self): + self.display_banner() + + def return_to_connect(self): pass -class SSHServer: - def __init__(self, host='127.0.0.1', port=2200): - self.host = host - self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.sock.bind((self.host, self.port)) - self.server_ssh = None - self.server_channel = None +class JumpServer: + def __init__(self): + self.listen_host = '0.0.0.0' + self.listen_port = 2222 + self.username = None + self.backend_host = None + self.backend_port = None + self.backend_username = None + self.backend_channel = None self.client_channel = None + self.sock = None - def invoke_with_backend(self): - pass + def display_navigation(self, username, client_channel): + nav = Navigation(username, client_channel) + nav.display() + return '127.0.0.1', 22, 'root' - def display_navigation(self): - pass + def get_client_channel(self, client, addr): + transport = paramiko.Transport(client, gss_kex=False) + transport.set_gss_host(socket.getfqdn("")) + try: + transport.load_server_moduli() + except: + logger.warning('Failed to load moduli -- gex will be unsupported.') + raise - def make_client_channel(self): - pass + transport.add_server_key(SSHServer.get_host_key()) + ssh_server = SSHServer(client, addr) + self.username = ssh_server.username + + try: + transport.start_server(server=ssh_server) + except paramiko.SSHException: + logger.warning('SSH negotiation failed.') + + self.client_channel = client_channel = transport.accept(20) + if client_channel is None: + logger.warning('No channel get.') + raise SSHServerException('No channel get.') + + if not ssh_server.event.is_set(): + logger.warning('Client never asked for a shell.') + raise SSHServerException('Client never asked for a shell.') + return client_channel + + def get_backend_channel(self, host, port, username): + backend_server = BackendServer(host, port, username) + self.backend_channel = backend_channel = backend_server.connect() + if not backend_channel: + logger.warning('Connect %(username)s@%(host)s:%(port)s failed' % { + 'username': username, + 'host': host, + 'port': port, + }) + + return backend_channel def handle_ssh_request(self, client, addr): - logger.info("Get connection from %(host)s:%(port)s" % { + logger.info("Get ssh request from %(host)s:%(port)s" % { 'host': addr[0], 'port': addr[1], }) try: - transport = paramiko.Transport(client, gss_kex=False) - transport.set_gss_host(socket.getfqdn("")) - try: - transport.load_server_moduli() - except: - logger.warning('(Failed to load moduli -- gex will be unsupported.)') - raise + client_channel = self.get_client_channel(client, addr) + host, port, username = self.display_navigation(self.username, client_channel) + backend_channel = self.get_backend_channel(host, port, username) - transport.add_server_key(SSHServerInterface.get_host_key()) - ssh_interface = SSHServerInterface(client, addr) - try: - transport.start_server(server=ssh_interface) - except paramiko.SSHException: - print('*** SSH negotiation failed.') - return - - self.client_channel = client_channel = transport.accept(20) - # self.client_channel = client_channel = transport.open_session() - # client_channel.get_pty(term='xterm') - if client_channel is None: - print('*** No channel.') - return - print('Authenticated!') - - client_channel.settimeout(100) - - client_channel.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') - client_channel.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') - client_channel.send('Happy birthday to Robot Dave!\r\n\r\n') - server_channel = self.connect() - if not ssh_interface.event.is_set(): - print('*** Client never asked for a shell.') - return + print(client_channel.get_id(), backend_channel.get_id()) while True: - r, w, x = select.select([client_channel, server_channel], [], []) + r, w, x = select.select([client_channel, backend_channel], [], []) if client_channel in r: data_client = client_channel.recv(1024) logger.info(data_client) if len(data_client) == 0: break - # client_channel.send(data_client) - server_channel.send(data_client) + backend_channel.send(data_client) - if server_channel in r: - data_server = server_channel.recv(1024) + if backend_channel in r: + data_server = backend_channel.recv(1024) if len(data_server) == 0: break client_channel.send(data_server) @@ -250,30 +283,35 @@ class SSHServer: # except IndexError: # pass - except Exception: + except IndexError: logger.info('Close with server %s from %s' % ('127.0.0.1', '127.0.0.1')) sys.exit(100) def listen(self): - self.sock.listen(5) - print('Start ssh server %(host)s:%(port)s' % {'host': self.host, 'port': self.port}) + self.sock = sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((self.listen_host, self.listen_port)) + sock.listen(5) + + print(time.ctime()) + print('Jumpserver version %s, more see https://www.jumpserver.org' % __version__) + print('Starting ssh server at %(host)s:%(port)s' % {'host': self.listen_host, 'port': self.listen_port}) + print('Quit the server with CONTROL-C.') + while True: try: client, addr = self.sock.accept() - print('Listening for connection ...') - # t = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) t = process.Process(target=self.handle_ssh_request, args=(client, addr)) - t.daemon = True t.start() except Exception as e: - print('*** Bind failed: ' + str(e)) + logger.error('Bind failed: ' + str(e)) traceback.print_exc() sys.exit(1) if __name__ == '__main__': - server = SSHServer(host='', port=2200) + server = JumpServer() try: server.listen() except KeyboardInterrupt: diff --git a/terminal/utils.py b/terminal/utils.py index 1aba6ba02..da648e05f 100644 --- a/terminal/utils.py +++ b/terminal/utils.py @@ -15,3 +15,5 @@ def get_logger(name): return logging.getLogger('jumpserver.%s' % name) +class SSHServerException(Exception): + pass From e627b14e55ede827c20efdb6964b16fce001c2b3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 25 Sep 2016 23:38:42 +0800 Subject: [PATCH 15/26] finish ssh server use more class --- terminal/ssh_server.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 0b8261963..0105d4cf4 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -186,15 +186,13 @@ class Navigation: class JumpServer: + backend_server_pools = [] + backend_channel_pools = [] + client_channel_pools = [] + def __init__(self): self.listen_host = '0.0.0.0' self.listen_port = 2222 - self.username = None - self.backend_host = None - self.backend_port = None - self.backend_username = None - self.backend_channel = None - self.client_channel = None self.sock = None def display_navigation(self, username, client_channel): @@ -220,7 +218,8 @@ class JumpServer: except paramiko.SSHException: logger.warning('SSH negotiation failed.') - self.client_channel = client_channel = transport.accept(20) + client_channel = transport.accept(20) + self.__class__.client_channel_pools.append(client_channel) if client_channel is None: logger.warning('No channel get.') raise SSHServerException('No channel get.') @@ -232,7 +231,9 @@ class JumpServer: def get_backend_channel(self, host, port, username): backend_server = BackendServer(host, port, username) - self.backend_channel = backend_channel = backend_server.connect() + backend_channel = backend_server.connect() + self.__class__.backend_server_pools.append(backend_server) + self.__class__.backend_channel_pools.append(backend_channel) if not backend_channel: logger.warning('Connect %(username)s@%(host)s:%(port)s failed' % { 'username': username, @@ -249,26 +250,23 @@ class JumpServer: }) try: client_channel = self.get_client_channel(client, addr) - host, port, username = self.display_navigation(self.username, client_channel) + host, port, username = self.display_navigation('root', client_channel) backend_channel = self.get_backend_channel(host, port, username) - print(client_channel.get_id(), backend_channel.get_id()) - while True: r, w, x = select.select([client_channel, backend_channel], [], []) if client_channel in r: - data_client = client_channel.recv(1024) - logger.info(data_client) - if len(data_client) == 0: + client_data = client_channel.recv(1024) + if len(client_data) == 0: break - backend_channel.send(data_client) + backend_channel.send(client_data) if backend_channel in r: - data_server = backend_channel.recv(1024) - if len(data_server) == 0: + backend_data = backend_channel.recv(1024) + if len(backend_data) == 0: break - client_channel.send(data_server) + client_channel.send(backend_data) # if len(recv_data) > 20: # server_data.append('...') @@ -301,7 +299,7 @@ class JumpServer: while True: try: client, addr = self.sock.accept() - t = process.Process(target=self.handle_ssh_request, args=(client, addr)) + t = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) t.daemon = True t.start() except Exception as e: From badd319bb49de2caa14fcfe0cd65bb8bd1c8ee88 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 26 Sep 2016 00:05:23 +0800 Subject: [PATCH 16/26] Modify some bug and add some logging --- terminal/ssh_server.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 0105d4cf4..583d89095 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -11,7 +11,7 @@ import time from binascii import hexlify import sys import threading -from multiprocessing import process +from multiprocessing.process import Process import traceback import tty import termios @@ -126,6 +126,9 @@ class SSHServer(paramiko.ServerInterface): def check_channel_shell_request(self, channel): self.event.set() self.__class__.channel_pools.append(channel) + channel.username = self.username + channel.addr = self.addr + return True def check_channel_pty_request(self, channel, term, width, height, pixelwidth, @@ -150,12 +153,15 @@ class BackendServer: ssh.connect(hostname=self.host, port=self.port, username=self.username, password=self.host_password, pkey=self.host_private_key, look_for_keys=False, allow_agent=True, compress=True, timeout=timeout) self.channel = channel = ssh.invoke_shell(term=term, width=width, height=height) - logger.info('Connect %(username)s@%(host)s:%(port)s successfully' % { + logger.info('Connect backend server %(username)s@%(host)s:%(port)s successfully' % { 'username': self.username, 'host': self.host, 'port': self.port, }) channel.settimeout(100) + channel.host = self.host + channel.port = self.port + channel.username = self.username return channel @property @@ -175,7 +181,8 @@ class Navigation: def display_banner(self): client_channel = self.client_channel client_channel.send('\r\n\r\n\t\tWelcome to use Jumpserver open source system !\r\n\r\n') - client_channel.send('If use find some bug please contact us \r\n') + client_channel.send('If you find some bug please contact us \r\n') + client_channel.send('See more at https://www.jumpserver.org\r\n') # client_channel.send(self.username) def display(self): @@ -211,7 +218,6 @@ class JumpServer: transport.add_server_key(SSHServer.get_host_key()) ssh_server = SSHServer(client, addr) - self.username = ssh_server.username try: transport.start_server(server=ssh_server) @@ -259,12 +265,20 @@ class JumpServer: if client_channel in r: client_data = client_channel.recv(1024) if len(client_data) == 0: + logger.info('Logout from ssh server %(host)s: %(username)s' % { + 'host': addr[0], + 'username': client_channel.username, + }) break backend_channel.send(client_data) if backend_channel in r: backend_data = backend_channel.recv(1024) if len(backend_data) == 0: + logger.info('Logout from backend server %(host)s: %(username)s' % { + 'host': backend_channel.host, + 'username': backend_channel.username, + }) break client_channel.send(backend_data) @@ -281,8 +295,9 @@ class JumpServer: # except IndexError: # pass + # Todo: catch other exception except IndexError: - logger.info('Close with server %s from %s' % ('127.0.0.1', '127.0.0.1')) + logger.info('Close with server %s from %s' % (addr[0], addr[1])) sys.exit(100) def listen(self): @@ -299,9 +314,9 @@ class JumpServer: while True: try: client, addr = self.sock.accept() - t = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) - t.daemon = True - t.start() + process = Process(target=self.handle_ssh_request, args=(client, addr)) + process.daemon = True + process.start() except Exception as e: logger.error('Bind failed: ' + str(e)) traceback.print_exc() From be1a374b1466e1680d399a1ec3da17830049174e Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 26 Sep 2016 22:16:21 +0800 Subject: [PATCH 17/26] Modify log --- terminal/ssh_config.py | 5 +++-- terminal/ssh_server.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/terminal/ssh_config.py b/terminal/ssh_config.py index a23ebbce7..910f7d70d 100644 --- a/terminal/ssh_config.py +++ b/terminal/ssh_config.py @@ -43,10 +43,11 @@ class Config: }, 'file': { 'level': 'DEBUG', - 'class': 'logging.FileHandler', + 'class': 'logging.handlers.TimedRotatingFileHandler', 'formatter': 'main', - 'mode': 'a', 'filename': os.path.join(LOG_DIR, LOG_FILENAME), + 'when': 'D', + 'backupCount': 10, }, }, 'loggers': { diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 583d89095..176c31b1c 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -275,6 +275,8 @@ class JumpServer: if backend_channel in r: backend_data = backend_channel.recv(1024) if len(backend_data) == 0: + client_channel.send('Disconnect from %s \r\n' % backend_channel.host) + client_channel.close() logger.info('Logout from backend server %(host)s: %(username)s' % { 'host': backend_channel.host, 'username': backend_channel.username, From b17a12662c3a6e4eb3157ad1d1e1906ae8773f12 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 27 Sep 2016 00:10:14 +0800 Subject: [PATCH 18/26] modify change windows size --- terminal/ssh_server.py | 27 +++++++++++++++++++++++---- terminal/utils.py | 15 +++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 176c31b1c..e8e8f60c8 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -36,7 +36,7 @@ except IndexError: from django.conf import settings from users.utils import ssh_key_gen, check_user_is_valid -from utils import get_logger, SSHServerException +from utils import get_logger, SSHServerException, control_char logger = get_logger(__name__) @@ -48,6 +48,7 @@ class SSHServer(paramiko.ServerInterface): def __init__(self, client, addr): self.event = threading.Event() + self.change_window_size_event = threading.Event() self.client = client self.addr = addr self.username = None @@ -133,9 +134,15 @@ class SSHServer(paramiko.ServerInterface): def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, modes): + channel.change_window_size_event = threading.Event() + channel.width = width + channel.height = height return True def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight): + channel.change_window_size_event.set() + channel.width = width + channel.height = height return True @@ -180,6 +187,7 @@ class Navigation: def display_banner(self): client_channel = self.client_channel + client_channel.send(control_char.clear) client_channel.send('\r\n\r\n\t\tWelcome to use Jumpserver open source system !\r\n\r\n') client_channel.send('If you find some bug please contact us \r\n') client_channel.send('See more at https://www.jumpserver.org\r\n') @@ -197,6 +205,10 @@ class JumpServer: backend_channel_pools = [] client_channel_pools = [] + CONTROL_CHAR = { + 'clear': '' + } + def __init__(self): self.listen_host = '0.0.0.0' self.listen_port = 2222 @@ -235,9 +247,9 @@ class JumpServer: raise SSHServerException('Client never asked for a shell.') return client_channel - def get_backend_channel(self, host, port, username): + def get_backend_channel(self, host, port, username, term='xterm', width=80, height=24): backend_server = BackendServer(host, port, username) - backend_channel = backend_server.connect() + backend_channel = backend_server.connect(term=term, width=width, height=height) self.__class__.backend_server_pools.append(backend_server) self.__class__.backend_channel_pools.append(backend_channel) if not backend_channel: @@ -257,11 +269,16 @@ class JumpServer: try: client_channel = self.get_client_channel(client, addr) host, port, username = self.display_navigation('root', client_channel) - backend_channel = self.get_backend_channel(host, port, username) + backend_channel = self.get_backend_channel(host, port, username, + width=client_channel.width, + height=client_channel.height) while True: r, w, x = select.select([client_channel, backend_channel], [], []) + if client_channel.change_window_size_event.is_set(): + backend_channel.resize_pty(width=client_channel.width, height=client_channel.height) + if client_channel in r: client_data = client_channel.recv(1024) if len(client_data) == 0: @@ -270,6 +287,7 @@ class JumpServer: 'username': client_channel.username, }) break + print('CC: ' + repr(client_data)) backend_channel.send(client_data) if backend_channel in r: @@ -282,6 +300,7 @@ class JumpServer: 'username': backend_channel.username, }) break + print('SS: ' + repr(backend_data)) client_channel.send(backend_data) # if len(recv_data) > 20: diff --git a/terminal/utils.py b/terminal/utils.py index da648e05f..c9c4f82cf 100644 --- a/terminal/utils.py +++ b/terminal/utils.py @@ -15,5 +15,20 @@ def get_logger(name): return logging.getLogger('jumpserver.%s' % name) +class ControlChar: + CHARS = { + 'clear': '\x1b[H\x1b[2J', + } + + def __init__(self): + pass + + def __getattr__(self, item): + return self.__class__.CHARS.get(item, '') + + class SSHServerException(Exception): pass + + +control_char = ControlChar() From acf51238d097e602540f0151a34e6152076f2540 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 29 Sep 2016 18:01:26 +0800 Subject: [PATCH 19/26] Modify ssh server bug for using public key --- apps/assets/migrations/__init__.py | 0 apps/common/utils.py | 10 +++++-- apps/perms/migrations/__init__.py | 0 apps/users/migrations/__init__.py | 0 connect.py | 32 ---------------------- requirements.txt | 1 + terminal/ssh_server.py | 25 ++++++++++++++++- test_rsa.key | 15 ---------- test_server.py | 44 ------------------------------ 9 files changed, 33 insertions(+), 94 deletions(-) delete mode 100644 apps/assets/migrations/__init__.py delete mode 100644 apps/perms/migrations/__init__.py delete mode 100644 apps/users/migrations/__init__.py delete mode 100644 connect.py delete mode 100644 test_rsa.key delete mode 100644 test_server.py diff --git a/apps/assets/migrations/__init__.py b/apps/assets/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/common/utils.py b/apps/common/utils.py index 3546f610f..da83cfca7 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -30,11 +30,17 @@ def get_object_or_none(model, **kwargs): def encrypt(*args, **kwargs): - return signing.dumps(*args, **kwargs) + try: + return signing.dumps(*args, **kwargs) + except signing.BadSignature: + return '' def decrypt(*args, **kwargs): - return signing.loads(*args, **kwargs) + try: + return signing.loads(*args, **kwargs) + except signing.BadSignature: + return '' def date_expired_default(): diff --git a/apps/perms/migrations/__init__.py b/apps/perms/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/users/migrations/__init__.py b/apps/users/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/connect.py b/connect.py deleted file mode 100644 index 51020d8f1..000000000 --- a/connect.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# - -import sys -import os - -# reload(sys) -# sys.setdefaultencoding('utf8') - -BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -sys.path.append(os.path.join(BASE_DIR, 'apps')) - -import re -import time -import datetime -import textwrap -import getpass -import readline -import django -import paramiko -import errno -import pyte -import operator -import struct, fcntl, signal, socket, select -from io import open as copen -import uuid - - -os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' - - diff --git a/requirements.txt b/requirements.txt index 311786d8d..4bac3860e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,3 +20,4 @@ django-simple-captcha==0.5.2 django-formtools==1.0 sshpubkeys==2.2.0 djangorestframework-bulk==0.2.1 +python-gssapi==0.6.4 diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index e8e8f60c8..36b0f9afd 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -41,6 +41,8 @@ from utils import get_logger, SSHServerException, control_char logger = get_logger(__name__) +paramiko.util.log_to_file(os.path.join(BASE_DIR, 'logs', 'paramiko.log')) + class SSHServer(paramiko.ServerInterface): host_key_path = os.path.join(BASE_DIR, 'host_rsa_key') @@ -79,6 +81,27 @@ class SSHServer(paramiko.ServerInterface): return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + def check_auth_gssapi_with_mic(self, username, + gss_authenticated=paramiko.AUTH_FAILED, + cc_file=None): + + if gss_authenticated == paramiko.AUTH_SUCCESSFUL: + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def check_auth_gssapi_keyex(self, username, + gss_authenticated=paramiko.AUTH_FAILED, + cc_file=None): + + if gss_authenticated == paramiko.AUTH_SUCCESSFUL: + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def enable_auth_gssapi(self): + UseGSSAPI = True + GSSAPICleanupCredentials = False + return UseGSSAPI + def check_auth_password(self, username, password): self.user = user = check_user_is_valid(username=username, password=password) self.username = username = user.username @@ -99,9 +122,9 @@ class SSHServer(paramiko.ServerInterface): def check_auth_publickey(self, username, public_key): self.user = user = check_user_is_valid(username=username, public_key=public_key) - self.username = username = user.username if self.user: + self.username = username = user.username logger.info('Accepted public key for %(username)s from %(host)s port %(port)s ' % { 'username': username, 'host': self.addr[0], diff --git a/test_rsa.key b/test_rsa.key deleted file mode 100644 index f50e9c538..000000000 --- a/test_rsa.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz -oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/ -d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB -gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0 -EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon -soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H -tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU -avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA -4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g -H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv -qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV -HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc -nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7 ------END RSA PRIVATE KEY----- diff --git a/test_server.py b/test_server.py deleted file mode 100644 index b6314f2bc..000000000 --- a/test_server.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# - -import socket -import sys -import threading - - -class ThreadSocket: - def __init__(self, host, port): - self.host = host - self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.sock.bind((self.host, self.port)) - - def listen(self): - self.sock.listen(5) - while True: - client, address = self.sock.accept() - client.settimeout(60) - threading.Thread(target=self.handle_client_request, args=(client, address)).start() - - def handle_client_request(self, client, address): - print("Get client: %s" % str(address)) - while True: - try: - data = client.recv(1024) - print("sleep : %s" % str(address)) - if data: - client.send(data) - else: - raise IndexError('Client has disconnected') - except: - client.close() - - -if __name__ == '__main__': - server = ThreadSocket('', 9000) - try: - server.listen() - except KeyboardInterrupt: - sys.exit(1) From e3c620e1382265049d4740ac70a0bed80362afe2 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 29 Sep 2016 18:35:52 +0800 Subject: [PATCH 20/26] Debug some bug for auth failed and exit --- terminal/ssh_server.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 36b0f9afd..fb98dbf3a 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -104,8 +104,8 @@ class SSHServer(paramiko.ServerInterface): def check_auth_password(self, username, password): self.user = user = check_user_is_valid(username=username, password=password) - self.username = username = user.username if self.user: + self.username = username = user.username logger.info('Accepted password for %(username)s from %(host)s port %(port)s ' % { 'username': username, 'host': self.addr[0], @@ -113,6 +113,7 @@ class SSHServer(paramiko.ServerInterface): }) return paramiko.AUTH_SUCCESSFUL else: + self.client.close() logger.info('Authentication password failed for %(username)s from %(host)s port %(port)s ' % { 'username': username, 'host': self.addr[0], @@ -235,7 +236,6 @@ class JumpServer: def __init__(self): self.listen_host = '0.0.0.0' self.listen_port = 2222 - self.sock = None def display_navigation(self, username, client_channel): nav = Navigation(username, client_channel) @@ -262,12 +262,12 @@ class JumpServer: client_channel = transport.accept(20) self.__class__.client_channel_pools.append(client_channel) if client_channel is None: - logger.warning('No channel get.') - raise SSHServerException('No channel get.') + logger.warning('No ssh channel get.') + client.close() + sys.exit(404) if not ssh_server.event.is_set(): logger.warning('Client never asked for a shell.') - raise SSHServerException('Client never asked for a shell.') return client_channel def get_backend_channel(self, host, port, username, term='xterm', width=80, height=24): @@ -345,7 +345,7 @@ class JumpServer: sys.exit(100) def listen(self): - self.sock = sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((self.listen_host, self.listen_port)) sock.listen(5) @@ -357,7 +357,7 @@ class JumpServer: while True: try: - client, addr = self.sock.accept() + client, addr = sock.accept() process = Process(target=self.handle_ssh_request, args=(client, addr)) process.daemon = True process.start() From b4c64991390661e51276edb5a41a21b1ab2a9b27 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 29 Sep 2016 21:36:15 +0800 Subject: [PATCH 21/26] Try to fix ssh server close client bug --- terminal/ssh_server.py | 62 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index fb98dbf3a..6d79c46a6 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -81,27 +81,6 @@ class SSHServer(paramiko.ServerInterface): return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED - def check_auth_gssapi_with_mic(self, username, - gss_authenticated=paramiko.AUTH_FAILED, - cc_file=None): - - if gss_authenticated == paramiko.AUTH_SUCCESSFUL: - return paramiko.AUTH_SUCCESSFUL - return paramiko.AUTH_FAILED - - def check_auth_gssapi_keyex(self, username, - gss_authenticated=paramiko.AUTH_FAILED, - cc_file=None): - - if gss_authenticated == paramiko.AUTH_SUCCESSFUL: - return paramiko.AUTH_SUCCESSFUL - return paramiko.AUTH_FAILED - - def enable_auth_gssapi(self): - UseGSSAPI = True - GSSAPICleanupCredentials = False - return UseGSSAPI - def check_auth_password(self, username, password): self.user = user = check_user_is_valid(username=username, password=password) if self.user: @@ -153,7 +132,6 @@ class SSHServer(paramiko.ServerInterface): self.__class__.channel_pools.append(channel) channel.username = self.username channel.addr = self.addr - return True def check_channel_pty_request(self, channel, term, width, height, pixelwidth, @@ -181,8 +159,14 @@ class BackendServer: def connect(self, term='xterm', width=80, height=24, timeout=10): self.ssh = ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(hostname=self.host, port=self.port, username=self.username, password=self.host_password, - pkey=self.host_private_key, look_for_keys=False, allow_agent=True, compress=True, timeout=timeout) + + try: + ssh.connect(hostname=self.host, port=self.port, username=self.username, password=self.host_password, + pkey=self.host_private_key, look_for_keys=False, allow_agent=True, compress=True, timeout=timeout) + except Exception: + logger.warning('Connect backend server %s failed' % self.host) + return None + self.channel = channel = ssh.invoke_shell(term=term, width=width, height=height) logger.info('Connect backend server %(username)s@%(host)s:%(port)s successfully' % { 'username': self.username, @@ -260,12 +244,11 @@ class JumpServer: logger.warning('SSH negotiation failed.') client_channel = transport.accept(20) - self.__class__.client_channel_pools.append(client_channel) if client_channel is None: logger.warning('No ssh channel get.') - client.close() - sys.exit(404) + return None + self.__class__.client_channel_pools.append(client_channel) if not ssh_server.event.is_set(): logger.warning('Client never asked for a shell.') return client_channel @@ -273,14 +256,17 @@ class JumpServer: def get_backend_channel(self, host, port, username, term='xterm', width=80, height=24): backend_server = BackendServer(host, port, username) backend_channel = backend_server.connect(term=term, width=width, height=height) - self.__class__.backend_server_pools.append(backend_server) - self.__class__.backend_channel_pools.append(backend_channel) - if not backend_channel: + + if backend_channel is None: logger.warning('Connect %(username)s@%(host)s:%(port)s failed' % { 'username': username, 'host': host, 'port': port, }) + return None + + self.__class__.backend_server_pools.append(backend_server) + self.__class__.backend_channel_pools.append(backend_channel) return backend_channel @@ -289,12 +275,28 @@ class JumpServer: 'host': addr[0], 'port': addr[1], }) + raise IndexError + dir(client) + client.close() + return False + try: client_channel = self.get_client_channel(client, addr) + if client_channel is None: + client.close() + return + host, port, username = self.display_navigation('root', client_channel) backend_channel = self.get_backend_channel(host, port, username, width=client_channel.width, height=client_channel.height) + if backend_channel is None: + client.shutdown() + client.close() + client.send('Close') + print(client) + print(dir(client)) + return while True: r, w, x = select.select([client_channel, backend_channel], [], []) From 0d25a8f5b765d4411eab1f93120ba9f406a3a93a Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 29 Sep 2016 23:52:07 +0800 Subject: [PATCH 22/26] Use thread replace process --- terminal/ssh_server.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 6d79c46a6..5b9f87b2b 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -92,7 +92,6 @@ class SSHServer(paramiko.ServerInterface): }) return paramiko.AUTH_SUCCESSFUL else: - self.client.close() logger.info('Authentication password failed for %(username)s from %(host)s port %(port)s ' % { 'username': username, 'host': self.addr[0], @@ -275,10 +274,6 @@ class JumpServer: 'host': addr[0], 'port': addr[1], }) - raise IndexError - dir(client) - client.close() - return False try: client_channel = self.get_client_channel(client, addr) @@ -360,9 +355,9 @@ class JumpServer: while True: try: client, addr = sock.accept() - process = Process(target=self.handle_ssh_request, args=(client, addr)) - process.daemon = True - process.start() + thread = threading.Thread(target=self.handle_ssh_request, args=(client, addr)) + thread.daemon = True + thread.start() except Exception as e: logger.error('Bind failed: ' + str(e)) traceback.print_exc() From d9866e1f38ebd8a78cc8c2e0238f39c1ac90e4c9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 30 Sep 2016 00:07:55 +0800 Subject: [PATCH 23/26] Debug to find command --- terminal/ssh_server.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 5b9f87b2b..698a58338 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -293,6 +293,9 @@ class JumpServer: print(dir(client)) return + input_data = [] + output_data = [] + id_ = 0 while True: r, w, x = select.select([client_channel, backend_channel], [], []) @@ -307,8 +310,9 @@ class JumpServer: 'username': client_channel.username, }) break - print('CC: ' + repr(client_data)) backend_channel.send(client_data) + input_data.append('%s: %s' % (id_, client_data[:5])) + id_ += 1 if backend_channel in r: backend_data = backend_channel.recv(1024) @@ -320,8 +324,10 @@ class JumpServer: 'username': backend_channel.username, }) break - print('SS: ' + repr(backend_data)) client_channel.send(backend_data) + output_data.append('%s: %s' % (id_-1, backend_data[:5])) + print('in: %s' % input_data) + print('out: %s' % output_data) # if len(recv_data) > 20: # server_data.append('...') From 0234ff0252d112c161740adc734762d3d6929739 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 2 Oct 2016 17:09:27 +0800 Subject: [PATCH 24/26] Pripare web terminal server --- terminal/ssh_server.py | 134 +++++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 51 deletions(-) diff --git a/terminal/ssh_server.py b/terminal/ssh_server.py index 698a58338..f8c649f13 100644 --- a/terminal/ssh_server.py +++ b/terminal/ssh_server.py @@ -207,6 +207,83 @@ class Navigation: pass +class ProxyChannel: + ENTER_CHAR = ['\r', '\n', '\r\n'] + input_data = [] + output_data = [] + + def __init__(self, client_channel, backend_channel, client_addr): + self.client_channel = client_channel + self.backend_channel = backend_channel + self.client_addr = client_addr + self.in_input_mode = True + + def stream_flow(self, input_=None, output_=None): + if input_: + self.in_input_mode = True + if input_ in ['\r', '\n', '\r\n']: + self.in_input_mode = False + + if output_: + print(''.join(self.__class__.output_data)) + if not self.in_input_mode: + command = ''.join(self.__class__.output_data) + del self.__class__.output_data + self.__class__.output_data = [] + self.__class__.output_data.append(output_) + + def proxy(self): + client_channel = self.client_channel + backend_channel = self.backend_channel + client_addr = self.client_addr + + while True: + r, w, x = select.select([client_channel, backend_channel], [], []) + + if client_channel.change_window_size_event.is_set(): + backend_channel.resize_pty(width=client_channel.width, height=client_channel.height) + + if client_channel in r: + self.in_input_mode = True + client_data = client_channel.recv(1024) + + if client_data in self.__class__.ENTER_CHAR: + self.in_input_mode = False + command = ''.join(self.__class__.output_data) + print('########### command ##########') + print(command) + print('########### end command ##########') + del self.__class__.output_data + self.__class__.output_data = [] + backend_channel.send(client_data) + output = ''.join(self.__class__.output_data) + print('>>>>>>>>>>> output <<<<<<<<<<') + print(output) + print('>>>>>>>>>>> end output <<<<<<<<<<') + continue + + if len(client_data) == 0: + logger.info('Logout from ssh server %(host)s: %(username)s' % { + 'host': client_addr[0], + 'username': client_channel.username, + }) + break + backend_channel.send(client_data) + + if backend_channel in r: + backend_data = backend_channel.recv(1024) + if len(backend_data) == 0: + client_channel.send('Disconnect from %s \r\n' % backend_channel.host) + client_channel.close() + logger.info('Logout from backend server %(host)s: %(username)s' % { + 'host': backend_channel.host, + 'username': backend_channel.username, + }) + break + self.__class__.output_data.append(backend_data) + client_channel.send(backend_data) + + class JumpServer: backend_server_pools = [] backend_channel_pools = [] @@ -223,7 +300,7 @@ class JumpServer: def display_navigation(self, username, client_channel): nav = Navigation(username, client_channel) nav.display() - return '127.0.0.1', 22, 'root' + return 'j', 22, 'root' def get_client_channel(self, client, addr): transport = paramiko.Transport(client, gss_kex=False) @@ -269,6 +346,9 @@ class JumpServer: return backend_channel + def command_flow(self, input_=None, output_=None): + pass + def handle_ssh_request(self, client, addr): logger.info("Get ssh request from %(host)s:%(port)s" % { 'host': addr[0], @@ -289,58 +369,10 @@ class JumpServer: client.shutdown() client.close() client.send('Close') - print(client) - print(dir(client)) return - input_data = [] - output_data = [] - id_ = 0 - while True: - r, w, x = select.select([client_channel, backend_channel], [], []) - - if client_channel.change_window_size_event.is_set(): - backend_channel.resize_pty(width=client_channel.width, height=client_channel.height) - - if client_channel in r: - client_data = client_channel.recv(1024) - if len(client_data) == 0: - logger.info('Logout from ssh server %(host)s: %(username)s' % { - 'host': addr[0], - 'username': client_channel.username, - }) - break - backend_channel.send(client_data) - input_data.append('%s: %s' % (id_, client_data[:5])) - id_ += 1 - - if backend_channel in r: - backend_data = backend_channel.recv(1024) - if len(backend_data) == 0: - client_channel.send('Disconnect from %s \r\n' % backend_channel.host) - client_channel.close() - logger.info('Logout from backend server %(host)s: %(username)s' % { - 'host': backend_channel.host, - 'username': backend_channel.username, - }) - break - client_channel.send(backend_data) - output_data.append('%s: %s' % (id_-1, backend_data[:5])) - print('in: %s' % input_data) - print('out: %s' % output_data) - - # if len(recv_data) > 20: - # server_data.append('...') - # else: - # server_data.append(recv_data) - # try: - # if repr(server_data[-2]) == u'\r\n': - # result = server_data.pop() - # server_data.pop() - # command = ''.join(server_data) - # server_data = [] - # except IndexError: - # pass + proxy_channel = ProxyChannel(client_channel, backend_channel, addr) + proxy_channel.proxy() # Todo: catch other exception except IndexError: From b54c973d82e471768541bab207c99c6cd28bbf2e Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 2 Oct 2016 21:38:27 +0800 Subject: [PATCH 25/26] Update requirement --- requirements.txt | 2 ++ terminal/web_ssh_server.py | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4bac3860e..5be33cb14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,3 +21,5 @@ django-formtools==1.0 sshpubkeys==2.2.0 djangorestframework-bulk==0.2.1 python-gssapi==0.6.4 +tornado==4.4.2 + diff --git a/terminal/web_ssh_server.py b/terminal/web_ssh_server.py index f93d0bec7..3d98261b1 100644 --- a/terminal/web_ssh_server.py +++ b/terminal/web_ssh_server.py @@ -1,7 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- -# +# - -if __name__ == '__main__': - pass From 820d608b185211587d3d321de9142b5f9af68ff9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 2 Oct 2016 21:43:22 +0800 Subject: [PATCH 26/26] Rm teminal app --- apps/jumpserver/settings.py | 34 +++--- apps/jumpserver/urls.py | 1 - apps/terminal/__init__.py | 0 apps/terminal/admin.py | 3 - apps/terminal/apps.py | 7 -- apps/terminal/hands.py | 5 - apps/terminal/migrations/__init__.py | 0 apps/terminal/models.py | 5 - apps/terminal/templates/main.html | 176 --------------------------- apps/terminal/tests.py | 3 - apps/terminal/urls.py | 10 -- apps/terminal/views.py | 28 ----- 12 files changed, 17 insertions(+), 255 deletions(-) delete mode 100644 apps/terminal/__init__.py delete mode 100644 apps/terminal/admin.py delete mode 100644 apps/terminal/apps.py delete mode 100644 apps/terminal/hands.py delete mode 100644 apps/terminal/migrations/__init__.py delete mode 100644 apps/terminal/models.py delete mode 100644 apps/terminal/templates/main.html delete mode 100644 apps/terminal/tests.py delete mode 100644 apps/terminal/urls.py delete mode 100644 apps/terminal/views.py diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index b9ff532fe..b57a1f07a 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -54,7 +54,7 @@ INSTALLED_APPS = [ 'users.apps.UsersConfig', 'assets.apps.AssetsConfig', 'perms.apps.PermsConfig', - 'terminal.apps.TerminalConfig', + # 'terminal.apps.TerminalConfig', 'ops.apps.OpsConfig', 'audits.apps.AuditsConfig', 'common.apps.CommonConfig', @@ -274,36 +274,36 @@ REST_FRAMEWORK = { } # This setting is required to override the Django's main loop, when running in # development mode, such as ./manage runserver -WSGI_APPLICATION = 'ws4redis.django_runserver.application' +# WSGI_APPLICATION = 'ws4redis.django_runserver.application' # URL that distinguishes websocket connections from normal requests -WEBSOCKET_URL = '/ws/' +# WEBSOCKET_URL = '/ws/' # WebSocket Redis -WS4REDIS_CONNECTION = { - 'host': CONFIG.REDIS_HOST or '127.0.0.1', - 'port': CONFIG.REDIS_PORT or 6379, - 'db': 2, -} +# WS4REDIS_CONNECTION = { +# 'host': CONFIG.REDIS_HOST or '127.0.0.1', +# 'port': CONFIG.REDIS_PORT or 6379, +# 'db': 2, +# } # Set the number of seconds each message shall persisted -WS4REDIS_EXPIRE = 3600 +# WS4REDIS_EXPIRE = 3600 -WS4REDIS_HEARTBEAT = 'love you' +# WS4REDIS_HEARTBEAT = 'love you' -WS4REDIS_PREFIX = 'demo' +# WS4REDIS_PREFIX = 'demo' -SESSION_ENGINE = 'redis_sessions.session' +# SESSION_ENGINE = 'redis_sessions.session' -SESSION_REDIS_PREFIX = 'session' +# SESSION_REDIS_PREFIX = 'session' -SESSION_REDIS_HOST = CONFIG.REDIS_HOST +# SESSION_REDIS_HOST = CONFIG.REDIS_HOST -SESSION_REDIS_PORT = CONFIG.REDIS_PORT +# SESSION_REDIS_PORT = CONFIG.REDIS_PORT -SESSION_REDIS_PASSWORD = CONFIG.REDIS_PASSWORD +# SESSION_REDIS_PASSWORD = CONFIG.REDIS_PASSWORD -SESSION_REDIS_DB = CONFIG.REDIS_DB +# SESSION_REDIS_DB = CONFIG.REDIS_DB # Custom User Auth model diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 21b144da8..7a1c3ae95 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -25,7 +25,6 @@ urlpatterns = [ url(r'^(api/)?users/', include('users.urls')), url(r'^assets/', include('assets.urls')), url(r'^perms/', include('perms.urls')), - url(r'^terminal/', include('terminal.urls')), ] diff --git a/apps/terminal/__init__.py b/apps/terminal/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/terminal/admin.py b/apps/terminal/admin.py deleted file mode 100644 index 8c38f3f3d..000000000 --- a/apps/terminal/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/apps/terminal/apps.py b/apps/terminal/apps.py deleted file mode 100644 index c81fa232b..000000000 --- a/apps/terminal/apps.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals - -from django.apps import AppConfig - - -class TerminalConfig(AppConfig): - name = 'terminal' diff --git a/apps/terminal/hands.py b/apps/terminal/hands.py deleted file mode 100644 index 0133d394f..000000000 --- a/apps/terminal/hands.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# - - diff --git a/apps/terminal/migrations/__init__.py b/apps/terminal/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/terminal/models.py b/apps/terminal/models.py deleted file mode 100644 index bd4b2abe9..000000000 --- a/apps/terminal/models.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals - -from django.db import models - -# Create your models here. diff --git a/apps/terminal/templates/main.html b/apps/terminal/templates/main.html deleted file mode 100644 index 0c9dd8e17..000000000 --- a/apps/terminal/templates/main.html +++ /dev/null @@ -1,176 +0,0 @@ -{% extends 'base.html' %} -{% block content %} - -
-
-
-
-
- - - -
-{% endblock %} - -{% block custom_foot_js %} - - - - -{% endblock %} diff --git a/apps/terminal/tests.py b/apps/terminal/tests.py deleted file mode 100644 index 7ce503c2d..000000000 --- a/apps/terminal/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/terminal/urls.py b/apps/terminal/urls.py deleted file mode 100644 index 6afe754bc..000000000 --- a/apps/terminal/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -# coding:utf-8 -from django.conf.urls import url - -import views - -app_name = 'terminal' - -urlpatterns = [ - url(r'^web-terminal$', views.TerminalView.as_view(), name='web-terminal'), -] diff --git a/apps/terminal/views.py b/apps/terminal/views.py deleted file mode 100644 index 0f9be9384..000000000 --- a/apps/terminal/views.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.shortcuts import render -from django.urls import reverse_lazy -from django.db.models import Q -from django.views.generic.list import ListView -from django.views.generic.edit import CreateView, DeleteView, UpdateView -from django.views.generic.detail import DetailView -from django.views.generic.base import TemplateView -from django.views import View -from django.http import HttpResponse -from ws4redis.redis_store import RedisMessage -from ws4redis.publisher import RedisPublisher -from django.conf import settings - - -# Create your views here. -class TerminalView(TemplateView): - template_name = 'main.html' - - def get(self, request, *args, **kwargs): - welcome = RedisMessage('Hello everybody') # create a welcome message to be sent to everybody - RedisPublisher(facility='foobar', broadcast=True).publish_message(welcome) - return super(TerminalView, self).get(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - redis_publisher = RedisPublisher(facility='foobar', groups=[request.POST.get('group')]) - message = RedisMessage(request.POST.get('message')) - redis_publisher.publish_message(message) - return HttpResponse('OK')