m/p/pki: implement CRLs
This implements revokation and CRL watching functionality in the main
metropolis PKI library, in preparation for use in the consensus library
(which has full CRL support). In the future, this should also be
extended to be used in Metropolis authentication/authorization.
This also introduces a breaking change by changing the layout of etcd
storage for the PKI library - but we're pre-MVP, so this is fine.
Change-Id: If0775f5447a76949d8498d8853dd7b9c03e0e6dc
Reviewed-on: https://review.monogon.dev/c/monogon/+/465
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/pkg/pki/certificate.go b/metropolis/pkg/pki/certificate.go
index e7788b1..f6d480a 100644
--- a/metropolis/pkg/pki/certificate.go
+++ b/metropolis/pkg/pki/certificate.go
@@ -185,7 +185,7 @@
}
}
- certPath := c.Namespace.etcdPath("%s-cert.der", c.Name)
+ certPath := c.Namespace.etcdPath("issued/%s-cert.der", c.Name)
// Try loading certificate from etcd.
certRes, err := kv.Get(ctx, certPath)
@@ -273,7 +273,7 @@
}
// First, try loading.
- privPath := c.Namespace.etcdPath("%s-privkey.bin", c.Name)
+ privPath := c.Namespace.etcdPath("keys/%s-privkey.bin", c.Name)
privRes, err := kv.Get(ctx, privPath)
if err != nil {
return fmt.Errorf("failed to get private key from etcd: %w", err)
@@ -307,6 +307,24 @@
return fmt.Errorf("key generation transaction failed: concurrent write")
}
+ crlPath := c.crlPath()
+ emptyCRL, err := c.makeCRL(ctx, kv, nil)
+ if err != nil {
+ return fmt.Errorf("failed to generate empty CRL: %w", err)
+ }
+
+ // Also attempt to emit an empty CRL if one doesn't exist yet.
+ _, err = kv.Txn(ctx).
+ If(
+ clientv3.Compare(clientv3.CreateRevision(crlPath), "=", 0),
+ ).
+ Then(
+ clientv3.OpPut(crlPath, string(emptyCRL)),
+ ).Commit()
+ if err != nil {
+ return fmt.Errorf("failed to upsert empty CRL")
+ }
+
c.PrivateKey = priv
c.PublicKey = pub
return nil