726
def is_sync_master():
728
log("Not syncing certs since there are no peer units.", level=INFO)
731
# If no ssl master elected and we are cluster leader, elect this unit.
732
if is_elected_leader(CLUSTER_RES):
734
for rid in relation_ids('cluster'):
735
for unit in related_units(rid):
736
m = relation_get(rid=rid, unit=unit,
737
attribute='ssl-cert-master')
742
if not master or ('unknown' in master and len(master) == 1):
743
log("Electing this unit as ssl-cert-master", level=DEBUG)
744
for rid in relation_ids('cluster'):
745
settings = {'ssl-cert-master': local_unit(),
746
'ssl-synced-units': None}
747
relation_set(relation_id=rid, relation_settings=settings)
749
# Return now and wait for cluster-relation-changed for sync.
752
if not is_ssl_cert_master():
753
log("Not ssl cert master - skipping sync", level=INFO)
723
759
def synchronize_ca(fatal=True):
724
760
"""Broadcast service credentials to peers.
734
770
paths_to_sync = [SYNC_FLAGS_DIR]
737
log("Not syncing certs since there are no peer units.", level=INFO)
740
# If no ssl master elected and we are cluster leader, elect this unit.
741
if is_elected_leader(CLUSTER_RES):
742
master = relation_get(attribute='ssl-cert-master')
743
if not master or master == 'unknown':
744
log("Electing this unit as ssl-cert-master", level=DEBUG)
745
for rid in relation_ids('cluster'):
746
relation_set(relation_id=rid,
747
relation_settings={'ssl-cert-master':
748
unit_get('private-address'),
749
'trigger': str(uuid.uuid4())})
750
# Return now and wait for echo before continuing.
753
if not is_ssl_cert_master():
754
log("Not ssl cert master - skipping sync", level=INFO)
757
772
if str_is_true(config('https-service-endpoints')):
758
773
log("Syncing all endpoint certs since https-service-endpoints=True",
795
809
log("Sync failed but fatal=False", level=INFO)
813
shutil.rmtree(SYNC_FLAGS_DIR)
814
mkdir(SYNC_FLAGS_DIR, SSH_USER, 'keystone', 0o775)
798
816
trigger = str(uuid.uuid4())
799
817
log("Sending restart-services-trigger=%s to all peers" % (trigger),
801
settings = {'restart-services-trigger': trigger}
804
shutil.rmtree(SYNC_FLAGS_DIR)
805
mkdir(SYNC_FLAGS_DIR, SSH_USER, 'keystone', 0o775)
807
# If we are the sync master but no longer leader then re-elect master.
808
if not is_elected_leader(CLUSTER_RES):
809
log("Re-electing ssl cert master.", level=INFO)
810
settings['ssl-cert-master'] = 'unknown'
812
log("Sync complete - sending peer info", level=DEBUG)
813
for rid in relation_ids('cluster'):
814
relation_set(relation_id=rid, **settings)
820
log("Sync complete", level=DEBUG)
821
return {'restart-services-trigger': trigger,
822
'ssl-synced-units': peer_units()}
825
def update_hash_from_path(hash, path, recurse_depth=10):
826
"""Recurse through path and update the provided hash for every file found.
828
if not recurse_depth:
829
log("Max recursion depth (%s) reached for update_hash_from_path() at "
830
"path='%s' - not going any deeper" % (recurse_depth, path),
834
for p in glob.glob("%s/*" % path):
836
update_hash_from_path(hash, p, recurse_depth=recurse_depth - 1)
838
with open(p, 'r') as fd:
839
hash.update(fd.read())
842
def synchronize_ca_if_changed(force=False, fatal=True):
843
"""Decorator to perform ssl cert sync if decorated function modifies them
846
If force is True a sync is done regardless.
848
def inner_synchronize_ca_if_changed1(f):
849
def inner_synchronize_ca_if_changed2(*args, **kwargs):
850
if not is_sync_master():
851
return f(*args, **kwargs)
855
# Ensure we don't do a double sync if we are nested.
856
if not force and SSL_SYNC_SEMAPHORE.acquire(blocking=0):
857
hash1 = hashlib.sha256()
858
for path in [SSL_DIR, APACHE_SSL_DIR, CA_CERT_PATH]:
859
update_hash_from_path(hash1, path)
861
hash1 = hash1.hexdigest()
863
ret = f(*args, **kwargs)
865
hash2 = hashlib.sha256()
866
for path in [SSL_DIR, APACHE_SSL_DIR, CA_CERT_PATH]:
867
update_hash_from_path(hash2, path)
869
hash2 = hash2.hexdigest()
871
log("SSL certs have changed - syncing peers",
873
peer_settings = synchronize_ca(fatal=fatal)
875
log("SSL certs have not changed - skipping sync",
878
ret = f(*args, **kwargs)
880
log("Doing forced ssl cert sync", level=DEBUG)
881
peer_settings = synchronize_ca(fatal=fatal)
883
# If we are the sync master but no longer leader then re-elect
885
if not is_elected_leader(CLUSTER_RES):
886
log("Re-electing ssl cert master.", level=INFO)
887
peer_settings['ssl-cert-master'] = 'unknown'
889
for rid in relation_ids('cluster'):
890
relation_set(relation_id=rid,
891
relation_settings=peer_settings)
895
SSL_SYNC_SEMAPHORE.release()
897
return inner_synchronize_ca_if_changed2
899
return inner_synchronize_ca_if_changed1
817
902
def get_ca(user='keystone', group='keystone'):