class SaltKeep(Keep): ''' RAET protocol estate on road data persistence for a given estate road specific data road/ keep/ stackname/ local/ estate.ext remote/ estate.name.ext estate.name.ext ''' LocalFields = [ 'name', 'uid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'puid', 'aha', 'role', 'sighex', 'prihex' ] LocalDumpFields = [ 'name', 'uid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'puid', 'aha', 'role' ] RemoteFields = [ 'name', 'uid', 'fuid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'main', 'kind', 'joined', 'role', 'acceptance', 'verhex', 'pubhex' ] RemoteDumpFields = [ 'name', 'uid', 'fuid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'main', 'kind', 'joined', 'role' ] Auto = raeting.AutoMode.never.value #auto accept def __init__(self, opts, prefix='estate', basedirpath='', auto=None, **kwa): ''' Setup RoadKeep instance ''' basedirpath = basedirpath or os.path.join(opts['cache_dir'], 'raet') super(SaltKeep, self).__init__(prefix=prefix, basedirpath=basedirpath, **kwa) self.auto = (auto if auto is not None else (raeting.AutoMode.always.value if opts['open_mode'] else (raeting.AutoMode.once.value if opts['auto_accept'] else raeting.AutoMode.never.value))) self.saltRaetKey = RaetKey(opts) def clearAllDir(self): ''' Clear all keep directories ''' super(SaltKeep, self).clearAllDir() self.clearRoleDir() def clearRoleDir(self): ''' Clear the Role directory ''' self.saltRaetKey.delete_pki_dir() def loadLocalRoleData(self): ''' Load and return the role data ''' keydata = self.saltRaetKey.read_local() if not keydata: keydata = odict([('sign', None), ('priv', None)]) data = odict([('sighex', keydata['sign']), ('prihex', keydata['priv'])]) return data def clearLocalRoleData(self): ''' Clear the local file ''' self.saltRaetKey.delete_local() def clearLocalRoleDir(self): ''' Clear the Local Role directory ''' self.saltRaetKey.delete_pki_dir() def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = super(SaltKeep, self).loadLocalData() if not data: return None roleData = self.loadLocalRoleData( ) # if not present defaults None values data.update([('sighex', roleData.get('sighex')), ('prihex', roleData.get('prihex'))]) return data def loadRemoteData(self, name): ''' Load and Return the data from the remote file ''' data = super(SaltKeep, self).loadRemoteData(name) if not data: return None mid = data['role'] for status in [acceptance.name for acceptance in Acceptance]: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: break if not keydata: data.update([('acceptance', None), ('verhex', None), ('pubhex', None)]) else: data.update(acceptance=raeting.Acceptance[status].value, verhex=keydata['verify'], pubhex=keydata['pub']) return data def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' keeps = super(SaltKeep, self).loadAllRemoteData() for name, data in keeps.items(): keeps[name].update([('acceptance', None), ('verhex', None), ('pubhex', None)]) for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: for name, data in keeps.items(): if data['role'] == mid: keeps[name].update([ ('acceptance', raeting.Acceptance[status].value), ('verhex', keydata['verify']), ('pubhex', keydata['pub']) ]) return keeps def clearRemoteRoleData(self, role): ''' Clear data from the role data file ''' self.saltRaetKey.delete_key(role) #now delete role key file def clearAllRemoteRoleData(self): ''' Remove all the role data files ''' self.saltRaetKey.delete_all() def clearRemoteRoleDir(self): ''' Clear the Remote Role directory ''' self.saltRaetKey.delete_pki_dir() def dumpLocal(self, local): ''' Dump local estate ''' data = odict([ ('name', local.name), ('uid', local.uid), ('ha', local.ha), ('iha', local.iha), ('natted', local.natted), ('fqdn', local.fqdn), ('dyned', local.dyned), ('sid', local.sid), ('puid', local.stack.puid), ('aha', local.stack.aha), ('role', local.role), ]) if self.verifyLocalData(data, localFields=self.LocalDumpFields): self.dumpLocalData(data) self.saltRaetKey.write_local(local.priver.keyhex, local.signer.keyhex) def dumpRemote(self, remote): ''' Dump remote estate ''' data = odict([ ('name', remote.name), ('uid', remote.uid), ('fuid', remote.fuid), ('ha', remote.ha), ('iha', remote.iha), ('natted', remote.natted), ('fqdn', remote.fqdn), ('dyned', remote.dyned), ('sid', remote.sid), ('main', remote.main), ('kind', remote.kind), ('joined', remote.joined), ('role', remote.role), ]) if self.verifyRemoteData(data, remoteFields=self.RemoteDumpFields): self.dumpRemoteData(data, remote.name) if remote.pubber.keyhex and remote.verfer.keyhex: # kludge to persist the keys since no way to write self.saltRaetKey.status(remote.role, remote.pubber.keyhex, remote.verfer.keyhex) def statusRemote(self, remote, dump=True): ''' Calls .statusRole on remote role and keys and updates remote.acceptance dump indicates if statusRole should update persisted values when appropriate. Returns status Where status is acceptance status of role and keys and has value from raeting.acceptances ''' status = self.statusRole(role=remote.role, verhex=remote.verfer.keyhex, pubhex=remote.pubber.keyhex, dump=dump) remote.acceptance = status return status def statusRole(self, role, verhex, pubhex, dump=True): ''' Returns status Where status is acceptance status of role and keys and has value from raeting.acceptances ''' status = raeting.Acceptance[self.saltRaetKey.status( role, pubhex, verhex)].value return status def rejectRemote(self, remote): ''' Set acceptance status to rejected ''' mid = remote.role self.saltRaetKey.reject(match=mid, include_accepted=True) remote.acceptance = raeting.Acceptance.rejected.value def pendRemote(self, remote): ''' Set acceptance status to pending ''' pass def acceptRemote(self, remote): ''' Set acceptance status to accepted ''' mid = remote.role self.saltRaetKey.accept(match=mid, include_rejected=True) remote.acceptance = raeting.Acceptance.accepted.value
class BasicTestCase(unittest.TestCase): """""" def setUp(self): self.store = storing.Store(stamp=0.0) self.timer = StoreTimer(store=self.store, duration=1.0) self.saltDirpath = tempfile.mkdtemp(prefix="salt", suffix="main", dir='/tmp') pkiDirpath = os.path.join(self.saltDirpath, 'pki') if not os.path.exists(pkiDirpath): os.makedirs(pkiDirpath) acceptedDirpath = os.path.join(pkiDirpath, 'accepted') if not os.path.exists(acceptedDirpath): os.makedirs(acceptedDirpath) pendingDirpath = os.path.join(pkiDirpath, 'pending') if not os.path.exists(pendingDirpath): os.makedirs(pendingDirpath) rejectedDirpath = os.path.join(pkiDirpath, 'rejected') if not os.path.exists(rejectedDirpath): os.makedirs(rejectedDirpath) self.localFilepath = os.path.join(pkiDirpath, 'local.key') if os.path.exists(self.localFilepath): mode = os.stat(self.localFilepath).st_mode os.chmod(self.localFilepath, mode | stat.S_IWUSR | stat.S_IWUSR) self.cacheDirpath = os.path.join(self.saltDirpath, 'cache') self.sockDirpath = os.path.join(self.saltDirpath, 'sock') self.opts = dict( __role='master', id='master', pki_dir=pkiDirpath, sock_dir=self.sockDirpath, cachedir=self.cacheDirpath, open_mode=False, auto_accept=True, transport='raet', ) self.mainKeeper = RaetKey(opts=self.opts) self.baseDirpath = tempfile.mkdtemp(prefix="salt", suffix="base", dir='/tmp') def tearDown(self): if os.path.exists(self.saltDirpath): shutil.rmtree(self.saltDirpath) def createRoadData(self, name, base): ''' Creates odict and populates with data to setup road stack { name: stack name local estate name dirpath: dirpath for keep files sighex: signing key verhex: verify key prihex: private key pubhex: public key } ''' data = odict() data['name'] = name data['dirpath'] = os.path.join(base, 'road', 'keep', name) signer = nacling.Signer() data['sighex'] = signer.keyhex data['verhex'] = signer.verhex privateer = nacling.Privateer() data['prihex'] = privateer.keyhex data['pubhex'] = privateer.pubhex return data def testAutoAccept(self): ''' Basic function of RaetKey in auto accept mode ''' console.terse("{0}\n".format(self.testAutoAccept.__doc__)) self.opts['auto_accept'] = True self.assertTrue(self.opts['auto_accept']) self.assertDictEqual(self.mainKeeper.all_keys(), {'accepted': [], 'local': [], 'rejected': [], 'pending': []}) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {}) main = self.createRoadData(name='main', base=self.baseDirpath) self.mainKeeper.write_local(main['prihex'], main['sighex']) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {'priv': main['prihex'], 'sign': main['sighex']}) allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': [], 'local': [self.localFilepath], 'rejected': [], 'pending': []}) other1 = self.createRoadData(name='other1', base=self.baseDirpath) other2 = self.createRoadData(name='other2', base=self.baseDirpath) status = self.mainKeeper.status(other1['name'], other1['pubhex'], other1['verhex']) self.assertEqual(status, 'accepted') status = self.mainKeeper.status(other2['name'], other2['pubhex'], other2['verhex']) self.assertEqual(status, 'accepted') allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': ['other1', 'other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': []} ) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other1', 'pub': other1['pubhex'], 'verify': other1['verhex']} ) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex']} ) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, {'accepted': ['other1', 'other2'], 'rejected': [], 'pending': []}) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual(allremotekeys, {'other1': {'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'accepted', 'pub': other1['pubhex'],}, 'other2': {'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'],} }) def testManualAccept(self): ''' Basic function of RaetKey in non auto accept mode ''' console.terse("{0}\n".format(self.testAutoAccept.__doc__)) self.opts['auto_accept'] = False self.assertFalse(self.opts['auto_accept']) self.assertDictEqual(self.mainKeeper.all_keys(), {'accepted': [], 'local': [], 'rejected': [], 'pending': []}) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {}) main = self.createRoadData(name='main', base=self.baseDirpath) self.mainKeeper.write_local(main['prihex'], main['sighex']) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {'priv': main['prihex'], 'sign': main['sighex']}) allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': [], 'local': [self.localFilepath], 'rejected': [], 'pending': []}) other1 = self.createRoadData(name='other1', base=self.baseDirpath) other2 = self.createRoadData(name='other2', base=self.baseDirpath) status = self.mainKeeper.status(other1['name'], other1['pubhex'], other1['verhex']) self.assertEqual(status, 'pending') status = self.mainKeeper.status(other2['name'], other2['pubhex'], other2['verhex']) self.assertEqual(status, 'pending') allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': [], 'local': [self.localFilepath], 'pending': ['other1', 'other2'], 'rejected': []} ) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, {}) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual(remotekeys, {}) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, {'accepted': [], 'rejected': [], 'pending': ['other1', 'other2']}) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual(allremotekeys, {'other1': {'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'pending', 'pub': other1['pubhex'],}, 'other2': {'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'pending', 'pub': other2['pubhex'],} }) self.mainKeeper.accept_all() allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': ['other1', 'other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': []} ) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other1', 'pub': other1['pubhex'], 'verify': other1['verhex']} ) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex']} ) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, {'accepted': ['other1', 'other2'], 'rejected': [], 'pending': []}) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual(allremotekeys, {'other1': {'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'accepted', 'pub': other1['pubhex'],}, 'other2': {'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'],} }) def testDelete(self): ''' Basic function of RaetKey to delete key ''' console.terse("{0}\n".format(self.testDelete.__doc__)) self.opts['auto_accept'] = True self.assertTrue(self.opts['auto_accept']) self.assertDictEqual(self.mainKeeper.all_keys(), {'accepted': [], 'local': [], 'rejected': [], 'pending': []}) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {}) main = self.createRoadData(name='main', base=self.baseDirpath) self.mainKeeper.write_local(main['prihex'], main['sighex']) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {'priv': main['prihex'], 'sign': main['sighex']}) allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': [], 'local': [self.localFilepath], 'rejected': [], 'pending': []}) other1 = self.createRoadData(name='other1', base=self.baseDirpath) other2 = self.createRoadData(name='other2', base=self.baseDirpath) status = self.mainKeeper.status(other1['name'], other1['pubhex'], other1['verhex']) self.assertEqual(status, 'accepted') status = self.mainKeeper.status(other2['name'], other2['pubhex'], other2['verhex']) self.assertEqual(status, 'accepted') allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': ['other1', 'other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': []} ) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other1', 'pub': other1['pubhex'], 'verify': other1['verhex']} ) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex']} ) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, {'accepted': ['other1', 'other2'], 'rejected': [], 'pending': []}) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual(allremotekeys, {'other1': {'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'accepted', 'pub': other1['pubhex']}, 'other2': {'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'],} }) self.mainKeeper.delete_key(match=other1['name']) allkeys = self.mainKeeper.all_keys() self.assertDictEqual(allkeys, {'accepted': ['other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': []} ) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, {} ) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual(remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex']} ) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, {'accepted': [ 'other2'], 'rejected': [], 'pending': []}) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual(allremotekeys, { 'other2': {'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'],} })
class SaltSafe(object): ''' Interface between Salt Key management and RAET keep key management ''' Auto = False #auto accept LocalFields = ['sighex', 'prihex'] RemoteFields = ['eid', 'name', 'acceptance', 'verhex', 'pubhex'] def __init__(self, opts=None, **kwa): ''' Setup SaltSafe instance ''' if opts is None: opts = {} self.saltRaetKey = RaetKey(opts) def verifyLocalData(self, data): ''' Returns True if the fields in .LocalFields match the fields in data ''' return (set(self.LocalFields) == set(data.keys())) def dumpLocalData(self, data): ''' Dump the key data from the local estate ''' self.saltRaetKey.write_local(data['prihex'], data['sighex']) def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = self.saltRaetKey.read_local() if not data: return None return (odict(sighex=data['sign'], prihex=data['priv'])) def clearLocalData(self): ''' Load and Return the data from the local estate ''' pass def verifyRemoteData(self, data): ''' Returns True if the fields in .RemoteFields match the fields in data ''' return (set(self.RemoteFields) == set(data.keys())) def dumpRemoteData(self, data, uid): ''' Dump the data from the remote estate given by uid ''' self.saltRaetKey.status(data['name'], data['eid'], data['pubhex'], data['verhex']) def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' data = odict() for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: rdata = odict() rdata['eid'] = keydata['device_id'] rdata['name'] = keydata['minion_id'] rdata['acceptance'] = raeting.ACCEPTANCES[status] rdata['verhex'] = keydata['verify'] rdata['pubhex'] = keydata['pub'] data[rdata['eid']] = rdata return data def clearAllRemoteData(self): ''' Remove all the remote estate files ''' self.saltRaetKey.delete_all() def dumpLocal(self, local): ''' Dump the key data from the local estate ''' data = odict([ ('sighex', local.signer.keyhex), ('prihex', local.priver.keyhex), ]) if self.verifyLocalData(data): self.dumpLocalData(data) def dumpRemote(self, remote): ''' Dump the data from the remote estate by calling status on it which will persist the data ''' data = odict([ ('eid', remote.eid), ('name', remote.name), ('acceptance', remote.acceptance), ('verhex', remote.verfer.keyhex), ('pubhex', remote.pubber.keyhex), ]) if self.verifyRemoteData(data): self.dumpRemoteData(data, remote.uid) def loadRemote(self, remote): ''' Load and Return the data from the remote estate file Override this in sub class to change uid ''' status = 'accepted' mid = remote.name keydata = self.saltRaetKey.read_remote(mid, status) if not keydata: return None data = odict() data['eid'] = keydata['device_id'] data['name'] = keydata['minion_id'] data['acceptance'] = raeting.ACCEPTANCES[status] data['verhex'] = keydata['verify'] data['pubhex'] = keydata['pub'] return data def clearRemote(self, remote): ''' Clear the remote estate file Override this in sub class to change uid ''' mid = remote.eid self.saltRaetKey.delete_key(mid) def statusRemote(self, remote, verhex, pubhex, main=True): ''' Evaluate acceptance status of remote estate per its keys persist key data differentially based on status ''' status = raeting.ACCEPTANCES[self.saltRaetKey.status( remote.name, remote.eid, pubhex, verhex)] if status != raeting.acceptances.rejected: if (verhex and verhex != remote.verfer.keyhex): remote.verfer = nacling.Verifier(verhex) if (pubhex and pubhex != remote.pubber.keyhex): remote.pubber = nacling.Publican(pubhex) remote.acceptance = status return status def rejectRemote(self, remote): ''' Set acceptance status to rejected ''' remote.acceptance = raeting.acceptances.rejected mid = remote.name self.saltRaetKey.reject(match=mid, include_accepted=True) def pendRemote(self, remote): ''' Set acceptance status to pending ''' pass def acceptRemote(self, remote): ''' Set acceptance status to accepted ''' remote.acceptance = raeting.acceptances.accepted mid = remote.name self.saltRaetKey.accept(match=mid, include_rejected=True)
class SaltKeep(Keep): ''' RAET protocol estate on road data persistence for a given estate road specific data road/ keep/ stackname/ local/ estate.ext remote/ estate.name.ext estate.name.ext ''' LocalFields = ['name', 'uid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'puid', 'aha', 'role', 'sighex','prihex'] LocalDumpFields = ['name', 'uid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'puid', 'aha', 'role'] RemoteFields = ['name', 'uid', 'fuid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'main', 'kind', 'joined', 'role', 'acceptance', 'verhex', 'pubhex'] RemoteDumpFields = ['name', 'uid', 'fuid', 'ha', 'iha', 'natted', 'fqdn', 'dyned', 'sid', 'main', 'kind', 'joined', 'role'] Auto = raeting.AutoMode.never.value #auto accept def __init__(self, opts, prefix='estate', basedirpath='', auto=None, **kwa): ''' Setup RoadKeep instance ''' basedirpath = basedirpath or os.path.join(opts['cache_dir'], 'raet') super(SaltKeep, self).__init__(prefix=prefix, basedirpath=basedirpath, **kwa) self.auto = (auto if auto is not None else (raeting.AutoMode.always.value if opts['open_mode'] else (raeting.AutoMode.once.value if opts['auto_accept'] else raeting.AutoMode.never.value))) self.saltRaetKey = RaetKey(opts) def clearAllDir(self): ''' Clear all keep directories ''' super(SaltKeep, self).clearAllDir() self.clearRoleDir() def clearRoleDir(self): ''' Clear the Role directory ''' self.saltRaetKey.delete_pki_dir() def loadLocalRoleData(self): ''' Load and return the role data ''' keydata = self.saltRaetKey.read_local() if not keydata: keydata = odict([('sign', None), ('priv', None)]) data = odict([('sighex', keydata['sign']), ('prihex', keydata['priv'])]) return data def clearLocalRoleData(self): ''' Clear the local file ''' self.saltRaetKey.delete_local() def clearLocalRoleDir(self): ''' Clear the Local Role directory ''' self.saltRaetKey.delete_pki_dir() def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = super(SaltKeep, self).loadLocalData() if not data: return None roleData = self.loadLocalRoleData() # if not present defaults None values data.update([('sighex', roleData.get('sighex')), ('prihex', roleData.get('prihex'))]) return data def loadRemoteData(self, name): ''' Load and Return the data from the remote file ''' data = super(SaltKeep, self).loadRemoteData(name) if not data: return None mid = data['role'] for status in [acceptance.name for acceptance in Acceptance]: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: break if not keydata: data.update([('acceptance', None), ('verhex', None), ('pubhex', None)]) else: data.update(acceptance=raeting.Acceptance[status].value, verhex=keydata['verify'], pubhex=keydata['pub']) return data def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' keeps = super(SaltKeep, self).loadAllRemoteData() for name, data in keeps.items(): keeps[name].update([('acceptance', None), ('verhex', None), ('pubhex', None)]) for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: for name, data in keeps.items(): if data['role'] == mid: keeps[name].update( [('acceptance', raeting.Acceptance[status].value), ('verhex', keydata['verify']), ('pubhex', keydata['pub'])]) return keeps def clearRemoteRoleData(self, role): ''' Clear data from the role data file ''' self.saltRaetKey.delete_key(role) #now delete role key file def clearAllRemoteRoleData(self): ''' Remove all the role data files ''' self.saltRaetKey.delete_all() def clearRemoteRoleDir(self): ''' Clear the Remote Role directory ''' self.saltRaetKey.delete_pki_dir() def dumpLocal(self, local): ''' Dump local estate ''' data = odict([ ('name', local.name), ('uid', local.uid), ('ha', local.ha), ('iha', local.iha), ('natted', local.natted), ('fqdn', local.fqdn), ('dyned', local.dyned), ('sid', local.sid), ('puid', local.stack.puid), ('aha', local.stack.aha), ('role', local.role), ]) if self.verifyLocalData(data, localFields =self.LocalDumpFields): self.dumpLocalData(data) self.saltRaetKey.write_local(local.priver.keyhex, local.signer.keyhex) def dumpRemote(self, remote): ''' Dump remote estate ''' data = odict([ ('name', remote.name), ('uid', remote.uid), ('fuid', remote.fuid), ('ha', remote.ha), ('iha', remote.iha), ('natted', remote.natted), ('fqdn', remote.fqdn), ('dyned', remote.dyned), ('sid', remote.sid), ('main', remote.main), ('kind', remote.kind), ('joined', remote.joined), ('role', remote.role), ]) if self.verifyRemoteData(data, remoteFields=self.RemoteDumpFields): self.dumpRemoteData(data, remote.name) if remote.pubber.keyhex and remote.verfer.keyhex: # kludge to persist the keys since no way to write self.saltRaetKey.status(remote.role, remote.pubber.keyhex, remote.verfer.keyhex) def statusRemote(self, remote, dump=True): ''' Calls .statusRole on remote role and keys and updates remote.acceptance dump indicates if statusRole should update persisted values when appropriate. Returns status Where status is acceptance status of role and keys and has value from raeting.acceptances ''' status = self.statusRole(role=remote.role, verhex=remote.verfer.keyhex, pubhex=remote.pubber.keyhex, dump=dump) remote.acceptance = status return status def statusRole(self, role, verhex, pubhex, dump=True): ''' Returns status Where status is acceptance status of role and keys and has value from raeting.acceptances ''' status = raeting.Acceptance[self.saltRaetKey.status(role, pubhex, verhex)].value return status def rejectRemote(self, remote): ''' Set acceptance status to rejected ''' mid = remote.role self.saltRaetKey.reject(match=mid, include_accepted=True) remote.acceptance = raeting.Acceptance.rejected.value def pendRemote(self, remote): ''' Set acceptance status to pending ''' pass def acceptRemote(self, remote): ''' Set acceptance status to accepted ''' mid = remote.role self.saltRaetKey.accept(match=mid, include_rejected=True) remote.acceptance = raeting.Acceptance.accepted.value
class BasicTestCase(unittest.TestCase): """""" def setUp(self): self.store = storing.Store(stamp=0.0) self.timer = StoreTimer(store=self.store, duration=1.0) self.saltDirpath = tempfile.mkdtemp(prefix="salt", suffix="main", dir='/tmp') pkiDirpath = os.path.join(self.saltDirpath, 'pki') if not os.path.exists(pkiDirpath): os.makedirs(pkiDirpath) acceptedDirpath = os.path.join(pkiDirpath, 'accepted') if not os.path.exists(acceptedDirpath): os.makedirs(acceptedDirpath) pendingDirpath = os.path.join(pkiDirpath, 'pending') if not os.path.exists(pendingDirpath): os.makedirs(pendingDirpath) rejectedDirpath = os.path.join(pkiDirpath, 'rejected') if not os.path.exists(rejectedDirpath): os.makedirs(rejectedDirpath) self.localFilepath = os.path.join(pkiDirpath, 'local.key') if os.path.exists(self.localFilepath): mode = os.stat(self.localFilepath).st_mode os.chmod(self.localFilepath, mode | stat.S_IWUSR | stat.S_IWUSR) self.cacheDirpath = os.path.join(self.saltDirpath, 'cache') self.sockDirpath = os.path.join(self.saltDirpath, 'sock') self.opts = dict( __role='master', id='master', pki_dir=pkiDirpath, sock_dir=self.sockDirpath, cachedir=self.cacheDirpath, open_mode=False, auto_accept=True, transport='raet', ) self.mainKeeper = RaetKey(opts=self.opts) self.baseDirpath = tempfile.mkdtemp(prefix="salt", suffix="base", dir='/tmp') def tearDown(self): if os.path.exists(self.saltDirpath): shutil.rmtree(self.saltDirpath) def createRoadData(self, name, base): ''' Creates odict and populates with data to setup road stack { name: stack name local estate name dirpath: dirpath for keep files sighex: signing key verhex: verify key prihex: private key pubhex: public key } ''' data = odict() data['name'] = name data['dirpath'] = os.path.join(base, 'road', 'keep', name) signer = nacling.Signer() data['sighex'] = signer.keyhex data['verhex'] = signer.verhex privateer = nacling.Privateer() data['prihex'] = privateer.keyhex data['pubhex'] = privateer.pubhex return data def testAutoAccept(self): ''' Basic function of RaetKey in auto accept mode ''' console.terse("{0}\n".format(self.testAutoAccept.__doc__)) self.opts['auto_accept'] = True self.assertTrue(self.opts['auto_accept']) self.assertDictEqual(self.mainKeeper.all_keys(), { 'accepted': [], 'local': [], 'rejected': [], 'pending': [] }) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {}) main = self.createRoadData(name='main', base=self.baseDirpath) self.mainKeeper.write_local(main['prihex'], main['sighex']) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, { 'priv': main['prihex'], 'sign': main['sighex'] }) allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': [], 'local': [self.localFilepath], 'rejected': [], 'pending': [] }) other1 = self.createRoadData(name='other1', base=self.baseDirpath) other2 = self.createRoadData(name='other2', base=self.baseDirpath) status = self.mainKeeper.status(other1['name'], other1['pubhex'], other1['verhex']) self.assertEqual(status, 'accepted') status = self.mainKeeper.status(other2['name'], other2['pubhex'], other2['verhex']) self.assertEqual(status, 'accepted') allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': ['other1', 'other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': [] }) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other1', 'pub': other1['pubhex'], 'verify': other1['verhex'] }) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex'] }) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, { 'accepted': ['other1', 'other2'], 'rejected': [], 'pending': [] }) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual( allremotekeys, { 'other1': { 'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'accepted', 'pub': other1['pubhex'], }, 'other2': { 'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'], } }) def testManualAccept(self): ''' Basic function of RaetKey in non auto accept mode ''' console.terse("{0}\n".format(self.testAutoAccept.__doc__)) self.opts['auto_accept'] = False self.assertFalse(self.opts['auto_accept']) self.assertDictEqual(self.mainKeeper.all_keys(), { 'accepted': [], 'local': [], 'rejected': [], 'pending': [] }) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {}) main = self.createRoadData(name='main', base=self.baseDirpath) self.mainKeeper.write_local(main['prihex'], main['sighex']) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, { 'priv': main['prihex'], 'sign': main['sighex'] }) allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': [], 'local': [self.localFilepath], 'rejected': [], 'pending': [] }) other1 = self.createRoadData(name='other1', base=self.baseDirpath) other2 = self.createRoadData(name='other2', base=self.baseDirpath) status = self.mainKeeper.status(other1['name'], other1['pubhex'], other1['verhex']) self.assertEqual(status, 'pending') status = self.mainKeeper.status(other2['name'], other2['pubhex'], other2['verhex']) self.assertEqual(status, 'pending') allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': [], 'local': [self.localFilepath], 'pending': ['other1', 'other2'], 'rejected': [] }) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, {}) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual(remotekeys, {}) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, { 'accepted': [], 'rejected': [], 'pending': ['other1', 'other2'] }) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual( allremotekeys, { 'other1': { 'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'pending', 'pub': other1['pubhex'], }, 'other2': { 'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'pending', 'pub': other2['pubhex'], } }) self.mainKeeper.accept_all() allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': ['other1', 'other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': [] }) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other1', 'pub': other1['pubhex'], 'verify': other1['verhex'] }) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex'] }) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, { 'accepted': ['other1', 'other2'], 'rejected': [], 'pending': [] }) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual( allremotekeys, { 'other1': { 'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'accepted', 'pub': other1['pubhex'], }, 'other2': { 'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'], } }) def testDelete(self): ''' Basic function of RaetKey to delete key ''' console.terse("{0}\n".format(self.testDelete.__doc__)) self.opts['auto_accept'] = True self.assertTrue(self.opts['auto_accept']) self.assertDictEqual(self.mainKeeper.all_keys(), { 'accepted': [], 'local': [], 'rejected': [], 'pending': [] }) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, {}) main = self.createRoadData(name='main', base=self.baseDirpath) self.mainKeeper.write_local(main['prihex'], main['sighex']) localkeys = self.mainKeeper.read_local() self.assertDictEqual(localkeys, { 'priv': main['prihex'], 'sign': main['sighex'] }) allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': [], 'local': [self.localFilepath], 'rejected': [], 'pending': [] }) other1 = self.createRoadData(name='other1', base=self.baseDirpath) other2 = self.createRoadData(name='other2', base=self.baseDirpath) status = self.mainKeeper.status(other1['name'], other1['pubhex'], other1['verhex']) self.assertEqual(status, 'accepted') status = self.mainKeeper.status(other2['name'], other2['pubhex'], other2['verhex']) self.assertEqual(status, 'accepted') allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': ['other1', 'other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': [] }) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other1', 'pub': other1['pubhex'], 'verify': other1['verhex'] }) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex'] }) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, { 'accepted': ['other1', 'other2'], 'rejected': [], 'pending': [] }) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual( allremotekeys, { 'other1': { 'verify': other1['verhex'], 'minion_id': 'other1', 'acceptance': 'accepted', 'pub': other1['pubhex'] }, 'other2': { 'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'], } }) self.mainKeeper.delete_key(match=other1['name']) allkeys = self.mainKeeper.all_keys() self.assertDictEqual( allkeys, { 'accepted': ['other2'], 'local': [self.localFilepath], 'pending': [], 'rejected': [] }) remotekeys = self.mainKeeper.read_remote(other1['name']) self.assertDictEqual(remotekeys, {}) remotekeys = self.mainKeeper.read_remote(other2['name']) self.assertDictEqual( remotekeys, { 'minion_id': 'other2', 'pub': other2['pubhex'], 'verify': other2['verhex'] }) listkeys = self.mainKeeper.list_keys() self.assertDictEqual(listkeys, { 'accepted': ['other2'], 'rejected': [], 'pending': [] }) allremotekeys = self.mainKeeper.read_all_remote() self.assertDictEqual( allremotekeys, { 'other2': { 'verify': other2['verhex'], 'minion_id': 'other2', 'acceptance': 'accepted', 'pub': other2['pubhex'], } })
class SaltKeep(Keep): ''' RAET protocol estate on road data persistence for a given estate road specific data road/ keep/ stackname/ local/ estate.ext remote/ estate.name.ext estate.name.ext ''' LocalFields = [ 'uid', 'name', 'ha', 'main', 'sid', 'neid', 'sighex', 'prihex', 'auto', 'role' ] LocalDumpFields = ['uid', 'name', 'ha', 'main', 'sid', 'neid', 'role'] RemoteFields = [ 'uid', 'name', 'ha', 'sid', 'joined', 'acceptance', 'verhex', 'pubhex', 'role' ] RemoteDumpFields = ['uid', 'name', 'ha', 'sid', 'joined', 'role'] Auto = False #auto accept def __init__(self, opts, prefix='estate', basedirpath='', auto=None, **kwa): ''' Setup RoadKeep instance ''' basedirpath = basedirpath or os.path.join(opts['cache_dir'], 'raet') super(SaltKeep, self).__init__(prefix=prefix, basedirpath=basedirpath, **kwa) self.auto = auto if auto is not None else opts['auto_accept'] self.saltRaetKey = RaetKey(opts) def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = super(SaltKeep, self).loadLocalData() if not data: return None srkdata = self.saltRaetKey.read_local() if not srkdata: srkdata = dict(sign=None, priv=None) data.update(sighex=srkdata['sign'], prihex=srkdata['priv'], auto=self.auto) return data def loadRemoteData(self, name): ''' Load and Return the data from the remote file ''' data = super(SaltKeep, self).loadRemoteData(name) if not data: return None mid = data['role'] statae = raeting.ACCEPTANCES.keys() for status in statae: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: break if not keydata: return None data.update(acceptance=raeting.ACCEPTANCES[status], verhex=keydata['verify'], pubhex=keydata['pub']) return data def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' keeps = super(SaltKeep, self).loadAllRemoteData() for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: for name, data in keeps.items(): if data['role'] == mid: keeps[name].update( acceptance=raeting.ACCEPTANCES[status], verhex=keydata['verify'], pubhex=keydata['pub']) return keeps def clearAllRemoteData(self): ''' Remove all the remote estate files ''' super(SaltKeep, self).clearAllRemoteData() self.saltRaetKey.delete_all() def dumpLocal(self, local): ''' Dump local estate ''' data = odict([ ('uid', local.uid), ('name', local.name), ('ha', local.ha), ('main', local.main), ('sid', local.sid), ('neid', local.neid), ('role', local.role), ]) if self.verifyLocalData(data, localFields=self.LocalDumpFields): self.dumpLocalData(data) self.saltRaetKey.write_local(local.priver.keyhex, local.signer.keyhex) def dumpRemote(self, remote): ''' Dump remote estate ''' data = odict([ ('uid', remote.uid), ('name', remote.name), ('ha', remote.ha), ('sid', remote.sid), ('joined', remote.joined), ('role', remote.role), ]) if self.verifyRemoteData(data, remoteFields=self.RemoteDumpFields): self.dumpRemoteData(data, remote.name) self.saltRaetKey.status(remote.role, remote.pubber.keyhex, remote.verfer.keyhex) def replaceRemoteRole(self, remote, old): ''' Replace the Salt RaetKey record at old role when remote.role has changed ''' new = remote.role if new != old: #self.dumpRemote(remote) # manually fix up acceptance if not pending # will be pending by default unless autoaccept if remote.acceptance == raeting.acceptances.accepted: self.acceptRemote(remote) elif remote.acceptance == raeting.acceptances.rejected: self.rejectRemote(remote) self.saltRaetKey.delete_key(old) #now delete old key file def statusRemote(self, remote, verhex, pubhex, main=True, dump=True): ''' Evaluate acceptance status of remote estate per its keys persist key data differentially based on status ''' status = raeting.ACCEPTANCES[self.saltRaetKey.status( remote.role, pubhex, verhex)] if status != raeting.acceptances.rejected: if (verhex and verhex != remote.verfer.keyhex): remote.verfer = nacling.Verifier(verhex) if (pubhex and pubhex != remote.pubber.keyhex): remote.pubber = nacling.Publican(pubhex) remote.acceptance = status return status def rejectRemote(self, remote): ''' Set acceptance status to rejected ''' mid = remote.role self.saltRaetKey.reject(match=mid, include_accepted=True) remote.acceptance = raeting.acceptances.rejected def pendRemote(self, remote): ''' Set acceptance status to pending ''' pass def acceptRemote(self, remote): ''' Set acceptance status to accepted ''' mid = remote.role self.saltRaetKey.accept(match=mid, include_rejected=True) remote.acceptance = raeting.acceptances.accepted
class SaltSafe(object): ''' RAET protocol estate safe (key) data persistence and status ''' Auto = False #auto accept def __init__(self, opts=None, **kwa): ''' Setup SaltSafe instance ''' from salt.key import RaetKey if opts is None: opts = {} self.saltRaetKey = RaetKey(opts) def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = self.saltRaetKey.read_local() if not data: return None return (odict(sighex=data['sign'], prihex=data['priv'])) def clearLocalData(self): ''' Load and Return the data from the local estate ''' pass def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' data = odict() for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: rdata = odict() rdata['eid'] = keydata['device_id'] rdata['name'] = keydata['minion_id'] rdata['acceptance'] = raeting.ACCEPTANCES[status] rdata['verhex'] = keydata['verify'] rdata['pubhex'] = keydata['pub'] data[rdata['eid']] = rdata return data def clearAllRemoteData(self): ''' Remove all the remote estate files ''' self.saltRaetKey.delete_all() def dumpLocalEstate(self, estate): ''' Dump the key data from the local estate ''' self.saltRaetKey.write_local(estate.priver.keyhex, estate.signer.keyhex) def dumpRemoteEstate(self, estate): ''' Dump the data from the remote estate ''' pass def dumpAllRemoteEstates(self, estates): ''' Dump the data from all the remote estates ''' for estate in estates: self.dumpRemoteEstate(estate) def loadRemoteEstate(self, estate, status='accepted'): ''' Load and Return the data from the remote estate file Override this in sub class to change uid ''' mid = estate.name keydata = self.saltRaetKey.read_remote(mid, status) if not keydata: return None data = odict() data['eid'] = keydata['device_id'] data['name'] = keydata['minion_id'] data['acceptance'] = raeting.ACCEPTANCES[status] data['verhex'] = keydata['verify'] data['pubhex'] = keydata['pub'] return data def clearRemoteEstate(self, estate): ''' Clear the remote estate file Override this in sub class to change uid ''' mid = estate.eid self.saltRaetKey.delete_key(mid) def statusRemoteEstate(self, estate, verhex, pubhex, main=True): ''' Evaluate acceptance status of estate per its keys persist key data differentially based on status ''' status = raeting.ACCEPTANCES[self.saltRaetKey.status(estate.name, estate.eid, pubhex, verhex)] if status != raeting.acceptances.rejected: if (verhex and verhex != estate.verfer.keyhex): estate.verfer = nacling.Verifier(verhex) if (pubhex and pubhex != estate.pubber.keyhex): estate.pubber = nacling.Publican(pubhex) estate.acceptance = status return status def rejectRemoteEstate(self, estate): ''' Set acceptance status to rejected ''' estate.acceptance = raeting.acceptances.rejected mid = estate.name self.saltRaetKey.reject(match=mid, include_accepted=True) def acceptRemoteEstate(self, estate): ''' Set acceptance status to accepted ''' estate.acceptance = raeting.acceptances.accepted mid = estate.name self.saltRaetKey.accept(match=mid, include_rejected=True)
class SaltSafe(object): ''' Interface between Salt Key management and RAET keep key management ''' Auto = False #auto accept LocalFields = ['sighex', 'prihex'] RemoteFields = ['eid', 'name', 'acceptance', 'verhex', 'pubhex'] def __init__(self, opts=None, **kwa): ''' Setup SaltSafe instance ''' if opts is None: opts = {} self.saltRaetKey = RaetKey(opts) def verifyLocalData(self, data): ''' Returns True if the fields in .LocalFields match the fields in data ''' return (set(self.LocalFields) == set(data.keys())) def dumpLocalData(self, data): ''' Dump the key data from the local estate ''' self.saltRaetKey.write_local(data['prihex'], data['sighex']) def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = self.saltRaetKey.read_local() if not data: return None return (odict(sighex=data['sign'], prihex=data['priv'])) def clearLocalData(self): ''' Load and Return the data from the local estate ''' pass def verifyRemoteData(self, data): ''' Returns True if the fields in .RemoteFields match the fields in data ''' return (set(self.RemoteFields) == set(data.keys())) def dumpRemoteData(self, data, uid): ''' Dump the data from the remote estate given by uid ''' self.saltRaetKey.status(data['name'], data['eid'], data['pubhex'], data['verhex']) def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' data = odict() for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: rdata = odict() rdata['eid'] = keydata['device_id'] rdata['name'] = keydata['minion_id'] rdata['acceptance'] = raeting.ACCEPTANCES[status] rdata['verhex'] = keydata['verify'] rdata['pubhex'] = keydata['pub'] data[rdata['eid']] = rdata return data def clearAllRemoteData(self): ''' Remove all the remote estate files ''' self.saltRaetKey.delete_all() def dumpLocal(self, local): ''' Dump the key data from the local estate ''' data = odict([ ('sighex', local.signer.keyhex), ('prihex', local.priver.keyhex), ]) if self.verifyLocalData(data): self.dumpLocalData(data) def dumpRemote(self, remote): ''' Dump the data from the remote estate by calling status on it which will persist the data ''' data = odict([ ('eid', remote.eid), ('name', remote.name), ('acceptance', remote.acceptance), ('verhex', remote.verfer.keyhex), ('pubhex', remote.pubber.keyhex), ]) if self.verifyRemoteData(data): self.dumpRemoteData(data, remote.uid) def loadRemote(self, remote): ''' Load and Return the data from the remote estate file Override this in sub class to change uid ''' status='accepted' mid = remote.name keydata = self.saltRaetKey.read_remote(mid, status) if not keydata: return None data = odict() data['eid'] = keydata['device_id'] data['name'] = keydata['minion_id'] data['acceptance'] = raeting.ACCEPTANCES[status] data['verhex'] = keydata['verify'] data['pubhex'] = keydata['pub'] return data def clearRemote(self, remote): ''' Clear the remote estate file Override this in sub class to change uid ''' mid = remote.eid self.saltRaetKey.delete_key(mid) def statusRemote(self, remote, verhex, pubhex, main=True): ''' Evaluate acceptance status of remote estate per its keys persist key data differentially based on status ''' status = raeting.ACCEPTANCES[self.saltRaetKey.status(remote.name, remote.eid, pubhex, verhex)] if status != raeting.acceptances.rejected: if (verhex and verhex != remote.verfer.keyhex): remote.verfer = nacling.Verifier(verhex) if (pubhex and pubhex != remote.pubber.keyhex): remote.pubber = nacling.Publican(pubhex) remote.acceptance = status return status def rejectRemote(self, remote): ''' Set acceptance status to rejected ''' remote.acceptance = raeting.acceptances.rejected mid = remote.name self.saltRaetKey.reject(match=mid, include_accepted=True) def pendRemote(self, remote): ''' Set acceptance status to pending ''' pass def acceptRemote(self, remote): ''' Set acceptance status to accepted ''' remote.acceptance = raeting.acceptances.accepted mid = remote.name self.saltRaetKey.accept(match=mid, include_rejected=True)
class SaltKeep(Keep): ''' RAET protocol estate on road data persistence for a given estate road specific data road/ keep/ stackname/ local/ estate.ext remote/ estate.name.ext estate.name.ext ''' LocalFields = ['uid', 'name', 'ha', 'main', 'sid', 'neid', 'sighex', 'prihex', 'auto', 'role'] LocalDumpFields = ['uid', 'name', 'ha', 'main', 'sid', 'neid', 'role'] RemoteFields = ['uid', 'name', 'ha', 'sid', 'joined', 'acceptance', 'verhex', 'pubhex', 'role'] RemoteDumpFields = ['uid', 'name', 'ha', 'sid', 'joined', 'role'] Auto = False #auto accept def __init__(self, opts, prefix='estate', basedirpath='', auto=None, **kwa): ''' Setup RoadKeep instance ''' basedirpath = basedirpath or os.path.join(opts['cache_dir'], 'raet') super(SaltKeep, self).__init__(prefix=prefix, basedirpath=basedirpath, **kwa) self.auto = auto if auto is not None else opts['auto_accept'] self.saltRaetKey = RaetKey(opts) def loadLocalData(self): ''' Load and Return the data from the local estate ''' data = super(SaltKeep, self).loadLocalData() if not data: return None srkdata = self.saltRaetKey.read_local() if not srkdata: srkdata = dict(sign=None, priv=None) data.update(sighex=srkdata['sign'], prihex=srkdata['priv'], auto=self.auto) return data def loadRemoteData(self, name): ''' Load and Return the data from the remote file ''' data = super(SaltKeep, self).loadRemoteData(name) if not data: return None mid = data['role'] statae = raeting.ACCEPTANCES.keys() for status in statae: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: break if not keydata: return None data.update(acceptance=raeting.ACCEPTANCES[status], verhex=keydata['verify'], pubhex=keydata['pub']) return data def loadAllRemoteData(self): ''' Load and Return the data from the all the remote estate files ''' keeps = super(SaltKeep, self).loadAllRemoteData() for status, mids in self.saltRaetKey.list_keys().items(): for mid in mids: keydata = self.saltRaetKey.read_remote(mid, status) if keydata: for name, data in keeps.items(): if data['role'] == mid: keeps[name].update(acceptance=raeting.ACCEPTANCES[status], verhex=keydata['verify'], pubhex=keydata['pub']) return keeps def clearAllRemoteData(self): ''' Remove all the remote estate files ''' super(SaltKeep, self).clearAllRemoteData() self.saltRaetKey.delete_all() def dumpLocal(self, local): ''' Dump local estate ''' data = odict([ ('uid', local.uid), ('name', local.name), ('ha', local.ha), ('main', local.main), ('sid', local.sid), ('neid', local.neid), ('role', local.role), ]) if self.verifyLocalData(data, localFields = self.LocalDumpFields): self.dumpLocalData(data) self.saltRaetKey.write_local(local.priver.keyhex, local.signer.keyhex) def dumpRemote(self, remote): ''' Dump remote estate ''' data = odict([ ('uid', remote.uid), ('name', remote.name), ('ha', remote.ha), ('sid', remote.sid), ('joined', remote.joined), ('role', remote.role), ]) if self.verifyRemoteData(data, remoteFields=self.RemoteDumpFields): self.dumpRemoteData(data, remote.name) self.saltRaetKey.status(remote.role, remote.pubber.keyhex, remote.verfer.keyhex) def replaceRemoteRole(self, remote, old): ''' Replace the Salt RaetKey record at old role when remote.role has changed ''' new = remote.role if new != old: #self.dumpRemote(remote) # manually fix up acceptance if not pending # will be pending by default unless autoaccept if remote.acceptance == raeting.acceptances.accepted: self.acceptRemote(remote) elif remote.acceptance == raeting.acceptances.rejected: self.rejectRemote(remote) self.saltRaetKey.delete_key(old) #now delete old key file def statusRemote(self, remote, verhex, pubhex, main=True, dump=True): ''' Evaluate acceptance status of remote estate per its keys persist key data differentially based on status ''' status = raeting.ACCEPTANCES[self.saltRaetKey.status(remote.role, pubhex, verhex)] if status != raeting.acceptances.rejected: if (verhex and verhex != remote.verfer.keyhex): remote.verfer = nacling.Verifier(verhex) if (pubhex and pubhex != remote.pubber.keyhex): remote.pubber = nacling.Publican(pubhex) remote.acceptance = status return status def rejectRemote(self, remote): ''' Set acceptance status to rejected ''' mid = remote.role self.saltRaetKey.reject(match=mid, include_accepted=True) remote.acceptance = raeting.acceptances.rejected def pendRemote(self, remote): ''' Set acceptance status to pending ''' pass def acceptRemote(self, remote): ''' Set acceptance status to accepted ''' mid = remote.role self.saltRaetKey.accept(match=mid, include_rejected=True) remote.acceptance = raeting.acceptances.accepted