-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Modularise eauth token storage and authentication
- Loading branch information
Kunal Bajpai
committed
Aug 3, 2017
1 parent
1b9cb26
commit b8e34e1
Showing
6 changed files
with
251 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# -*- coding: utf-8 -*- | ||
''' | ||
salt.tokens | ||
~~~~~~~~~~~~ | ||
This module implements all the token stores used by salt during eauth authentication. | ||
Each store must implement the following methods: | ||
:mk_token: function to mint a new unique token and store it | ||
:get_token: function to get data of a given token if it exists | ||
:rm_token: remove the given token from storage | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
''' | ||
Stores eauth tokens in the filesystem of the master. Location is configured by the master config option 'token_dir' | ||
''' | ||
|
||
from __future__ import absolute_import | ||
|
||
import hashlib | ||
import os | ||
import logging | ||
|
||
import salt.utils | ||
import salt.payload | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
__virtualname__ = 'localfs' | ||
|
||
|
||
def mk_token(opts, tdata): | ||
''' | ||
Mint a new token using the config option hash_type and store tdata with 'token' attribute set | ||
to the token. | ||
This module uses the hash of random 512 bytes as a token. | ||
:param opts: Salt master config options | ||
:param tdata: Token data to be stored with 'token' attirbute of this dict set to the token. | ||
:returns: tdata with token if successful. Empty dict if failed. | ||
''' | ||
hash_type = getattr(hashlib, opts.get('hash_type', 'md5')) | ||
tok = str(hash_type(os.urandom(512)).hexdigest()) | ||
t_path = os.path.join(opts['token_dir'], tok) | ||
while os.path.isfile(t_path): | ||
tok = str(hash_type(os.urandom(512)).hexdigest()) | ||
t_path = os.path.join(opts['token_dir'], tok) | ||
tdata['token'] = tok | ||
serial = salt.payload.Serial(opts) | ||
try: | ||
with salt.utils.files.set_umask(0o177): | ||
with salt.utils.files.fopen(t_path, 'w+b') as fp_: | ||
fp_.write(serial.dumps(tdata)) | ||
except (IOError, OSError): | ||
log.warning('Authentication failure: can not write token file "{0}".'.format(t_path)) | ||
return {} | ||
return tdata | ||
|
||
|
||
def get_token(opts, tok): | ||
''' | ||
Fetch the token data from the store. | ||
:param opts: Salt master config options | ||
:param tok: Token value to get | ||
:returns: Token data if successful. Empty dict if failed. | ||
''' | ||
t_path = os.path.join(opts['token_dir'], tok) | ||
t_path = os.path.join(opts['token_dir'], tok) | ||
if not os.path.isfile(t_path): | ||
return {} | ||
serial = salt.payload.Serial(opts) | ||
try: | ||
with salt.utils.files.fopen(t_path, 'rb') as fp_: | ||
tdata = serial.loads(fp_.read()) | ||
return tdata | ||
except (IOError, OSError): | ||
log.warning('Authentication failure: can not read token file "{0}".'.format(t_path)) | ||
return {} | ||
|
||
|
||
def rm_token(opts, tok): | ||
''' | ||
Remove token from the store. | ||
:param opts: Salt master config options | ||
:param tok: Token to remove | ||
:returns: Empty dict if successful. None if failed. | ||
''' | ||
try: | ||
os.remove(t_path) | ||
return {} | ||
except (IOError, OSError): | ||
log.warning('Could not remove token {0}'.format(tok)) | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
''' | ||
Provide token storage in Redis cluster. | ||
To get started simply start a redis cluster and assign all hashslots to the connected nodes. | ||
Add the redis hostname and port to master configs as eauth_redis_host and eauth_redis_port. | ||
Default values for these configs are as follow: | ||
.. code-block:: yaml | ||
eauth_redis_host: localhost | ||
eauth_redis_port: 6379 | ||
:depends: - redis-py-cluster Python package | ||
''' | ||
|
||
from __future__ import absolute_import | ||
|
||
|
||
try: | ||
import rediscluster | ||
HAS_REDIS = True | ||
except ImportError: | ||
HAS_REDIS = False | ||
|
||
|
||
import os | ||
import logging | ||
import hashlib | ||
|
||
import salt.payload | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
__virtualname__ = 'rediscluster' | ||
|
||
|
||
def __virtual__(): | ||
if not HAS_REDIS: | ||
return False, 'Could not use redis for tokens; '\ | ||
'rediscluster python client is not installed.' | ||
return __virtualname__ | ||
|
||
|
||
def _redis_client(opts): | ||
''' | ||
Connect to the redis host and return a StrictRedisCluster client object. | ||
If connection fails then return None. | ||
''' | ||
redis_host = opts.get("eauth_redis_host", "localhost") | ||
redis_port = opts.get("eauth_redis_port", 6379) | ||
try: | ||
return rediscluster.StrictRedisCluster(host=redis_host, port=redis_port) | ||
except rediscluster.exceptions.RedisClusterException as err: | ||
log.warning("Failed to connect to redis at {0}:{1} - {2}".format(redis_host, redis_port, err)) | ||
return None | ||
|
||
|
||
def mk_token(opts, tdata): | ||
''' | ||
Mint a new token using the config option hash_type and store tdata with 'token' attribute set | ||
to the token. | ||
This module uses the hash of random 512 bytes as a token. | ||
:param opts: Salt master config options | ||
:param tdata: Token data to be stored with 'token' attirbute of this dict set to the token. | ||
:returns: tdata with token if successful. Empty dict if failed. | ||
''' | ||
redis_client = _redis_client(opts) | ||
if not redis_client: | ||
return {} | ||
hash_type = getattr(hashlib, opts.get('hash_type', 'md5')) | ||
tok = str(hash_type(os.urandom(512)).hexdigest()) | ||
try: | ||
while redis_client.get(tok) is not None: | ||
tok = str(hash_type(os.urandom(512)).hexdigest()) | ||
except Exception as err: | ||
log.warning("Authentication failure: cannot get token {0} from redis: {1}".format(tok, err)) | ||
return {} | ||
tdata['token'] = tok | ||
serial = salt.payload.Serial(opts) | ||
try: | ||
redis_client.set(tok, serial.dumps(tdata)) | ||
except Exception as err: | ||
log.warning("Authentication failure: cannot save token {0} to redis: {1}".format(tok, err)) | ||
return {} | ||
return tdata | ||
|
||
|
||
def get_token(opts, tok): | ||
''' | ||
Fetch the token data from the store. | ||
:param opts: Salt master config options | ||
:param tok: Token value to get | ||
:returns: Token data if successful. Empty dict if failed. | ||
''' | ||
redis_client = _redis_client(opts) | ||
if not redis_client: | ||
return {} | ||
serial = salt.payload.Serial(opts) | ||
try: | ||
tdata = serial.loads(redis_client.get(tok)) | ||
return tdata | ||
except Exception as err: | ||
log.warning("Authentication failure: cannot get token {0} from redis: {1}".format(tok, err)) | ||
return {} | ||
|
||
|
||
def rm_token(opts, tok): | ||
''' | ||
Remove token from the store. | ||
:param opts: Salt master config options | ||
:param tok: Token to remove | ||
:returns: Empty dict if successful. None if failed. | ||
''' | ||
redis_client = _redis_client(opts) | ||
if not redis_client: | ||
return | ||
try: | ||
redis_client.delete(tok) | ||
return {} | ||
except Exception as err: | ||
log.warning("Could not remove token {0}: {1}".format(tok, err)) |