Skip to content

Commit

Permalink
[DPE-3753] mongos must check tls inconsistencies (#388)
Browse files Browse the repository at this point in the history
## Issue
mongos charm must check + report tls inconsistencies

## Solution
add a check in the shared lib code
  • Loading branch information
MiaAltieri authored Mar 26, 2024
1 parent 9e1ccd9 commit d264906
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 10 deletions.
111 changes: 109 additions & 2 deletions lib/charms/mongodb/v0/config_server_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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):
Expand All @@ -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
14 changes: 7 additions & 7 deletions lib/charms/mongodb/v1/shards_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -711,15 +711,15 @@ 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)),
)
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)),
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit d264906

Please sign in to comment.