checkId: check.crypto.pack.admission plugin: stellaops.doctor.security severity: fail tags: [security, crypto, supply-chain, trust-root]

Crypto Provider Pack Admission

This Doctor check reports the admission state of the mounted, signed crypto-provider packs (com.stellaops.crypto.{hsm,fips,gost,sm,openpgp,eidas}) and their trust-root — the supply-chain boundary for all regional crypto providers. It is read-only (report-only). Before it existed, pack admission had zero Doctor visibility: Doctor’s own mounted loader (DoctorMountedRuntimePluginLoader) only loads module==doctor plugins, so the crypto packs admitted by MountedSmCryptoRuntimePluginLoader were never observed (gap-matrix check.crypto.pack.admission, P0).

This is the CRYPTO pack trust-root — a distinct mount and trust boundary from the Doctor plugin trust-root. The Doctor plugin trust-root (integration.trust.cosign-root, default /app/etc/certificates/trust-roots/plugins/doctor/cosign.pub) is covered by check.core.plugins.trustroot. This check covers the crypto pack trust-root (default /app/etc/certificates/trust-roots/plugins/crypto/cosign.pub) and the packs it admits.

What It Checks

Resolves the pack profile root and trust-root from the manifest entry integration.crypto.pack / the SmRemote:RuntimePlugins config section (with container defaults applied when unset):

It enumerates each mounted bundle directory and reports a structural admission outcome by mirroring the loader’s admission chain without importing the loader or activating any assembly: each bundle must carry a manifest.json and a detached <assembly>.sig signature, and the configured trust-root file must be present.

ConditionResult
No pack root mounted (regional crypto not deployed)skip (honest — never a false pass)
Pack root mounted but emptyskip
AllowUnsigned=true (admission boundary disabled)fail
Trust-root missing while packs are present (loader would reject every pack)fail
A bundle is missing its manifest.json or detached <assembly>.sig (unsigned)fail (names the rejected pack)
All mounted packs are signed and the trust-root is presentpass

Read-only & secret safety

The check never admits an unsigned pack, never mutates a pack, never activates an assembly, and never prints key material. The trust-root is reported by path + presence only — its key bytes are never read or emitted. The actual cryptographic signature verification remains the loader’s job at startup (fail-closed); Doctor reports the structural pre-conditions that determine whether the loader will admit each pack.

Why It Matters

The signed crypto packs are the supply-chain boundary for every regional crypto provider (eIDAS / FIPS / GOST / SM). If AllowUnsigned is left enabled, an unsigned or tampered provider pack could be admitted and execute. If the crypto trust-root goes missing, the loader silently rejects every pack — the exact “checks vanish → all green” hole (ADR-026 D6) but for crypto providers: the provider quietly fails closed and a 200-looking platform hides a broken regional-crypto posture. This check makes that state visible.

Common Causes

How to Fix

Remediation is manual and report-only. Doctor never admits an unsigned pack, mutates pack state, or alters the trust-root — mount the signed pack, restore the trust-root, or re-sign the bundle yourself.

Docker Compose

# Disable the unsigned escape hatch (the default) so admission fails closed:
#   SmRemote__RuntimePlugins__AllowUnsigned=false

# Restore the CRYPTO trust-root (distinct from the Doctor plugin trust-root):
#   place cosign.pub at /app/etc/certificates/trust-roots/plugins/crypto/cosign.pub

# Re-package a rejected pack WITH a signature (producer):
devops/build/package-runtime-plugins.ps1 -Module crypto -Profile crypto-sm \
  -UseOfflineDevSigner -SignCryptoBundles

# Re-mount the signed pack(s) and restart the crypto host:
docker compose -f devops/compose/docker-compose.crypto-provider.smremote.yml up -d smremote

Bare Metal / systemd

# Ensure AllowUnsigned=false and the trust-root + signed bundles are in the configured paths,
# then restart the crypto host service:
systemctl restart stellaops-smremote

Kubernetes / Helm

# Mount the signed crypto pack and the crypto trust-root as read-only volumes; keep
# SmRemote__RuntimePlugins__AllowUnsigned=false in the Helm values, then:
kubectl rollout restart deployment/stellaops-smremote -n stellaops

Verification

stella doctor run --check check.crypto.pack.admission