Gears take a defense-in-depth approach to security, combining Rust's compile-time safety guarantees with layered static analysis, runtime enforcement, continuous scanning, and structured development processes. This document summarizes the security measures in place across the project.
- Security in Gears
- Table of Contents
- 1. Rust Language Safety
- 2. Compile-Time Tenant Scoping (Secure ORM)
- 3. Authentication & Authorization Architecture
- 4. Credentials Storage Architecture
- 5. Outbound API Gateway (OAGW)
- 6. Compile-Time Linting — Clippy
- 7. Compile-Time Linting — Custom Dylint Rules
- 8. Dependency Security — cargo-deny
- 9. Cryptographic Stack & FIPS-140-3
- Default (non-FIPS) cryptographic stack
- FIPS-140-3 build (
--features fips) - Algorithm scope on the wire
- Build-time dependency-graph policy
- Runtime Operational Environment validation
- Runtime failure modes
- TLS configuration knobs
- Non-cryptographic
sha2andrandusage - File-storage content hashing is out of FIPS scope
- Approved-only deployment checklist
- Enabling OS FIPS mode (Windows)
- How to verify a build is FIPS-conformant
- What this does NOT claim
- Deep references
- 10. Continuous Fuzzing
- 11. Security Scanners in CI
- 12. PR Review Bots
- 13. Specification Templates & SDLC
- 14. Repository Scaffolding — Gears CLI
- 15. Opportunities for Improvement
Rust eliminates entire categories of vulnerabilities at compile time:
| Vulnerability Class | How Rust Prevents It |
|---|---|
| Null pointer dereference | No null — Option<T> forces explicit handling |
| Use-after-free / double-free | Ownership system with borrow checker |
| Data races | Send/Sync traits enforced at compile time |
| Buffer overflows | Bounds-checked indexing; slices carry length |
| Uninitialized memory | All variables must be initialized before use |
| Integer overflow | Checked in debug builds; explicit wrapping/saturating in release |
Additional Rust-specific project practices:
#[deny(warnings)]— all compiler warnings are treated as errors in CI (RUSTFLAGS="-D warnings")#[deny(clippy::unwrap_used)]/#[deny(clippy::expect_used)]— panicking onNone/Erris forbidden in production code- No
unsafewithout justification — Clippy pedantic rules surface unnecessaryunsafeusage
Source:
libs/toolkit-db-macros·guidelines/SECURITY.md·docs/toolkit_unified_system/06_authn_authz_secure_orm.md
Gears provide a compile-time enforced secure ORM layer over SeaORM. The #[derive(Scopable)] macro ensures every database entity explicitly declares its scoping dimensions:
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Scopable)]
#[sea_orm(table_name = "users")]
#[secure(
tenant_col = "tenant_id",
resource_col = "id",
no_owner,
no_type
)]
pub struct Model {
#[sea_orm(primary_key)]
pub id: Uuid,
pub tenant_id: Uuid,
pub email: String,
}Key compile-time guarantees:
- Explicit scoping required — every entity must declare all four dimensions (
tenant,resource,owner,type). Missing declarations cause a compile error. - No accidental bypass —
clippy.tomlconfiguresdisallowed-methodsto block directsea_orm::Select::all(),::one(),::count(),UpdateMany::exec(), andDeleteMany::exec(). All queries must go throughSecureSelect/SecureUpdateMany/SecureDeleteMany. - Deny-by-default — empty
AccessScope(no tenant IDs, no resource IDs) producesWHERE 1=0, denying all rows. - Immutable tenant ownership — updates cannot change
tenant_id(enforced insecure_insert). - No SQL injection — all queries use SeaORM's parameterized query builder.
Source:
docs/arch/authorization/·gears/system/authn-resolver/·gears/system/authz-resolver/·gears/system/tenant-resolver/
Gears implement a PDP/PEP authorization model per NIST SP 800-162, extended with OpenID AuthZEN 1.0 constraint semantics (see ADR-0001):
Client → AuthN Middleware → AuthN Resolver (token validation)
→ Gear Handler (PEP) → AuthZ Resolver (PDP, policy evaluation)
→ Constraints compiled to AccessScope
→ Database (query with WHERE clauses from constraints)
Every authenticated request produces a SecurityContext:
pub struct SecurityContext {
subject_id: Uuid,
subject_type: Option<String>,
subject_tenant_id: Uuid, // every subject belongs to a tenant
token_scopes: Vec<String>, // capability ceiling (["*"] = unrestricted)
bearer_token: Option<SecretString>, // redacted in Debug, never serialized
}bearer_token is stored as Secret<String> — redacted in Debug/Display, never serialized or logged. Introspection caches key by sha256(token), not the raw token.
Source:
OIDC AuthN Plugin DESIGN.md· ADR-0002 · ADR-0003
Validates bearer JWTs via OIDC discovery and JWKS, extracts claims, and constructs the SecurityContext. AuthN and AuthZ are split into independent resolver gears (ADR-0002) with pluggable vendor-specific implementations.
The current OIDC AuthN plugin supports:
- JWT tokens — local validation via OIDC discovery → JWKS → signature verification (
kid,exp, optionalaud), with configurable claim mapping toSecurityContextfields. - Opaque tokens — out of scope for this plugin; non-JWT bearer tokens are rejected.
- S2S identity —
exchange_client_credentials(OAuth2 client credentials grant) for service-to-service calls, producing the sameSecurityContextpipeline. - Caching — JWKS cached with refresh and bounded stale serving; S2S tokens cached with TTL bounded by
min(token_exp - now, configured_ttl).
Source:
DESIGN.md·AUTHZ_USAGE_SCENARIOS.md
Plain AuthZEN point-in-time true/false decisions are insufficient for LIST queries with pagination. The design extends AuthZEN with context.constraints — a predicate DSL so the PEP receives SQL-friendly filters in O(1) PDP calls per query instead of per-row evaluation.
Constraint predicates:
| Predicate | Purpose |
|---|---|
eq(property, value) |
Exact match (e.g., owner_id) |
in(property, values) |
Set membership |
in_tenant_subtree(root_id, barrier_mode, status) |
Hierarchical tenant scoping via tenant_closure |
in_group(group_ids) |
Resource group membership |
in_group_subtree(group_ids) |
Hierarchical group membership |
Constraints OR across alternatives, AND predicates within each constraint. PEP compiles constraints into AccessScope → SecureORM translates to SQL WHERE clauses.
Fail-closed PEP enforcement — PEP denies access on:
- Missing or
falsedecision - Unreachable PDP
- Missing constraints when
require_constraints: true - Unknown predicates or property names
- Empty predicate lists, empty
in/group_idsvalues, or emptyconstraints: []
Capability negotiation — PEP sends capabilities, supported_properties, and require_constraints with each evaluation request. The PDP must not emit unsupported property names and must degrade gracefully (expand groups to explicit in lists, or deny).
Token scopes as capability ceiling — effective_access = min(token_scopes, user_permissions). First-party apps typically carry ["*"] (unrestricted).
Deny contract — decision: false must include a deny_reason with a GTS error_code. Details are logged for audit but never exposed to clients (generic 403 only, no policy leakage).
404 vs 403 for point reads — Constrained queries returning 0 rows yield 404, preventing existence leakage.
TOCTOU mitigation — For UPDATE/DELETE, PEP prefetches the target attribute and uses eq predicates in the WHERE clause so the mutation is atomic with the authorization check.
Source:
TENANT_MODEL.md·gears/system/tenant-resolver/
Hierarchical multi-tenancy with a single-root tree topology (exactly one tenant with no parent; all others descend from it):
- Isolation by default — every resource carries
owner_tenant_idas the primary partition key; tenants cannot access each other's data. - Hierarchical access — parent tenants may access child data. Subject tenant (home identity) and context tenant (operational scope) are distinguished, enabling scoped admin patterns.
- Barriers — child tenants set
self_managed = trueto create a privacy barrier. Parents cannot see that subtree's business data by default;BarrierModecontrols per-resource-type relaxation (e.g., billing ignores barriers while tasks respect them). tenant_closuretable — materialized(ancestor_id, descendant_id, barrier, descendant_status)enables efficientin_tenant_subtreepredicate compilation to SQL subqueries.
Tenant Resolver is a plugin-based system gear providing tenant graph operations (get, ancestors, descendants, is_ancestor) via TenantResolverClient:
- Plugin architecture — gateway discovers a backend plugin by GTS vendor string; routes all calls through
TenantResolverPluginClient. Built-in plugins:static-tr-plugin(in-memory tree from config),single-tenant-tr-plugin(enforcesctx.subject_tenant_id()as the only tenant). - Barrier-aware traversal — ancestor/descendant walks respect
self_managedbarriers and use visited sets for cycle safety. - Status filtering — queries filter by
TenantStatus; filtering a suspended parent excludes its entire subtree. - PIP role — Tenant Resolver serves as a Policy Information Point (PIP): the AuthZ plugin queries it for hierarchy data when building tenant constraints.
Source:
RESOURCE_GROUP_MODEL.md
Optional M:N, tenant-scoped resource grouping that acts as a PIP alongside the tenant hierarchy:
- Groups enable attribute-based grouping of resources for authorization (e.g., project groups, organizational units).
- The AuthZ plugin queries group membership/hierarchy when building
in_group/in_group_subtreepredicates. ResourceGroupReadHierarchysupports hierarchical group traversal.- Group constraints are always paired with tenant predicates — defense in depth prevents cross-tenant leakage through group membership alone.
Source: gts-spec ·
dylint_lints/de09_gts_layer/·gears/system/types-registry/
Gears use the Global Type System (GTS) as the foundation for attribute-based access control. GTS defines a hierarchical identifier scheme for data types and instances:
gts.<vendor>.<package>.<namespace>.<type>.v<MAJOR>[.<MINOR>]~
How GTS enables ABAC:
- Token claims — authenticated user tokens carry GTS type patterns in
token_scopes, defining the capability ceiling for the subject (e.g.,["gts.cf.core.srr.resource.v1~*"]grants access to all SRR resource types under that schema). - Wildcard matching — GTS supports segment-wise wildcard patterns (
*), chain-aware evaluation, and attribute predicates for fine-grained policy expressions. - Authorization resources — PDP evaluations reference GTS-typed resources (e.g.,
gts.cf.core.oagw.proxy.v1~:invokefor outbound gateway proxy access). - Secure ORM integration (under development) — the
ScopableEntitytrait supports atype_coldimension. The planned flow: AuthZ Resolver (PDP) evaluates GTS type constraints → compiles them intoAccessScope→ Secure ORM translates to SQLWHEREclauses, automatically filtering rows by type at the database level.
Current implementation status:
| Component | Status |
|---|---|
| GTS identifier parsing & validation | Implemented |
| GTS type patterns in token scopes | Implemented |
Wildcard pattern matching (GtsWildcard) |
Implemented |
| GTS → UUID resolution (Types Registry) | Implemented |
| Domain-level type filtering (e.g., SRR) | Implemented |
| GTS-typed authorization resources | Implemented |
Secure ORM type_col auto-injection via PDP |
Under development |
Custom dylint rules (DE0901, DE0902) validate GTS identifier correctness at compile time, preventing malformed type strings from entering the codebase.
Gears provide a plugin-based credential storage gateway for managing secrets across the platform. The architecture separates the gateway (routing, authorization) from storage backends (plugin implementations).
Consumer → CredStoreClientV1 → Gateway Service → GTS Plugin Discovery
→ CredStorePluginClientV1 (vendor backend)
The SDK enforces secure handling of secret material at the type level:
| Protection | Mechanism |
|---|---|
| Memory safety | SecretValue wraps Vec<u8> with zeroize on Drop — secret bytes are wiped from memory when no longer needed |
| Log safety | Debug and Display implementations on SecretValue emit [REDACTED] — secrets cannot leak through logging |
| Serialization safety | SecretValue does not implement Serialize/Deserialize — secrets cannot be accidentally persisted or transmitted |
| Key validation | SecretRef validates keys as [a-zA-Z0-9_-]+ (max 255 chars, no colons) — prevents injection via key names |
| Anti-enumeration | Failed lookups return Ok(None), not a distinct "forbidden" error — prevents existence probing |
Credentials are scoped along three visibility levels:
- Private —
(tenant, owner, key): only the owning subject within a tenant can access. - Tenant —
(tenant, key): any subject within the tenant can access. - Shared — tenant-scoped with descendant visibility via gateway hierarchy walk-up.
The gateway enforces authorization via SecurityContext; plugins are single-tenant-level adapters that handle storage only. Built-in reference plugin: static-credstore-plugin (YAML-defined, in-memory — development/test use).
The credential storage design specifies AES-256-GCM encryption with per-tenant keys and a KeyProvider abstraction supporting both DatabaseKeyProvider (co-located keys) and ExternalKeyProvider (Vault/KMS integration) for key–data separation.
Source:
gears/system/oagw/·gears/system/oagw/docs/DESIGN.md
OAGW is a centralized outbound API gateway built on Pingora. All platform traffic to external HTTP services is routed through OAGW, enforcing security and observability policies via a Control Plane / Data Plane architecture.
Every proxy request is authorized via PolicyEnforcer before routing:
self.policy_enforcer
.access_scope_with(
&ctx,
&resources::PROXY, // gts.cf.core.oagw.proxy.v1~
actions::INVOKE,
None,
&AccessRequest::new()
.require_constraints(false)
.context_tenant_id(ctx.subject_tenant_id()),
)
.await?;Ancestor bind flows (descendant reusing parent upstream aliases) have separate authorization actions: bind, override_auth, override_rate, add_plugins.
Outbound authentication credentials are never stored in OAGW configuration. Auth configs reference secrets via cred:// URIs, resolved through CredStoreClientV1 under the caller's SecurityContext. OAuth2 client-credentials tokens are cached with keys scoped by (tenant, subject, auth_method), preventing cross-tenant token reuse.
Per-upstream/route authentication plugins modify outbound requests:
| Plugin | Mechanism |
|---|---|
noop |
No authentication (pass-through) |
api-key |
Injects API key from credential store |
oauth2-client-credentials |
Client credentials grant (form/basic), token caching with tenant isolation |
| Control | Protection |
|---|---|
| Path traversal | Alias extracted from first path segment; only suffix is normalized |
| Body size | Configurable cap (default 100 MB) |
| Content validation | Content-Type and Transfer-Encoding validated before forwarding |
| Hop-by-hop headers | Stripped per HTTP spec; internal headers controlled |
| CORS | OPTIONS preflight returns 204 without upstream resolution; real requests validate Origin/method against config |
| Rate limiting | Per-upstream/route limits enforced in the data plane |
| Error isolation | X-OAGW-Error-Source header distinguishes gateway errors from upstream errors; RFC 9457 problem responses |
The project enforces 90+ Clippy rules at deny level, including the full pedantic group. Security-relevant highlights:
| Rule | Why It Matters |
|---|---|
unwrap_used, expect_used |
Prevents panics in production (denial-of-service) |
await_holding_lock, await_holding_refcell_ref |
Prevents deadlocks in async code |
cast_possible_truncation, cast_sign_loss, cast_precision_loss |
Prevents silent data corruption |
integer_division |
Prevents silent truncation |
float_cmp, float_cmp_const |
Prevents incorrect equality checks |
large_stack_arrays, large_types_passed_by_value |
Prevents stack overflows |
rc_mutex |
Prevents common concurrency anti-patterns |
regex_creation_in_loops |
Prevents ReDoS-adjacent performance issues |
cognitive_complexity (threshold: 20) |
Keeps code reviewable and auditable |
clippy.toml additionally enforces:
disallowed-methodsblocking direct SeaORM execution methods (must use Secure wrappers)disallowed-typesblockingLinkedList(poor cache locality, potential DoS amplification)- Stack size threshold of 512 KB
- Max 2 boolean fields per struct (prevents boolean blindness)
Source:
dylint_lints/
Project-specific architectural lints run on every CI build via cargo dylint. These enforce design boundaries that generic linters cannot:
| ID | Lint | Security Relevance |
|---|---|---|
| DE0706 | no_direct_sqlx |
Prohibits direct sqlx usage — forces all DB access through SeaORM/SecORM |
| DE0708 | no_non_fips_hasher |
Prohibits sha2/sha1/md5 imports outside allow-list — prevents unreviewed non-FIPS crypto usage |
| DE0103 | no_http_types_in_contract |
Prevents HTTP types leaking into contract layer |
| DE0301 | no_infra_in_domain |
Prevents domain layer from importing sea_orm, sqlx, axum, hyper, http |
| DE0308 | no_http_in_domain |
Prevents HTTP types in domain logic |
| DE0801 | api_endpoint_version |
Enforces versioned API paths (/{service}/v{N}/{resource}) |
| DE1301 | no_print_macros |
Forbids println!/dbg! in production code (prevents info leakage) |
The architectural lints in the DE03xx series enforce strict layering (contract → domain → infrastructure), preventing accidental coupling that could undermine security boundaries.
Source:
deny.toml· CI job:.github/workflows/ci.yml(securityjob)
cargo deny check runs in CI and enforces:
- RustSec advisory database — known vulnerabilities are treated as hard errors
- License allow-list — only approved OSS licenses (MIT, Apache-2.0, BSD, MPL-2.0, etc.)
- Source restrictions — only
crates.ioallowed; unknown registries and git sources warned - Duplicate version detection — warns on multiple versions of the same crate in the dependency graph
Source: FIPS PRD · ADRs in
docs/security/fips/adrs/·libs/toolkit/src/bootstrap/crypto.rs·libs/rustls-corecrypto-provider/·deny-fips.toml
The project uses aws-lc-rs (via rustls) as its primary TLS cryptographic backend. JWT validation uses jsonwebtoken.
| Layer | Library | Backend |
|---|---|---|
| TLS | rustls + hyper-rustls |
aws-lc-rs |
| Certificate verification | rustls-webpki |
aws-lc-rs, ring |
| JWT validation | jsonwebtoken |
sha2, hmac, ring |
| Database TLS | sqlx (tls-rustls-aws-lc-rs) |
aws-lc-rs |
Gears applications can be built with FIPS-validated cryptography by enabling the fips feature flag on cf-gears-toolkit, cf-gears-toolkit-http, and any binary that ships TLS. A single feature flag selects a per-target CMVP-validated backend behind one shared rustls 0.23 TLS state machine. The TLS data plane routes through a FIPS 140-3 validated module on Linux and macOS; on Windows it is currently FIPS 140-2 (140-3 in CMVP processing — see the per-target table and What this does NOT claim):
| Target | Validated module | How it routes |
|---|---|---|
| Linux (x86_64, aarch64) | AWS-LC FIPS Provider v2 — CMVP cert #4816 (FIPS 140-3) | rustls/fips + aws-lc-fips-sys; activated via target-gated shim cf-gears-rustls-fips-shim |
| macOS (any arch) | Apple corecrypto User-Space Module — per-macOS-major CMVP cert (search by "Apple corecrypto Module" on the CMVP database) | In-tree cf-gears-rustls-corecrypto-provider over Security.framework + CommonCrypto |
| Windows (x86_64) | Microsoft Windows CNG — Cryptographic Primitives Library (bcryptprimitives.dll, loaded via bcrypt.dll); per-Windows-build CMVP cert, currently FIPS 140-2 validated (FIPS 140-3 in CMVP processing — see What this does NOT claim) |
Community rustls-cng-crypto (caret-pinned 0.1.x); requires OS-level FIPS-mode via HKLM\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy = 1 |
cargo build -p cf-gears-server --features fipstoolkit::bootstrap::init_crypto_provider is invoked automatically as the first step of init_procedure (used by run_server and run_migrate) — no explicit setup in main(). The function dispatches per cfg(target_os, feature = "fips") and installs the per-OS provider once via OnceLock. Subsequent calls return the cached first-call result.
Build prerequisites:
| Target | Toolchain |
|---|---|
Linux + fips |
C toolchain, cmake, perl, go (required by aws-lc-fips-sys build script — module integrity checks) |
macOS + fips |
Xcode Command Line Tools + Rust toolchain. No cmake / perl / go. The per-target shim excludes aws-lc-fips-sys from the macOS build graph entirely. |
Windows + fips (native) |
MSVC + Windows SDK. No cmake / perl / go. CNG is loaded at runtime from bcrypt.dll. |
Windows + fips (cross-compile from Linux/macOS) |
cargo install cargo-xwin plus ninja. See make check-windows-fips. |
Under --features fips, the ClientHello and ServerHello offer only FIPS-Approved algorithms. No ChaCha20-Poly1305, X25519, X25519MLKEM768 / post-quantum hybrids, Ed25519, MD5, or SHA-1 (outside the SP 800-131A legacy signature-verify allowance).
| Category | Algorithms |
|---|---|
| TLS versions | TLS 1.2, TLS 1.3 (no TLS 1.0/1.1). macOS is TLS 1.3-only under FIPS — see TLS 1.2 PRF note below. |
| TLS 1.3 cipher suites | TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384 |
| TLS 1.2 cipher suites | ECDHE_{ECDSA,RSA}_WITH_AES_{128,256}_GCM_SHA{256,384} (×4 — Linux + Windows only) |
| Key exchange | NIST P-256, P-384 ECDHE |
| Signature verify | ECDSA P-256/P-384/P-521, RSA-PSS, RSA PKCS#1 v1.5 (SHA-256/384/512) |
| Signature sign (server-side / mTLS) | Same scope as verify; routed through the validated module (SecKeyCreateSignature on macOS, BCryptSignHash on Windows, EVP_PKEY_sign on Linux) |
| Hash / HMAC / HKDF | SHA-256, SHA-384 (HKDF is an Approved KDF per NIST SP 800-56C) |
| TLS 1.2 Extended Master Secret (RFC 7627) | required (require_ems = true) per NIST SP 800-52 Rev. 2 §3.5 |
| RSA modulus floor | ≥ 2048 bits (NIST FIPS 186-5 §5.1) — enforced at server-side key load on macOS |
A FIPS build must not pull in a non-FIPS-validated crypto crate, even transitively. We enforce this at dependency-resolution time via cargo-deny: under --features fips, the build fails if any banned crypto crate appears in cargo tree. This is the Rust analogue of Go 1.25's fips140=only runtime gate — Rust has no language-level hook for "refuse this crypto call", so we cut it off one step earlier, before the offending code ever links. Configured via deny-fips.toml; rationale in ADR 0005.
make fips-policy # Standalone — runs cargo deny check bans --config deny-fips.toml
make security # Runs both `deny` (license/advisory) and `fips-policy`Phase A (shipped) bans crates not currently in the graph — zero-pain regression gate: future PRs adding md2/md4/ripemd, chacha20poly1305/salsa20, the Curve25519 family (x25519-dalek, ed25519-dalek, …), alternative TLS frameworks (openssl, boring, native-tls), or alternative rustls CryptoProviders (rustls-symcrypt, rustls-mbedcrypto-provider, rustls-openssl, rustls-rustcrypto, rustls-graviola, rustls-wolfcrypto-provider, boring-rustls-provider) all fail the gate.
Non-FIPS hasher guard — Dylint lint DE0708 (no_non_fips_hasher) rejects new sha2/sha1/md5 imports outside an explicit allow-list (one entry: file-storage content hashing — see Non-cryptographic sha2 and rand usage), preventing unreviewed non-FIPS crypto usage from creeping in. All previous direct use sites have been replaced with inline FNV-1a (a deterministic, non-cryptographic fingerprint): libs/toolkit-odata/src/pagination.rs (cursor consistency) and oidc-authn-plugin/src/infra/token_client.rs (credential cache key). sha2 remains in the dependency graph as a Phase B transitive (via sqlx-core, sqlx-postgres, lopdf, rust-embed-utils) and will be promoted to Phase A once those pull-throughs are eliminated.
Phase B (pending transitive cleanup) is documented inline in deny-fips.toml — ring, non-FIPS aws-lc-rs, chacha20, md-5, sha1, blake2/blake3, aes, hmac, hkdf, etc. — currently pulled by upstream deps (pingora-rustls/ureq, rustls's default features, rand). Each moves to Phase A as its upstream pull-through is replaced. Tracking: ADR 0005 §"Phasing" and FIPS PRD §13 TODO-7 — promotion to Phase A is the unit of work; no per-crate sub-tickets today.
A FIPS 140-3 claim is only valid when the running OS version lies inside the Operational Environment (OE) listed on the active CMVP certificate. The OE check has different shapes per OS:
| Target | Runtime gate | Behaviour on mismatch |
|---|---|---|
| macOS | cf_gears_rustls_corecrypto_provider::oe::validate_oe() reads kern.osproductversion via sysctlbyname and matches against SUPPORTED_OE_MACOS_MAJOR. Fires as a side-effect of the first fips_provider() call. |
Under --features fips: panic (deliberate fail-closed — cannot be silently caught by an intermediate Result-handling caller). Override via CF_GEARS_FIPS_OE_OVERRIDE=1 for CI on pre-release macOS only. |
| Linux | Not yet implemented at runtime. | OE coverage verified manually per release (CMVP cert search for cert #4816). Tracked as TODO-8 in FIPS PRD §13. |
| Windows | OS-level FIPS-mode flag check (via rustls-cng-crypto's empty-provider gate). Build-number-vs-CMVP-OE check not yet implemented at runtime. |
If FipsAlgorithmPolicy = 0: bootstrap refuses to install the provider. Build-number OE check tracked as TODO-8. |
What a --features fips process does when its environment cannot satisfy the FIPS claim. Each row is fail-closed — the gear never serves traffic under a degraded or non-validated provider.
OS (--features fips) |
Failure mode | Process behaviour |
|---|---|---|
| macOS | OE mismatch — running macOS major ∉ SUPPORTED_OE_MACOS_MAJOR, with CF_GEARS_FIPS_OE_OVERRIDE unset |
Fail-closed panic. oe::validate_oe() returns Err ⇒ fips_witness_ok() caches false ⇒ every primitive's fips() returns false ⇒ CryptoProvider::fips() is false ⇒ the post-install assert!(provider.fips()) in init_crypto_provider panics. Defense-in-depth: even if that assert were bypassed, tls::apply_fips_hardening returns Err(TlsConfigError::FipsHardeningFailed), so any TLS-config build also fails recoverably. CI on pre-release macOS sets CF_GEARS_FIPS_OE_OVERRIDE=1. |
| Windows | OS FIPS-mode off (FipsAlgorithmPolicy != 1) |
Bootstrap fails closed (no panic). rustls_cng_crypto::fips_provider() yields a provider with empty cipher_suites; init_crypto_provider detects the empty provider and returns CryptoProviderError::SystemFipsModeNotEnabled; the binary refuses to start. |
| Linux | AWS-LC POST (power-on self-test) failure | Panic at provider construction. rustls::crypto::default_fips_provider() (AWS-LC FIPS module) aborts on a POST failure; init_crypto_provider returns Err for the install-conflict case it can observe, but an underlying POST failure propagates as a panic/abort. main.rs / init_procedure handle the Err path from init_crypto_provider but cannot catch the POST abort. |
The HTTP client (cf-gears-toolkit-http) exposes the following transport knobs (see libs/toolkit-http/src/config.rs). None of them relax FIPS hardening — under --features fips, tls::apply_fips_hardening still asserts ClientConfig::fips(), so any setting incompatible with the active FIPS provider surfaces as an error at build time.
| Knob | Type | Effect |
|---|---|---|
| Transport security | TransportSecurity (TlsOnly | AllowInsecureHttp); HttpClientBuilder::deny_insecure_http() |
Whether cleartext http:// is permitted. Defaults to TlsOnly under --features fips, AllowInsecureHttp otherwise. Selecting AllowInsecureHttp under FIPS makes build() return HttpError::InsecureTransport. |
| Minimum TLS version | TlsVersion (Tls12 | Tls13) |
Tls12 advertises TLS 1.2 + 1.3 (rustls safe default); Tls13 is TLS 1.3-only. Does not relax FIPS hardening. |
| Mutual TLS identity | ClientAuthConfig |
PEM cert-chain + private-key file paths (PKCS#8 / PKCS#1 / SEC1). Files are read lazily at build; no key bytes are held in the cloneable config. |
| Trust roots | TlsRootConfig |
Selects the certificate trust anchors (e.g. the native OS root store). |
| Extended Master Secret | require_ems (set true by apply_fips_hardening) |
Enforces RFC 7627 EMS under FIPS per NIST SP 800-52 Rev. 2 §3.5. |
Any residual sha2 / rand usage in the tree is non-cryptographic and is not part of the FIPS claim. Non-cryptographic fingerprints use inline FNV-1a (OData pagination cursor consistency in libs/toolkit-odata/src/pagination.rs; the OIDC token-cache key in oidc-authn-plugin), and the rand ecosystem is pulled in transitively rather than used for key material on the TLS data plane. New sha2/sha1/md5 imports are rejected at compile time by Dylint DE0708 (no_non_fips_hasher) outside an explicit allow-list. See the Non-FIPS hasher guard note above and What this does NOT claim below for the transitive-dependency posture.
DE0708 allow-list entry — file-storage content hashing. gears/file-storage/file-storage/src/infra/content/hash.rs is the single SHA-256 call site in the file-storage gear and is on the DE0708 allow-list. It is used for content addressing/integrity — the expected_hash upload constraint and version-identity check (SHA-256 is mandated by file-storage ADR-0002) — and to derive the opaque content ETag. It is not used for signatures, key derivation, or password storage: the signed-URL signing primitive runs behind a replaceable SignatureProvider abstraction (file-storage ADR-0004), so a FIPS deployment swaps the signing module without touching this hasher. All sha2 usage in the gear is confined to this one reviewable module.
File-storage's content hash is excluded from the FIPS claim — but the exclusion turns on the hash's purpose, not on its algorithm. Both content-hash modes are SHA-256 throughout (a FIPS-Approved algorithm), so there is no non-Approved-algorithm question to carve out in the first place: single-part uploads use sha256(whole object); multipart uploads use a bespoke SHA-256 composite — a one-level Merkle over per-part SHA-256 digests (root = sha256 of a "v1,{offset}:{sha256(part)},…" manifest). Neither mode is a FIPS-scoped security function: the job of this hash is to verify that a stored file was not corrupted and was split into parts and uploaded/reassembled correctly (content-addressed identity/dedup), not to defend a security boundary against deliberate tampering. That non-adversarial purpose — not an algorithm-approval exception — is what places it outside the FIPS-Approved-algorithm list in Algorithm scope on the wire. This sits on top of the existing decoupling described above: content hashing already runs through a plain sha2 crate call site, outside the FIPS-validated TLS module.
The one exception is the expected_hash upload-verification path, which is security-relevant: it defends against a client falsely claiming a different object than it actually uploaded, so it remains SHA-256 and is not covered by this exclusion.
Because both modes are already 100% SHA-256, there is no build-time Cargo feature gating and no config-time rejection to enforce on FIPS builds — the exclusion is a scoping clarification, not a runtime guardrail.
Recorded in file-storage ADR-0006 (cpt-cf-file-storage-adr-content-hash-modes), which supersedes ADR-0002 for the content-hash-modes decision and records the two-mode, SHA-256-only design. This is an additive clarification of scope, not a change to the TLS/signing FIPS-module claims described elsewhere in this section.
Before relying on the FIPS claim in a deployment:
- OS FIPS mode — confirm the OS is in an approved state: Windows
HKLM\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy = 1; macOS major version insideSUPPORTED_OE_MACOS_MAJOR; Linux OE inside CMVP cert #4816. - Static-linkage check — run the linkage smoke (
otool -Lon macOS,dumpbin /importson Windows) and confirm only the OS-supplied / validated crypto module is loaded — see How to verify a build is FIPS-conformant. - No-sccache FIPS build job — build the FIPS provider in a dedicated CI job with
sccachedisabled, so AWS-LC FIPS integrity / self-test artifacts are produced fresh rather than served from a compilation cache.
The Windows CNG FIPS provider only enforces its FIPS-Approved algorithm subset when the operating system itself is in FIPS mode; otherwise Gears bootstrap fails closed with CryptoProviderError::SystemFipsModeNotEnabled. Enable system-wide FIPS mode via Group Policy: Computer Configuration → Windows Settings → Security Settings → Local Policies → Security Options → "System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing" → Enabled. Or via the registry:
reg add HKLM\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy /v Enabled /t REG_DWORD /d 1 /fA reboot is required after either change. See Microsoft's FIPS 140 validation reference for the authoritative posture documentation.
# 1. Wire-level — what the ClientHello actually offers:
cargo run -p cf-gears-fips-probe --features fips -- --url https://www.howsmyssl.com/a/check
# Expected: given_cipher_suites = AES-GCM only, given_named_groups = secp256r1/secp384r1,
# post_quantum_key_agreement: false, [OK] No ChaCha20 in ClientHello.
# 2. Dep-graph regression (cheap, no compile step):
cargo tree --target aarch64-apple-darwin -p cf-gears-example-server --features fips \
-e features | grep -E 'corecrypto|aws-lc-fips'
# Expected on macOS: cf-gears-rustls-corecrypto-provider present, aws-lc-fips-sys ABSENT.
cargo tree --target x86_64-unknown-linux-gnu -p cf-gears-example-server --features fips \
-e features | grep 'aws-lc-fips'
# Expected on Linux: aws-lc-fips-sys present.
cargo tree --target x86_64-pc-windows-msvc -p cf-gears-example-server --features fips \
-e features | grep -E 'cng-crypto|aws-lc-fips'
# Expected on Windows: rustls-cng-crypto present, aws-lc-fips-sys ABSENT.
# 3. Linkage smoke — confirm only OS-supplied crypto framework is loaded:
otool -L target/release/cf-gears-server | grep -E 'aws|crypto|ssl|ring' # macOS — expect only Security.framework
dumpbin /imports target\release\cf-gears-server.exe | findstr /i "bcrypt aws" # Windows — expect only bcrypt.dll
vmmap <cf-gears-server-pid> | grep -E 'corecrypto|Security\.framework' # macOS runtime — confirm corecrypto is mapped into the live process
# 4. Dep-graph policy regression (rejects non-FIPS crypto crates):
make fips-policy
# 5. Wire-shape regression for our macOS provider:
cargo test -p cf-gears-rustls-corecrypto-provider --features fips --test fips_provider_invariantsSee examples/cf-gears-fips-probe/README.md for the full four-layer verification chain (linkage, runtime, wire-level, cert-validation).
- Gears itself is not on the CMVP Validated Modules list. The validated modules are Apple corecrypto, AWS-LC FIPS Provider, and Microsoft Windows CNG. Gears are consumers of those modules.
- The Windows path is FIPS 140-2 today, not 140-3. The Windows CNG module that Gears routes through — the Cryptographic Primitives Library (
bcryptprimitives.dll) — holds a CMVP FIPS 140-2 certificate per Windows build; Microsoft's FIPS 140-3 validation of the Windows cryptographic modules is still in CMVP processing. A--features fipsWindows binary therefore carries a 140-2 claim on the TLS data plane until the 140-3 certificate is issued. Linux (AWS-LC, cert #4816) and macOS (Apple corecrypto) carry 140-3 claims. - CMVP OE-coverage is the deployment's responsibility. A FIPS claim is void if the running OS version is not inside the cert's OE. The macOS runtime gate is fail-closed; Linux + Windows OE coverage is verified manually per release.
CryptoProvider::fips() = trueis a runtime witness, not just design intent. On macOS it reflects the OE check (oe::fips_witness_ok); on Windows, the OS FIPS-mode flag. On Linux, runtime OE-validation is not yet implemented; OE coverage is verified manually per release via the §release-checklist CMVP-cert search.- TLS 1.2 PRF on macOS is not CAVS-listed. Apple corecrypto exposes generic HMAC primitives but not a CAVS-listed dedicated TLS PRF (unlike
aws-lc-fips'stls_prf::Algorithm). Consequence:fips_provider()on macOS is TLS-1.3-only; customers requiring TLS 1.2 on macOS+FIPS must accept that those connections do not carry a FIPS claim. - JWT signature validation does not go through the FIPS path.
jsonwebtokenusesring/ non-FIPSaws-lc-rsfor RSA / ECDSA verification on bearer tokens. Treat tokens as authentication context, not as data covered by the cryptographic claim. Out of scope today; tracked as TODO-7 in FIPS PRD §13. Cleanup is gated bydeny-fips.tomlPhase B promotion. - Non-FIPS crypto crates remain in the final binary on macOS+fips.
ringis pulled in transitively bypingora-rustls,pingora-pool, andureq; non-FIPSaws-lc-rsis pulled in by rustls's default feature set;chacha20is pulled in by therandecosystem. These are not invoked on the TLS data plane (the installedCryptoProviderroutes every TLS primitive through the validated module) but the symbols are linked into the binary. Linkage smoke (above) confirms no non-validated shared libraries appear at runtime. - Server-side TLS keys load from PEM/DER bytes by default. The bytes transit user-space memory before reaching
SecKeyCreateWithData/BCryptImportKeyPair/EVP_PKEY_new. Strict-FIPS auditors operating under "no plaintext CSPs outside the boundary" require a Keychain / NCrypt / HSM flow; tracked as TODO-1. - Server-side TLS termination (inbound HTTPS) is out of scope. The FIPS scope here covers the toolkit's outbound TLS data path; inbound HTTPS termination is delegated to the reverse proxy in front of the gear and is the deployment's responsibility.
- The wrapper crates are not themselves CMVP-listed. Neither
rustls-cng-cryptonorcf-gears-rustls-corecrypto-provideris a CMVP-validated module — each is a thin wrapper over the validated system module it consumes (Windows CNG and Apple corecrypto respectively). The chain-of-trust comes from the underlying validated module, not the wrapper crate.
- FIPS PRD — full strategy: requirements (FRs, NFRs), ecosystem constraints, alternatives we rejected (OpenSSL FIPS Provider 3.1.2, Go 1.25,
native-tls), per-OS rationale, verification gates, open TODOs. - ADR 0001 — macOS FIPS via custom corecrypto CryptoProvider — why we built
cf-gears-rustls-corecrypto-providerover Apple corecrypto rather thannative-tlsor "Linux-only FIPS". - ADR 0002 — FIPS feature flag via target-conditional shim crate — empty
cf-gears-rustls-fips-shimcrate that encodes per-target Cargo feature activation. - ADR 0003 — Windows FIPS via
rustls-cng-crypto— whyrustls-cng-cryptooverrustls-symcrypttoday, plus documented migration trigger. - ADR 0004 — macOS server-side TLS via corecrypto — server-side TLS / mTLS signing via
SecKeyCreateSignature; SEC1-publicKey-only EC key load that preserves the FIPS boundary; honest TLS-1.2-PRF posture. - ADR 0005 — Workspace-level FIPS dependency policy via cargo-deny —
deny-fips.toml,make fips-policy, Phase A/B/C plan.
Source:
fuzz/· CI workflow:.github/workflows/clusterfuzzlite.yml
Gears use cargo-fuzz with ClusterFuzzLite for continuous fuzzing. Fuzzing discovers panics, logic bugs, and algorithmic complexity attacks in parsers and validators.
Fuzz targets:
| Target | Priority | Component | Status |
|---|---|---|---|
fuzz_odata_filter |
HIGH | OData $filter query parser |
Implemented |
fuzz_odata_cursor |
HIGH | Pagination cursor decoder (base64+JSON) | Implemented |
fuzz_odata_orderby |
MEDIUM | OData $orderby token parser |
Implemented |
fuzz_yaml_config |
HIGH | YAML configuration parser | Planned |
fuzz_html_parser |
MEDIUM | HTML document parser | Planned |
fuzz_pdf_parser |
MEDIUM | PDF document parser | Planned |
CI integration:
- On pull requests: ClusterFuzzLite runs with address sanitizer for 10 minutes per target
- On main branch / nightly: Extended 1-hour runs per target
- Crash artifacts and SARIF results uploaded for triage
Local usage:
make fuzz # Smoke test all targets (30s each)
make fuzz-run FUZZ_TARGET=fuzz_odata_filter FUZZ_SECONDS=300
make fuzz-list # List available targetsMultiple automated scanners run on every pull request and/or on schedule:
| Scanner | What It Checks | Trigger |
|---|---|---|
| CodeQL | Static analysis for security vulnerabilities (Actions, Python, Rust) | PRs to main + weekly schedule |
| OpenSSF Scorecard | Supply-chain security posture (branch protection, dependency pinning, CI/CD hardness) | Weekly + branch protection changes |
| cargo-deny | RustSec advisories, license compliance, source restrictions | Every CI run |
| ClusterFuzzLite | Crash/panic/complexity bugs via fuzzing with address sanitizer | PRs to main/develop |
| Dependabot | Dependency alerts (including malware), security updates, version updates | Continuous (repository-level) |
| Snyk | Dependency vulnerability scanning | Configured at repository/organization level |
| Aikido | Application security posture management | Configured at repository/organization level |
The OpenSSF Scorecard badge is displayed in the project README:
Every pull request is reviewed by automated bots before human review:
| Bot | Mode | Purpose |
|---|---|---|
| CodeRabbit | Automatic on every PR | AI-powered code review with security awareness |
| Graphite | Manual trigger | Stacked PR management and review automation |
| Claude Code | Manual trigger | LLM-powered deep code review |
Source:
docs/spec-templates/·docs/spec-templates/gears-sdlc/
Gears follow a spec-driven development lifecycle where PRD and DESIGN documents are written before implementation. Security is addressed at multiple points:
- PRD template — Non-Functional Requirements section references project-wide security baselines and automated security scans
- DESIGN template — dependency rules mandate
SecurityContextpropagation across all in-process calls - ISO 29148 alignment — global guidelines reference
guidelines/SECURITY.mdfor security policies and threat models - Testing strategy — 90%+ code coverage target with explicit security testing category (unit, integration, e2e, security, performance)
- Git/PR record — all changes flow through PRs with review and immutable merge/audit trail
Gears provide a CLI tool for scaffolding new repositories that automatically inherit the platform's security posture:
| Inherited Configuration | Description |
|---|---|
| Compiler configuration | rust-toolchain.toml, workspace lint rules (#[deny(warnings)], 90+ Clippy rules at deny level), unsafe_code = "forbid" |
| Custom dylint rules | Architectural boundary enforcement (DE01xx–DE13xx series), GTS validation (DE09xx) |
| Makefile targets | make deny (cargo-deny), make fuzz (continuous fuzzing), make dylint (custom lints), make safety (full suite) |
| cargo-deny configuration | deny.toml with RustSec advisory checks, license allow-lists, source restrictions |
This ensures every new service or gear repository starts with the same defense-in-depth baseline described in this document, eliminating configuration drift across the platform.
The following areas have been identified for future hardening:
- FIPS-140-3 — non-TLS crypto cleanup — the
--features fipsbuild routes the TLS data plane through a CMVP-validated module on Linux, macOS, and Windows (see §9). Open items, tracked in the FIPS PRD §13:- TODO-7 — JWT signature validation (
jsonwebtoken) currently usesring/ non-FIPSaws-lc-rs. Audit the surface and either replace upstream, fork, or restrict JWT to symmetric HMAC. Build-time floor enforced viadeny-fips.tomlPhase B promotion. - TODO-8 — Runtime Operational Environment validation on Linux + Windows (macOS already has a sysctl-based fail-closed gate). Today OE coverage on Linux + Windows is verified manually per release via CMVP cert search.
- TODO-1 — Keychain / NCrypt / HSM-stored private keys for server-side TLS (today's PEM/DER load is acceptable for development and most production deployments where filesystem permissions guard the key).
- TODO-7 — JWT signature validation (
- Secure ORM type-column auto-injection — the
ScopableEntitytrait supports atype_coldimension, but automatic GTS type constraint injection from PDP →AccessScope→ SQLWHEREis under development - Tenant Resolver access-control plugins — the
Unauthorizederror variant is reserved in the SDK, but no production plugin enforces caller-vs-target authorization (the static plugin allows any caller to query any configured tenant; the single-tenant plugin uses identity matching only). A policy-backed plugin would enforce fine-grained tenant visibility - Security guidelines in spec templates — add explicit security checklist sections to PRD and DESIGN templates (threat modeling, data classification, authentication requirements per feature)
- Security-focused dylint lints — extend the
DE07xxseries with additional rules:- Detecting hardcoded secrets or API keys
- Enforcing
SecretString/SecretValueusage for sensitive fields - Flagging raw SQL string construction
- Validating
SecurityContextpropagation in gear handlers
- Fuzz target expansion — current implemented targets cover OData parsers (
fuzz_odata_filter,fuzz_odata_cursor,fuzz_odata_orderby). Planned targets:fuzz_yaml_config,fuzz_html_parser,fuzz_pdf_parser,fuzz_json_config,fuzz_markdown_parser - Kani formal verification — expand use of the Kani Rust Verifier for proving safety properties on critical code paths (
make kani) - SBOM generation — add Software Bill of Materials generation to CI for supply-chain transparency
This document is maintained alongside the codebase. For implementation-level security guidelines, see guidelines/SECURITY.md. For the authorization architecture, see docs/arch/authorization/DESIGN.md.