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
+ "crypto/x509"
19
+ "encoding/json"
20
+ "encoding/pem"
21
+ "fmt"
22
+
23
+ "github.com/caddyserver/certmagic"
24
+
25
+ "github.com/caddyserver/caddy/v2"
26
+)
27
+
28
+func init() {
29
+ caddy.RegisterModule(LeafStorageLoader{})
30
+}
31
+
32
+// LeafStorageLoader loads leaf certificates from the
33
+// globally configured storage module.
34
+type LeafStorageLoader struct {
35
+ // A list of certificate file names to be loaded from storage.
36
+ Certificates []string `json:"certificates,omitempty"`
37
+
38
+ // The storage module where the trusted leaf certificates are stored. Absent
39
+ // explicit storage implies the use of Caddy default storage.
40
+ StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
41
+
42
+ // Reference to the globally configured storage module.
43
+ storage certmagic.Storage
44
+
45
+ ctx caddy.Context
46
+}
47
+
48
+// CaddyModule returns the Caddy module information.
49
+func (LeafStorageLoader) CaddyModule() caddy.ModuleInfo {
50
+ return caddy.ModuleInfo{
51
+ ID: "tls.leaf_cert_loader.storage",
52
+ New: func() caddy.Module { return new(LeafStorageLoader) },
53
+ }
54
+}
55
+
56
+// Provision loads the storage module for sl.
57
+func (sl *LeafStorageLoader) Provision(ctx caddy.Context) error {
58
+ if sl.StorageRaw != nil {
59
+ val, err := ctx.LoadModule(sl, "StorageRaw")
60
+ if err != nil {
61
+ return fmt.Errorf("loading storage module: %v", err)
62
+ }
63
+ cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
64
+ if err != nil {
65
+ return fmt.Errorf("creating storage configuration: %v", err)
66
+ }
67
+ sl.storage = cmStorage
68
+ }
69
+ if sl.storage == nil {
70
+ sl.storage = ctx.Storage()
71
+ }
72
+ sl.ctx = ctx
73
+
74
+ repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
75
+ if !ok {
76
+ repl = caddy.NewReplacer()
77
+ }
78
+ for k, path := range sl.Certificates {
79
+ sl.Certificates[k] = repl.ReplaceKnown(path, "")
80
+ }
81
+ return nil
82
+}
83
+
84
+// LoadLeafCertificates returns the certificates to be loaded by sl.
85
+func (sl LeafStorageLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
86
+ certificates := make([]*x509.Certificate, 0, len(sl.Certificates))
87
+ for _, path := range sl.Certificates {
88
+ certData, err := sl.storage.Load(sl.ctx, path)
89
+ if err != nil {
90
+ return nil, err
91
+ }
92
+
93
+ ders, err := convertPEMToDER(certData)
94
+ if err != nil {
95
+ return nil, err
96
+ }
97
+ certs, err := x509.ParseCertificates(ders)
98
+ if err != nil {
99
+ return nil, err
100
+ }
101
+ certificates = append(certificates, certs...)
102
+ }
103
+ return certificates, nil
104
+}
105
+
106
+func convertPEMToDER(pemData []byte) ([]byte, error) {
107
+ var ders []byte
108
+ // while block is not nil, we have more certificates in the file
109
+ for block, rest := pem.Decode(pemData); block != nil; block, rest = pem.Decode(rest) {
110
+ if block.Type != "CERTIFICATE" {
111
+ return nil, fmt.Errorf("no CERTIFICATE pem block found in the given pem data")
112
+ }
113
+ ders = append(
114
+ ders,
115
+ block.Bytes...,
116
+ )
117
+ }
118
+ // if we decoded nothing, return an error
119
+ if len(ders) == 0 {
120
+ return nil, fmt.Errorf("no CERTIFICATE pem block found in the given pem data")
121
+ }
122
+ return ders, nil
123
+}
124
+
125
+// Interface guard
126
+var (
127
+ _ LeafCertificateLoader = (*LeafStorageLoader)(nil)
128
+ _ caddy.Provisioner = (*LeafStorageLoader)(nil)
129
+)