Unverified Commit 72867760 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #47 from jumpserver/dev

支持OTP
parents 38776824 d151c04c
...@@ -181,6 +181,7 @@ class ProxyNamespace(BaseNamespace): ...@@ -181,6 +181,7 @@ class ProxyNamespace(BaseNamespace):
user_id = host.get('user', None) user_id = host.get('user', None)
logger.debug("self.current_user") logger.debug("self.current_user")
self.current_user = self.app.service.get_user_profile(user_id) self.current_user = self.app.service.get_user_profile(user_id)
self.clients[request.sid]["request"].user = self.current_user
logger.debug(self.current_user) logger.debug(self.current_user)
# { # {
......
...@@ -24,6 +24,8 @@ class SSHInterface(paramiko.ServerInterface): ...@@ -24,6 +24,8 @@ class SSHInterface(paramiko.ServerInterface):
self._request = weakref.ref(request) self._request = weakref.ref(request)
self.event = threading.Event() self.event = threading.Event()
self.auth_valid = False self.auth_valid = False
self.otp_auth = False
self.info = None
@property @property
def app(self): def app(self):
...@@ -35,58 +37,84 @@ class SSHInterface(paramiko.ServerInterface): ...@@ -35,58 +37,84 @@ class SSHInterface(paramiko.ServerInterface):
def check_auth_interactive(self, username, submethods): def check_auth_interactive(self, username, submethods):
logger.info("Check auth interactive: %s %s" % (username, submethods)) logger.info("Check auth interactive: %s %s" % (username, submethods))
return paramiko.AUTH_FAILED instructions = 'Please enter 6 digits.'
interactive = paramiko.server.InteractiveQuery(instructions=instructions)
interactive.add_prompt(prompt='[OTP auth]: ')
return interactive
def check_auth_interactive_response(self, responses): def check_auth_interactive_response(self, responses):
logger.info("Check auth interactive response: %s " % responses) logger.info("Check auth interactive response: %s " % responses)
# TODO:MFA Auth # TODO:MFA Auth
pass otp_code = responses[0]
if not otp_code or not len(otp_code) == 6 or not otp_code.isdigit():
return paramiko.AUTH_FAILED
return self.check_auth_otp(otp_code)
def check_auth_otp(self, otp_code):
seed = self.info.get('seed', '')
if not seed:
return paramiko.AUTH_FAILED
is_valid = self.app.service.authenticate_otp(seed, otp_code)
if is_valid:
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def enable_auth_gssapi(self): def enable_auth_gssapi(self):
return False return False
def get_allowed_auths(self, username): def get_allowed_auths(self, username):
supported = [] supported = []
if self.otp_auth:
return 'keyboard-interactive'
if self.app.config["PASSWORD_AUTH"]: if self.app.config["PASSWORD_AUTH"]:
supported.append("password") supported.append("password")
if self.app.config["PUBLIC_KEY_AUTH"]: if self.app.config["PUBLIC_KEY_AUTH"]:
supported.append("publickey") supported.append("publickey")
return ",".join(supported) return ",".join(supported)
def check_auth_none(self, username): def check_auth_none(self, username):
return paramiko.AUTH_FAILED return paramiko.AUTH_FAILED
def check_auth_password(self, username, password): def check_auth_password(self, username, password):
valid = self.validate_auth(username, password=password) user = self.validate_auth(username, password=password)
if not valid: if not user:
logger.warning("Password and public key auth <%s> failed, reject it" % username) logger.warning("Password and public key auth <%s> failed, reject it" % username)
return paramiko.AUTH_FAILED return paramiko.AUTH_FAILED
else: else:
logger.info("Password auth <%s> success" % username) logger.info("Password auth <%s> success" % username)
if self.otp_auth:
return paramiko.AUTH_PARTIALLY_SUCCESSFUL
return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_SUCCESSFUL
def check_auth_publickey(self, username, key): def check_auth_publickey(self, username, key):
key = key.get_base64() key = key.get_base64()
valid = self.validate_auth(username, public_key=key) user = self.validate_auth(username, public_key=key)
if not valid: if not user:
logger.debug("Public key auth <%s> failed, try to password" % username) logger.debug("Public key auth <%s> failed, try to password" % username)
return paramiko.AUTH_FAILED return paramiko.AUTH_FAILED
else: else:
logger.debug("Public key auth <%s> success" % username) logger.debug("Public key auth <%s> success" % username)
if self.otp_auth:
return paramiko.AUTH_PARTIALLY_SUCCESSFUL
return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_SUCCESSFUL
def validate_auth(self, username, password="", public_key=""): def validate_auth(self, username, password="", public_key=""):
user, _ = self.app.service.authenticate( info = self.app.service.authenticate(
username, password=password, public_key=public_key, username, password=password, public_key=public_key,
remote_addr=self.request.remote_ip, remote_addr=self.request.remote_ip
) )
user = info.get('user', None)
if user: if user:
self.request.user = user self.request.user = user
return True self.info = info
else:
return False seed = info.get('seed', None)
token = info.get('token', None)
if seed and not token:
self.otp_auth = True
return user
def check_channel_direct_tcpip_request(self, chanid, origin, destination): def check_channel_direct_tcpip_request(self, chanid, origin, destination):
logger.debug("Check channel direct tcpip request: %d %s %s" % logger.debug("Check channel direct tcpip request: %d %s %s" %
......
...@@ -20,7 +20,7 @@ Jinja2==2.10 ...@@ -20,7 +20,7 @@ Jinja2==2.10
jmespath==0.9.3 jmespath==0.9.3
jms-es-sdk==0.5.2 jms-es-sdk==0.5.2
jms-storage==0.0.12 jms-storage==0.0.12
jumpserver-python-sdk==0.0.39 jumpserver-python-sdk==0.0.41
MarkupSafe==1.0 MarkupSafe==1.0
oss2==2.4.0 oss2==2.4.0
paramiko==2.4.0 paramiko==2.4.0
......
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