@ -19,6 +19,7 @@ import (
"net/http"
"net/http"
"os"
"os"
"path/filepath"
"path/filepath"
"regexp"
"strings"
"strings"
"time"
"time"
@ -66,6 +67,7 @@ type Handler struct {
PathPrefix string ` json:"path_prefix,omitempty" `
PathPrefix string ` json:"path_prefix,omitempty" `
acmeEndpoints http . Handler
acmeEndpoints http . Handler
logger * zap . Logger
}
}
// CaddyModule returns the Caddy module information.
// CaddyModule returns the Caddy module information.
@ -78,7 +80,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
// Provision sets up the ACME server handler.
// Provision sets up the ACME server handler.
func ( ash * Handler ) Provision ( ctx caddy . Context ) error {
func ( ash * Handler ) Provision ( ctx caddy . Context ) error {
logger : = ctx . Logger ( ash )
ash . logger = ctx . Logger ( ash )
// set some defaults
// set some defaults
if ash . CA == "" {
if ash . CA == "" {
ash . CA = caddypki . DefaultCAID
ash . CA = caddypki . DefaultCAID
@ -101,25 +103,9 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
return fmt . Errorf ( "no certificate authority configured with id: %s" , ash . CA )
return fmt . Errorf ( "no certificate authority configured with id: %s" , ash . CA )
}
}
dbFolder := filepath . Join ( caddy . AppDataDir ( ) , "acme_server" )
database , err := ash . openDatabase ( )
dbPath := filepath . Join ( dbFolder , "db" )
// TODO: See https://github.com/smallstep/nosql/issues/7
err = os . MkdirAll ( dbFolder , 0755 )
if err != nil {
if err != nil {
return fmt . Errorf ( "making folder for ACME server database: %v" , err )
return err
}
// Check to see if previous db exists
var stat os . FileInfo
stat , err = os . Stat ( dbPath )
if stat != nil && err == nil {
// A badger db is found and should be removed
if stat . IsDir ( ) {
logger . Warn ( "Found an old badger database and removing it" ,
zap . String ( "path" , dbPath ) )
_ = os . RemoveAll ( dbPath )
}
}
}
authorityConfig := caddypki . AuthorityConfig {
authorityConfig := caddypki . AuthorityConfig {
@ -136,10 +122,7 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
} ,
} ,
} ,
} ,
} ,
} ,
DB : & db . Config {
DB : database ,
Type : "bbolt" ,
DataSource : dbPath ,
} ,
}
}
auth , err := ca . NewAuthority ( authorityConfig )
auth , err := ca . NewAuthority ( authorityConfig )
@ -175,11 +158,68 @@ func (ash Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
return next . ServeHTTP ( w , r )
return next . ServeHTTP ( w , r )
}
}
func ( ash Handler ) getDatabaseKey ( ) string {
key := ash . CA
key = strings . ToLower ( key )
key = strings . TrimSpace ( key )
return keyCleaner . ReplaceAllLiteralString ( key , "" )
}
// Cleanup implements caddy.CleanerUpper and closes any idle databases.
func ( ash Handler ) Cleanup ( ) error {
key := ash . getDatabaseKey ( )
deleted , err := databasePool . Delete ( key )
if deleted {
ash . logger . Debug ( "unloading unused CA database" , zap . String ( "db_key" , key ) )
}
if err != nil {
ash . logger . Error ( "closing CA database" , zap . String ( "db_key" , key ) , zap . Error ( err ) )
}
return err
}
func ( ash Handler ) openDatabase ( ) ( * db . AuthDB , error ) {
key := ash . getDatabaseKey ( )
database , loaded , err := databasePool . LoadOrNew ( key , func ( ) ( caddy . Destructor , error ) {
dbFolder := filepath . Join ( caddy . AppDataDir ( ) , "acme_server" , key )
dbPath := filepath . Join ( dbFolder , "db" )
err := os . MkdirAll ( dbFolder , 0755 )
if err != nil {
return nil , fmt . Errorf ( "making folder for CA database: %v" , err )
}
dbConfig := & db . Config {
Type : "bbolt" ,
DataSource : dbPath ,
}
database , err := db . New ( dbConfig )
return databaseCloser { & database } , err
} )
if loaded {
ash . logger . Debug ( "loaded preexisting CA database" , zap . String ( "db_key" , key ) )
}
return database . ( databaseCloser ) . DB , err
}
const (
const (
defaultHost = "localhost"
defaultHost = "localhost"
defaultPathPrefix = "/acme/"
defaultPathPrefix = "/acme/"
)
)
var keyCleaner = regexp . MustCompile ( ` [^\w.-_] ` )
var databasePool = caddy . NewUsagePool ( )
type databaseCloser struct {
DB * db . AuthDB
}
func ( closer databaseCloser ) Destruct ( ) error {
return ( * closer . DB ) . Shutdown ( )
}
// Interface guards
// Interface guards
var (
var (
_ caddyhttp . MiddlewareHandler = ( * Handler ) ( nil )
_ caddyhttp . MiddlewareHandler = ( * Handler ) ( nil )