Commit cfef3744 authored by ibuler's avatar ibuler

Update ssh server

parent 0d4ca971
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from users.utils import ssh_key_gen from users.utils import ssh_key_gen, check_user_is_valid
-----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-----
...@@ -3,19 +3,6 @@ ...@@ -3,19 +3,6 @@
# #
import sys import sys
import os 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 import base64
from binascii import hexlify from binascii import hexlify
import sys import sys
...@@ -30,14 +17,24 @@ import socket ...@@ -30,14 +17,24 @@ import socket
import select import select
import errno import errno
import paramiko import paramiko
import django
from paramiko.py3compat import b, u, decodebytes 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): class SSHService(paramiko.ServerInterface):
...@@ -46,30 +43,31 @@ class SSHService(paramiko.ServerInterface): ...@@ -46,30 +43,31 @@ class SSHService(paramiko.ServerInterface):
# b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' # b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT'
# b'UWT10hcuO4Ks8=') # b'UWT10hcuO4Ks8=')
# good_pub_key = paramiko.RSAKey(data=decodebytes(data)) # 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') host_key_path = os.path.join(BASE_DIR, 'keys', 'host_rsa_key')
ssh_pub_key_path = ssh_key_path + '.pub'
def __init__(self): def __init__(self):
self.event = threading.Event() self.event = threading.Event()
self.user = None
@classmethod
def host_key(cls):
return cls.get_host_key()
@classmethod @classmethod
def get_host_key(cls): def get_host_key(cls):
if os.path.isfile(cls.ssh_pub_key_path): logger.debug("Get ssh server host key")
with open(cls.ssh_pub_key_path) as f: if not os.path.isfile(cls.host_key_path):
ssh_pub_key = f.read() cls.host_key_gen()
else: return paramiko.RSAKey(filename=cls.host_key_path)
ssh_key, ssh_pub_key = cls.host_key_gen()
return ssh_pub_key
@classmethod @classmethod
def host_key_gen(cls): def host_key_gen(cls):
logger.debug("Generate ssh server host key")
ssh_key, ssh_pub_key = ssh_key_gen() ssh_key, ssh_pub_key = ssh_key_gen()
with open(cls.ssh_key_path, 'w') as f: with open(cls.host_key_path, 'w') as f:
with open(cls.ssh_pub_key_path, 'w') as f2:
f.write(ssh_key) f.write(ssh_key)
f2.write(ssh_pub_key)
return ssh_key, ssh_pub_key
def check_channel_request(self, kind, chanid): def check_channel_request(self, kind, chanid):
if kind == 'session': if kind == 'session':
...@@ -77,18 +75,30 @@ class SSHService(paramiko.ServerInterface): ...@@ -77,18 +75,30 @@ class SSHService(paramiko.ServerInterface):
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, username, password): 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 return paramiko.AUTH_SUCCESSFUL
else:
logger.warning('User: %s password auth failed' % username)
return paramiko.AUTH_FAILED return paramiko.AUTH_FAILED
def check_auth_publickey(self, username, key): def check_auth_publickey(self, username, public_key):
print('Auth attempt with key: ' + u(hexlify(key.get_fingerprint()))) self.user = check_user_is_valid(username=username, public_key=public_key)
if (username == 'robey') and (key == self.good_pub_key): if self.user:
logger.info('User: %s public key auth passed' % username)
return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_SUCCESSFUL
else:
logger.warning('User: %s public key auth failed' % username)
return paramiko.AUTH_FAILED return paramiko.AUTH_FAILED
def get_allowed_auths(self, username): 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): def check_channel_shell_request(self, channel):
self.event.set() self.event.set()
...@@ -100,7 +110,7 @@ class SSHService(paramiko.ServerInterface): ...@@ -100,7 +110,7 @@ class SSHService(paramiko.ServerInterface):
class SSHServer: class SSHServer:
def __init__(self, host, port): def __init__(self, host='127.0.0.1', port=2200):
self.host = host self.host = host
self.port = port self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...@@ -118,58 +128,57 @@ class SSHServer: ...@@ -118,58 +128,57 @@ class SSHServer:
return channel return channel
def handle_ssh_request(self, client, addr): def handle_ssh_request(self, client, addr):
print('Got a connection!') logger.info("Get connection from " + str(addr))
try: try:
t = paramiko.Transport(client, gss_kex=False) transport = paramiko.Transport(client, gss_kex=False)
t.set_gss_host(socket.getfqdn("")) transport.set_gss_host(socket.getfqdn(""))
try: try:
t.load_server_moduli() transport.load_server_moduli()
except: except:
print('(Failed to load moduli -- gex will be unsupported.)') logger.warning('(Failed to load moduli -- gex will be unsupported.)')
raise raise
t.add_server_key(host_key)
transport.add_server_key(SSHService.get_host_key())
service = SSHService() service = SSHService()
try: try:
t.start_server(server=service) transport.start_server(server=service)
except paramiko.SSHException: except paramiko.SSHException:
print('*** SSH negotiation failed.') print('*** SSH negotiation failed.')
return return
chan = t.accept(20) channel = transport.accept(20)
if channel is None:
if chan is None:
print('*** No channel.') print('*** No channel.')
return return
print('Authenticated!') print('Authenticated!')
chan.settimeout(100) channel.settimeout(100)
chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') channel.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') channel.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') channel.send('Happy birthday to Robot Dave!\r\n\r\n')
server_chan = self.connect() server_channel = self.connect()
if not service.event.is_set(): if not service.event.is_set():
print('*** Client never asked for a shell.') print('*** Client never asked for a shell.')
return return
server_data = [] server_data = []
input_mode = True input_mode = True
while True: while True:
r, w, e = select.select([server_chan, chan], [], []) r, w, e = select.select([server_channel, channel], [], [])
if chan in r: if channel in r:
recv_data = chan.recv(1024).decode('utf8') recv_data = channel.recv(1024).decode('utf8')
# print("From client: " + repr(recv_data)) # print("From client: " + repr(recv_data))
if len(recv_data) == 0: if len(recv_data) == 0:
break break
server_chan.send(recv_data) server_channel.send(recv_data)
if server_chan in r: if server_channel in r:
recv_data = server_chan.recv(1024).decode('utf8') recv_data = server_channel.recv(1024).decode('utf8')
# print("From server: " + repr(recv_data)) # print("From server: " + repr(recv_data))
if len(recv_data) == 0: if len(recv_data) == 0:
break break
chan.send(recv_data) channel.send(recv_data)
if len(recv_data) > 20: if len(recv_data) > 20:
server_data.append('...') server_data.append('...')
else: else:
...@@ -190,13 +199,14 @@ class SSHServer: ...@@ -190,13 +199,14 @@ class SSHServer:
print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
traceback.print_exc() traceback.print_exc()
try: try:
t.close() transport.close()
except: except:
pass pass
sys.exit(1) sys.exit(1)
def listen(self): def listen(self):
self.sock.listen(5) self.sock.listen(5)
print('Start ssh server %(host)s:%(port)s' % {'host': self.host, 'port': self.port})
while True: while True:
try: try:
client, addr = self.sock.accept() client, addr = self.sock.accept()
...@@ -209,7 +219,7 @@ class SSHServer: ...@@ -209,7 +219,7 @@ class SSHServer:
if __name__ == '__main__': if __name__ == '__main__':
server = SSHServer('', 2200) server = SSHServer(host='', port=2200)
try: try:
server.listen() server.listen()
except KeyboardInterrupt: except KeyboardInterrupt:
......
...@@ -109,6 +109,12 @@ class User(AbstractUser): ...@@ -109,6 +109,12 @@ class User(AbstractUser):
else: else:
return True return True
@property
def is_valid(self):
if self.is_active and not self.is_expired:
return True
return False
@property @property
def private_key(self): def private_key(self):
return decrypt(self._private_key) return decrypt(self._private_key)
......
...@@ -13,7 +13,8 @@ from django.utils.translation import ugettext as _ ...@@ -13,7 +13,8 @@ from django.utils.translation import ugettext as _
from paramiko.rsakey import RSAKey from paramiko.rsakey import RSAKey
from common.tasks import send_mail_async 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: try:
...@@ -203,3 +204,20 @@ def validate_ssh_pk(text): ...@@ -203,3 +204,20 @@ def validate_ssh_pk(text):
return optionState(text[1:]) return optionState(text[1:])
return startState([n.strip() for n in text.splitlines()]) 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
...@@ -70,6 +70,10 @@ class Config: ...@@ -70,6 +70,10 @@ class Config:
# EMAIL_USE_TLS = False # If port is 587, set True # EMAIL_USE_TLS = False # If port is 587, set True
# EMAIL_SUBJECT_PREFIX = '[Jumpserver] ' # EMAIL_SUBJECT_PREFIX = '[Jumpserver] '
# SSH use password or public key for auth
SSH_PASSWORD_AUTH = False
SSH_PUBLIC_KEY_AUTH = True
def __init__(self): def __init__(self):
pass pass
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment