summaryrefslogtreecommitdiff
path: root/modules/caddypki/pki.go
blob: 2caeb2b597632f26a086b36294ef77a1a0ecde51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 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 (
	"fmt"

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

func init() {
	caddy.RegisterModule(PKI{})
}

// PKI provides Public Key Infrastructure facilities for Caddy.
//
// This app can define certificate authorities (CAs) which are capable
// of signing certificates. Other modules can be configured to use
// the CAs defined by this app for issuing certificates or getting
// key information needed for establishing trust.
type PKI struct {
	// The certificate authorities to manage. Each CA is keyed by an
	// ID that is used to uniquely identify it from other CAs.
	// At runtime, the GetCA() method should be used instead to ensure
	// the default CA is provisioned if it hadn't already been.
	// The default CA ID is "local".
	CAs map[string]*CA `json:"certificate_authorities,omitempty"`

	ctx caddy.Context
	log *zap.Logger
}

// CaddyModule returns the Caddy module information.
func (PKI) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "pki",
		New: func() caddy.Module { return new(PKI) },
	}
}

// Provision sets up the configuration for the PKI app.
func (p *PKI) Provision(ctx caddy.Context) error {
	p.ctx = ctx
	p.log = ctx.Logger()

	for caID, ca := range p.CAs {
		err := ca.Provision(ctx, caID, p.log)
		if err != nil {
			return fmt.Errorf("provisioning CA '%s': %v", caID, err)
		}
	}

	// if this app is initialized at all, ensure there's at
	// least a default CA that can be used: the standard CA
	// which is used implicitly for signing local-use certs
	if len(p.CAs) == 0 {
		err := p.ProvisionDefaultCA(ctx)
		if err != nil {
			return fmt.Errorf("provisioning CA '%s': %v", DefaultCAID, err)
		}
	}

	return nil
}

// ProvisionDefaultCA sets up the default CA.
func (p *PKI) ProvisionDefaultCA(ctx caddy.Context) error {
	if p.CAs == nil {
		p.CAs = make(map[string]*CA)
	}

	p.CAs[DefaultCAID] = new(CA)
	return p.CAs[DefaultCAID].Provision(ctx, DefaultCAID, p.log)
}

// Start starts the PKI app.
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.Info("root certificate trust store installation disabled; unconfigured clients may show warnings",
				zap.String("path", ca.rootCertPath))
			continue
		}

		if err := ca.installRoot(); err != nil {
			// could be some system dependencies that are missing;
			// shouldn't totally prevent startup, but we should log it
			ca.log.Error("failed to install root certificate",
				zap.Error(err),
				zap.String("certificate_file", ca.rootCertPath))
		}
	}

	// see if root/intermediates need renewal...
	p.renewCerts()

	// ...and keep them renewed
	go p.maintenance()

	return nil
}

// Stop stops the PKI app.
func (p *PKI) Stop() error {
	return nil
}

// GetCA retrieves a CA by ID. If the ID is the default
// CA ID, and it hasn't been provisioned yet, it will
// be provisioned.
func (p *PKI) GetCA(ctx caddy.Context, id string) (*CA, error) {
	ca, ok := p.CAs[id]
	if !ok {
		// for anything other than the default CA ID, error out if it wasn't configured
		if id != DefaultCAID {
			return nil, fmt.Errorf("no certificate authority configured with id: %s", id)
		}

		// for the default CA ID, provision it, because we want it to "just work"
		err := p.ProvisionDefaultCA(ctx)
		if err != nil {
			return nil, fmt.Errorf("failed to provision default CA: %s", err)
		}
		ca = p.CAs[id]
	}

	return ca, nil
}

// Interface guards
var (
	_ caddy.Provisioner = (*PKI)(nil)
	_ caddy.App         = (*PKI)(nil)
)