github.com/caddyserver/caddy/v2/modules/caddypki/acmeserver
No package summary is available.
Package
Files: 4. Third party imports: 11. Imports from organisation: 0. Tests: 0. Benchmarks: 0.
Constants
const (
HTTP_01 ACMEChallenge = "http-01"
DNS_01 ACMEChallenge = "dns-01"
TLS_ALPN_01 ACMEChallenge = "tls-alpn-01"
)
const defaultPathPrefix = "/acme/"
Vars
var (
keyCleaner = regexp.MustCompile(`[^\w.-_]`)
databasePool = caddy.NewUsagePool()
)
var (
_ caddyhttp.MiddlewareHandler = (*Handler)(nil)
_ caddy.Provisioner = (*Handler)(nil)
)
Types
ACMEChallenge
ACMEChallenge is an opaque string that represents supported ACME challenges.
type ACMEChallenge string
ACMEChallenges
ACMEChallenges is a list of ACME challenges.
type ACMEChallenges []ACMEChallenge
Handler
Handler is an ACME server handler.
type Handler struct {
// The ID of the CA to use for signing. This refers to
// the ID given to the CA in the `pki` app. If omitted,
// the default ID is "local".
CA string `json:"ca,omitempty"`
// The lifetime for issued certificates
Lifetime caddy.Duration `json:"lifetime,omitempty"`
// The hostname or IP address by which ACME clients
// will access the server. This is used to populate
// the ACME directory endpoint. If not set, the Host
// header of the request will be used.
// COMPATIBILITY NOTE / TODO: This property may go away in the
// future. Do not rely on this property long-term; check release notes.
Host string `json:"host,omitempty"`
// The path prefix under which to serve all ACME
// endpoints. All other requests will not be served
// by this handler and will be passed through to
// the next one. Default: "/acme/".
// COMPATIBILITY NOTE / TODO: This property may go away in the
// future, as it is currently only required due to
// limitations in the underlying library. Do not rely
// on this property long-term; check release notes.
PathPrefix string `json:"path_prefix,omitempty"`
// If true, the CA's root will be the issuer instead of
// the intermediate. This is NOT recommended and should
// only be used when devices/clients do not properly
// validate certificate chains. EXPERIMENTAL: Might be
// changed or removed in the future.
SignWithRoot bool `json:"sign_with_root,omitempty"`
// The addresses of DNS resolvers to use when looking up
// the TXT records for solving DNS challenges.
// It accepts [network addresses](/docs/conventions#network-addresses)
// with port range of only 1. If the host is an IP address,
// it will be dialed directly to resolve the upstream server.
// If the host is not an IP address, the addresses are resolved
// using the [name resolution convention](https://golang.org/pkg/net/#hdr-Name_Resolution)
// of the Go standard library. If the array contains more
// than 1 resolver address, one is chosen at random.
Resolvers []string `json:"resolvers,omitempty"`
// Specify the set of enabled ACME challenges. An empty or absent value
// means all challenges are enabled. Accepted values are:
// "http-01", "dns-01", "tls-alpn-01"
Challenges ACMEChallenges `json:"challenges,omitempty" `
// The policy to use for issuing certificates
Policy *Policy `json:"policy,omitempty"`
logger *zap.Logger
resolvers []caddy.NetworkAddress
ctx caddy.Context
acmeDB acme.DB
acmeAuth *authority.Authority
acmeClient acme.Client
acmeLinker acme.Linker
acmeEndpoints http.Handler
}
Policy
Policy defines the criteria for the ACME server of when to issue a certificate. Refer to the Certificate Issuance Policy on Smallstep website for the evaluation criteria.
type Policy struct {
// If a rule set is configured to allow a certain type of name,
// all other types of names are automatically denied.
Allow *RuleSet `json:"allow,omitempty"`
// If a rule set is configured to deny a certain type of name,
// all other types of names are still allowed.
Deny *RuleSet `json:"deny,omitempty"`
// If set to true, the ACME server will allow issuing wildcard certificates.
AllowWildcardNames bool `json:"allow_wildcard_names,omitempty"`
}
RuleSet
RuleSet is the specific set of SAN criteria for a certificate to be issued or denied.
type RuleSet struct {
// Domains is a list of DNS domains that are allowed to be issued.
// It can be in the form of FQDN for specific domain name, or
// a wildcard domain name format, e.g. *.example.com, to allow
// sub-domains of a domain.
Domains []string `json:"domains,omitempty"`
// IP ranges in the form of CIDR notation or specific IP addresses
// to be approved or denied for certificates. Non-CIDR IP addresses
// are matched exactly.
IPRanges []string `json:"ip_ranges,omitempty"`
}
databaseCloser
This type doesn't have documentation.
type databaseCloser struct {
DB *db.AuthDB
}
resolverClient
This type doesn't have documentation.
type resolverClient struct {
acme.Client
resolver *net.Resolver
ctx context.Context
}
Functions
func (*ACMEChallenge) UnmarshalJSON
The unmarshaller first marshals the value into a string. Then it trims any space around it and lowercase it for normaliztion. The method does not and should not validate the value within accepted enums.
func (c *ACMEChallenge) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
*c = ACMEChallenge(strings.ToLower(strings.TrimSpace(s)))
return nil
}
Cognitive complexity: 2
, Cyclomatic complexity: 2
func (*Handler) Provision
Provision sets up the ACME server handler.
func (ash *Handler) Provision(ctx caddy.Context) error {
ash.ctx = ctx
ash.logger = ctx.Logger()
// set some defaults
if ash.CA == "" {
ash.CA = caddypki.DefaultCAID
}
if ash.PathPrefix == "" {
ash.PathPrefix = defaultPathPrefix
}
if ash.Lifetime == 0 {
ash.Lifetime = caddy.Duration(12 * time.Hour)
}
if len(ash.Challenges) > 0 {
if err := ash.Challenges.validate(); err != nil {
return err
}
}
// get a reference to the configured CA
appModule, err := ctx.App("pki")
if err != nil {
return err
}
pkiApp := appModule.(*caddypki.PKI)
ca, err := pkiApp.GetCA(ctx, ash.CA)
if err != nil {
return err
}
// make sure leaf cert lifetime is less than the intermediate cert lifetime. this check only
// applies for caddy-managed intermediate certificates
if ca.Intermediate == nil && ash.Lifetime >= ca.IntermediateLifetime {
return fmt.Errorf("certificate lifetime (%s) should be less than intermediate certificate lifetime (%s)", time.Duration(ash.Lifetime), time.Duration(ca.IntermediateLifetime))
}
database, err := ash.openDatabase()
if err != nil {
return err
}
authorityConfig := caddypki.AuthorityConfig{
SignWithRoot: ash.SignWithRoot,
AuthConfig: &authority.AuthConfig{
Provisioners: provisioner.List{
&provisioner.ACME{
Name: ash.CA,
Challenges: ash.Challenges.toSmallstepType(),
Options: &provisioner.Options{
X509: ash.Policy.normalizeRules(),
},
Type: provisioner.TypeACME.String(),
Claims: &provisioner.Claims{
MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute},
MaxTLSDur: &provisioner.Duration{Duration: 24 * time.Hour * 365},
DefaultTLSDur: &provisioner.Duration{Duration: time.Duration(ash.Lifetime)},
},
},
},
},
DB: database,
}
ash.acmeAuth, err = ca.NewAuthority(authorityConfig)
if err != nil {
return err
}
ash.acmeDB, err = acmeNoSQL.New(ash.acmeAuth.GetDatabase().(nosql.DB))
if err != nil {
return fmt.Errorf("configuring ACME DB: %v", err)
}
ash.acmeClient, err = ash.makeClient()
if err != nil {
return err
}
ash.acmeLinker = acme.NewLinker(
ash.Host,
strings.Trim(ash.PathPrefix, "/"),
)
// extract its http.Handler so we can use it directly
r := chi.NewRouter()
r.Route(ash.PathPrefix, func(r chi.Router) {
api.Route(r)
})
ash.acmeEndpoints = r
return nil
}
Cognitive complexity: 34
, Cyclomatic complexity: 14
func (ACMEChallenge) String
String returns a string representation of the challenge.
func (c ACMEChallenge) String() string {
return strings.ToLower(string(c))
}
Cognitive complexity: 0
, Cyclomatic complexity: 1
func (Handler) CaddyModule
CaddyModule returns the Caddy module information.
func (Handler) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.acme_server",
New: func() caddy.Module { return new(Handler) },
}
}
Cognitive complexity: 2
, Cyclomatic complexity: 1
func (Handler) Cleanup
Cleanup implements caddy.CleanerUpper and closes any idle databases.
func (ash Handler) Cleanup() error {
key := ash.getDatabaseKey()
deleted, err := databasePool.Delete(key)
if deleted {
if c := ash.logger.Check(zapcore.DebugLevel, "unloading unused CA database"); c != nil {
c.Write(zap.String("db_key", key))
}
}
if err != nil {
if c := ash.logger.Check(zapcore.ErrorLevel, "closing CA database"); c != nil {
c.Write(zap.String("db_key", key), zap.Error(err))
}
}
return err
}
Cognitive complexity: 8
, Cyclomatic complexity: 5
func (Handler) ServeHTTP
func (ash Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
if strings.HasPrefix(r.URL.Path, ash.PathPrefix) {
acmeCtx := acme.NewContext(
r.Context(),
ash.acmeDB,
ash.acmeClient,
ash.acmeLinker,
nil,
)
acmeCtx = authority.NewContext(acmeCtx, ash.acmeAuth)
r = r.WithContext(acmeCtx)
ash.acmeEndpoints.ServeHTTP(w, r)
return nil
}
return next.ServeHTTP(w, r)
}
Cognitive complexity: 2
, Cyclomatic complexity: 2
func (databaseCloser) Destruct
func (closer databaseCloser) Destruct() error {
return (*closer.DB).Shutdown()
}
Cognitive complexity: 0
, Cyclomatic complexity: 1
func (resolverClient) LookupTxt
func (c resolverClient) LookupTxt(name string) ([]string, error) {
return c.resolver.LookupTXT(c.ctx, name)
}
Cognitive complexity: 0
, Cyclomatic complexity: 1
Private functions
func init
init ()
func parseACMEServer
parseACMEServer sets up an ACME server handler from Caddyfile tokens.
acme_server [<matcher>] {
ca <id>
lifetime <duration>
resolvers <addresses...>
challenges <challenges...>
allow_wildcard_names
allow {
domains <domains...>
ip_ranges <addresses...>
}
deny {
domains <domains...>
ip_ranges <addresses...>
}
sign_with_root
}
parseACMEServer (h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
References: caddypki.CA, httpcaddyfile.ConfigValue, time.Duration.
func stringToChallenges
stringToChallenges (chs []string) ACMEChallenges
func normalizeAllowRules
normalizeAllowRules returns nil
if policy is nil, the Allow
rule is nil
,
or all rules within the Allow
rule are empty. Otherwise, it returns the X509NameOptions
with the content of the Allow
rule.
normalizeAllowRules () *policy.X509NameOptions
References: policy.X509NameOptions.
func normalizeDenyRules
normalizeDenyRules returns nil
if policy is nil, the Deny
rule is nil
,
or all rules within the Deny
rule are empty. Otherwise, it returns the X509NameOptions
with the content of the Deny
rule.
normalizeDenyRules () *policy.X509NameOptions
References: policy.X509NameOptions.
func normalizeRules
normalizeRules returns nil
if policy is nil, the Allow
and Deny
rules are nil
,
normalizeRules () *provisioner.X509Options
References: provisioner.X509Options.
func validate
validate checks if the given challenge is supported.
validate () error
References: fmt.Errorf.
func toSmallstepType
toSmallstepType () []provisioner.ACMEChallenge
References: provisioner.ACMEChallenge.
func getDatabaseKey
getDatabaseKey () string
References: strings.ToLower, strings.TrimSpace.
func makeClient
makeClient creates an ACME client which will use a custom resolver instead of net.DefaultResolver.
makeClient () (acme.Client, error)
References: acme.NewClient, context.Context, fmt.Errorf, net.Conn, net.DefaultResolver, net.Dialer, net.Resolver, time.Second, weakrand.Intn.
func openDatabase
openDatabase () (*db.AuthDB, error)
References: db.Config, db.New, filepath.Join, fmt.Errorf, os.MkdirAll, zap.String, zapcore.DebugLevel.
Tests
Files: 1. Third party imports: 2. Imports from organisation: 0. Tests: 3. Benchmarks: 0.