mirror of
https://github.com/coredns/coredns.git
synced 2026-04-05 03:35:33 -04:00
plugin/tls: Add the keylog option to configure TLSConfig.KeyLogWriter (#7537)
* tls: Add the keylog option to configure TLSConfig.KeyLogWriter Signed-off-by: Ilya Kulakov <kulakov.ilya@gmail.com> * tls: Close keylog file on instance shutdown. Signed-off-by: Ilya Kulakov <kulakov.ilya@gmail.com> --------- Signed-off-by: Ilya Kulakov <kulakov.ilya@gmail.com>
This commit is contained in:
@@ -27,6 +27,7 @@ Parameter CA is optional. If not set, system CAs can be used to verify the clien
|
||||
~~~ txt
|
||||
tls CERT KEY [CA] {
|
||||
client_auth nocert|request|require|verify_if_given|require_and_verify
|
||||
keylog FILE
|
||||
}
|
||||
~~~
|
||||
|
||||
@@ -35,6 +36,9 @@ The option value corresponds to the [ClientAuthType values of the Go tls package
|
||||
The default is "nocert". Note that it makes no sense to specify parameter CA unless this option is
|
||||
set to verify\_if\_given or require\_and\_verify.
|
||||
|
||||
The keylog can be specified to export TLS master secrets in key log format to allow external programs
|
||||
to decrypt TLS connections. It compromises security and should only be used for debugging!
|
||||
|
||||
## Examples
|
||||
|
||||
Start a DNS-over-TLS server that picks up incoming DNS-over-TLS queries on port 5553 and uses the
|
||||
|
||||
@@ -2,14 +2,18 @@ package tls
|
||||
|
||||
import (
|
||||
ctls "crypto/tls"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/plugin"
|
||||
clog "github.com/coredns/coredns/plugin/pkg/log"
|
||||
"github.com/coredns/coredns/plugin/pkg/tls"
|
||||
)
|
||||
|
||||
var log = clog.NewWithPlugin("tls")
|
||||
|
||||
func init() { plugin.Register("tls", setup) }
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
@@ -33,6 +37,7 @@ func parseTLS(c *caddy.Controller) error {
|
||||
return plugin.Error("tls", c.ArgErr())
|
||||
}
|
||||
clientAuth := ctls.NoClientCert
|
||||
var keyLog string
|
||||
for c.NextBlock() {
|
||||
switch c.Val() {
|
||||
case "client_auth":
|
||||
@@ -54,6 +59,15 @@ func parseTLS(c *caddy.Controller) error {
|
||||
default:
|
||||
return c.Errf("unknown authentication type '%s'", authTypeArgs[0])
|
||||
}
|
||||
case "keylog":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) != 1 {
|
||||
return c.ArgErr()
|
||||
}
|
||||
keyLog = args[0]
|
||||
if !filepath.IsAbs(keyLog) && config.Root != "" {
|
||||
keyLog = filepath.Join(config.Root, keyLog)
|
||||
}
|
||||
default:
|
||||
return c.Errf("unknown option '%s'", c.Val())
|
||||
}
|
||||
@@ -71,6 +85,23 @@ func parseTLS(c *caddy.Controller) error {
|
||||
// NewTLSConfigFromArgs only sets RootCAs, so we need to let ClientCAs refer to it.
|
||||
tls.ClientCAs = tls.RootCAs
|
||||
|
||||
if len(keyLog) > 0 {
|
||||
absKeyLog, err := filepath.Abs(keyLog)
|
||||
if err != nil {
|
||||
return c.Errf("unable to write TLS Key Log to %q: %s", keyLog, err)
|
||||
}
|
||||
f, err := os.OpenFile(absKeyLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return c.Errf("unable to write TLS Key Log to %q: %s", absKeyLog, err)
|
||||
}
|
||||
c.OnShutdown(func() error {
|
||||
f.Close()
|
||||
return nil
|
||||
})
|
||||
tls.KeyLogWriter = f
|
||||
log.Warningf("Writing TLS Key Log to %q\n", absKeyLog)
|
||||
}
|
||||
|
||||
config.TLSConfig = tls
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -2,6 +2,8 @@ package tls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -23,6 +25,7 @@ func TestTLS(t *testing.T) {
|
||||
{"tls test_cert.pem test_key.pem test_ca.pem {\nclient_auth require\n}", false, "", ""},
|
||||
{"tls test_cert.pem test_key.pem test_ca.pem {\nclient_auth verify_if_given\n}", false, "", ""},
|
||||
{"tls test_cert.pem test_key.pem test_ca.pem {\nclient_auth require_and_verify\n}", false, "", ""},
|
||||
{"tls test_cert.pem test_key.pem test_ca.pem {\nkeylog tls.log\n}", false, "", ""},
|
||||
// negative
|
||||
{"tls test_cert.pem test_key.pem test_ca.pem {\nunknown\n}", true, "", "unknown option"},
|
||||
// client_auth takes exactly one parameter, which must be one of known keywords.
|
||||
@@ -85,3 +88,39 @@ func TestTLSClientAuthentication(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSKeyLog(t *testing.T) {
|
||||
t.Run("No Path", func(t *testing.T) {
|
||||
input := "tls test_cert.pem test_key.pem test_ca.pem {\nkeylog\n}"
|
||||
c := caddy.NewTestController("dns", input)
|
||||
err := setup(c)
|
||||
if err == nil {
|
||||
t.Error("Expected error but found none")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Bad Path", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
os.Chmod(tmpDir, 0000)
|
||||
input := "tls test_cert.pem test_key.pem test_ca.pem {\nkeylog " + filepath.Join(tmpDir, "tls.log") + "\n}"
|
||||
c := caddy.NewTestController("dns", input)
|
||||
err := setup(c)
|
||||
if err == nil {
|
||||
t.Error("Expected error but found none")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Good Path", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
input := "tls test_cert.pem test_key.pem test_ca.pem {\nkeylog " + filepath.Join(tmpDir, "tls.log") + "\n}"
|
||||
c := caddy.NewTestController("dns", input)
|
||||
err := setup(c)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error but found %v", err)
|
||||
}
|
||||
cfg := dnsserver.GetConfig(c)
|
||||
if cfg.TLSConfig.KeyLogWriter == nil {
|
||||
t.Fatal("KeyLogWriter is not set")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user