mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-25 17:56:34 +08:00
caddyhttp: Support TLS key logging for debugging (#4808)
* Add SSL key logging. * Resolve merge conflict with master * Add Caddyfile support; various fixes * Also commit go.mod and go.sum, oops * Appease linter * Minor tweaks * Add doc comment Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
parent
74547f5bed
commit
616418281b
|
@ -53,8 +53,7 @@ func init() {
|
||||||
|
|
||||||
// parseBind parses the bind directive. Syntax:
|
// parseBind parses the bind directive. Syntax:
|
||||||
//
|
//
|
||||||
// bind <addresses...>
|
// bind <addresses...>
|
||||||
//
|
|
||||||
func parseBind(h Helper) ([]ConfigValue, error) {
|
func parseBind(h Helper) ([]ConfigValue, error) {
|
||||||
var lnHosts []string
|
var lnHosts []string
|
||||||
for h.Next() {
|
for h.Next() {
|
||||||
|
@ -65,28 +64,28 @@ func parseBind(h Helper) ([]ConfigValue, error) {
|
||||||
|
|
||||||
// parseTLS parses the tls directive. Syntax:
|
// parseTLS parses the tls directive. Syntax:
|
||||||
//
|
//
|
||||||
// tls [<email>|internal]|[<cert_file> <key_file>] {
|
// tls [<email>|internal]|[<cert_file> <key_file>] {
|
||||||
// protocols <min> [<max>]
|
// protocols <min> [<max>]
|
||||||
// ciphers <cipher_suites...>
|
// ciphers <cipher_suites...>
|
||||||
// curves <curves...>
|
// curves <curves...>
|
||||||
// client_auth {
|
// client_auth {
|
||||||
// mode [request|require|verify_if_given|require_and_verify]
|
// mode [request|require|verify_if_given|require_and_verify]
|
||||||
// trusted_ca_cert <base64_der>
|
// trusted_ca_cert <base64_der>
|
||||||
// trusted_ca_cert_file <filename>
|
// trusted_ca_cert_file <filename>
|
||||||
// trusted_leaf_cert <base64_der>
|
// trusted_leaf_cert <base64_der>
|
||||||
// trusted_leaf_cert_file <filename>
|
// trusted_leaf_cert_file <filename>
|
||||||
// }
|
// }
|
||||||
// alpn <values...>
|
// alpn <values...>
|
||||||
// load <paths...>
|
// load <paths...>
|
||||||
// ca <acme_ca_endpoint>
|
// ca <acme_ca_endpoint>
|
||||||
// ca_root <pem_file>
|
// ca_root <pem_file>
|
||||||
// dns <provider_name> [...]
|
// dns <provider_name> [...]
|
||||||
// on_demand
|
// on_demand
|
||||||
// eab <key_id> <mac_key>
|
// eab <key_id> <mac_key>
|
||||||
// issuer <module_name> [...]
|
// issuer <module_name> [...]
|
||||||
// get_certificate <module_name> [...]
|
// get_certificate <module_name> [...]
|
||||||
// }
|
// insecure_secrets_log <log_file>
|
||||||
//
|
// }
|
||||||
func parseTLS(h Helper) ([]ConfigValue, error) {
|
func parseTLS(h Helper) ([]ConfigValue, error) {
|
||||||
cp := new(caddytls.ConnectionPolicy)
|
cp := new(caddytls.ConnectionPolicy)
|
||||||
var fileLoader caddytls.FileLoader
|
var fileLoader caddytls.FileLoader
|
||||||
|
@ -396,6 +395,12 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
|
||||||
}
|
}
|
||||||
onDemand = true
|
onDemand = true
|
||||||
|
|
||||||
|
case "insecure_secrets_log":
|
||||||
|
if !h.NextArg() {
|
||||||
|
return nil, h.ArgErr()
|
||||||
|
}
|
||||||
|
cp.InsecureSecretsLog = h.Val()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, h.Errf("unknown subdirective: %s", h.Val())
|
return nil, h.Errf("unknown subdirective: %s", h.Val())
|
||||||
}
|
}
|
||||||
|
@ -516,8 +521,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
|
||||||
|
|
||||||
// parseRoot parses the root directive. Syntax:
|
// parseRoot parses the root directive. Syntax:
|
||||||
//
|
//
|
||||||
// root [<matcher>] <path>
|
// root [<matcher>] <path>
|
||||||
//
|
|
||||||
func parseRoot(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
func parseRoot(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||||
var root string
|
var root string
|
||||||
for h.Next() {
|
for h.Next() {
|
||||||
|
@ -695,12 +699,11 @@ func parseHandleErrors(h Helper) ([]ConfigValue, error) {
|
||||||
|
|
||||||
// parseLog parses the log directive. Syntax:
|
// parseLog parses the log directive. Syntax:
|
||||||
//
|
//
|
||||||
// log {
|
// log {
|
||||||
// output <writer_module> ...
|
// output <writer_module> ...
|
||||||
// format <encoder_module> ...
|
// format <encoder_module> ...
|
||||||
// level <level>
|
// level <level>
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
func parseLog(h Helper) ([]ConfigValue, error) {
|
func parseLog(h Helper) ([]ConfigValue, error) {
|
||||||
return parseLogHelper(h, nil)
|
return parseLogHelper(h, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,13 +421,13 @@ func parseOCSPStaplingOptions(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
|
|
||||||
// parseLogOptions parses the global log option. Syntax:
|
// parseLogOptions parses the global log option. Syntax:
|
||||||
//
|
//
|
||||||
// log [name] {
|
// log [name] {
|
||||||
// output <writer_module> ...
|
// output <writer_module> ...
|
||||||
// format <encoder_module> ...
|
// format <encoder_module> ...
|
||||||
// level <level>
|
// level <level>
|
||||||
// include <namespaces...>
|
// include <namespaces...>
|
||||||
// exclude <namespaces...>
|
// exclude <namespaces...>
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// When the name argument is unspecified, this directive modifies the default
|
// When the name argument is unspecified, this directive modifies the default
|
||||||
// logger.
|
// logger.
|
||||||
|
|
|
@ -20,11 +20,14 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/mholt/acmez"
|
"github.com/mholt/acmez"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -156,6 +159,16 @@ type ConnectionPolicy struct {
|
||||||
// is no policy configured for the empty SNI value.
|
// is no policy configured for the empty SNI value.
|
||||||
DefaultSNI string `json:"default_sni,omitempty"`
|
DefaultSNI string `json:"default_sni,omitempty"`
|
||||||
|
|
||||||
|
// Also known as "SSLKEYLOGFILE", TLS secrets will be written to
|
||||||
|
// this file in NSS key log format which can then be parsed by
|
||||||
|
// Wireshark and other tools. This is INSECURE as it allows other
|
||||||
|
// programs or tools to decrypt TLS connections. However, this
|
||||||
|
// capability can be useful for debugging and troubleshooting.
|
||||||
|
// **ENABLING THIS LOG COMPROMISES SECURITY!**
|
||||||
|
//
|
||||||
|
// This feature is EXPERIMENTAL and subject to change or removal.
|
||||||
|
InsecureSecretsLog string `json:"insecure_secrets_log,omitempty"`
|
||||||
|
|
||||||
// TLSConfig is the fully-formed, standard lib TLS config
|
// TLSConfig is the fully-formed, standard lib TLS config
|
||||||
// used to serve TLS connections. Provision all
|
// used to serve TLS connections. Provision all
|
||||||
// ConnectionPolicies to populate this. It is exported only
|
// ConnectionPolicies to populate this. It is exported only
|
||||||
|
@ -280,6 +293,30 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.InsecureSecretsLog != "" {
|
||||||
|
filename, err := caddy.NewReplacer().ReplaceOrErr(p.InsecureSecretsLog, true, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filename, err = filepath.Abs(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logFile, _, err := secretsLogPool.LoadOrNew(filename, func() (caddy.Destructor, error) {
|
||||||
|
w, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||||
|
return destructableWriter{w}, err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.OnCancel(func() { _, _ = secretsLogPool.Delete(filename) })
|
||||||
|
|
||||||
|
cfg.KeyLogWriter = logFile.(io.Writer)
|
||||||
|
|
||||||
|
tlsApp.logger.Warn("TLS SECURITY COMPROMISED: secrets logging is enabled!",
|
||||||
|
zap.String("log_filename", filename))
|
||||||
|
}
|
||||||
|
|
||||||
setDefaultTLSParams(cfg)
|
setDefaultTLSParams(cfg)
|
||||||
|
|
||||||
p.TLSConfig = cfg
|
p.TLSConfig = cfg
|
||||||
|
@ -297,7 +334,8 @@ func (p ConnectionPolicy) SettingsEmpty() bool {
|
||||||
p.ProtocolMin == "" &&
|
p.ProtocolMin == "" &&
|
||||||
p.ProtocolMax == "" &&
|
p.ProtocolMax == "" &&
|
||||||
p.ClientAuthentication == nil &&
|
p.ClientAuthentication == nil &&
|
||||||
p.DefaultSNI == ""
|
p.DefaultSNI == "" &&
|
||||||
|
p.InsecureSecretsLog == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientAuthentication configures TLS client auth.
|
// ClientAuthentication configures TLS client auth.
|
||||||
|
@ -542,3 +580,9 @@ type ClientCertificateVerifier interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultALPN = []string{"h2", "http/1.1"}
|
var defaultALPN = []string{"h2", "http/1.1"}
|
||||||
|
|
||||||
|
type destructableWriter struct{ *os.File }
|
||||||
|
|
||||||
|
func (d destructableWriter) Destruct() error { return d.Close() }
|
||||||
|
|
||||||
|
var secretsLogPool = caddy.NewUsagePool()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user