Browse Source

Update ssh server

pull/530/head
ibuler 8 years ago
parent
commit
cfef374454
  1. 3
      apps/terminal/hands.py
  2. 27
      apps/terminal/keys/host_rsa_key
  3. 130
      apps/terminal/server.py
  4. 6
      apps/users/models.py
  5. 20
      apps/users/utils.py
  6. 4
      config-example.py

3
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

27
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-----

130
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:

6
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)

20
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

4
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

Loading…
Cancel
Save