synapse 2.177.0__py311-none-any.whl → 2.179.0__py311-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synapse might be problematic. Click here for more details.
- synapse/cortex.py +170 -31
- synapse/datamodel.py +47 -1
- synapse/exc.py +1 -0
- synapse/lib/aha.py +362 -88
- synapse/lib/ast.py +26 -22
- synapse/lib/base.py +39 -12
- synapse/lib/cell.py +315 -119
- synapse/lib/config.py +15 -11
- synapse/lib/coro.py +27 -0
- synapse/lib/drive.py +551 -0
- synapse/lib/layer.py +0 -5
- synapse/lib/link.py +1 -1
- synapse/lib/lmdbslab.py +3 -3
- synapse/lib/nexus.py +24 -12
- synapse/lib/schemas.py +39 -0
- synapse/lib/snap.py +17 -7
- synapse/lib/storm.py +3 -1
- synapse/lib/stormhttp.py +1 -0
- synapse/lib/stormlib/imap.py +6 -2
- synapse/lib/stormlib/modelext.py +29 -3
- synapse/lib/stormlib/smtp.py +12 -2
- synapse/lib/stormlib/stix.py +40 -17
- synapse/lib/stormlib/vault.py +2 -2
- synapse/lib/stormtypes.py +1 -1
- synapse/lib/types.py +9 -0
- synapse/lib/version.py +2 -2
- synapse/lookup/pe.py +303 -38
- synapse/models/dns.py +24 -1
- synapse/models/geospace.py +4 -1
- synapse/models/infotech.py +26 -1
- synapse/telepath.py +32 -17
- synapse/tests/files/aha/certs/cas/synapse.crt +28 -0
- synapse/tests/files/aha/certs/cas/synapse.key +51 -0
- synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.crt +30 -0
- synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.key +51 -0
- synapse/tests/files/aha/certs/users/root@synapse.crt +29 -0
- synapse/tests/files/aha/certs/users/root@synapse.key +51 -0
- synapse/tests/files/rstorm/testsvc.py +1 -1
- synapse/tests/test_axon.py +1 -1
- synapse/tests/test_cortex.py +67 -60
- synapse/tests/test_lib_agenda.py +3 -3
- synapse/tests/test_lib_aha.py +353 -490
- synapse/tests/test_lib_base.py +20 -0
- synapse/tests/test_lib_cell.py +273 -22
- synapse/tests/test_lib_config.py +4 -3
- synapse/tests/test_lib_coro.py +12 -0
- synapse/tests/test_lib_nexus.py +8 -0
- synapse/tests/test_lib_stormhttp.py +40 -0
- synapse/tests/test_lib_stormlib_aha.py +35 -35
- synapse/tests/test_lib_stormlib_cell.py +4 -15
- synapse/tests/test_lib_stormlib_imap.py +14 -3
- synapse/tests/test_lib_stormlib_modelext.py +55 -3
- synapse/tests/test_lib_stormlib_smtp.py +51 -0
- synapse/tests/test_lib_stormlib_stix.py +15 -0
- synapse/tests/test_lib_stormlib_vault.py +11 -1
- synapse/tests/test_lib_stormtypes.py +5 -0
- synapse/tests/test_lib_types.py +9 -0
- synapse/tests/test_model_dns.py +8 -0
- synapse/tests/test_model_geospace.py +3 -1
- synapse/tests/test_model_infotech.py +47 -0
- synapse/tests/test_model_syn.py +11 -0
- synapse/tests/test_tools_aha.py +78 -101
- synapse/tests/test_utils_stormcov.py +1 -1
- synapse/tests/utils.py +86 -120
- synapse/tools/aha/clone.py +50 -0
- synapse/tools/aha/enroll.py +2 -1
- synapse/tools/backup.py +2 -2
- synapse/tools/changelog.py +31 -1
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/METADATA +48 -48
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/RECORD +73 -65
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/WHEEL +1 -1
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/LICENSE +0 -0
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/top_level.txt +0 -0
synapse/lib/aha.py
CHANGED
|
@@ -12,6 +12,7 @@ import synapse.common as s_common
|
|
|
12
12
|
import synapse.daemon as s_daemon
|
|
13
13
|
import synapse.telepath as s_telepath
|
|
14
14
|
|
|
15
|
+
import synapse.lib.base as s_base
|
|
15
16
|
import synapse.lib.cell as s_cell
|
|
16
17
|
import synapse.lib.coro as s_coro
|
|
17
18
|
import synapse.lib.nexus as s_nexus
|
|
@@ -125,11 +126,15 @@ class AhaServicesV1(s_httpapi.Handler):
|
|
|
125
126
|
|
|
126
127
|
class AhaApi(s_cell.CellApi):
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
@s_cell.adminapi()
|
|
130
|
+
async def addAhaClone(self, host, port=27492, conf=None):
|
|
131
|
+
return await self.cell.addAhaClone(host, port=port, conf=conf)
|
|
132
|
+
|
|
133
|
+
async def getAhaUrls(self, user='root'):
|
|
134
|
+
ahaurls = await self.cell.getAhaUrls(user=user)
|
|
135
|
+
if ahaurls is None:
|
|
136
|
+
return ()
|
|
137
|
+
return ahaurls
|
|
133
138
|
|
|
134
139
|
async def getAhaSvc(self, name, filters=None):
|
|
135
140
|
'''
|
|
@@ -260,7 +265,6 @@ class AhaApi(s_cell.CellApi):
|
|
|
260
265
|
return await self.cell.signHostCsr(csrtext, signas=signas, sans=sans)
|
|
261
266
|
|
|
262
267
|
async def signUserCsr(self, csrtext, signas=None):
|
|
263
|
-
|
|
264
268
|
await self._reqUserAllowed(('aha', 'csr', 'user'))
|
|
265
269
|
return await self.cell.signUserCsr(csrtext, signas=signas)
|
|
266
270
|
|
|
@@ -299,6 +303,20 @@ class AhaApi(s_cell.CellApi):
|
|
|
299
303
|
async for item in self.cell.getAhaPools():
|
|
300
304
|
yield item
|
|
301
305
|
|
|
306
|
+
async def getAhaServers(self):
|
|
307
|
+
return await self.cell.getAhaServers()
|
|
308
|
+
|
|
309
|
+
async def getAhaServer(self, host, port):
|
|
310
|
+
return await self.cell.getAhaServer(host, port)
|
|
311
|
+
|
|
312
|
+
@s_cell.adminapi()
|
|
313
|
+
async def addAhaServer(self, server):
|
|
314
|
+
return await self.cell.addAhaServer(server)
|
|
315
|
+
|
|
316
|
+
@s_cell.adminapi()
|
|
317
|
+
async def delAhaServer(self, host, port):
|
|
318
|
+
return await self.cell.delAhaServer(host, port)
|
|
319
|
+
|
|
302
320
|
@s_cell.adminapi()
|
|
303
321
|
async def addAhaSvcProv(self, name, provinfo=None):
|
|
304
322
|
'''
|
|
@@ -341,6 +359,14 @@ class AhaApi(s_cell.CellApi):
|
|
|
341
359
|
'''
|
|
342
360
|
return await self.cell.clearAhaUserEnrolls()
|
|
343
361
|
|
|
362
|
+
@s_cell.adminapi()
|
|
363
|
+
async def clearAhaClones(self):
|
|
364
|
+
'''
|
|
365
|
+
Remove all unused AHA clone provisioning values.
|
|
366
|
+
'''
|
|
367
|
+
return await self.cell.clearAhaClones()
|
|
368
|
+
|
|
369
|
+
|
|
344
370
|
class ProvDmon(s_daemon.Daemon):
|
|
345
371
|
|
|
346
372
|
async def __anit__(self, aha):
|
|
@@ -366,10 +392,34 @@ class ProvDmon(s_daemon.Daemon):
|
|
|
366
392
|
await self.aha.delAhaUserEnroll(name)
|
|
367
393
|
return EnrollApi(self.aha, userinfo)
|
|
368
394
|
|
|
395
|
+
clone = await self.aha.getAhaClone(name)
|
|
396
|
+
if clone is not None:
|
|
397
|
+
host = clone.get('host')
|
|
398
|
+
mesg = f'Retrieved AHA clone info for {host} iden {name}'
|
|
399
|
+
logger.info(mesg, extra=await self.aha.getLogExtra(iden=name, host=host))
|
|
400
|
+
return CloneApi(self.aha, clone)
|
|
401
|
+
|
|
369
402
|
mesg = f'Invalid provisioning identifier name={name}. This could be' \
|
|
370
403
|
f' caused by the re-use of a provisioning URL.'
|
|
371
404
|
raise s_exc.NoSuchName(mesg=mesg, name=name)
|
|
372
405
|
|
|
406
|
+
class CloneApi:
|
|
407
|
+
|
|
408
|
+
def __init__(self, aha, clone):
|
|
409
|
+
self.aha = aha
|
|
410
|
+
self.clone = clone
|
|
411
|
+
|
|
412
|
+
async def getCloneDef(self):
|
|
413
|
+
return self.clone
|
|
414
|
+
|
|
415
|
+
async def readyToMirror(self):
|
|
416
|
+
return await self.aha.readyToMirror()
|
|
417
|
+
|
|
418
|
+
async def iterNewBackupArchive(self, name=None, remove=False):
|
|
419
|
+
async with self.aha.getLocalProxy() as proxy:
|
|
420
|
+
async for byts in proxy.iterNewBackupArchive(name=name, remove=remove):
|
|
421
|
+
yield byts
|
|
422
|
+
|
|
373
423
|
class EnrollApi:
|
|
374
424
|
|
|
375
425
|
def __init__(self, aha, userinfo):
|
|
@@ -377,20 +427,21 @@ class EnrollApi:
|
|
|
377
427
|
self.userinfo = userinfo
|
|
378
428
|
|
|
379
429
|
async def getUserInfo(self):
|
|
430
|
+
user = self.userinfo.get('name')
|
|
380
431
|
return {
|
|
381
|
-
'aha:urls': self.aha.
|
|
382
|
-
'aha:user':
|
|
383
|
-
'aha:network': self.aha.conf.
|
|
432
|
+
'aha:urls': await self.aha.getAhaUrls(user=user),
|
|
433
|
+
'aha:user': user,
|
|
434
|
+
'aha:network': self.aha.conf.req('aha:network'),
|
|
384
435
|
}
|
|
385
436
|
|
|
386
437
|
async def getCaCert(self):
|
|
387
|
-
ahanetw = self.aha.conf.
|
|
438
|
+
ahanetw = self.aha.conf.req('aha:network')
|
|
388
439
|
return self.aha.certdir.getCaCertBytes(ahanetw)
|
|
389
440
|
|
|
390
441
|
async def signUserCsr(self, byts):
|
|
391
442
|
|
|
392
443
|
ahauser = self.userinfo.get('name')
|
|
393
|
-
ahanetw = self.aha.conf.
|
|
444
|
+
ahanetw = self.aha.conf.req('aha:network')
|
|
394
445
|
|
|
395
446
|
username = f'{ahauser}@{ahanetw}'
|
|
396
447
|
|
|
@@ -416,7 +467,7 @@ class ProvApi:
|
|
|
416
467
|
return self.provinfo
|
|
417
468
|
|
|
418
469
|
async def getCaCert(self):
|
|
419
|
-
ahanetw = self.aha.conf.
|
|
470
|
+
ahanetw = self.aha.conf.req('aha:network')
|
|
420
471
|
return self.aha.certdir.getCaCertBytes(ahanetw)
|
|
421
472
|
|
|
422
473
|
async def signHostCsr(self, byts):
|
|
@@ -464,8 +515,17 @@ class AhaCell(s_cell.Cell):
|
|
|
464
515
|
confbase['mirror']['hidedocs'] = False # type: ignore
|
|
465
516
|
confbase['mirror']['hidecmdl'] = False # type: ignore
|
|
466
517
|
confdefs = {
|
|
518
|
+
'clone': {
|
|
519
|
+
'hidecmdl': True,
|
|
520
|
+
'description': 'Bootstrap a clone from the AHA clone URL.',
|
|
521
|
+
'type': ['string', 'null'],
|
|
522
|
+
},
|
|
523
|
+
'dns:name': {
|
|
524
|
+
'description': 'The registered DNS name used to reach the AHA service.',
|
|
525
|
+
'type': ['string', 'null'],
|
|
526
|
+
},
|
|
467
527
|
'aha:urls': {
|
|
468
|
-
'description': '
|
|
528
|
+
'description': 'Deprecated. AHA servers can now manage this automatically.',
|
|
469
529
|
'type': ['string', 'array'],
|
|
470
530
|
'items': {'type': 'string'},
|
|
471
531
|
},
|
|
@@ -480,6 +540,35 @@ class AhaCell(s_cell.Cell):
|
|
|
480
540
|
def getEnvPrefix(cls):
|
|
481
541
|
return (f'SYN_AHA', f'SYN_{cls.__name__.upper()}', )
|
|
482
542
|
|
|
543
|
+
async def _initCellBoot(self):
|
|
544
|
+
|
|
545
|
+
curl = self.conf.get('clone')
|
|
546
|
+
if curl is None:
|
|
547
|
+
return
|
|
548
|
+
|
|
549
|
+
path = s_common.genpath(self.dirn, 'cell.guid')
|
|
550
|
+
if os.path.isfile(path):
|
|
551
|
+
logger.info('Cloning AHA: cell.guid detected. Skipping.')
|
|
552
|
+
return
|
|
553
|
+
|
|
554
|
+
logger.warning(f'Cloning AHA: {curl}')
|
|
555
|
+
|
|
556
|
+
async with await s_telepath.openurl(curl) as proxy:
|
|
557
|
+
clone = await proxy.getCloneDef()
|
|
558
|
+
await self._initCloneCell(proxy)
|
|
559
|
+
|
|
560
|
+
logger.warning('Cloning AHA: done!')
|
|
561
|
+
|
|
562
|
+
conf = s_common.yamlload(self.dirn, 'cell.yaml')
|
|
563
|
+
if conf is None:
|
|
564
|
+
conf = {}
|
|
565
|
+
|
|
566
|
+
conf.update(clone.get('conf', {}))
|
|
567
|
+
|
|
568
|
+
s_common.yamlsave(conf, self.dirn, 'cell.yaml')
|
|
569
|
+
|
|
570
|
+
self.conf.update(conf)
|
|
571
|
+
|
|
483
572
|
async def initServiceStorage(self):
|
|
484
573
|
|
|
485
574
|
# TODO plumb using a remote jsonstor?
|
|
@@ -499,9 +588,66 @@ class AhaCell(s_cell.Cell):
|
|
|
499
588
|
self.slab.initdb('aha:provs')
|
|
500
589
|
self.slab.initdb('aha:enrolls')
|
|
501
590
|
|
|
591
|
+
self.slab.initdb('aha:clones')
|
|
592
|
+
self.slab.initdb('aha:servers')
|
|
593
|
+
|
|
502
594
|
self.slab.initdb('aha:pools')
|
|
595
|
+
|
|
503
596
|
self.poolwindows = collections.defaultdict(list)
|
|
504
597
|
|
|
598
|
+
async def getAhaServer(self, host, port):
|
|
599
|
+
lkey = s_msgpack.en((host, port))
|
|
600
|
+
byts = self.slab.get(lkey, db='aha:servers')
|
|
601
|
+
if byts is not None:
|
|
602
|
+
return s_msgpack.un(byts)
|
|
603
|
+
|
|
604
|
+
async def addAhaServer(self, server):
|
|
605
|
+
|
|
606
|
+
host = server.get('host')
|
|
607
|
+
port = server.setdefault('port', 27492)
|
|
608
|
+
|
|
609
|
+
# avoid a noop nexus change...
|
|
610
|
+
oldv = await self.getAhaServer(host, port)
|
|
611
|
+
if s_common.flatten(server) == s_common.flatten(oldv):
|
|
612
|
+
return False
|
|
613
|
+
|
|
614
|
+
return await self._push('aha:server:add', server)
|
|
615
|
+
|
|
616
|
+
@s_nexus.Pusher.onPush('aha:server:add')
|
|
617
|
+
async def _addAhaServer(self, server):
|
|
618
|
+
# TODO schema
|
|
619
|
+
host = server.get('host')
|
|
620
|
+
port = server.get('port')
|
|
621
|
+
|
|
622
|
+
lkey = s_msgpack.en((host, port))
|
|
623
|
+
|
|
624
|
+
byts = self.slab.get(lkey, db='aha:servers')
|
|
625
|
+
if byts is not None:
|
|
626
|
+
oldv = s_msgpack.un(byts)
|
|
627
|
+
if s_common.flatten(server) == s_common.flatten(oldv):
|
|
628
|
+
return False
|
|
629
|
+
|
|
630
|
+
self.slab.put(lkey, s_msgpack.en(server), db='aha:servers')
|
|
631
|
+
|
|
632
|
+
return True
|
|
633
|
+
|
|
634
|
+
@s_nexus.Pusher.onPushAuto('aha:server:del')
|
|
635
|
+
async def delAhaServer(self, host, port):
|
|
636
|
+
|
|
637
|
+
lkey = s_msgpack.en((host, port))
|
|
638
|
+
|
|
639
|
+
byts = self.slab.pop(lkey, db='aha:servers')
|
|
640
|
+
if byts is None:
|
|
641
|
+
return None
|
|
642
|
+
|
|
643
|
+
return s_msgpack.un(byts)
|
|
644
|
+
|
|
645
|
+
async def getAhaServers(self):
|
|
646
|
+
servers = []
|
|
647
|
+
for _, byts in self.slab.scanByFull(db='aha:servers'):
|
|
648
|
+
servers.append(s_msgpack.un(byts))
|
|
649
|
+
return servers
|
|
650
|
+
|
|
505
651
|
async def iterPoolTopo(self, name):
|
|
506
652
|
|
|
507
653
|
name = self._getAhaName(name)
|
|
@@ -537,41 +683,101 @@ class AhaCell(s_cell.Cell):
|
|
|
537
683
|
self.addHttpApi('/api/v1/aha/provision/service', AhaProvisionServiceV1, {'cell': self})
|
|
538
684
|
|
|
539
685
|
async def initServiceRuntime(self):
|
|
686
|
+
|
|
540
687
|
self.addActiveCoro(self._clearInactiveSessions)
|
|
541
688
|
|
|
542
689
|
if self.isactive:
|
|
543
|
-
# bootstrap a CA for our aha:network
|
|
544
|
-
netw = self.conf.get('aha:network')
|
|
545
|
-
if netw is not None:
|
|
546
690
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
await self.genCaCert(netw)
|
|
691
|
+
# bootstrap a CA for our aha:network
|
|
692
|
+
netw = self.conf.req('aha:network')
|
|
550
693
|
|
|
551
|
-
|
|
694
|
+
if self.certdir.getCaCertPath(netw) is None:
|
|
695
|
+
logger.info(f'Adding CA certificate for {netw}')
|
|
696
|
+
await self.genCaCert(netw)
|
|
552
697
|
|
|
698
|
+
name = self.conf.get('aha:name')
|
|
699
|
+
if name is not None:
|
|
553
700
|
host = f'{name}.{netw}'
|
|
554
701
|
if self.certdir.getHostCertPath(host) is None:
|
|
555
702
|
logger.info(f'Adding server certificate for {host}')
|
|
556
703
|
await self._genHostCert(host, signas=netw)
|
|
557
704
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
705
|
+
root = f'root@{netw}'
|
|
706
|
+
await self._genUserCert(root, signas=netw)
|
|
707
|
+
|
|
708
|
+
user = self.conf.get('aha:admin')
|
|
709
|
+
if user is not None:
|
|
710
|
+
await self._genUserCert(user, signas=netw)
|
|
711
|
+
|
|
712
|
+
def _getDnsName(self):
|
|
713
|
+
# emulate the old aha name.network behavior if the
|
|
714
|
+
# explicit option is not set.
|
|
715
|
+
|
|
716
|
+
hostname = self.conf.get('dns:name')
|
|
717
|
+
if hostname is not None:
|
|
718
|
+
return hostname
|
|
719
|
+
|
|
720
|
+
ahaname = self.conf.get('aha:name')
|
|
721
|
+
ahanetw = self.conf.get('aha:network')
|
|
722
|
+
if ahaname is not None and ahanetw is not None:
|
|
723
|
+
return f'{ahaname}.{ahanetw}'
|
|
724
|
+
|
|
725
|
+
def _getProvListen(self):
|
|
726
|
+
|
|
727
|
+
lisn = self.conf.get('provision:listen')
|
|
728
|
+
if lisn is not None:
|
|
729
|
+
return lisn
|
|
730
|
+
|
|
731
|
+
# this may not use _getDnsName() in order to maintain
|
|
732
|
+
# backward compatibilty with aha name.network configs
|
|
733
|
+
# that do not intend to listen for provisioning.
|
|
734
|
+
hostname = self.conf.get('dns:name')
|
|
735
|
+
if hostname is not None:
|
|
736
|
+
return f'ssl://0.0.0.0:27272?hostname={hostname}'
|
|
737
|
+
|
|
738
|
+
def _getDmonListen(self):
|
|
739
|
+
|
|
740
|
+
lisn = self.conf.get('dmon:listen', s_common.novalu)
|
|
741
|
+
if lisn is not s_common.novalu:
|
|
742
|
+
return lisn
|
|
743
|
+
|
|
744
|
+
network = self.conf.req('aha:network')
|
|
745
|
+
dnsname = self._getDnsName()
|
|
746
|
+
if dnsname is not None:
|
|
747
|
+
return f'ssl://0.0.0.0?hostname={dnsname}&ca={network}'
|
|
748
|
+
|
|
749
|
+
def _reqProvListen(self):
|
|
750
|
+
lisn = self._getProvListen()
|
|
751
|
+
if lisn is not None:
|
|
752
|
+
return lisn
|
|
753
|
+
|
|
754
|
+
mesg = 'The AHA server is not configured for provisioning.'
|
|
755
|
+
raise s_exc.NeedConfValu(mesg=mesg)
|
|
563
756
|
|
|
564
757
|
async def initServiceNetwork(self):
|
|
565
758
|
|
|
759
|
+
# bootstrap CA/host certs first
|
|
760
|
+
network = self.conf.req('aha:network')
|
|
761
|
+
|
|
762
|
+
hostname = self._getDnsName()
|
|
763
|
+
if hostname is not None and network is not None:
|
|
764
|
+
await self._genHostCert(hostname, signas=network)
|
|
765
|
+
|
|
566
766
|
await s_cell.Cell.initServiceNetwork(self)
|
|
567
767
|
|
|
768
|
+
# all AHA mirrors are registered
|
|
769
|
+
if hostname is not None and self.sockaddr is not None:
|
|
770
|
+
server = {'host': hostname, 'port': self.sockaddr[1]}
|
|
771
|
+
await self.addAhaServer(server)
|
|
772
|
+
|
|
568
773
|
self.provdmon = None
|
|
569
774
|
|
|
570
|
-
provurl = self.
|
|
775
|
+
provurl = self._getProvListen()
|
|
571
776
|
if provurl is not None:
|
|
572
777
|
self.provdmon = await ProvDmon.anit(self)
|
|
573
778
|
self.onfini(self.provdmon)
|
|
574
|
-
|
|
779
|
+
logger.info(f'provision listening: {provurl}')
|
|
780
|
+
self.provaddr = await self.provdmon.listen(provurl)
|
|
575
781
|
|
|
576
782
|
async def _clearInactiveSessions(self):
|
|
577
783
|
|
|
@@ -590,6 +796,41 @@ class AhaCell(s_cell.Cell):
|
|
|
590
796
|
# Wait until we are cancelled or the cell is fini.
|
|
591
797
|
await self.waitfini()
|
|
592
798
|
|
|
799
|
+
async def _waitAhaSvcOnline(self, name, timeout=None):
|
|
800
|
+
|
|
801
|
+
name = self._getAhaName(name)
|
|
802
|
+
|
|
803
|
+
while True:
|
|
804
|
+
|
|
805
|
+
async with self.nexslock:
|
|
806
|
+
|
|
807
|
+
retn = await self.getAhaSvc(name)
|
|
808
|
+
if retn['svcinfo'].get('online') is not None:
|
|
809
|
+
return retn
|
|
810
|
+
|
|
811
|
+
waiter = self.waiter(1, f'aha:svcadd:{name}')
|
|
812
|
+
|
|
813
|
+
if await waiter.wait(timeout=timeout) is None:
|
|
814
|
+
raise s_exc.TimeOut(mesg=f'Timeout waiting for aha:svcadd:{name}')
|
|
815
|
+
|
|
816
|
+
async def _waitAhaSvcDown(self, name, timeout=None):
|
|
817
|
+
|
|
818
|
+
name = self._getAhaName(name)
|
|
819
|
+
|
|
820
|
+
while True:
|
|
821
|
+
|
|
822
|
+
async with self.nexslock:
|
|
823
|
+
|
|
824
|
+
retn = await self.getAhaSvc(name)
|
|
825
|
+
online = retn['svcinfo'].get('online')
|
|
826
|
+
if online is None:
|
|
827
|
+
return retn
|
|
828
|
+
|
|
829
|
+
waiter = self.waiter(1, f'aha:svcdown:{name}')
|
|
830
|
+
|
|
831
|
+
if await waiter.wait(timeout=timeout) is None:
|
|
832
|
+
raise s_exc.TimeOut(mesg=f'Timeout waiting for aha:svcdown:{name}')
|
|
833
|
+
|
|
593
834
|
async def getAhaSvcs(self, network=None):
|
|
594
835
|
path = ('aha', 'services')
|
|
595
836
|
if network is not None:
|
|
@@ -654,15 +895,12 @@ class AhaCell(s_cell.Cell):
|
|
|
654
895
|
|
|
655
896
|
# mostly for testing...
|
|
656
897
|
await self.fire('aha:svcadd', svcinfo=svcinfo)
|
|
898
|
+
await self.fire(f'aha:svcadd:{svcfull}', svcinfo=svcinfo)
|
|
657
899
|
|
|
658
900
|
def _getAhaName(self, name):
|
|
659
901
|
# the modern version of names is absolute or ...
|
|
660
902
|
if name.endswith('...'):
|
|
661
|
-
|
|
662
|
-
if netw is None: # pragma: no cover
|
|
663
|
-
mesg = 'AHA Server requires aha:network configuration.'
|
|
664
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
665
|
-
name = name[:-2] + netw
|
|
903
|
+
return name[:-2] + self.conf.req('aha:network')
|
|
666
904
|
return name
|
|
667
905
|
|
|
668
906
|
async def getAhaPool(self, name):
|
|
@@ -780,6 +1018,7 @@ class AhaCell(s_cell.Cell):
|
|
|
780
1018
|
@s_nexus.Pusher.onPushAuto('aha:svc:del')
|
|
781
1019
|
async def delAhaSvc(self, name, network=None):
|
|
782
1020
|
|
|
1021
|
+
name = self._getAhaName(name)
|
|
783
1022
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
784
1023
|
|
|
785
1024
|
logger.info(f'Deleting service [{svcfull}].', extra=await self.getLogExtra(name=svcname, netw=svcnetw))
|
|
@@ -794,6 +1033,8 @@ class AhaCell(s_cell.Cell):
|
|
|
794
1033
|
await self.fire('aha:svcdel', svcname=svcname, svcnetw=svcnetw)
|
|
795
1034
|
|
|
796
1035
|
async def setAhaSvcDown(self, name, linkiden, network=None):
|
|
1036
|
+
|
|
1037
|
+
name = self._getAhaName(name)
|
|
797
1038
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
798
1039
|
path = ('aha', 'services', svcnetw, svcname)
|
|
799
1040
|
|
|
@@ -805,8 +1046,10 @@ class AhaCell(s_cell.Cell):
|
|
|
805
1046
|
|
|
806
1047
|
@s_nexus.Pusher.onPush('aha:svc:down')
|
|
807
1048
|
async def _setAhaSvcDown(self, name, linkiden, network=None):
|
|
1049
|
+
|
|
808
1050
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
809
1051
|
path = ('aha', 'services', svcnetw, svcname)
|
|
1052
|
+
|
|
810
1053
|
if await self.jsonstor.cmpDelPathObjProp(path, 'svcinfo/online', linkiden):
|
|
811
1054
|
await self.jsonstor.setPathObjProp(path, 'svcinfo/ready', False)
|
|
812
1055
|
|
|
@@ -818,6 +1061,7 @@ class AhaCell(s_cell.Cell):
|
|
|
818
1061
|
await link.fini()
|
|
819
1062
|
|
|
820
1063
|
await self.fire('aha:svcdown', svcname=svcname, svcnetw=svcnetw)
|
|
1064
|
+
await self.fire(f'aha:svcdown:{svcfull}', svcname=svcname, svcnetw=svcnetw)
|
|
821
1065
|
|
|
822
1066
|
logger.info(f'Set [{svcfull}] offline.',
|
|
823
1067
|
extra=await self.getLogExtra(name=svcname, netw=svcnetw))
|
|
@@ -918,7 +1162,7 @@ class AhaCell(s_cell.Cell):
|
|
|
918
1162
|
|
|
919
1163
|
async def _genHostCert(self, hostname, signas=None):
|
|
920
1164
|
|
|
921
|
-
if
|
|
1165
|
+
if self.certdir.getHostCertPath(hostname) is not None:
|
|
922
1166
|
return
|
|
923
1167
|
|
|
924
1168
|
pkey, cert = await s_coro.executor(self.certdir.genHostCert, hostname, signas=signas, save=False)
|
|
@@ -927,8 +1171,12 @@ class AhaCell(s_cell.Cell):
|
|
|
927
1171
|
await self.saveHostCert(hostname, pkey, cert)
|
|
928
1172
|
|
|
929
1173
|
async def _genUserCert(self, username, signas=None):
|
|
930
|
-
|
|
1174
|
+
|
|
1175
|
+
if self.certdir.getUserCertPath(username) is not None:
|
|
931
1176
|
return
|
|
1177
|
+
|
|
1178
|
+
logger.info(f'Adding user certificate for {username}')
|
|
1179
|
+
|
|
932
1180
|
pkey, cert = await s_coro.executor(self.certdir.genUserCert, username, signas=signas, save=False)
|
|
933
1181
|
pkey = self.certdir._pkeyToByts(pkey).decode()
|
|
934
1182
|
cert = self.certdir._certToByts(cert).decode()
|
|
@@ -971,8 +1219,8 @@ class AhaCell(s_cell.Cell):
|
|
|
971
1219
|
|
|
972
1220
|
hostname = xcsr.subject.get_attributes_for_oid(c_x509.NameOID.COMMON_NAME)[0].value
|
|
973
1221
|
|
|
974
|
-
hostpath =
|
|
975
|
-
if
|
|
1222
|
+
hostpath = self.certdir.getHostCertPath(hostname)
|
|
1223
|
+
if hostpath is not None:
|
|
976
1224
|
os.unlink(hostpath)
|
|
977
1225
|
|
|
978
1226
|
if signas is None:
|
|
@@ -990,8 +1238,8 @@ class AhaCell(s_cell.Cell):
|
|
|
990
1238
|
|
|
991
1239
|
username = xcsr.subject.get_attributes_for_oid(c_x509.NameOID.COMMON_NAME)[0].value
|
|
992
1240
|
|
|
993
|
-
userpath =
|
|
994
|
-
if
|
|
1241
|
+
userpath = self.certdir.getUserCertPath(username)
|
|
1242
|
+
if userpath is not None:
|
|
995
1243
|
os.unlink(userpath)
|
|
996
1244
|
|
|
997
1245
|
if signas is None:
|
|
@@ -1004,41 +1252,75 @@ class AhaCell(s_cell.Cell):
|
|
|
1004
1252
|
|
|
1005
1253
|
return self.certdir._certToByts(cert).decode()
|
|
1006
1254
|
|
|
1007
|
-
def
|
|
1255
|
+
async def getAhaUrls(self, user='root'):
|
|
1256
|
+
|
|
1257
|
+
# for backward compat...
|
|
1008
1258
|
urls = self.conf.get('aha:urls')
|
|
1009
1259
|
if urls is not None:
|
|
1010
|
-
if isinstance(urls, str):
|
|
1011
|
-
return (urls,)
|
|
1012
1260
|
return urls
|
|
1013
1261
|
|
|
1014
|
-
|
|
1015
|
-
if ahaname is None:
|
|
1016
|
-
return None
|
|
1262
|
+
network = self.conf.req('aha:network')
|
|
1017
1263
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1264
|
+
urls = []
|
|
1265
|
+
for server in await self.getAhaServers():
|
|
1266
|
+
host = server.get('host')
|
|
1267
|
+
port = server.get('port')
|
|
1268
|
+
urls.append(f'ssl://{host}:{port}?certname={user}@{network}')
|
|
1021
1269
|
|
|
1022
|
-
|
|
1023
|
-
return None
|
|
1270
|
+
return urls
|
|
1024
1271
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1272
|
+
def getMyUrl(self, user='root'):
|
|
1273
|
+
port = self.sockaddr[1]
|
|
1274
|
+
host = self._getDnsName()
|
|
1275
|
+
network = self.conf.req('aha:network')
|
|
1276
|
+
return f'ssl://{host}:{port}?certname={user}@{network}'
|
|
1027
1277
|
|
|
1028
|
-
async def
|
|
1278
|
+
async def getAhaClone(self, iden):
|
|
1279
|
+
lkey = s_common.uhex(iden)
|
|
1280
|
+
byts = self.slab.get(lkey, db='aha:clones')
|
|
1281
|
+
if byts is not None:
|
|
1282
|
+
return s_msgpack.un(byts)
|
|
1029
1283
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1284
|
+
async def addAhaClone(self, host, port=27492, conf=None):
|
|
1285
|
+
|
|
1286
|
+
if conf is None:
|
|
1287
|
+
conf = {}
|
|
1288
|
+
|
|
1289
|
+
network = self.conf.req('aha:network')
|
|
1290
|
+
|
|
1291
|
+
conf['mirror'] = self.getMyUrl()
|
|
1292
|
+
|
|
1293
|
+
conf['dns:name'] = host
|
|
1294
|
+
conf['aha:network'] = network
|
|
1295
|
+
conf['dmon:listen'] = f'ssl://0.0.0.0:{port}?hostname={host}&ca={network}'
|
|
1296
|
+
|
|
1297
|
+
iden = s_common.guid()
|
|
1298
|
+
clone = {
|
|
1299
|
+
'iden': iden,
|
|
1300
|
+
'host': host,
|
|
1301
|
+
'port': port,
|
|
1302
|
+
'conf': conf,
|
|
1303
|
+
}
|
|
1304
|
+
await self._push('aha:clone:add', clone)
|
|
1305
|
+
|
|
1306
|
+
logger.info(f'Created AHA clone provisioning for {host} with iden {iden}',
|
|
1307
|
+
extra=await self.getLogExtra(iden=iden, name=host, netw=network))
|
|
1308
|
+
|
|
1309
|
+
return self._getProvClientUrl(iden)
|
|
1034
1310
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1311
|
+
@s_nexus.Pusher.onPush('aha:clone:add')
|
|
1312
|
+
async def _addAhaClone(self, clone):
|
|
1313
|
+
iden = clone.get('iden')
|
|
1314
|
+
lkey = s_common.uhex(iden)
|
|
1315
|
+
self.slab.put(lkey, s_msgpack.en(clone), db='aha:clones')
|
|
1316
|
+
|
|
1317
|
+
async def addAhaSvcProv(self, name, provinfo=None):
|
|
1038
1318
|
|
|
1039
1319
|
if not name:
|
|
1040
1320
|
raise s_exc.BadArg(mesg='Empty name values are not allowed for provisioning.')
|
|
1041
1321
|
|
|
1322
|
+
self._reqProvListen()
|
|
1323
|
+
|
|
1042
1324
|
if provinfo is None:
|
|
1043
1325
|
provinfo = {}
|
|
1044
1326
|
|
|
@@ -1048,23 +1330,16 @@ class AhaCell(s_cell.Cell):
|
|
|
1048
1330
|
|
|
1049
1331
|
conf = provinfo.setdefault('conf', {})
|
|
1050
1332
|
|
|
1051
|
-
|
|
1333
|
+
netw = self.conf.req('aha:network')
|
|
1052
1334
|
|
|
1053
1335
|
ahaadmin = self.conf.get('aha:admin')
|
|
1054
1336
|
if ahaadmin is not None: # pragma: no cover
|
|
1055
1337
|
conf.setdefault('aha:admin', ahaadmin)
|
|
1056
1338
|
|
|
1057
|
-
conf.setdefault('aha:user', 'root')
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
netw = conf.get('aha:network')
|
|
1061
|
-
if netw is None:
|
|
1062
|
-
mesg = 'AHA server has no configured aha:network.'
|
|
1063
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
1339
|
+
ahauser = conf.setdefault('aha:user', 'root')
|
|
1340
|
+
ahaurls = await self.getAhaUrls(user=ahauser)
|
|
1064
1341
|
|
|
1065
|
-
|
|
1066
|
-
mesg = f'Provisioning aha:network must be equal to the Aha servers network. Expected {mynetw}, got {netw}'
|
|
1067
|
-
raise s_exc.BadConfValu(mesg=mesg, name='aha:network', expected=mynetw, got=netw)
|
|
1342
|
+
conf['aha:network'] = netw
|
|
1068
1343
|
|
|
1069
1344
|
hostname = f'{name}.{netw}'
|
|
1070
1345
|
|
|
@@ -1088,15 +1363,11 @@ class AhaCell(s_cell.Cell):
|
|
|
1088
1363
|
if peer:
|
|
1089
1364
|
conf.setdefault('aha:leader', leader)
|
|
1090
1365
|
|
|
1091
|
-
# allow user to win over leader
|
|
1092
|
-
ahauser = conf.get('aha:user')
|
|
1093
|
-
ahaurls = s_telepath.modurl(ahaurls, user=ahauser)
|
|
1094
|
-
|
|
1095
1366
|
conf.setdefault('aha:registry', ahaurls)
|
|
1096
1367
|
|
|
1097
1368
|
mirname = provinfo.get('mirror')
|
|
1098
1369
|
if mirname is not None:
|
|
1099
|
-
conf['mirror'] = f'aha://{ahauser}@{mirname}
|
|
1370
|
+
conf['mirror'] = f'aha://{ahauser}@{mirname}...'
|
|
1100
1371
|
|
|
1101
1372
|
user = await self.auth.getUserByName(ahauser)
|
|
1102
1373
|
if user is None:
|
|
@@ -1122,7 +1393,10 @@ class AhaCell(s_cell.Cell):
|
|
|
1122
1393
|
|
|
1123
1394
|
def _getProvClientUrl(self, iden):
|
|
1124
1395
|
|
|
1125
|
-
provlisn = self.
|
|
1396
|
+
provlisn = self._getProvListen()
|
|
1397
|
+
|
|
1398
|
+
provport = self.provaddr[1]
|
|
1399
|
+
provhost = self._getDnsName()
|
|
1126
1400
|
|
|
1127
1401
|
urlinfo = s_telepath.chopurl(provlisn)
|
|
1128
1402
|
|
|
@@ -1133,8 +1407,8 @@ class AhaCell(s_cell.Cell):
|
|
|
1133
1407
|
host = urlinfo.get('host')
|
|
1134
1408
|
|
|
1135
1409
|
newinfo = {
|
|
1136
|
-
'host':
|
|
1137
|
-
'port':
|
|
1410
|
+
'host': provhost,
|
|
1411
|
+
'port': provport,
|
|
1138
1412
|
'scheme': scheme,
|
|
1139
1413
|
'path': '/' + iden,
|
|
1140
1414
|
}
|
|
@@ -1171,25 +1445,25 @@ class AhaCell(s_cell.Cell):
|
|
|
1171
1445
|
userinfo = s_msgpack.un(byts)
|
|
1172
1446
|
logger.info(f'Deleted user enrollment username={userinfo.get("name")}, iden={iden.decode()}')
|
|
1173
1447
|
|
|
1448
|
+
@s_nexus.Pusher.onPushAuto('aha:clone:clear')
|
|
1449
|
+
async def clearAhaClones(self):
|
|
1450
|
+
for lkey, byts in self.slab.scanByFull(db='aha:clones'):
|
|
1451
|
+
self.slab.delete(lkey, db='aha:clones')
|
|
1452
|
+
cloninfo = s_msgpack.un(byts)
|
|
1453
|
+
logger.info(f'Deleted AHA clone enrollment username={cloninfo.get("host")}, iden={s_common.ehex(lkey)}')
|
|
1454
|
+
|
|
1174
1455
|
@s_nexus.Pusher.onPushAuto('aha:svc:prov:del')
|
|
1175
1456
|
async def delAhaSvcProv(self, iden):
|
|
1176
1457
|
self.slab.delete(iden.encode(), db='aha:provs')
|
|
1177
1458
|
|
|
1178
1459
|
async def addAhaUserEnroll(self, name, userinfo=None, again=False):
|
|
1179
1460
|
|
|
1180
|
-
provurl = self.conf.get('provision:listen')
|
|
1181
|
-
if provurl is None:
|
|
1182
|
-
mesg = 'The AHA server does not have a provision:listen URL!'
|
|
1183
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
1184
|
-
|
|
1185
|
-
ahanetw = self.conf.get('aha:network')
|
|
1186
|
-
if ahanetw is None:
|
|
1187
|
-
mesg = 'AHA server requires aha:network configuration.'
|
|
1188
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
1189
|
-
|
|
1190
1461
|
if not name:
|
|
1191
1462
|
raise s_exc.BadArg(mesg='Empty name values are not allowed for provisioning.')
|
|
1192
1463
|
|
|
1464
|
+
provurl = self._reqProvListen()
|
|
1465
|
+
ahanetw = self.conf.req('aha:network')
|
|
1466
|
+
|
|
1193
1467
|
username = f'{name}@{ahanetw}'
|
|
1194
1468
|
|
|
1195
1469
|
if len(username) > 64:
|