diff --git a/lib/charms/mongodb/v0/config_server_interface.py b/lib/charms/mongodb/v0/config_server_interface.py index a8791a1f..7d863d7e 100644 --- a/lib/charms/mongodb/v0/config_server_interface.py +++ b/lib/charms/mongodb/v0/config_server_interface.py @@ -16,7 +16,13 @@ from charms.mongodb.v1.mongos import MongosConnection from ops.charm import CharmBase, EventBase, RelationBrokenEvent from ops.framework import Object -from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, WaitingStatus +from ops.model import ( + ActiveStatus, + BlockedStatus, + MaintenanceStatus, + StatusBase, + WaitingStatus, +) from config import Config @@ -36,7 +42,7 @@ # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 9 +LIBPATCH = 10 class ClusterProvider(Object): @@ -230,6 +236,10 @@ def _on_database_created(self, event) -> None: def _on_relation_changed(self, event) -> None: """Starts/restarts monogs with config server information.""" + if not self.pass_hook_checks(event): + logger.info("pre-hook checks did not pass, not executing event") + return + key_file_contents = self.database_requires.fetch_relation_field( event.relation.id, KEYFILE_KEY ) @@ -286,6 +296,34 @@ def _on_relation_broken(self, event: RelationBrokenEvent) -> None: self.charm.remove_connection_info() # BEGIN: helper functions + def pass_hook_checks(self, event): + """Runs the pre-hooks checks for ClusterRequirer, returns True if all pass.""" + if self.is_mongos_tls_missing(): + logger.info( + "Deferring %s. Config-server uses TLS, but mongos does not. Please synchronise encryption methods.", + str(type(event)), + ) + event.defer() + return False + + if self.is_config_server_tls_missing(): + logger.info( + "Deferring %s. mongos uses TLS, but config-server does not. Please synchronise encryption methods.", + str(type(event)), + ) + event.defer() + return False + + if not self.is_ca_compatible(): + logger.info( + "Deferring %s. mongos is integrated to a different CA than the config server. Please use the same CA for all cluster components.", + str(type(event)), + ) + + event.defer() + return False + + return True def is_mongos_running(self) -> bool: """Returns true if mongos service is running.""" @@ -326,6 +364,22 @@ def update_keyfile(self, key_file_contents: str) -> bool: return True + def get_tls_statuses(self) -> Optional[StatusBase]: + """Returns statuses relevant to TLS.""" + if self.is_mongos_tls_missing(): + return BlockedStatus("mongos requires TLS to be enabled.") + + if self.is_config_server_tls_missing(): + return BlockedStatus("mongos has TLS enabled, but config-server does not.") + + if not self.is_ca_compatible(): + logger.error( + "mongos is integrated to a different CA than the config server. Please use the same CA for all cluster components." + ) + return BlockedStatus("mongos CA and Config-Server CA don't match.") + + return + def get_config_server_name(self) -> Optional[str]: """Returns the name of the Juju Application that mongos is using as a config server.""" if not self.model.get_relation(self.relation_name): @@ -334,4 +388,57 @@ def get_config_server_name(self) -> Optional[str]: # metadata.yaml prevents having multiple config servers return self.model.get_relation(self.relation_name).app.name + def is_ca_compatible(self) -> bool: + """Returns true if both the mongos and the config server use the same CA.""" + config_server_relation = self.charm.model.get_relation(self.relation_name) + # base-case: nothing to compare + if not config_server_relation: + return True + + config_server_tls_ca = self.database_requires.fetch_relation_field( + config_server_relation.id, INT_TLS_CA_KEY + ) + + mongos_tls_ca = self.charm.tls.get_tls_secret( + internal=True, label_name=Config.TLS.SECRET_CA_LABEL + ) + + # base-case: missing one or more CA's to compare + if not config_server_tls_ca and not mongos_tls_ca: + return True + + return config_server_tls_ca == mongos_tls_ca + + def is_mongos_tls_missing(self) -> bool: + """Returns true if the config-server has TLS enabled but mongos does not.""" + config_server_relation = self.charm.model.get_relation(self.relation_name) + if not config_server_relation: + return False + + mongos_has_tls = self.charm.model.get_relation(Config.TLS.TLS_PEER_RELATION) is not None + config_server_has_tls = ( + self.database_requires.fetch_relation_field(config_server_relation.id, INT_TLS_CA_KEY) + is not None + ) + if config_server_has_tls and not mongos_has_tls: + return True + + return False + + def is_config_server_tls_missing(self) -> bool: + """Returns true if the mongos has TLS enabled but the config-server does not.""" + config_server_relation = self.charm.model.get_relation(self.relation_name) + if not config_server_relation: + return False + + mongos_has_tls = self.charm.model.get_relation(Config.TLS.TLS_PEER_RELATION) is not None + config_server_has_tls = ( + self.database_requires.fetch_relation_field(config_server_relation.id, INT_TLS_CA_KEY) + is not None + ) + if not config_server_has_tls and mongos_has_tls: + return True + + return False + # END: helper functions diff --git a/lib/charms/mongodb/v1/shards_interface.py b/lib/charms/mongodb/v1/shards_interface.py index 1326b3bc..9253e145 100644 --- a/lib/charms/mongodb/v1/shards_interface.py +++ b/lib/charms/mongodb/v1/shards_interface.py @@ -56,7 +56,7 @@ # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 12 +LIBPATCH = 13 KEYFILE_KEY = "key-file" HOSTS_KEY = "host" OPERATOR_PASSWORD_KEY = MongoDBUser.get_password_key_name_for_user(OperatorUser.get_username()) @@ -711,7 +711,7 @@ def pass_hook_checks(self, event): logger.info("Config-server relation never set up, no need to process broken event.") return False - if self.is_shard_tls_needed(): + if self.is_shard_tls_missing(): logger.info( "Deferring %s. Config-server uses TLS, but shard does not. Please synchronise encryption methods.", str(type(event)), @@ -719,7 +719,7 @@ def pass_hook_checks(self, event): event.defer() return False - if self.is_config_server_tls_needed(): + if self.is_config_server_tls_missing(): logger.info( "Deferring %s. Shard uses TLS, but config-server does not. Please synchronise encryption methods.", str(type(event)), @@ -817,10 +817,10 @@ def get_relations_statuses(self) -> Optional[StatusBase]: def get_tls_statuses(self) -> Optional[StatusBase]: """Returns statuses relevant to TLS.""" - if self.is_shard_tls_needed(): + if self.is_shard_tls_missing(): return BlockedStatus("Shard requires TLS to be enabled.") - if self.is_config_server_tls_needed(): + if self.is_config_server_tls_missing(): return BlockedStatus("Shard has TLS enabled, but config-server does not.") if not self.is_ca_compatible(): @@ -1112,7 +1112,7 @@ def is_ca_compatible(self) -> bool: return config_server_tls_ca == shard_tls_ca - def is_shard_tls_needed(self) -> bool: + def is_shard_tls_missing(self) -> bool: """Returns true if the config-server has TLS enabled but the shard does not.""" config_server_relation = self.charm.model.get_relation(self.relation_name) if not config_server_relation: @@ -1128,7 +1128,7 @@ def is_shard_tls_needed(self) -> bool: return False - def is_config_server_tls_needed(self) -> bool: + def is_config_server_tls_missing(self) -> bool: """Returns true if the shard has TLS enabled but the config-server does not.""" config_server_relation = self.charm.model.get_relation(self.relation_name) if not config_server_relation: diff --git a/src/charm.py b/src/charm.py index 32e30d47..3b58dedc 100755 --- a/src/charm.py +++ b/src/charm.py @@ -554,7 +554,7 @@ def _on_update_status(self, event: UpdateStatusEvent): # edge case: mongod will fail to run if 1. they are running as shard and 2. they # have already been added to the cluster with internal membership via TLS and 3. # they remove support for TLS - if self.is_role(Config.Role.SHARD) and self.shard.is_shard_tls_needed(): + if self.is_role(Config.Role.SHARD) and self.shard.is_shard_tls_missing(): self.unit.status = BlockedStatus("Shard requires TLS to be enabled.") return else: