@marcel91200 / caddy Mirror · read-only / commits / c5197f59
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 14, 05:15 UTC

acme_server: fix reload of acme database (#3874)

* acme_server: Refactor database creation apart from authority creation This is a WIP commit that doesn't really offer anything other than setting us up for using a UsagePool to gracefully reload acme_server configs. * Implement UsagePool * Remove unused context * Fix initializing non-ACME CA This will handle cases where a DB is not provided * Sanitize acme db path and clean debug logs * Move regex to package level to prevent recompiling

Ian committed Nov 23, 2020 at 12:58 UTC c5197f59991df7fbe6b921a2a84483d108525d03
2 files changed +71 -27
modules/caddypki/acmeserver/acmeserver.go
+63 -23
@@ -19,6 +19,7 @@ import (
19 "net/http"
20 "os"
21 "path/filepath"
22 + "regexp"
23 "strings"
24 "time"
25
@@ -66,6 +67,7 @@ type Handler struct {
67 PathPrefix string `json:"path_prefix,omitempty"`
68
69 acmeEndpoints http.Handler
70 + logger *zap.Logger
71 }
72
73 // CaddyModule returns the Caddy module information.
@@ -78,7 +80,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
80
81 // Provision sets up the ACME server handler.
82 func (ash *Handler) Provision(ctx caddy.Context) error {
81 - logger := ctx.Logger(ash)
83 + ash.logger = ctx.Logger(ash)
84 // set some defaults
85 if ash.CA == "" {
86 ash.CA = caddypki.DefaultCAID
@@ -101,25 +103,9 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
103 return fmt.Errorf("no certificate authority configured with id: %s", ash.CA)
104 }
105
104 - dbFolder := filepath.Join(caddy.AppDataDir(), "acme_server")
105 - dbPath := filepath.Join(dbFolder, "db")
106 -
107 - // TODO: See https://github.com/smallstep/nosql/issues/7
108 - err = os.MkdirAll(dbFolder, 0755)
106 + database, err := ash.openDatabase()
107 if err != nil {
110 - return fmt.Errorf("making folder for ACME server database: %v", err)
111 - }
112 -
113 - // Check to see if previous db exists
114 - var stat os.FileInfo
115 - stat, err = os.Stat(dbPath)
116 - if stat != nil && err == nil {
117 - // A badger db is found and should be removed
118 - if stat.IsDir() {
119 - logger.Warn("Found an old badger database and removing it",
120 - zap.String("path", dbPath))
121 - _ = os.RemoveAll(dbPath)
122 - }
108 + return err
109 }
110
111 authorityConfig := caddypki.AuthorityConfig{
@@ -136,10 +122,7 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
122 },
123 },
124 },
139 - DB: &db.Config{
140 - Type: "bbolt",
141 - DataSource: dbPath,
142 - },
125 + DB: database,
126 }
127
128 auth, err := ca.NewAuthority(authorityConfig)
@@ -175,11 +158,68 @@ func (ash Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
158 return next.ServeHTTP(w, r)
159 }
160
161 +func (ash Handler) getDatabaseKey() string {
162 + key := ash.CA
163 + key = strings.ToLower(key)
164 + key = strings.TrimSpace(key)
165 + return keyCleaner.ReplaceAllLiteralString(key, "")
166 +}
167 +
168 +// Cleanup implements caddy.CleanerUpper and closes any idle databases.
169 +func (ash Handler) Cleanup() error {
170 + key := ash.getDatabaseKey()
171 + deleted, err := databasePool.Delete(key)
172 + if deleted {
173 + ash.logger.Debug("unloading unused CA database", zap.String("db_key", key))
174 + }
175 + if err != nil {
176 + ash.logger.Error("closing CA database", zap.String("db_key", key), zap.Error(err))
177 + }
178 + return err
179 +}
180 +
181 +func (ash Handler) openDatabase() (*db.AuthDB, error) {
182 + key := ash.getDatabaseKey()
183 + database, loaded, err := databasePool.LoadOrNew(key, func() (caddy.Destructor, error) {
184 + dbFolder := filepath.Join(caddy.AppDataDir(), "acme_server", key)
185 + dbPath := filepath.Join(dbFolder, "db")
186 +
187 + err := os.MkdirAll(dbFolder, 0755)
188 + if err != nil {
189 + return nil, fmt.Errorf("making folder for CA database: %v", err)
190 + }
191 +
192 + dbConfig := &db.Config{
193 + Type: "bbolt",
194 + DataSource: dbPath,
195 + }
196 + database, err := db.New(dbConfig)
197 + return databaseCloser{&database}, err
198 + })
199 +
200 + if loaded {
201 + ash.logger.Debug("loaded preexisting CA database", zap.String("db_key", key))
202 + }
203 +
204 + return database.(databaseCloser).DB, err
205 +}
206 +
207 const (
208 defaultHost = "localhost"
209 defaultPathPrefix = "/acme/"
210 )
211
212 +var keyCleaner = regexp.MustCompile(`[^\w.-_]`)
213 +var databasePool = caddy.NewUsagePool()
214 +
215 +type databaseCloser struct {
216 + DB *db.AuthDB
217 +}
218 +
219 +func (closer databaseCloser) Destruct() error {
220 + return (*closer.DB).Shutdown()
221 +}
222 +
223 // Interface guards
224 var (
225 _ caddyhttp.MiddlewareHandler = (*Handler)(nil)
modules/caddypki/ca.go
+8 -4
@@ -195,14 +195,18 @@ func (ca CA) NewAuthority(authorityConfig AuthorityConfig) (*authority.Authority
195 issuerKey = ca.IntermediateKey()
196 }
197
198 - auth, err := authority.NewEmbedded(
198 + opts := []authority.Option{
199 authority.WithConfig(&authority.Config{
200 AuthorityConfig: authorityConfig.AuthConfig,
201 - DB: authorityConfig.DB,
201 }),
202 authority.WithX509Signer(issuerCert, issuerKey.(crypto.Signer)),
203 authority.WithX509RootCerts(rootCert),
205 - )
204 + }
205 + // Add a database if we have one
206 + if authorityConfig.DB != nil {
207 + opts = append(opts, authority.WithDatabase(*authorityConfig.DB))
208 + }
209 + auth, err := authority.NewEmbedded(opts...)
210 if err != nil {
211 return nil, fmt.Errorf("initializing certificate authority: %v", err)
212 }
@@ -382,7 +386,7 @@ type AuthorityConfig struct {
386 SignWithRoot bool
387
388 // TODO: should we just embed the underlying authority.Config struct type?
385 - DB *db.Config
389 + DB *db.AuthDB
390 AuthConfig *authority.AuthConfig
391 }
392