@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 207 lines 5.45 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddytls
16
17 import (
18 "bytes"
19 "context"
20 "crypto/x509"
21 "encoding/pem"
22 "time"
23
24 "github.com/caddyserver/certmagic"
25 "github.com/smallstep/certificates/authority/provisioner"
26 "go.uber.org/zap"
27
28 "github.com/caddyserver/caddy/v2"
29 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
30 "github.com/caddyserver/caddy/v2/modules/caddypki"
31 )
32
33 func init() {
34 caddy.RegisterModule(InternalIssuer{})
35 }
36
37 // InternalIssuer is a certificate issuer that generates
38 // certificates internally using a locally-configured
39 // CA which can be customized using the `pki` app.
40 type InternalIssuer struct {
41 // The ID of the CA to use for signing. The default
42 // CA ID is "local". The CA can be configured with the
43 // `pki` app.
44 CA string `json:"ca,omitempty"`
45
46 // The validity period of certificates.
47 Lifetime caddy.Duration `json:"lifetime,omitempty"`
48
49 // If true, the root will be the issuer instead of
50 // the intermediate. This is NOT recommended and should
51 // only be used when devices/clients do not properly
52 // validate certificate chains.
53 SignWithRoot bool `json:"sign_with_root,omitempty"`
54
55 ca *caddypki.CA
56 logger *zap.Logger
57 }
58
59 // CaddyModule returns the Caddy module information.
60 func (InternalIssuer) CaddyModule() caddy.ModuleInfo {
61 return caddy.ModuleInfo{
62 ID: "tls.issuance.internal",
63 New: func() caddy.Module { return new(InternalIssuer) },
64 }
65 }
66
67 // Provision sets up the issuer.
68 func (iss *InternalIssuer) Provision(ctx caddy.Context) error {
69 iss.logger = ctx.Logger()
70
71 // set some defaults
72 if iss.CA == "" {
73 iss.CA = caddypki.DefaultCAID
74 }
75
76 // get a reference to the configured CA
77 appModule, err := ctx.App("pki")
78 if err != nil {
79 return err
80 }
81 pkiApp := appModule.(*caddypki.PKI)
82 ca, err := pkiApp.GetCA(ctx, iss.CA)
83 if err != nil {
84 return err
85 }
86 iss.ca = ca
87
88 // set any other default values
89 if iss.Lifetime == 0 {
90 iss.Lifetime = caddy.Duration(defaultInternalCertLifetime)
91 }
92
93 return nil
94 }
95
96 // IssuerKey returns the unique issuer key for the
97 // configured CA endpoint.
98 func (iss InternalIssuer) IssuerKey() string {
99 return iss.ca.ID
100 }
101
102 // Issue issues a certificate to satisfy the CSR.
103 func (iss InternalIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) {
104 // prepare the signing authority
105 authCfg := caddypki.AuthorityConfig{
106 SignWithRoot: iss.SignWithRoot,
107 }
108 auth, err := iss.ca.NewAuthority(authCfg)
109 if err != nil {
110 return nil, err
111 }
112
113 // get the cert (public key) that will be used for signing
114 var issuerCert *x509.Certificate
115 if iss.SignWithRoot {
116 issuerCert = iss.ca.RootCertificate()
117 } else {
118 chain := iss.ca.IntermediateCertificateChain()
119 issuerCert = chain[0]
120 }
121
122 // ensure issued certificate does not expire later than its issuer
123 lifetime := time.Duration(iss.Lifetime)
124 if time.Now().Add(lifetime).After(issuerCert.NotAfter) {
125 lifetime = time.Until(issuerCert.NotAfter)
126 iss.logger.Warn("cert lifetime would exceed issuer NotAfter, clamping lifetime",
127 zap.Duration("orig_lifetime", time.Duration(iss.Lifetime)),
128 zap.Duration("lifetime", lifetime),
129 zap.Time("not_after", issuerCert.NotAfter),
130 )
131 }
132
133 certChain, err := auth.SignWithContext(ctx, csr, provisioner.SignOptions{}, customCertLifetime(caddy.Duration(lifetime)))
134 if err != nil {
135 return nil, err
136 }
137
138 var buf bytes.Buffer
139 for _, cert := range certChain {
140 err := pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
141 if err != nil {
142 return nil, err
143 }
144 }
145
146 return &certmagic.IssuedCertificate{
147 Certificate: buf.Bytes(),
148 }, nil
149 }
150
151 // UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
152 //
153 // ... internal {
154 // ca <name>
155 // lifetime <duration>
156 // sign_with_root
157 // }
158 func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
159 d.Next() // consume issuer name
160 for d.NextBlock(0) {
161 switch d.Val() {
162 case "ca":
163 if !d.AllArgs(&iss.CA) {
164 return d.ArgErr()
165 }
166
167 case "lifetime":
168 if !d.NextArg() {
169 return d.ArgErr()
170 }
171 dur, err := caddy.ParseDuration(d.Val())
172 if err != nil {
173 return err
174 }
175 iss.Lifetime = caddy.Duration(dur)
176
177 case "sign_with_root":
178 if d.NextArg() {
179 return d.ArgErr()
180 }
181 iss.SignWithRoot = true
182
183 default:
184 return d.Errf("unrecognized subdirective '%s'", d.Val())
185 }
186 }
187 return nil
188 }
189
190 // customCertLifetime allows us to customize certificates that are issued
191 // by Smallstep libs, particularly the NotBefore & NotAfter dates.
192 type customCertLifetime time.Duration
193
194 func (d customCertLifetime) Modify(cert *x509.Certificate, _ provisioner.SignOptions) error {
195 cert.NotBefore = time.Now()
196 cert.NotAfter = cert.NotBefore.Add(time.Duration(d))
197 return nil
198 }
199
200 const defaultInternalCertLifetime = 12 * time.Hour
201
202 // Interface guards
203 var (
204 _ caddy.Provisioner = (*InternalIssuer)(nil)
205 _ certmagic.Issuer = (*InternalIssuer)(nil)
206 _ provisioner.CertificateModifier = (*customCertLifetime)(nil)
207 )