mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-02 07:13:02 +00:00
feat(fips): read FIPS mode from general_settings.fips_mode instead of LITELLM_FIPS_MODE
This commit is contained in:
parent
c4728fc95e
commit
6569e2c132
7 changed files with 142 additions and 100 deletions
|
|
@ -2507,6 +2507,13 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase):
|
|||
None,
|
||||
description="local development only: start even when master_key is unset, empty, or a publicly known default",
|
||||
)
|
||||
fips_mode: bool | None = Field(
|
||||
None,
|
||||
description=(
|
||||
"refuse to start unless Python's and the cryptography library's OpenSSL enforce FIPS, and only start on "
|
||||
"a new database or one set up in FIPS mode"
|
||||
),
|
||||
)
|
||||
coordination_redis: CoordinationRedisParams | None = Field(
|
||||
None,
|
||||
description=(
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
import os
|
||||
from collections.abc import Mapping
|
||||
from typing import Final
|
||||
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.repositories.fips_repository import FIPS_MARKER, FipsRepository
|
||||
from litellm.secret_managers.main import str_to_bool
|
||||
|
||||
FIPS_MODE_ENV_VAR: Final = "LITELLM_FIPS_MODE"
|
||||
FIPS_MODE_SETTING: Final = "fips_mode"
|
||||
_SETTING: Final = f"general_settings.{FIPS_MODE_SETTING}"
|
||||
|
||||
|
||||
class FipsModeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def is_fips_mode() -> bool:
|
||||
return str_to_bool(os.getenv(FIPS_MODE_ENV_VAR)) is True
|
||||
def _fips_mode_setting(general_settings: Mapping[str, object]) -> bool | None:
|
||||
"""general_settings.fips_mode as a boolean: False when unset, None when it is not a boolean."""
|
||||
value: Final = general_settings.get(FIPS_MODE_SETTING)
|
||||
if value is None or isinstance(value, bool):
|
||||
return value is True
|
||||
return str_to_bool(value) if isinstance(value, str) else None
|
||||
|
||||
|
||||
def is_fips_mode(general_settings: Mapping[str, object]) -> bool:
|
||||
return _fips_mode_setting(general_settings) is True
|
||||
|
||||
|
||||
def openssl_enforces_fips() -> bool:
|
||||
|
|
@ -49,31 +58,34 @@ def cryptography_enforces_fips() -> bool:
|
|||
return True
|
||||
|
||||
|
||||
def fips_mode_refusal() -> str | None:
|
||||
"""Why the proxy must not start: LITELLM_FIPS_MODE is not a boolean, or is on but Python's or the
|
||||
def fips_mode_refusal(general_settings: Mapping[str, object]) -> str | None:
|
||||
"""Why the proxy must not start: general_settings.fips_mode is not a boolean, or is on but Python's or the
|
||||
cryptography library's OpenSSL does not enforce FIPS. None when it may start."""
|
||||
value: Final = os.getenv(FIPS_MODE_ENV_VAR)
|
||||
if value and str_to_bool(value) is None:
|
||||
return f"{FIPS_MODE_ENV_VAR}={value!r} is not a boolean. Set it to true or false, or unset it."
|
||||
if not is_fips_mode():
|
||||
setting: Final = _fips_mode_setting(general_settings)
|
||||
if setting is None:
|
||||
return (
|
||||
f"{_SETTING}={general_settings.get(FIPS_MODE_SETTING)!r} is not a boolean. Set it to true or false, "
|
||||
"or remove it."
|
||||
)
|
||||
if not setting:
|
||||
return None
|
||||
if not openssl_enforces_fips():
|
||||
return (
|
||||
f"{FIPS_MODE_ENV_VAR} is on but OpenSSL does not enforce FIPS. Run the proxy on an OpenSSL with "
|
||||
f"its FIPS provider active and default_properties = fips=yes, or unset {FIPS_MODE_ENV_VAR}."
|
||||
f"{_SETTING} is on but OpenSSL does not enforce FIPS. Run the proxy on an OpenSSL with its FIPS "
|
||||
f"provider active and default_properties = fips=yes, or turn {_SETTING} off."
|
||||
)
|
||||
if not cryptography_enforces_fips():
|
||||
return (
|
||||
f"{FIPS_MODE_ENV_VAR} is on but the cryptography library's OpenSSL has no working FIPS provider. Build it "
|
||||
"against the system OpenSSL (pip install --no-binary cryptography cryptography), or unset "
|
||||
f"{FIPS_MODE_ENV_VAR}."
|
||||
f"{_SETTING} is on but the cryptography library's OpenSSL has no working FIPS provider. Build it "
|
||||
"against the system OpenSSL (pip install --no-binary cryptography cryptography), or turn "
|
||||
f"{_SETTING} off."
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def enforce_fips_mode() -> None:
|
||||
def enforce_fips_mode(general_settings: Mapping[str, object]) -> None:
|
||||
"""Raise FipsModeError with the fips_mode_refusal reason, if there is one."""
|
||||
refusal: Final = fips_mode_refusal()
|
||||
refusal: Final = fips_mode_refusal(general_settings)
|
||||
if refusal is not None:
|
||||
raise FipsModeError(refusal)
|
||||
|
||||
|
|
@ -86,40 +98,42 @@ async def _is_marked_fips(database: FipsRepository) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
async def fips_database_refusal(database: FipsRepository | None, *, database_is_configured: bool) -> str | None:
|
||||
"""Why the proxy must not start on its database: LITELLM_FIPS_MODE is off on a database set up in FIPS mode, or
|
||||
on over a database that can't be checked or already holds rows written without it. None when it may start."""
|
||||
if not is_fips_mode():
|
||||
async def fips_database_refusal(
|
||||
database: FipsRepository | None, *, fips_mode: bool, database_is_configured: bool
|
||||
) -> str | None:
|
||||
"""Why the proxy must not start on its database: FIPS mode is off on a database set up in FIPS mode, or on over
|
||||
a database that can't be checked or already holds rows written without it. None when it may start."""
|
||||
if not fips_mode:
|
||||
if database is not None and await _is_marked_fips(database):
|
||||
return (
|
||||
f"This database was set up with {FIPS_MODE_ENV_VAR}=true, so the proxy only starts on it in FIPS mode. "
|
||||
f"This database was set up with {_SETTING}: true, so the proxy only starts on it in FIPS mode. "
|
||||
f"To stop using FIPS mode on it for good, run "
|
||||
f"DELETE FROM \"LiteLLM_Config\" WHERE param_name = '{FIPS_MARKER}'."
|
||||
)
|
||||
return None
|
||||
if database is None:
|
||||
return (
|
||||
f"{FIPS_MODE_ENV_VAR} is on but the database can't be reached to check it."
|
||||
if database_is_configured
|
||||
else None
|
||||
)
|
||||
return f"{_SETTING} is on but the database can't be reached to check it." if database_is_configured else None
|
||||
try:
|
||||
state: Final = await database.state()
|
||||
except Exception as error: # noqa: BLE001 # a value, so FIPS mode refuses a database it can't check
|
||||
return f"{FIPS_MODE_ENV_VAR} is on but the database could not be checked: {error}"
|
||||
return f"{_SETTING} is on but the database could not be checked: {error}"
|
||||
if state.holds_rows and not state.marked_fips:
|
||||
return (
|
||||
f"{FIPS_MODE_ENV_VAR} is on but this database already holds data written without it. "
|
||||
f"{_SETTING} is on but this database already holds data written without it. "
|
||||
"FIPS mode only starts on a new, empty database."
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
async def enforce_fips_database(database: FipsRepository | None, *, database_is_configured: bool) -> None:
|
||||
async def enforce_fips_database(
|
||||
database: FipsRepository | None, *, fips_mode: bool, database_is_configured: bool
|
||||
) -> None:
|
||||
"""Raise FipsModeError with the fips_database_refusal reason, if there is one, then mark the database as set up
|
||||
in FIPS mode when LITELLM_FIPS_MODE is on."""
|
||||
refusal: Final = await fips_database_refusal(database, database_is_configured=database_is_configured)
|
||||
in FIPS mode when FIPS mode is on."""
|
||||
refusal: Final = await fips_database_refusal(
|
||||
database, fips_mode=fips_mode, database_is_configured=database_is_configured
|
||||
)
|
||||
if refusal is not None:
|
||||
raise FipsModeError(refusal)
|
||||
if database is not None and is_fips_mode():
|
||||
if database is not None and fips_mode:
|
||||
await database.mark_fips()
|
||||
|
|
|
|||
|
|
@ -499,7 +499,7 @@ from litellm.proxy.common_utils.encrypt_decrypt_utils import (
|
|||
encrypt_value_helper,
|
||||
)
|
||||
from litellm.proxy.common_utils.error_body_call_id import JSON_OBJECT, error_body_call_id, with_call_id
|
||||
from litellm.proxy.common_utils.fips import enforce_fips_database, enforce_fips_mode
|
||||
from litellm.proxy.common_utils.fips import enforce_fips_database, enforce_fips_mode, is_fips_mode
|
||||
from litellm.proxy.common_utils.healthy_model_filter import (
|
||||
get_hidden_unhealthy_model_names,
|
||||
is_healthy_only_listing_default,
|
||||
|
|
@ -1607,7 +1607,7 @@ async def _proxy_lifespan(app: FastAPI, resources: AsyncExitStack) -> AsyncGener
|
|||
if decoded_worker_config is not None:
|
||||
await initialize_from_worker_config(decoded_worker_config)
|
||||
|
||||
enforce_fips_mode()
|
||||
enforce_fips_mode(general_settings_view())
|
||||
|
||||
async with AsyncExitStack() as boot_database:
|
||||
enforce_master_key_boot_verdict(
|
||||
|
|
@ -1643,6 +1643,7 @@ async def _proxy_lifespan(app: FastAPI, resources: AsyncExitStack) -> AsyncGener
|
|||
|
||||
await enforce_fips_database(
|
||||
None if prisma_client is None else FipsRepository(prisma_client),
|
||||
fips_mode=is_fips_mode(general_settings_view()),
|
||||
database_is_configured=get_secret("DATABASE_URL", None) is not None,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from tests.integration._support.database import scratch_database, write_rows
|
|||
from tests.integration._support.process import refused_boot_log
|
||||
|
||||
|
||||
def _config(directory: Path, upstream_url: str) -> Path:
|
||||
def _config(directory: Path, upstream_url: str, fips_mode: object) -> Path:
|
||||
config: Final = directory / "fips_boot.yaml"
|
||||
config.write_text(
|
||||
json.dumps(
|
||||
|
|
@ -21,6 +21,7 @@ def _config(directory: Path, upstream_url: str) -> Path:
|
|||
"litellm_params": {"model": "openai/fips-model", "api_base": upstream_url, "api_key": "sk-x"},
|
||||
}
|
||||
],
|
||||
"general_settings": {"fips_mode": fips_mode},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -30,17 +31,13 @@ def _config(directory: Path, upstream_url: str) -> Path:
|
|||
def test_fips_mode_refuses_to_start_when_openssl_does_not_enforce_fips(gateway: Gateway, tmp_path: Path) -> None:
|
||||
if _hashlib.get_fips_mode() == 1:
|
||||
pytest.skip("this runner's OpenSSL is in FIPS mode")
|
||||
log: Final = refused_boot_log(
|
||||
gateway, tmp_path, {"LITELLM_FIPS_MODE": "true"}, config=_config(tmp_path, gateway.upstream_url)
|
||||
)
|
||||
assert "LITELLM_FIPS_MODE is on but OpenSSL does not enforce FIPS" in log, log
|
||||
log: Final = refused_boot_log(gateway, tmp_path, {}, config=_config(tmp_path, gateway.upstream_url, True))
|
||||
assert "general_settings.fips_mode is on but OpenSSL does not enforce FIPS" in log, log
|
||||
|
||||
|
||||
def test_fips_mode_refuses_to_start_on_a_value_that_is_not_a_boolean(gateway: Gateway, tmp_path: Path) -> None:
|
||||
log: Final = refused_boot_log(
|
||||
gateway, tmp_path, {"LITELLM_FIPS_MODE": "enforced"}, config=_config(tmp_path, gateway.upstream_url)
|
||||
)
|
||||
assert "LITELLM_FIPS_MODE='enforced' is not a boolean" in log, log
|
||||
log: Final = refused_boot_log(gateway, tmp_path, {}, config=_config(tmp_path, gateway.upstream_url, "enforced"))
|
||||
assert "general_settings.fips_mode='enforced' is not a boolean" in log, log
|
||||
|
||||
|
||||
def test_proxy_refuses_to_start_without_fips_mode_on_a_database_set_up_in_fips_mode(
|
||||
|
|
@ -61,7 +58,7 @@ def test_proxy_refuses_to_start_without_fips_mode_on_a_database_set_up_in_fips_m
|
|||
log: Final = refused_boot_log(
|
||||
gateway,
|
||||
tmp_path,
|
||||
{"LITELLM_FIPS_MODE": "false", "DATABASE_URL": database_url},
|
||||
config=_config(tmp_path, gateway.upstream_url),
|
||||
{"DATABASE_URL": database_url},
|
||||
config=_config(tmp_path, gateway.upstream_url, False),
|
||||
)
|
||||
assert "This database was set up with LITELLM_FIPS_MODE=true" in log, log
|
||||
assert "This database was set up with general_settings.fips_mode: true" in log, log
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ def _marker_rows(database_url: str) -> int:
|
|||
@pytest.mark.parametrize(
|
||||
"fips_mode, setup, refusal, marked_after",
|
||||
[
|
||||
("true", (_CONFIG_TABLE, _KEY_TABLE), None, 1),
|
||||
("true", (_CONFIG_TABLE, _KEY_TABLE, _MARKER, _KEY), None, 1),
|
||||
("true", (_CONFIG_TABLE, _KEY_TABLE, _KEY), "already holds data written without it", 0),
|
||||
("true", (_CONFIG_TABLE, _KEY_TABLE, _SETTING), "already holds data written without it", 0),
|
||||
("true", (), "could not be checked", None),
|
||||
("false", (_CONFIG_TABLE, _KEY_TABLE, _MARKER), "set up with LITELLM_FIPS_MODE=true", 1),
|
||||
("false", (_CONFIG_TABLE, _KEY_TABLE, _KEY), None, 0),
|
||||
("false", (), None, None),
|
||||
(True, (_CONFIG_TABLE, _KEY_TABLE), None, 1),
|
||||
(True, (_CONFIG_TABLE, _KEY_TABLE, _MARKER, _KEY), None, 1),
|
||||
(True, (_CONFIG_TABLE, _KEY_TABLE, _KEY), "already holds data written without it", 0),
|
||||
(True, (_CONFIG_TABLE, _KEY_TABLE, _SETTING), "already holds data written without it", 0),
|
||||
(True, (), "could not be checked", None),
|
||||
(False, (_CONFIG_TABLE, _KEY_TABLE, _MARKER), "set up with general_settings.fips_mode: true", 1),
|
||||
(False, (_CONFIG_TABLE, _KEY_TABLE, _KEY), None, 0),
|
||||
(False, (), None, None),
|
||||
],
|
||||
ids=[
|
||||
"fips-on-new-database",
|
||||
|
|
@ -52,12 +52,11 @@ def _marker_rows(database_url: str) -> int:
|
|||
)
|
||||
async def test_fips_database_check(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
fips_mode: str,
|
||||
fips_mode: bool,
|
||||
setup: tuple[LiteralString, ...],
|
||||
refusal: str | None,
|
||||
marked_after: int | None,
|
||||
) -> None:
|
||||
monkeypatch.setenv("LITELLM_FIPS_MODE", fips_mode)
|
||||
with scratch_database() as database_url:
|
||||
for statement in setup:
|
||||
write_rows(statement, (), database_url=database_url)
|
||||
|
|
@ -66,7 +65,7 @@ async def test_fips_database_check(
|
|||
await client.connect()
|
||||
try:
|
||||
with pytest.raises(FipsModeError, match=refusal) if refusal else nullcontext():
|
||||
await enforce_fips_database(FipsRepository(client), database_is_configured=True)
|
||||
await enforce_fips_database(FipsRepository(client), fips_mode=fips_mode, database_is_configured=True)
|
||||
finally:
|
||||
await client.aclose()
|
||||
if marked_after is not None:
|
||||
|
|
@ -77,7 +76,6 @@ async def test_fips_database_check(
|
|||
async def test_pods_starting_together_in_fips_mode_on_a_new_database_both_start(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("LITELLM_FIPS_MODE", "true")
|
||||
with scratch_database() as database_url:
|
||||
for statement in (_CONFIG_TABLE, _KEY_TABLE):
|
||||
write_rows(statement, (), database_url=database_url)
|
||||
|
|
@ -86,7 +84,10 @@ async def test_pods_starting_together_in_fips_mode_on_a_new_database_both_start(
|
|||
await asyncio.gather(*(pod.connect() for pod in pods))
|
||||
try:
|
||||
await asyncio.gather(
|
||||
*(enforce_fips_database(FipsRepository(pod), database_is_configured=True) for pod in pods)
|
||||
*(
|
||||
enforce_fips_database(FipsRepository(pod), fips_mode=True, database_is_configured=True)
|
||||
for pod in pods
|
||||
)
|
||||
)
|
||||
finally:
|
||||
await asyncio.gather(*(pod.aclose() for pod in pods))
|
||||
|
|
|
|||
|
|
@ -32,13 +32,19 @@ def _fake_openssl(monkeypatch: pytest.MonkeyPatch, *, fips_flag: int, md5_refuse
|
|||
monkeypatch.setattr(_hashlib, "new", new)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value, expected", [("true", True), (" TRUE ", True), ("false", False), (None, False)])
|
||||
def test_is_fips_mode(monkeypatch: pytest.MonkeyPatch, value: str | None, expected: bool) -> None:
|
||||
if value is None:
|
||||
monkeypatch.delenv("LITELLM_FIPS_MODE", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("LITELLM_FIPS_MODE", value)
|
||||
assert is_fips_mode() is expected
|
||||
@pytest.mark.parametrize(
|
||||
"general_settings, expected",
|
||||
[
|
||||
({"fips_mode": True}, True),
|
||||
({"fips_mode": " TRUE "}, True),
|
||||
({"fips_mode": False}, False),
|
||||
({"fips_mode": "false"}, False),
|
||||
({"fips_mode": None}, False),
|
||||
({}, False),
|
||||
],
|
||||
)
|
||||
def test_is_fips_mode(general_settings: Mapping[str, object], expected: bool) -> None:
|
||||
assert is_fips_mode(general_settings) is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -90,34 +96,40 @@ def test_this_runners_cryptography_openssl_does_not_enforce_fips() -> None:
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fips_mode, openssl_fips, cryptography_fips, refusal",
|
||||
"general_settings, openssl_fips, cryptography_fips, refusal",
|
||||
[
|
||||
(None, False, False, None),
|
||||
("", False, False, None),
|
||||
("false", False, False, None),
|
||||
("true", True, True, None),
|
||||
("true", False, True, "is on but OpenSSL does not enforce FIPS"),
|
||||
("true", True, False, "cryptography library's OpenSSL has no working FIPS provider"),
|
||||
("enforced", True, True, "LITELLM_FIPS_MODE='enforced' is not a boolean"),
|
||||
({}, False, False, None),
|
||||
({"fips_mode": False}, False, False, None),
|
||||
({"fips_mode": True}, True, True, None),
|
||||
({"fips_mode": "true"}, True, True, None),
|
||||
({"fips_mode": True}, False, True, "general_settings.fips_mode is on but OpenSSL does not enforce FIPS"),
|
||||
({"fips_mode": True}, True, False, "cryptography library's OpenSSL has no working FIPS provider"),
|
||||
({"fips_mode": "enforced"}, True, True, "general_settings.fips_mode='enforced' is not a boolean"),
|
||||
({"fips_mode": 1}, True, True, "general_settings.fips_mode=1 is not a boolean"),
|
||||
],
|
||||
ids=[
|
||||
"unset",
|
||||
"false",
|
||||
"fips",
|
||||
"fips-from-a-string",
|
||||
"openssl-not-fips",
|
||||
"cryptography-not-fips",
|
||||
"not-a-boolean",
|
||||
"a-number",
|
||||
],
|
||||
ids=["unset", "empty", "false", "fips", "openssl-not-fips", "cryptography-not-fips", "not-a-boolean"],
|
||||
)
|
||||
def test_enforce_fips_mode(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
fips_mode: str | None,
|
||||
general_settings: Mapping[str, object],
|
||||
openssl_fips: bool,
|
||||
cryptography_fips: bool,
|
||||
refusal: str | None,
|
||||
) -> None:
|
||||
if fips_mode is None:
|
||||
monkeypatch.delenv("LITELLM_FIPS_MODE", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("LITELLM_FIPS_MODE", fips_mode)
|
||||
_fake_openssl(monkeypatch, fips_flag=int(openssl_fips), md5_refused=openssl_fips)
|
||||
monkeypatch.setattr(rust_openssl, "is_fips_enabled", lambda: cryptography_fips)
|
||||
|
||||
with pytest.raises(FipsModeError, match=refusal) if refusal else nullcontext():
|
||||
enforce_fips_mode()
|
||||
enforce_fips_mode(general_settings)
|
||||
|
||||
|
||||
class _Database:
|
||||
|
|
@ -151,13 +163,13 @@ class _Prisma:
|
|||
@pytest.mark.parametrize(
|
||||
"fips_mode, database, refusal, marked_after",
|
||||
[
|
||||
("true", _Database(marked_fips=False, holds_rows=False), None, True),
|
||||
("true", _Database(marked_fips=True, holds_rows=True), None, True),
|
||||
("true", _Database(marked_fips=False, holds_rows=True), "already holds data written without it", False),
|
||||
("true", _Database(marked_fips=False, holds_rows=False, readable=False), "could not be checked", False),
|
||||
("false", _Database(marked_fips=True, holds_rows=True), "set up with LITELLM_FIPS_MODE=true", True),
|
||||
("false", _Database(marked_fips=False, holds_rows=True), None, False),
|
||||
("false", _Database(marked_fips=True, holds_rows=True, readable=False), None, True),
|
||||
(True, _Database(marked_fips=False, holds_rows=False), None, True),
|
||||
(True, _Database(marked_fips=True, holds_rows=True), None, True),
|
||||
(True, _Database(marked_fips=False, holds_rows=True), "already holds data written without it", False),
|
||||
(True, _Database(marked_fips=False, holds_rows=False, readable=False), "could not be checked", False),
|
||||
(False, _Database(marked_fips=True, holds_rows=True), "set up with general_settings.fips_mode: true", True),
|
||||
(False, _Database(marked_fips=False, holds_rows=True), None, False),
|
||||
(False, _Database(marked_fips=True, holds_rows=True, readable=False), None, True),
|
||||
],
|
||||
ids=[
|
||||
"fips-on-new-database",
|
||||
|
|
@ -170,12 +182,10 @@ class _Prisma:
|
|||
],
|
||||
)
|
||||
async def test_enforce_fips_database(
|
||||
monkeypatch: pytest.MonkeyPatch, fips_mode: str, database: _Database, refusal: str | None, marked_after: bool
|
||||
fips_mode: bool, database: _Database, refusal: str | None, marked_after: bool
|
||||
) -> None:
|
||||
monkeypatch.setenv("LITELLM_FIPS_MODE", fips_mode)
|
||||
|
||||
with pytest.raises(FipsModeError, match=refusal) if refusal else nullcontext():
|
||||
await enforce_fips_database(FipsRepository(_Prisma(database)), database_is_configured=True)
|
||||
await enforce_fips_database(FipsRepository(_Prisma(database)), fips_mode=fips_mode, database_is_configured=True)
|
||||
|
||||
assert database.marked_fips is marked_after
|
||||
|
||||
|
|
@ -183,13 +193,11 @@ async def test_enforce_fips_database(
|
|||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"fips_mode, database_is_configured, refusal",
|
||||
[("true", True, "can't be reached"), ("true", False, None), ("false", True, None)],
|
||||
[(True, True, "can't be reached"), (True, False, None), (False, True, None)],
|
||||
ids=["fips-on-unreachable", "fips-on-no-database", "fips-off-unreachable"],
|
||||
)
|
||||
async def test_enforce_fips_database_without_a_connected_database(
|
||||
monkeypatch: pytest.MonkeyPatch, fips_mode: str, database_is_configured: bool, refusal: str | None
|
||||
fips_mode: bool, database_is_configured: bool, refusal: str | None
|
||||
) -> None:
|
||||
monkeypatch.setenv("LITELLM_FIPS_MODE", fips_mode)
|
||||
|
||||
with pytest.raises(FipsModeError, match=refusal) if refusal else nullcontext():
|
||||
await enforce_fips_database(None, database_is_configured=database_is_configured)
|
||||
await enforce_fips_database(None, fips_mode=fips_mode, database_is_configured=database_is_configured)
|
||||
|
|
|
|||
|
|
@ -1814,10 +1814,24 @@ async def test_proxy_startup_refuses_a_database_set_up_in_fips_mode_when_fips_mo
|
|||
from litellm.proxy.proxy_server import proxy_startup_event
|
||||
|
||||
_boot_with_general_settings(monkeypatch, tmp_path, {"master_key": "sk-a-safe-master-key"})
|
||||
monkeypatch.delenv("LITELLM_FIPS_MODE", raising=False)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", _PrismaClientOnAFipsDatabase())
|
||||
|
||||
with pytest.raises(FipsModeError, match="set up with LITELLM_FIPS_MODE=true"):
|
||||
with pytest.raises(FipsModeError, match=r"set up with general_settings\.fips_mode: true"):
|
||||
async with proxy_startup_event(FastAPI()):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_proxy_startup_reads_fips_mode_from_the_config_file(
|
||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
from litellm.proxy.common_utils.fips import FipsModeError
|
||||
from litellm.proxy.proxy_server import proxy_startup_event
|
||||
|
||||
_boot_with_general_settings(monkeypatch, tmp_path, {"master_key": "sk-a-safe-master-key", "fips_mode": True})
|
||||
monkeypatch.setattr("litellm.proxy.common_utils.fips.openssl_enforces_fips", lambda: False)
|
||||
|
||||
with pytest.raises(FipsModeError, match=r"general_settings\.fips_mode is on but OpenSSL does not enforce FIPS"):
|
||||
async with proxy_startup_event(FastAPI()):
|
||||
pass
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue