summaryrefslogtreecommitdiff
path: root/modules/caddypki/maintain.go
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2020-03-13 11:06:08 -0600
committerGitHub <noreply@github.com>2020-03-13 11:06:08 -0600
commit5a19db5dc2db7c02d0f99630a07a64cacb7f7b44 (patch)
treed820ee2920d97d7cf2faf0fd9541156e20c88d60 /modules/caddypki/maintain.go
parentcfe85a9fe625fea55dc4f809fd91b5c061064508 (diff)
v2: Implement 'pki' app powered by Smallstep for localhost certificates (#3125)
* pki: Initial commit of PKI app (WIP) (see #2502 and #3021) * pki: Ability to use root/intermediates, and sign with root * pki: Fix benign misnamings left over from copy+paste * pki: Only install root if not already trusted * Make HTTPS port the default; all names use auto-HTTPS; bug fixes * Fix build - what happened to our CI tests?? * Fix go.mod
Diffstat (limited to 'modules/caddypki/maintain.go')
-rw-r--r--modules/caddypki/maintain.go99
1 files changed, 99 insertions, 0 deletions
diff --git a/modules/caddypki/maintain.go b/modules/caddypki/maintain.go
new file mode 100644
index 0000000..2fce0d9
--- /dev/null
+++ b/modules/caddypki/maintain.go
@@ -0,0 +1,99 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package caddypki
+
+import (
+ "crypto/x509"
+ "fmt"
+ "time"
+
+ "go.uber.org/zap"
+)
+
+func (p *PKI) maintenance() {
+ ticker := time.NewTicker(10 * time.Minute) // TODO: make configurable
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ p.renewCerts()
+ case <-p.ctx.Done():
+ return
+ }
+ }
+}
+
+func (p *PKI) renewCerts() {
+ for _, ca := range p.CAs {
+ err := p.renewCertsForCA(ca)
+ if err != nil {
+ p.log.Error("renewing intermediate certificates",
+ zap.Error(err),
+ zap.String("ca", ca.id))
+ }
+ }
+}
+
+func (p *PKI) renewCertsForCA(ca *CA) error {
+ ca.mu.Lock()
+ defer ca.mu.Unlock()
+
+ log := p.log.With(zap.String("ca", ca.id))
+
+ // only maintain the root if it's not manually provided in the config
+ if ca.Root == nil {
+ if needsRenewal(ca.root) {
+ // TODO: implement root renewal (use same key)
+ log.Warn("root certificate expiring soon (FIXME: ROOT RENEWAL NOT YET IMPLEMENTED)",
+ zap.Duration("time_remaining", time.Until(ca.inter.NotAfter)),
+ )
+ }
+ }
+
+ // only maintain the intermediate if it's not manually provided in the config
+ if ca.Intermediate == nil {
+ if needsRenewal(ca.inter) {
+ log.Info("intermediate expires soon; renewing",
+ zap.Duration("time_remaining", time.Until(ca.inter.NotAfter)),
+ )
+
+ rootCert, rootKey, err := ca.loadOrGenRoot()
+ if err != nil {
+ return fmt.Errorf("loading root key: %v", err)
+ }
+ interCert, interKey, err := ca.genIntermediate(rootCert, rootKey)
+ if err != nil {
+ return fmt.Errorf("generating new certificate: %v", err)
+ }
+ ca.inter, ca.interKey = interCert, interKey
+
+ log.Info("renewed intermediate",
+ zap.Time("new_expiration", ca.inter.NotAfter),
+ )
+ }
+ }
+
+ return nil
+}
+
+func needsRenewal(cert *x509.Certificate) bool {
+ lifetime := cert.NotAfter.Sub(cert.NotBefore)
+ renewalWindow := time.Duration(float64(lifetime) * renewalWindowRatio)
+ renewalWindowStart := cert.NotAfter.Add(-renewalWindow)
+ return time.Now().After(renewalWindowStart)
+}
+
+const renewalWindowRatio = 0.2 // TODO: make configurable