GUIDE · PREVIEW
GUIDE / SER.56
source: docs/guide/services/Key Service.md
Services

Key Service

Role

The key service provides per-service encryption keys via deterministic HKDF derivation. It's a lightweight key dispenser: given a service name, it returns the derived key. No dynamic secrets, no leasing, no policy engine. The key service is ~300 lines of Rust.

The key service runs as a tier 1 node service on localhost port 7209.

What It Does

  • Master key management: The master key is sealed to the TPM NV index (or derived from a YubiKey HMAC challenge-response on first boot). The key service unseals the master at startup and holds it in memory.
  • Per-service key derivation: HKDF-SHA256(master_key, service_name) -> deterministic key. The same service name always produces the same key on any node with the same master key.
  • Localhost only: Listens on 127.0.0.1:7209. Only processes on the same node can request keys. No network exposure.

Key Delivery and Tier Visibility

How a service receives its key depends on its trust boundary relative to the host OS (trust boundaries):

Tier Main OS can read service memory? Key delivery Protection against
Node services (tier 1) Yes (same kernel, same address space) Direct: key service returns key via localhost IPC External attackers only
Org containers (tier 2) Yes (shared kernel, root can read /proc/pid/mem) IPC via overlay loopback: key service on WG address, conn_auth (Ed25519) required Network attackers + other containers
Org VMs (tier 3) No (hypervisor boundary, separate kernel) virtio-vsock or overlay: key injected into VM at boot, host never sees plaintext Compromised host OS
Org VMs + SEV-SNP (tier 3+) No (hardware memory encryption) Attestation-gated: VM proves its identity to key service via SEV-SNP attestation before key release Compromised host OS + compromised hypervisor
User VMs (tier 4) No (hypervisor + user key) User-key-encrypted channel: key derived from user's YubiKey/CAC, host and org cannot decrypt Compromised host + compromised org (zero-knowledge)

For tier 1-2, the key service provides protection from outside the node. For tier 3+, it provides protection from the node itself. The tier determines whether key injection is "just keeping it off the network" vs "keeping it away from the host OS."

Why Not Vault/OpenBao

HashiCorp Vault (and its fork OpenBao) provide dynamic secrets, leasing, policy engines, audit logging, and dozens of secret backends. FortrOS's key service provides one thing: deterministic HKDF derivation.

The tradeoff:

  • Vault: Powerful, flexible, complex. Requires its own storage backend, HA configuration, unsealing ceremony, policy authoring. Good for organizations that need dynamic database credentials, PKI automation, or cloud IAM integration.
  • Key service: Simple, deterministic, auditable in one sitting. Good for FortrOS's use case where all secrets are derived from a master key and service identity.

Vault can be deployed as a tier 2 org service for organizations that need its capabilities. The key service is the tier 1 foundation that's always available -- it handles per-service encryption for scratch volumes, shard storage, and service-owned data.

The Derivation Chain

YubiKey HMAC / TPM NV
  -> master_key
    -> HKDF(master, "key-service")    -> key service internal key
    -> HKDF(master, "provisioner")    -> provisioner's encryption key
    -> HKDF(master, "my-database")    -> database service's encryption key
    -> HKDF(master, "build-service")  -> build service's encryption key

Each service gets a unique key. Compromising one service's key reveals nothing about another's. The master key never leaves the key service process.

Secrets Manager (Tier 2)

For organizations that need more than deterministic derivation (stored secrets, dynamic credentials, key rotation), a full secrets manager can be deployed as a tier 2 org service (or tier 3 VM for stronger isolation). The secrets manager itself gets its encryption key from the key service.

The secrets manager is the first service that other services depend on for runtime secrets. Its bootstrap path:

  1. Key service derives the secrets manager's key
  2. Secrets manager unlocks its encrypted state (from shard storage)
  3. Other services request secrets from the secrets manager
  4. If the secrets manager isn't available, services fall back to key-service- derived keys (deterministic, always available, but less flexible)

In disaster recovery: admin YubiKey brings up a node -> key service starts -> secrets manager can start if its shards are available on this node -> other services follow.

Links