diff --git a/salt/auth/__init__.py b/salt/auth/__init__.py index e4396ff7e720..ae6c3de8761e 100644 --- a/salt/auth/__init__.py +++ b/salt/auth/__init__.py @@ -136,13 +136,24 @@ def get_groups(self, load): except Exception: return None - def mk_token(self, load): + def mk_unauth_token(self, load): + ''' + Get an unauthenticated token (for example when authentication is + taken care of by a frontal sur as nginx or apache that proxies + to salt-api). Use with caution. + ''' + # TODO check config allows this + return self.mk_token(load, auth=False) + + + def mk_token(self, load, auth=True): ''' Run time_auth and create a token. Return False or the token ''' - ret = self.time_auth(load) - if ret is False: - return {} + if auth: # and config.get (allow token with no auth, or pick from rest_cherrypy:trust_remote_user = True + ret = self.time_auth(load) + if ret is False: + return {} fstr = '{0}.auth'.format(load['eauth']) hash_type = getattr(hashlib, self.opts.get('hash_type', 'md5')) tok = str(hash_type(os.urandom(512)).hexdigest()) @@ -407,11 +418,17 @@ def token_cli(self, eauth, load): os.umask(oldmask) return tdata - def mk_token(self, load): + def mk_unauth_token(self, load): + return self.mk_token(load, auth=False) + + def mk_token(self, load, auth=True): ''' Request a token from the master ''' - load['cmd'] = 'mk_token' + if auth: # TODO more checks + load['cmd'] = 'mk_token' + else: + load['cmd'] = 'mk_unauth_token' tdata = self._send_token_request(load) return tdata diff --git a/salt/auth/kerberos.py b/salt/auth/kerberos.py new file mode 100644 index 000000000000..12a0689c9ede --- /dev/null +++ b/salt/auth/kerberos.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +''' +Provide authentication using Kerberos by trusting webapp. + +This is a placeholder auth module that does not authenticate with kerberos + +.. versionadded:: TODO +''' + +from __future__ import absolute_import +import logging + +log = logging.getLogger(__name__) + + +def auth(username, **kwargs): + ''' + Refuse authentication always. This gives the opportunity to use + the Remote User mecanism in salt-api + + This prevents a local user from bypassing authorisation + + TOOD : link to documentation about how to enable X-Remote-User in salt-api + + ''' + log.debug('returning False for kerberos trusted auth') + return False + diff --git a/salt/master.py b/salt/master.py index 90a487a2ab60..15eddb27756c 100644 --- a/salt/master.py +++ b/salt/master.py @@ -2096,6 +2096,9 @@ def wheel(self, clear_load): args=exc.args, message=str(exc))) + def mk_unauth_token(self, clear_load): + return self.loadauth.mk_unauth_token(clear_load) + def mk_token(self, clear_load): ''' Create and return an authentication token, the clear load needs to diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index 5931dbd93b72..2398f66af76a 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -99,6 +99,18 @@ root_prefix : ``/`` A URL path to the main entry point for the application. This is useful for serving multiple applications from the same URL. + authorized_ips + A list of IPs to restrict the authorized IPs that can connect to your + salt-api + trust_remote_user: ``False`` + Puts the salt-api in "trust" mode, where it will trust the remote header + set by the frontal which handles the authentication. This is a common + pattern in web applications, see + `django documentation `_ + about this. USE WITH CAUTION. This requires to make sure the frontal + is doing the authentication and that you are only listenning to localhost + and that no users ca request salt-api directly (for example from the + server it is running on). Also set authorized_ips to 127.0.0.1. .. _rest_cherrypy-auth: @@ -1415,7 +1427,13 @@ def POST(self, **kwargs): else: creds = cherrypy.serving.request.lowstate - token = self.auth.mk_token(creds) + auth=True + logger.debug('HEADER {0}'.format(cherrypy.serving.request.headers)) + if cherrypy.config.get('saltopts', {}).get('rest_cherrypy', {}).get('trust_remote_user', False): + if cherrypy.serving.request.headers.get('X-Remote-User', '') == creds['username']: + logger.debug('X-Remote-User is {0}, trusting frontal'.format(creds['username'])) + auth = False + token = self.auth.mk_token(creds, auth=auth) if 'token' not in token: raise cherrypy.HTTPError(401, 'Could not authenticate using provided credentials')