ArielGlenn has submitted this change and it was merged. Change subject: 2014.7.5 jessie, backport patches for singleton SAuth class ......................................................................
2014.7.5 jessie, backport patches for singleton SAuth class This permits the minion to cache the its authentication information instead of needing to reauthenticate on each command received. Authentication is quite expensive, using public keys and all. See the patch header for the list of specific upstream commits. The 2015.8 salt series has these patches already. Change-Id: I7f3b1ef1232da4059a499e958c613d9dd58d00b8 --- M debian/patches/series A debian/patches/singleton_sauth_WMF.patch 2 files changed, 240 insertions(+), 0 deletions(-) diff --git a/debian/patches/series b/debian/patches/series index 469243b..2f6a5f9 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -7,3 +7,4 @@ batch_returns_bad_dict_WMF.patch event_reading_returns_prematurely_WMF.patch ping_minions_without_data_cache_WMF.patch +singleton_sauth_WMF.patch diff --git a/debian/patches/singleton_sauth_WMF.patch b/debian/patches/singleton_sauth_WMF.patch new file mode 100644 index 0000000..886a15f --- /dev/null +++ b/debian/patches/singleton_sauth_WMF.patch @@ -0,0 +1,239 @@ +Description: Backport of patches for singleton SAuth class + This is a combination of three upstream commits: + 20fcfbc6b3f0dcdd9a09ca520a18e65dd8b0a950 + Refactor SAuth() to return singletons for each master/minion pair. + 18bc50e6dcce5410c3d2c6a549e043ee59891ee9 + Rework to expose __authenticate() + a1dd24b5ea7e09fb3198ea2a83a71de167e52d92 + Remove clear() method +Author: Ariel T. Glenn <[email protected]> +Forwarded: no +--- a/salt/minion.py ++++ b/salt/minion.py +@@ -883,19 +883,7 @@ + try: + data = self.crypticle.loads(load) + except AuthenticationError: +- # decryption of the payload failed, try to re-auth but wait +- # random seconds if set in config with random_reauth_delay +- if 'random_reauth_delay' in self.opts: +- reauth_delay = randint(0, float(self.opts['random_reauth_delay'])) +- # This mitigates the issue wherein a long-running job might not return +- # on a master key rotation. However, new commands issued during the re-auth +- # splay period will still fail to return. +- if not salt.utils.minion.running(self.opts): +- log.debug('Waiting {0} seconds to re-authenticate'.format(reauth_delay)) +- time.sleep(reauth_delay) +- else: +- log.warning('Ignoring re-auth delay because jobs are running') +- ++ # decryption of the payload failed, try to re-auth + self.authenticate() + data = self.crypticle.loads(load) + +@@ -1367,7 +1355,8 @@ + self.opts['master_ip'] + ) + ) +- auth = salt.crypt.Auth(self.opts) ++ auth = salt.crypt.SAuth(self.opts) ++ auth.authenticate() + self.tok = auth.gen_token('salt') + acceptance_wait_time = self.opts['acceptance_wait_time'] + acceptance_wait_time_max = self.opts['acceptance_wait_time_max'] +--- a/salt/transport/__init__.py ++++ b/salt/transport/__init__.py +@@ -229,15 +229,13 @@ + self.crypt = kwargs.get('crypt', 'aes') + + self.serial = salt.payload.Serial(opts) +- if self.crypt != 'clear': +- if 'auth' in kwargs: +- self.auth = kwargs['auth'] +- else: +- self.auth = salt.crypt.SAuth(opts) + if 'master_uri' in kwargs: + self.master_uri = kwargs['master_uri'] + else: + self.master_uri = opts['master_uri'] ++ if self.crypt != 'clear': ++ # we don't need to worry about auth as a kwarg, since its a singleton ++ self.auth = salt.crypt.SAuth(self.opts) + + def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60): + ret = self.sreq.send('aes', self.auth.crypticle.dumps(load), tries, timeout) +@@ -269,7 +267,7 @@ + try: + return _do_transfer() + except salt.crypt.AuthenticationError: +- self.auth = salt.crypt.SAuth(self.opts) ++ self.auth.authenticate() + return _do_transfer() + + def _uncrypted_transfer(self, load, tries=3, timeout=60): +--- a/salt/crypt.py ++++ b/salt/crypt.py +@@ -286,12 +286,36 @@ + return self.pub_signature + + +-class Auth(object): ++class SAuth(object): + ''' +- The Auth class provides the sequence for setting up communication with +- the master server from a minion. ++ Set up an object to maintain authentication with the salt master + ''' ++ # This class is only a singleton per minion/master pair ++ instances = {} ++ ++ def __new__(cls, opts): ++ ''' ++ Only create one instance of SAuth per __key() ++ ''' ++ key = cls.__key(opts) ++ if key not in SAuth.instances: ++ SAuth.instances[key] = object.__new__(cls) ++ SAuth.instances[key].__singleton_init__(opts) ++ return SAuth.instances[key] ++ ++ @classmethod ++ def __key(cls, opts): ++ return (opts['pki_dir'], # where the keys are stored ++ opts['id'], # minion ID ++ opts['master_uri'], # master ID ++ ) ++ ++ # has to remain empty for singletons, since __init__ will *always* be called + def __init__(self, opts): ++ pass ++ ++ # an init for the singleton instance to call ++ def __singleton_init__(self, opts): + ''' + Init an Auth instance + +@@ -313,6 +337,41 @@ + if not os.path.isfile(self.pub_path): + self.get_keys() + ++ self.crypticle = self.authenticate() ++ ++ def authenticate(self): ++ ''' ++ Authenticate with the master, this method breaks the functional ++ paradigm, it will update the master information from a fresh sign ++ in, signing in can occur as often as needed to keep up with the ++ revolving master AES key. ++ ++ :rtype: Crypticle ++ :returns: A crypticle used for encryption operations ++ ''' ++ acceptance_wait_time = self.opts['acceptance_wait_time'] ++ acceptance_wait_time_max = self.opts['acceptance_wait_time_max'] ++ if not acceptance_wait_time_max: ++ acceptance_wait_time_max = acceptance_wait_time ++ ++ while True: ++ creds = self.sign_in() ++ if creds == 'retry': ++ if self.opts.get('caller'): ++ print('Minion failed to authenticate with the master, ' ++ 'has the minion key been accepted?') ++ sys.exit(2) ++ if acceptance_wait_time: ++ log.info('Waiting {0} seconds before retry.'.format(acceptance_wait_time)) ++ time.sleep(acceptance_wait_time) ++ if acceptance_wait_time < acceptance_wait_time_max: ++ acceptance_wait_time += acceptance_wait_time ++ log.debug('Authentication wait time is {0}'.format(acceptance_wait_time)) ++ continue ++ break ++ self.creds = creds ++ return Crypticle(self.opts, creds['aes']) ++ + def get_keys(self): + ''' + Return keypair object for the minion. +@@ -630,8 +689,21 @@ + + ''' + auth = {} ++ ++ auth_timeout = self.opts.get('auth_timeout', None) ++ if auth_timeout is not None: ++ timeout = auth_timeout ++ auth_safemode = self.opts.get('auth_safemode', None) ++ if auth_safemode is not None: ++ safe = auth_safemode ++ auth_tries = self.opts.get('auth_tries', None) ++ if auth_tries is not None: ++ tries = auth_tries ++ + m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub) + ++ auth['master_uri'] = self.opts['master_uri'] ++ + sreq = salt.payload.SREQ( + self.opts['master_uri'], + opts=self.opts +@@ -733,7 +805,8 @@ + SIG_SIZE = hashlib.sha256().digest_size + + def __init__(self, opts, key_string, key_size=192): +- self.keys = self.extract_keys(key_string, key_size) ++ self.key_string = key_string ++ self.keys = self.extract_keys(self.key_string, key_size) + self.key_size = key_size + self.serial = salt.payload.Serial(opts) + +@@ -799,49 +872,3 @@ + if not data.startswith(self.PICKLE_PAD): + return {} + return self.serial.loads(data[len(self.PICKLE_PAD):]) +- +- +-class SAuth(Auth): +- ''' +- Set up an object to maintain the standalone authentication session +- with the salt master +- ''' +- def __init__(self, opts): +- super(SAuth, self).__init__(opts) +- self.crypticle = self.__authenticate() +- +- def __authenticate(self): +- ''' +- Authenticate with the master, this method breaks the functional +- paradigm, it will update the master information from a fresh sign +- in, signing in can occur as often as needed to keep up with the +- revolving master AES key. +- +- :rtype: Crypticle +- :returns: A crypticle used for encryption operations +- ''' +- acceptance_wait_time = self.opts['acceptance_wait_time'] +- acceptance_wait_time_max = self.opts['acceptance_wait_time_max'] +- if not acceptance_wait_time_max: +- acceptance_wait_time_max = acceptance_wait_time +- +- while True: +- creds = self.sign_in( +- self.opts.get('auth_timeout', 60), +- self.opts.get('auth_safemode', self.opts.get('_safe_auth', True)), +- self.opts.get('auth_tries', 1) +- ) +- if creds == 'retry': +- if self.opts.get('caller'): +- print('Minion failed to authenticate with the master, ' +- 'has the minion key been accepted?') +- sys.exit(2) +- if acceptance_wait_time: +- log.info('Waiting {0} seconds before retry.'.format(acceptance_wait_time)) +- time.sleep(acceptance_wait_time) +- if acceptance_wait_time < acceptance_wait_time_max: +- acceptance_wait_time += acceptance_wait_time +- log.debug('Authentication wait time is {0}'.format(acceptance_wait_time)) +- continue +- break +- return Crypticle(self.opts, creds['aes']) -- To view, visit https://gerrit.wikimedia.org/r/259674 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I7f3b1ef1232da4059a499e958c613d9dd58d00b8 Gerrit-PatchSet: 2 Gerrit-Project: operations/debs/salt Gerrit-Branch: jessie Gerrit-Owner: ArielGlenn <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
