pki: Add trust subcommand to install root cert (closes #3204)

This commit is contained in:
Matthew Holt 2020-03-31 17:56:36 -06:00
parent 904d9cab39
commit 244b839f98
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
5 changed files with 73 additions and 20 deletions

View File

@ -372,7 +372,7 @@ func run(newCfg *Config, start bool) error {
} }
if newCfg.storage == nil { if newCfg.storage == nil {
newCfg.storage = &certmagic.FileStorage{Path: AppDataDir()} newCfg.storage = DefaultStorage
} }
certmagic.Default.Storage = newCfg.storage certmagic.Default.Storage = newCfg.storage

View File

@ -24,6 +24,7 @@ import (
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
"github.com/smallstep/truststore"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -323,6 +324,27 @@ func (ca CA) newReplacer() *caddy.Replacer {
return repl return repl
} }
// installRoot installs this CA's root certificate into the
// local trust store(s) if it is not already trusted. The CA
// must already be provisioned.
func (ca CA) installRoot() error {
// avoid password prompt if already trusted
if trusted(ca.root) {
ca.log.Info("root certificate is already trusted by system",
zap.String("path", ca.rootCertPath))
return nil
}
ca.log.Warn("installing root certificate (you might be prompted for password)",
zap.String("path", ca.rootCertPath))
return truststore.Install(ca.root,
truststore.WithDebug(),
truststore.WithFirefox(),
truststore.WithJava(),
)
}
const ( const (
defaultCAID = "local" defaultCAID = "local"
defaultCAName = "Caddy Local Authority" defaultCAName = "Caddy Local Authority"

View File

@ -15,6 +15,7 @@
package caddypki package caddypki
import ( import (
"context"
"flag" "flag"
"fmt" "fmt"
"os" "os"
@ -26,6 +27,25 @@ import (
) )
func init() { func init() {
caddycmd.RegisterCommand(caddycmd.Command{
Name: "trust",
Func: cmdTrust,
Short: "Installs a CA certificate into local trust stores",
Long: `
Adds a root certificate into the local trust stores. Intended for
development environments only.
Since Caddy will install its root certificates into the local trust
stores automatically when they are first generated, this command is
only necessary if you need to pre-install the certificates before
using them; for example, if you have elevated privileges at one
point but not later, you will want to use this command so that a
password prompt is not required later.
This command installs the root certificate only for Caddy's
default CA.`,
})
caddycmd.RegisterCommand(caddycmd.Command{ caddycmd.RegisterCommand(caddycmd.Command{
Name: "untrust", Name: "untrust",
Func: cmdUntrust, Func: cmdUntrust,
@ -57,6 +77,30 @@ If no flags are specified, --ca=local is assumed.`,
}) })
} }
func cmdTrust(fs caddycmd.Flags) (int, error) {
// we have to create a sort of dummy context so that
// the CA can provision itself...
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
defer cancel()
// provision the CA, which generates and stores a root
// certificate if one doesn't already exist in storage
ca := CA{
storage: caddy.DefaultStorage,
}
err := ca.Provision(ctx, defaultCAID, caddy.Log())
if err != nil {
return caddy.ExitCodeFailedStartup, err
}
err = ca.installRoot()
if err != nil {
return caddy.ExitCodeFailedStartup, err
}
return caddy.ExitCodeSuccess, nil
}
func cmdUntrust(fs caddycmd.Flags) (int, error) { func cmdUntrust(fs caddycmd.Flags) (int, error) {
ca := fs.String("ca") ca := fs.String("ca")
cert := fs.String("cert") cert := fs.String("cert")

View File

@ -18,7 +18,6 @@ import (
"fmt" "fmt"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/smallstep/truststore"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -71,30 +70,15 @@ func (p *PKI) Start() error {
// install roots to trust store, if not disabled // install roots to trust store, if not disabled
for _, ca := range p.CAs { for _, ca := range p.CAs {
if ca.InstallTrust != nil && !*ca.InstallTrust { if ca.InstallTrust != nil && !*ca.InstallTrust {
ca.log.Warn("root certificate trust store installation disabled; local clients may show warnings", ca.log.Warn("root certificate trust store installation disabled; unconfigured clients may show warnings",
zap.String("path", ca.rootCertPath)) zap.String("path", ca.rootCertPath))
continue continue
} }
// avoid password prompt if already trusted if err := ca.installRoot(); err != nil {
if trusted(ca.root) {
ca.log.Info("root certificate is already trusted by system",
zap.String("path", ca.rootCertPath))
continue
}
ca.log.Warn("trusting root certificate (you might be prompted for password)",
zap.String("path", ca.rootCertPath))
err := truststore.Install(ca.root,
truststore.WithDebug(),
truststore.WithFirefox(),
truststore.WithJava(),
)
if err != nil {
// could be some system dependencies that are missing; // could be some system dependencies that are missing;
// shouldn't totally prevent startup, but we should log it // shouldn't totally prevent startup, but we should log it
p.log.Error("failed to install root certificate", ca.log.Error("failed to install root certificate",
zap.Error(err), zap.Error(err),
zap.String("certificate_file", ca.rootCertPath)) zap.String("certificate_file", ca.rootCertPath))
} }

View File

@ -155,3 +155,6 @@ func AppDataDir() string {
// ConfigAutosavePath is the default path to which the last config will be persisted. // ConfigAutosavePath is the default path to which the last config will be persisted.
var ConfigAutosavePath = filepath.Join(AppConfigDir(), "autosave.json") var ConfigAutosavePath = filepath.Join(AppConfigDir(), "autosave.json")
// DefaultStorage is Caddy's default storage module.
var DefaultStorage = &certmagic.FileStorage{Path: AppDataDir()}