Skip to content

feat(security): add FIPS mode detection and replace non-FIPS crypto primitives#341

Draft
alexsander-souza wants to merge 1 commit into
canonical:masterfrom
alexsander-souza:fips_detection
Draft

feat(security): add FIPS mode detection and replace non-FIPS crypto primitives#341
alexsander-souza wants to merge 1 commit into
canonical:masterfrom
alexsander-souza:fips_detection

Conversation

@alexsander-souza

Copy link
Copy Markdown
Contributor

Introduce maascommon.fips module with host FIPS mode detection via /proc/sys/crypto/fips_enabled, FIPS-approved SSH algorithm configs, and SSH key validation helpers.

Replace Fernet (AES-CBC + HMAC-SHA256 construction) with AES-256-GCM for pre-shared key encryption in provisioningserver.security, as AES-GCM is directly FIPS 140-3 approved. Update all consumers (beaconing, RPC region/cluster services).

Add FIPS policy enforcement for certificate key generation (reject DSA, enforce minimum RSA 2048-bit keys).

Replace PostgreSQL md5() in BMC unique index with sha256() via Alembic migration, since md5() is blocked under FIPS mode.

Mark non-security SHA-1 usages with usedforsecurity=False (API doc hashing, WebSocket accept, test factory) and remove MD5 password hasher from Django development settings when FIPS is active.

Add FIPS structured-log event constants for consistent observability.

@alexsander-souza alexsander-souza requested a review from a team June 10, 2026 21:36
@maas-lander

Copy link
Copy Markdown
Collaborator

Check where you would like a Mattermost message to be sent to when CI completes and this PR is merged

  • Direct message
  • ~maas

@r00ta r00ta self-requested a review June 10, 2026 21:43
primitives

Introduce maascommon.fips module with host FIPS mode detection via
/proc/sys/crypto/fips_enabled, FIPS-approved SSH algorithm configs,
and SSH key validation helpers.

Replace Fernet (AES-CBC + HMAC-SHA256 construction) with AES-256-GCM
for pre-shared key encryption in provisioningserver.security, as
AES-GCM is directly FIPS 140-3 approved. Update all consumers
(beaconing, RPC region/cluster services).

Add FIPS policy enforcement for certificate key generation (reject
DSA, enforce minimum RSA 2048-bit keys).

Replace PostgreSQL md5() in BMC unique index with sha256() via
Alembic migration, since md5() is blocked under FIPS mode.

Mark non-security SHA-1 usages with usedforsecurity=False (API doc
hashing, WebSocket accept, test factory) and remove MD5 password
hasher from Django development settings when FIPS is active.

Add FIPS structured-log event constants for consistent observability.

@r00ta r00ta left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some questions inline

"django.contrib.auth.hashers.PBKDF2PasswordHasher",
)
PASSWORD_HASHERS = tuple(
h for h in _base_hashers if not (is_fips_enabled() and "MD5" in h)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we don't support moving an environment initialized without fips to an env with fips enabled. Correct?

)


def downgrade() -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need, we don't support downgrading

raise CertificateError(
f"RSA key size {key_bits} is below FIPS minimum of 2048 bits"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to validate other types or do we let them through without checks?

backend=default_backend(),
)
key = kdf.derive(secret)
key = urlsafe_b64encode(key)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why key = urlsafe_b64encode(key) has been removed?

token = fernet.encrypt(message)
nonce = os.urandom(12)
ts = int(time.time()).to_bytes(8, "big")
ciphertext = aesgcm.encrypt(nonce, message, ts)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have performance tests around the new encryption vs the fernet one?

@alexsander-souza alexsander-souza marked this pull request as draft June 23, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants