#15: implement ACME certificate store, Traefik/Coolify cert watcher & secrets vault
This commit is contained in:
1 parent
769e0b59f0
commit
6ef427dc09
11 files changed
+1667
No files matched your search
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS acme_pending_keys;
|
||||
DROP TABLE IF EXISTS acme_certificates;
|
||||
DROP TABLE IF EXISTS acme_accounts;
|
||||
@@ -0,0 +1,56 @@
|
||||
-- Self-managed ACME state (PLAN.md section 8.3, mode B).
|
||||
--
|
||||
-- This lives in Postgres rather than on disk so that recreating the container
|
||||
-- keeps the account and the issued certificates. Losing them would force a
|
||||
-- re-registration and a re-issuance on every deploy, which reaches the CA's
|
||||
-- rate limits quickly.
|
||||
--
|
||||
-- Every secret column is encrypted with the same KEK as the DKIM keys: these
|
||||
-- are reusable secrets, so they are sealed, never hashed.
|
||||
|
||||
CREATE TABLE acme_accounts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
-- One account per directory URL, so staging and production coexist.
|
||||
directory_url VARCHAR(255) NOT NULL UNIQUE,
|
||||
email VARCHAR(255),
|
||||
registration_uri TEXT,
|
||||
private_key_enc BYTEA NOT NULL,
|
||||
private_key_nonce BYTEA NOT NULL,
|
||||
key_version INT NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE acme_certificates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
domain CITEXT NOT NULL UNIQUE,
|
||||
certificate_pem TEXT NOT NULL,
|
||||
private_key_enc BYTEA NOT NULL,
|
||||
private_key_nonce BYTEA NOT NULL,
|
||||
key_version INT NOT NULL DEFAULT 1,
|
||||
-- Increments on every key change. A TLSA record published for a
|
||||
-- generation must outlive every certificate of that generation
|
||||
-- (RFC 7671 section 8.1).
|
||||
key_generation INT NOT NULL DEFAULT 1,
|
||||
not_after TIMESTAMPTZ NOT NULL,
|
||||
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX idx_acme_certificates_expiry ON acme_certificates(not_after);
|
||||
|
||||
-- The key reserved for the next issuance. It exists ahead of the certificate
|
||||
-- precisely so its TLSA association can be published and allowed to propagate
|
||||
-- before the certificate that uses it is served.
|
||||
CREATE TABLE acme_pending_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
domain CITEXT NOT NULL UNIQUE,
|
||||
private_key_enc BYTEA NOT NULL,
|
||||
private_key_nonce BYTEA NOT NULL,
|
||||
key_version INT NOT NULL DEFAULT 1,
|
||||
-- SHA-256 of the SubjectPublicKeyInfo, which is the "3 1 1" TLSA value
|
||||
-- for this key. Stored so the rollover can publish it without re-deriving.
|
||||
tlsa_sha256 CHAR(64) NOT NULL,
|
||||
-- When the association was published; the rollover waits out two TTLs
|
||||
-- from here before switching the certificate over.
|
||||
published_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
Reference in new issue
Block a user