Skip to content

Commit

Permalink
pki: Add trust subcommand to install root cert (closes caddyserver#3204)
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Mar 31, 2020
1 parent 904d9ca commit 244b839
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 20 deletions.
2 changes: 1 addition & 1 deletion caddy.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func run(newCfg *Config, start bool) error {
}

if newCfg.storage == nil {
newCfg.storage = &certmagic.FileStorage{Path: AppDataDir()}
newCfg.storage = DefaultStorage
}
certmagic.Default.Storage = newCfg.storage

Expand Down
22 changes: 22 additions & 0 deletions modules/caddypki/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/certmagic"
"github.com/smallstep/truststore"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -323,6 +324,27 @@ func (ca CA) newReplacer() *caddy.Replacer {
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 (
defaultCAID = "local"
defaultCAName = "Caddy Local Authority"
Expand Down
44 changes: 44 additions & 0 deletions modules/caddypki/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package caddypki

import (
"context"
"flag"
"fmt"
"os"
Expand All @@ -26,6 +27,25 @@ import (
)

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{
Name: "untrust",
Func: cmdUntrust,
Expand Down Expand Up @@ -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) {
ca := fs.String("ca")
cert := fs.String("cert")
Expand Down
22 changes: 3 additions & 19 deletions modules/caddypki/pki.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"fmt"

"github.com/caddyserver/caddy/v2"
"github.com/smallstep/truststore"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -71,30 +70,15 @@ func (p *PKI) Start() error {
// install roots to trust store, if not disabled
for _, ca := range p.CAs {
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))
continue
}

// 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))
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 {
if err := ca.installRoot(); err != nil {
// could be some system dependencies that are missing;
// 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.String("certificate_file", ca.rootCertPath))
}
Expand Down
3 changes: 3 additions & 0 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,6 @@ func AppDataDir() string {

// ConfigAutosavePath is the default path to which the last config will be persisted.
var ConfigAutosavePath = filepath.Join(AppConfigDir(), "autosave.json")

// DefaultStorage is Caddy's default storage module.
var DefaultStorage = &certmagic.FileStorage{Path: AppDataDir()}

0 comments on commit 244b839

Please sign in to comment.