Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
elasticapm/conf: block disabling of verify cert server in fips mode
This requires to add validation support to _BoolConfigValue
  • Loading branch information
xrmx committed Mar 3, 2025
commit cffdb786f6e57544f280f19f7252bfc5ec236d9f
33 changes: 32 additions & 1 deletion elasticapm/conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import threading
from datetime import timedelta

import _hashlib

from elasticapm.conf.constants import BASE_SANITIZE_FIELD_NAMES, TRACE_CONTINUATION_STRATEGY
from elasticapm.utils import compat, starmatch_to_regex
from elasticapm.utils.logging import get_logger
Expand Down Expand Up @@ -220,6 +222,8 @@ class _BoolConfigValue(_ConfigValue):
def __init__(self, dict_key, true_string="true", false_string="false", **kwargs) -> None:
self.true_string = true_string
self.false_string = false_string
# this is necessary to have the bool type preserved in _validate
kwargs["type"] = bool
super(_BoolConfigValue, self).__init__(dict_key, **kwargs)

def __set__(self, instance, value) -> None:
Expand All @@ -228,6 +232,7 @@ def __set__(self, instance, value) -> None:
value = True
elif value.lower() == self.false_string:
value = False
value = self._validate(instance, value)
self._callback_if_changed(instance, value)
instance._values[self.dict_key] = bool(value)

Expand Down Expand Up @@ -373,6 +378,30 @@ def __call__(self, value, field_name):
return value


def _in_fips_mode():
try:
return _hashlib.get_fips_mode() == 1
except AttributeError:
# versions older of Python3.9 do not have the helper
return False


class SupportedValueInFipsModeValidator(object):
"""If FIPS mode is enabled only supported_value is accepted"""

def __init__(self, supported_value) -> None:
self.supported_value = supported_value

def __call__(self, value, field_name):
if _in_fips_mode():
if value != self.supported_value:
raise ConfigurationError(
"{}={} must be set to {} if FIPS mode is enabled".format(field_name, value, self.supported_value),
field_name,
)
return value


class EnumerationValidator(object):
"""
Validator which ensures that a given config value is chosen from a list
Expand Down Expand Up @@ -579,7 +608,9 @@ class Config(_ConfigBase):
server_url = _ConfigValue("SERVER_URL", default="http://127.0.0.1:8200", required=True)
server_cert = _ConfigValue("SERVER_CERT", validators=[FileIsReadableValidator()])
server_ca_cert_file = _ConfigValue("SERVER_CA_CERT_FILE", validators=[FileIsReadableValidator()])
verify_server_cert = _BoolConfigValue("VERIFY_SERVER_CERT", default=True)
verify_server_cert = _BoolConfigValue(
"VERIFY_SERVER_CERT", default=True, validators=[SupportedValueInFipsModeValidator(supported_value=True)]
)
use_certifi = _BoolConfigValue("USE_CERTIFI", default=True)
include_paths = _ListConfigValue("INCLUDE_PATHS")
exclude_paths = _ListConfigValue("EXCLUDE_PATHS", default=compat.get_default_library_patters())
Expand Down
35 changes: 35 additions & 0 deletions tests/config/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import mock
import pytest

import elasticapm.conf
from elasticapm.conf import (
Config,
ConfigurationError,
Expand All @@ -47,6 +48,7 @@
FileIsReadableValidator,
PrecisionValidator,
RegexValidator,
SupportedValueInFipsModeValidator,
UnitValidator,
VersionedConfig,
_BoolConfigValue,
Expand Down Expand Up @@ -490,3 +492,36 @@ def test_exclude_range_validator_not_in_range():
with pytest.raises(ConfigurationError) as e:
validator(10, "field")
assert "cannot be in range" in e.value.args[0]


def test_supported_value_in_fips_mode_validator_in_fips_mode_with_invalid_value(monkeypatch):
monkeypatch.setattr(elasticapm.conf, "_in_fips_mode", lambda: True)
exception_message = "VERIFY_SERVER_CERT=False must be set to True if FIPS mode is enabled"
validator = SupportedValueInFipsModeValidator(supported_value=True)
with pytest.raises(ConfigurationError) as e:
validator(False, "VERIFY_SERVER_CERT")
assert exception_message == e.value.args[0]

config = Config({"VERIFY_SERVER_CERT": False})
assert config.errors["VERIFY_SERVER_CERT"] == exception_message


def test_supported_value_in_fips_mode_validator_in_fips_mode_with_valid_value(monkeypatch):
monkeypatch.setattr(elasticapm.conf, "_in_fips_mode", lambda: True)
validator = SupportedValueInFipsModeValidator(supported_value=True)
assert validator(True, "VERIFY_SERVER_CERT") == True
config = Config({"VERIFY_SERVER_CERT": True})
assert config.verify_server_cert == True
assert "VERIFY_SERVER_CERT" not in config.errors


def test_supported_value_in_fips_mode_validator_not_in_fips_mode(monkeypatch):
monkeypatch.setattr(elasticapm.conf, "_in_fips_mode", lambda: False)
validator = SupportedValueInFipsModeValidator(supported_value=True)
assert validator(True, "field") == True
assert validator(False, "field") == False

config = Config({"VERIFY_SERVER_CERT": False})
assert not config.errors
config = Config({"VERIFY_SERVER_CERT": True})
assert not config.errors