feat(proxy): refuse FIPS mode when cryptography's OpenSSL is not FIPS

The stock cryptography wheel bundles its own non-FIPS OpenSSL, which serves
JWT, Fernet and AES-GCM even when Python's OpenSSL enforces FIPS.
This commit is contained in:
yucheng 2026-10-27 14:17:56 -07:00
parent db7c9aa546
commit 75c6d07a8f
3 changed files with 52 additions and 12 deletions

View file

@ -30,9 +30,19 @@ def openssl_enforces_fips() -> bool:
return False
def cryptography_enforces_fips() -> bool:
"""The cryptography library's own OpenSSL runs with FIPS-only defaults."""
try:
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
return rust_openssl.is_fips_enabled()
except (ImportError, AttributeError):
return False
def enforce_fips_mode() -> None:
"""Refuse to start when LITELLM_FIPS_MODE is not a boolean, or is on but TLS verification is off
or OpenSSL does not enforce FIPS."""
or Python's or the cryptography library's OpenSSL does not enforce FIPS."""
value: Final = os.getenv(FIPS_MODE_ENV_VAR)
if value and str_to_bool(value) is None:
raise FipsModeError(f"{FIPS_MODE_ENV_VAR}={value!r} is not a boolean. Set it to true or false, or unset it.")
@ -51,3 +61,8 @@ def enforce_fips_mode() -> None:
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}."
)
if not cryptography_enforces_fips():
raise FipsModeError(
f"{FIPS_MODE_ENV_VAR} is on but the cryptography library's OpenSSL does not enforce FIPS. Build it against "
f"the system OpenSSL (pip install --no-binary cryptography cryptography), or unset {FIPS_MODE_ENV_VAR}."
)

View file

@ -2,9 +2,16 @@ import _hashlib
from contextlib import nullcontext
import pytest
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
import litellm
from litellm.proxy.common_utils.fips import FipsModeError, enforce_fips_mode, is_fips_mode, openssl_enforces_fips
from litellm.proxy.common_utils.fips import (
FipsModeError,
cryptography_enforces_fips,
enforce_fips_mode,
is_fips_mode,
openssl_enforces_fips,
)
def _fake_openssl(monkeypatch, *, fips_flag: int, md5_refused: bool) -> None:
@ -43,23 +50,36 @@ def test_openssl_without_get_fips_mode_does_not_enforce_fips(monkeypatch):
assert openssl_enforces_fips() is False
@pytest.mark.parametrize("fips_enabled", [True, False])
def test_cryptography_enforces_fips_reads_its_openssl_fips_flag(monkeypatch, fips_enabled):
monkeypatch.setattr(rust_openssl, "is_fips_enabled", lambda: fips_enabled)
assert cryptography_enforces_fips() is fips_enabled
def test_cryptography_without_is_fips_enabled_does_not_enforce_fips(monkeypatch):
monkeypatch.delattr(rust_openssl, "is_fips_enabled")
assert cryptography_enforces_fips() is False
def test_this_runners_openssl_does_not_enforce_fips():
if _hashlib.get_fips_mode() == 1:
pytest.skip("this runner's OpenSSL is in FIPS mode")
assert openssl_enforces_fips() is False
assert cryptography_enforces_fips() is False
@pytest.mark.parametrize(
"fips_mode, ssl_verify_env, ssl_verify_setting, openssl_fips, refusal",
"fips_mode, ssl_verify_env, ssl_verify_setting, openssl_fips, cryptography_fips, refusal",
[
(None, "false", False, False, None),
("", "false", False, False, None),
("false", "false", False, False, None),
("true", None, True, True, None),
("true", "false", True, True, "TLS certificate verification is disabled"),
("true", None, False, True, "TLS certificate verification is disabled"),
("true", None, True, False, "OpenSSL does not enforce FIPS"),
("enforced", None, True, True, "LITELLM_FIPS_MODE='enforced' is not a boolean"),
(None, "false", False, False, False, None),
("", "false", False, False, False, None),
("false", "false", False, False, False, None),
("true", None, True, True, True, None),
("true", "false", True, True, True, "TLS certificate verification is disabled"),
("true", None, False, True, True, "TLS certificate verification is disabled"),
("true", None, True, False, True, "is on but OpenSSL does not enforce FIPS"),
("true", None, True, True, False, "cryptography library's OpenSSL does not enforce FIPS"),
("enforced", None, True, True, True, "LITELLM_FIPS_MODE='enforced' is not a boolean"),
],
ids=[
"unset",
@ -69,10 +89,13 @@ def test_this_runners_openssl_does_not_enforce_fips():
"ssl-verify-env-off",
"ssl-verify-setting-off",
"openssl-not-fips",
"cryptography-not-fips",
"not-a-boolean",
],
)
def test_enforce_fips_mode(monkeypatch, fips_mode, ssl_verify_env, ssl_verify_setting, openssl_fips, refusal):
def test_enforce_fips_mode(
monkeypatch, fips_mode, ssl_verify_env, ssl_verify_setting, openssl_fips, cryptography_fips, refusal
):
for name, value in (("LITELLM_FIPS_MODE", fips_mode), ("SSL_VERIFY", ssl_verify_env)):
if value is None:
monkeypatch.delenv(name, raising=False)
@ -80,6 +103,7 @@ def test_enforce_fips_mode(monkeypatch, fips_mode, ssl_verify_env, ssl_verify_se
monkeypatch.setenv(name, value)
monkeypatch.setattr(litellm, "ssl_verify", ssl_verify_setting)
_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()

View file

@ -1801,6 +1801,7 @@ async def test_proxy_startup_refuses_fips_mode_when_the_password_migration_fails
monkeypatch.delenv("SSL_VERIFY", raising=False)
monkeypatch.setenv("LITELLM_FIPS_MODE", "true")
monkeypatch.setattr("litellm.proxy.common_utils.fips.openssl_enforces_fips", lambda: True)
monkeypatch.setattr("litellm.proxy.common_utils.fips.cryptography_enforces_fips", lambda: True)
monkeypatch.setattr(ProxyStartupEvent, "setup_prisma_client", AsyncMock(return_value=MockPrisma()))
scrypt_refused = ValueError("[digital envelope routines] unsupported")
monkeypatch.setattr(