66. pepsi-keys¶
Manage the end-to-end key store.
66.1. Role¶
pepsi-keys is the operator’s tool for Pepsi’s end-to-end cryptography key
store: pepsi.crypto_identity (the key pairs of the addresses we serve,
private half included and wrapped at rest), pepsi.peer_key (remote
correspondents’ cached public keys and certificates) and pepsi.ca_trust (the
CA certificates inbound S/MIME chains are validated against). It connects
through the shared [pepsi-postgres] section and reads the [pepsi]
CRYPTO_* options and the [pepsi-crypto] section. It is not a stage.
Reference: pepsi-keys(1); the design is explained in
Key management.
It is one of the standalone binaries, outside the unified pepsi executable,
for two reasons: it is the only program that generates key material (folding it
in would link key generation and certificate issuance into every stage worker),
and a site may install it setuid so users can manage their own identities — a
bit a shared multi-call binary cannot carry.
66.2. Features¶
identity list / show — what material exists for an address: protocol, purpose, status, whether it is the primary for its capability, whether it is published, whether the private half is present, purged or was never held (
clientfor the user’s own MUA key, whose private half lives in their mail client), and — inshow— the custody, whether it is the address’s public face and its key-server state.identity generate — create key material and store it. OpenPGP produces one transferable key (Ed25519 by default, RSA optional); S/MIME produces a signing and an encryption certificate, each self-signed for immediate use, or one certificate with both key usages under
CRYPTO_SMIME_SHARED_KEY.--csradditionally emits a PKCS#10 request over each key, so a real CA can issue a replacement without re-keying.identity import / export / csr — store material generated elsewhere (an OpenPGP public key imported without
--privateis registered as the user’s own MUA key), write material back out in its own binary form (--pemfor X.509), or build a certificate request over an identity’s existing key.identity revoke / retire / forget-private — the lifecycle. Revocation and the periodic
retiresweep destroy private signing material and retain decryption material;revoke --uploadpublishes a revocation certificate to the key server first, since that must happen while the signing key still exists.forget-privateis the explicit, confirmed (-yto skip the prompt) way to destroy a decryption key.identity set-primary / publish / unpublish / delete — choose the material used for new messages, opt an address in or out of publication (
publishcan also upload to a key server), or remove a row outright.peer list / show / import / pin / unpin / refresh / prune / remove — the cached peer keys.
refreshre-validates keys that already exist and never discovers one for an address that has none;prunedeletes expired keys and aged-out discovery requests (--dry-runto look first).showapplies the matching rule (an exact address always beats a hand-entered*@domainfallback);importstores a key with sourcemanual, the only source permitted to name a whole domain.ca list / add / enable / disable / remove — the S/MIME trust anchors. A certificate without
basicConstraints CA:TRUEis refused, since no chain could ever end at it. Being accepted here is not the same as being usable: an anchor’s ownnameConstraints,pathLenConstraintand policy constraints bind every chain through it (RFC 5937), and a CA carrying a critical extension Pepsi cannot process is skipped during path building (RFC 5280 §4.2).wrap rotate — re-wrap every stored private key under a new key-encryption key, incrementally and with
--dry-runto look first.dns — print the
OPENPGPKEY(RFC 7929) andSMIMEA(RFC 8162) records that publish an address, under the hashed owner names those standards define. Only worth publishing in a DNSSEC-signed zone, which the output says.
66.3. Privilege model¶
Reaching a private key takes two independently guarded things: the
pepsi-crypto database role — the only one granted the
crypto_identity.private_wrapped column, every other service role holding a
column-level grant that omits it — and the key-encryption key, which lives only
in secrets.d/pepsi-crypto.secret. A stolen database yields ciphertext; the
secret alone yields nothing.
pepsi-keys assumes that role only for the duration of each database call:
started as root it becomes the account (root has no database role of
its own) and returns afterwards. PostgreSQL peer authentication keys off the
effective uid, not the gid, so a setgid bit would grant the group that guards
the secret fragment but not the database identity.
The binary ships without a setuid bit, unlike pepsi-whitelist: one
program able to open every private key in a deployment is a much larger prize
than a single whitelist table. A site that wants users to manage their own
identities installs it setuid pepsi-crypto; identities are owned by the
passwd login their address resolves to, and an unprivileged caller of the setuid
binary may see and change only their own. Without the bit every caller connects
as themselves and the database grants alone decide, so making it setuid is a
site decision rather than a code change. Peer keys and trust anchors are
deployment-wide and operator-only, listing included.
66.4. See also¶
Key management, pepsi-setup, pepsi-whitelist, pepsi-settings, Architecture, pepsi-keys(1), pepsi.conf(5).