forked from forgejo/forgejo
Use caddy's certmagic library for extensible/robust ACME handling (#14177)
* use certmagic for more extensible/robust ACME cert handling * accept TOS based on config option Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
parent
bc05ddc0eb
commit
d2ea21d0d8
437 changed files with 56286 additions and 4270 deletions
281
vendor/github.com/caddyserver/certmagic/storage.go
generated
vendored
Normal file
281
vendor/github.com/caddyserver/certmagic/storage.go
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
|||
// Copyright 2015 Matthew Holt
|
||||
//
|
||||
// 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 certmagic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Storage is a type that implements a key-value store.
|
||||
// Keys are prefix-based, with forward slash '/' as separators
|
||||
// and without a leading slash.
|
||||
//
|
||||
// Processes running in a cluster will wish to use the
|
||||
// same Storage value (its implementation and configuration)
|
||||
// in order to share certificates and other TLS resources
|
||||
// with the cluster.
|
||||
//
|
||||
// The Load, Delete, List, and Stat methods should return
|
||||
// ErrNotExist if the key does not exist.
|
||||
//
|
||||
// Implementations of Storage must be safe for concurrent use.
|
||||
type Storage interface {
|
||||
// Locker provides atomic synchronization
|
||||
// operations, making Storage safe to share.
|
||||
Locker
|
||||
|
||||
// Store puts value at key.
|
||||
Store(key string, value []byte) error
|
||||
|
||||
// Load retrieves the value at key.
|
||||
Load(key string) ([]byte, error)
|
||||
|
||||
// Delete deletes key. An error should be
|
||||
// returned only if the key still exists
|
||||
// when the method returns.
|
||||
Delete(key string) error
|
||||
|
||||
// Exists returns true if the key exists
|
||||
// and there was no error checking.
|
||||
Exists(key string) bool
|
||||
|
||||
// List returns all keys that match prefix.
|
||||
// If recursive is true, non-terminal keys
|
||||
// will be enumerated (i.e. "directories"
|
||||
// should be walked); otherwise, only keys
|
||||
// prefixed exactly by prefix will be listed.
|
||||
List(prefix string, recursive bool) ([]string, error)
|
||||
|
||||
// Stat returns information about key.
|
||||
Stat(key string) (KeyInfo, error)
|
||||
}
|
||||
|
||||
// Locker facilitates synchronization of certificate tasks across
|
||||
// machines and networks.
|
||||
type Locker interface {
|
||||
// Lock acquires the lock for key, blocking until the lock
|
||||
// can be obtained or an error is returned. Note that, even
|
||||
// after acquiring a lock, an idempotent operation may have
|
||||
// already been performed by another process that acquired
|
||||
// the lock before - so always check to make sure idempotent
|
||||
// operations still need to be performed after acquiring the
|
||||
// lock.
|
||||
//
|
||||
// The actual implementation of obtaining of a lock must be
|
||||
// an atomic operation so that multiple Lock calls at the
|
||||
// same time always results in only one caller receiving the
|
||||
// lock at any given time.
|
||||
//
|
||||
// To prevent deadlocks, all implementations (where this concern
|
||||
// is relevant) should put a reasonable expiration on the lock in
|
||||
// case Unlock is unable to be called due to some sort of network
|
||||
// failure or system crash. Additionally, implementations should
|
||||
// honor context cancellation as much as possible (in case the
|
||||
// caller wishes to give up and free resources before the lock
|
||||
// can be obtained).
|
||||
Lock(ctx context.Context, key string) error
|
||||
|
||||
// Unlock releases the lock for key. This method must ONLY be
|
||||
// called after a successful call to Lock, and only after the
|
||||
// critical section is finished, even if it errored or timed
|
||||
// out. Unlock cleans up any resources allocated during Lock.
|
||||
Unlock(key string) error
|
||||
}
|
||||
|
||||
// KeyInfo holds information about a key in storage.
|
||||
// Key and IsTerminal are required; Modified and Size
|
||||
// are optional if the storage implementation is not
|
||||
// able to get that information. Setting them will
|
||||
// make certain operations more consistent or
|
||||
// predictable, but it is not crucial to basic
|
||||
// functionality.
|
||||
type KeyInfo struct {
|
||||
Key string
|
||||
Modified time.Time
|
||||
Size int64
|
||||
IsTerminal bool // false for keys that only contain other keys (like directories)
|
||||
}
|
||||
|
||||
// storeTx stores all the values or none at all.
|
||||
func storeTx(s Storage, all []keyValue) error {
|
||||
for i, kv := range all {
|
||||
err := s.Store(kv.key, kv.value)
|
||||
if err != nil {
|
||||
for j := i - 1; j >= 0; j-- {
|
||||
s.Delete(all[j].key)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// keyValue pairs a key and a value.
|
||||
type keyValue struct {
|
||||
key string
|
||||
value []byte
|
||||
}
|
||||
|
||||
// KeyBuilder provides a namespace for methods that
|
||||
// build keys and key prefixes, for addressing items
|
||||
// in a Storage implementation.
|
||||
type KeyBuilder struct{}
|
||||
|
||||
// CertsPrefix returns the storage key prefix for
|
||||
// the given certificate issuer.
|
||||
func (keys KeyBuilder) CertsPrefix(issuerKey string) string {
|
||||
return path.Join(prefixCerts, keys.Safe(issuerKey))
|
||||
}
|
||||
|
||||
// CertsSitePrefix returns a key prefix for items associated with
|
||||
// the site given by domain using the given issuer key.
|
||||
func (keys KeyBuilder) CertsSitePrefix(issuerKey, domain string) string {
|
||||
return path.Join(keys.CertsPrefix(issuerKey), keys.Safe(domain))
|
||||
}
|
||||
|
||||
// SiteCert returns the path to the certificate file for domain
|
||||
// that is associated with the issuer with the given issuerKey.
|
||||
func (keys KeyBuilder) SiteCert(issuerKey, domain string) string {
|
||||
safeDomain := keys.Safe(domain)
|
||||
return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".crt")
|
||||
}
|
||||
|
||||
// SitePrivateKey returns the path to the private key file for domain
|
||||
// that is associated with the certificate from the given issuer with
|
||||
// the given issuerKey.
|
||||
func (keys KeyBuilder) SitePrivateKey(issuerKey, domain string) string {
|
||||
safeDomain := keys.Safe(domain)
|
||||
return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".key")
|
||||
}
|
||||
|
||||
// SiteMeta returns the path to the metadata file for domain that
|
||||
// is associated with the certificate from the given issuer with
|
||||
// the given issuerKey.
|
||||
func (keys KeyBuilder) SiteMeta(issuerKey, domain string) string {
|
||||
safeDomain := keys.Safe(domain)
|
||||
return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".json")
|
||||
}
|
||||
|
||||
// OCSPStaple returns a key for the OCSP staple associated
|
||||
// with the given certificate. If you have the PEM bundle
|
||||
// handy, pass that in to save an extra encoding step.
|
||||
func (keys KeyBuilder) OCSPStaple(cert *Certificate, pemBundle []byte) string {
|
||||
var ocspFileName string
|
||||
if len(cert.Names) > 0 {
|
||||
firstName := keys.Safe(cert.Names[0])
|
||||
ocspFileName = firstName + "-"
|
||||
}
|
||||
ocspFileName += fastHash(pemBundle)
|
||||
return path.Join(prefixOCSP, ocspFileName)
|
||||
}
|
||||
|
||||
// Safe standardizes and sanitizes str for use as
|
||||
// a single component of a storage key. This method
|
||||
// is idempotent.
|
||||
func (keys KeyBuilder) Safe(str string) string {
|
||||
str = strings.ToLower(str)
|
||||
str = strings.TrimSpace(str)
|
||||
|
||||
// replace a few specific characters
|
||||
repl := strings.NewReplacer(
|
||||
" ", "_",
|
||||
"+", "_plus_",
|
||||
"*", "wildcard_",
|
||||
":", "-",
|
||||
"..", "", // prevent directory traversal (regex allows single dots)
|
||||
)
|
||||
str = repl.Replace(str)
|
||||
|
||||
// finally remove all non-word characters
|
||||
return safeKeyRE.ReplaceAllLiteralString(str, "")
|
||||
}
|
||||
|
||||
// CleanUpOwnLocks immediately cleans up all
|
||||
// current locks obtained by this process. Since
|
||||
// this does not cancel the operations that
|
||||
// the locks are synchronizing, this should be
|
||||
// called only immediately before process exit.
|
||||
func CleanUpOwnLocks() {
|
||||
locksMu.Lock()
|
||||
defer locksMu.Unlock()
|
||||
for lockKey, storage := range locks {
|
||||
err := storage.Unlock(lockKey)
|
||||
if err == nil {
|
||||
delete(locks, lockKey)
|
||||
} else {
|
||||
log.Printf("[ERROR] Unable to clean up lock: %v (lock=%s storage=%s)",
|
||||
err, lockKey, storage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func acquireLock(ctx context.Context, storage Storage, lockKey string) error {
|
||||
err := storage.Lock(ctx, lockKey)
|
||||
if err == nil {
|
||||
locksMu.Lock()
|
||||
locks[lockKey] = storage
|
||||
locksMu.Unlock()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func releaseLock(storage Storage, lockKey string) error {
|
||||
err := storage.Unlock(lockKey)
|
||||
if err == nil {
|
||||
locksMu.Lock()
|
||||
delete(locks, lockKey)
|
||||
locksMu.Unlock()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// locks stores a reference to all the current
|
||||
// locks obtained by this process.
|
||||
var locks = make(map[string]Storage)
|
||||
var locksMu sync.Mutex
|
||||
|
||||
// StorageKeys provides methods for accessing
|
||||
// keys and key prefixes for items in a Storage.
|
||||
// Typically, you will not need to use this
|
||||
// because accessing storage is abstracted away
|
||||
// for most cases. Only use this if you need to
|
||||
// directly access TLS assets in your application.
|
||||
var StorageKeys KeyBuilder
|
||||
|
||||
const (
|
||||
prefixCerts = "certificates"
|
||||
prefixOCSP = "ocsp"
|
||||
)
|
||||
|
||||
// safeKeyRE matches any undesirable characters in storage keys.
|
||||
// Note that this allows dots, so you'll have to strip ".." manually.
|
||||
var safeKeyRE = regexp.MustCompile(`[^\w@.-]`)
|
||||
|
||||
// ErrNotExist is returned by Storage implementations when
|
||||
// a resource is not found. It is similar to os.IsNotExist
|
||||
// except this is a type, not a variable.
|
||||
type ErrNotExist interface {
|
||||
error
|
||||
}
|
||||
|
||||
// defaultFileStorage is a convenient, default storage
|
||||
// implementation using the local file system.
|
||||
var defaultFileStorage = &FileStorage{Path: dataDir()}
|
Loading…
Add table
Add a link
Reference in a new issue