{"record":{"id":"7af0337d79687e51","repo":"caddyserver/caddy","slug":"no-permission-module-configured-certificates-not","errorCode":null,"errorMessage":"no permission module configured; certificates not allowed except from external Managers","messagePattern":"no permission module configured; certificates not allowed except from external Managers","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"modules/caddytls/automation.go","lineNumber":315,"sourceCode":"\t\t// but it may still be optional for explicit subjects (bounded, non-wildcard), for the\n\t\t// internal issuer since it doesn't cause public PKI pressure on ACME servers; subtly, it\n\t\t// is useful to allow on-demand TLS to be enabled so Managers can be used, but to still\n\t\t// prevent issuance from Issuers (when Managers don't provide a certificate) if there's no\n\t\t// permission module configured\n\t\tnoProtections := ap.isWildcardOrDefault() && !ap.onlyInternalIssuer() && (tlsApp.Automation == nil || tlsApp.Automation.OnDemand == nil || tlsApp.Automation.OnDemand.permission == nil)\n\t\tfailClosed := noProtections && !ap.hadExplicitManagers // don't allow on-demand issuance (other than implicit managers) if no managers have been explicitly configured\n\t\tif noProtections {\n\t\t\tif !ap.hadExplicitManagers {\n\t\t\t\t// no managers, no explicitly-configured permission module, this is a config error\n\t\t\t\treturn certmagic.Config{}, fmt.Errorf(\"on-demand TLS cannot be enabled without a permission module to prevent abuse; please refer to documentation for details\")\n\t\t\t}\n\t\t\t// allow on-demand to be enabled but only for the purpose of the Managers; issuance won't be allowed from Issuers\n\t\t\ttlsApp.logger.Warn(\"on-demand TLS can only get certificates from the configured external manager(s) because no ask endpoint / permission module is specified\")\n\t\t}\n\t\tond = &certmagic.OnDemandConfig{\n\t\t\tDecisionFunc: func(ctx context.Context, name string) error {\n\t\t\t\tif failClosed {\n\t\t\t\t\treturn fmt.Errorf(\"no permission module configured; certificates not allowed except from external Managers\")\n\t\t\t\t}\n\t\t\t\tif tlsApp.Automation == nil || tlsApp.Automation.OnDemand == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\t// logging the remote IP can be useful for servers that want to count\n\t\t\t\t// attempts from clients to detect patterns of abuse -- it should NOT be\n\t\t\t\t// used solely for decision making, however\n\t\t\t\tvar remoteIP string\n\t\t\t\tif hello, ok := ctx.Value(certmagic.ClientHelloInfoCtxKey).(*tls.ClientHelloInfo); ok && hello != nil {\n\t\t\t\t\tif remote := hello.Conn.RemoteAddr(); remote != nil {\n\t\t\t\t\t\tremoteIP, _, _ = net.SplitHostPort(remote.String())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif c := tlsApp.logger.Check(zapcore.DebugLevel, \"asking for permission for on-demand certificate\"); c != nil {\n\t\t\t\t\tc.Write(\n\t\t\t\t\t\tzap.String(\"remote_ip\", remoteIP),\n\t\t\t\t\t\tzap.String(\"domain\", name),","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/automation.go#L297-L333","documentation":"This is the runtime twin of the config-time on-demand guard: the DecisionFunc installed in certmagic's OnDemandConfig rejects a certificate request because failClosed is true — the policy has no permission module but does have explicitly-configured managers. On-demand TLS is permitted only so the managers can supply certificates; actual issuance from ACME/internal issuers is denied at handshake time.","triggerScenarios":"A TLS handshake presents a ServerName for which no configured manager returns a certificate, and the policy lacks an ask endpoint/permission module, so the DecisionFunc returns this error instead of letting certmagic issue a new public cert.","commonSituations":"A get_certificate (e.g. Tailscale) setup where a client requests a name the manager does not cover; scan/bot traffic probing arbitrary SNI names on a server configured with managers-only on-demand TLS.","solutions":["Expected behavior in most cases: only names provided by the configured manager(s) are served; ensure the requested name is actually available from the manager.","If real on-demand ACME issuance is needed, add an on_demand_tls permission module (ask endpoint or permission module) so the DecisionFunc can approve names.","Add the specific hostnames as explicit subjects on a normal (non-on-demand) policy if they should be issued up front."],"exampleFix":"# before: managers-only on-demand, handshake for unknown name fails\n{\n\ton_demand_tls {\n\t\t# nothing\n\t}\n}\n\n# after: permit on-demand issuance via ask endpoint\n{\n\ton_demand_tls {\n\t\task http://localhost:5555/check\n\t}\n}","handlingStrategy":"validation","validationCode":"// If issuance (not just managers) is expected, require a permission module up front.\nif len(policy.Managers) > 0 && (tlsApp.Automation == nil || tlsApp.Automation.OnDemand == nil || tlsApp.Automation.OnDemand.permission == nil) {\n    log.Warn(\"on-demand issuance is fail-closed: only manager-provided certs will be served\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Expect unknown-SNI handshakes to fail by design in managers-only setups; monitor, don't 'fix' with open issuance.","If users report handshake failures for legitimate names, add a permission module covering those names.","Log DecisionFunc denials at the edge to distinguish scans from real user impact."],"tags":["tls","on-demand","handshake","security","runtime"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}