mirror of
https://github.com/coredns/coredns.git
synced 2026-04-05 11:45: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
|
~~~ txt
|
||||||
tls CERT KEY [CA] {
|
tls CERT KEY [CA] {
|
||||||
client_auth nocert|request|require|verify_if_given|require_and_verify
|
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
|
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.
|
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
|
## Examples
|
||||||
|
|
||||||
Start a DNS-over-TLS server that picks up incoming DNS-over-TLS queries on port 5553 and uses the
|
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 (
|
import (
|
||||||
ctls "crypto/tls"
|
ctls "crypto/tls"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/coredns/caddy"
|
"github.com/coredns/caddy"
|
||||||
"github.com/coredns/coredns/core/dnsserver"
|
"github.com/coredns/coredns/core/dnsserver"
|
||||||
"github.com/coredns/coredns/plugin"
|
"github.com/coredns/coredns/plugin"
|
||||||
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
||||||
"github.com/coredns/coredns/plugin/pkg/tls"
|
"github.com/coredns/coredns/plugin/pkg/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var log = clog.NewWithPlugin("tls")
|
||||||
|
|
||||||
func init() { plugin.Register("tls", setup) }
|
func init() { plugin.Register("tls", setup) }
|
||||||
|
|
||||||
func setup(c *caddy.Controller) error {
|
func setup(c *caddy.Controller) error {
|
||||||
@@ -33,6 +37,7 @@ func parseTLS(c *caddy.Controller) error {
|
|||||||
return plugin.Error("tls", c.ArgErr())
|
return plugin.Error("tls", c.ArgErr())
|
||||||
}
|
}
|
||||||
clientAuth := ctls.NoClientCert
|
clientAuth := ctls.NoClientCert
|
||||||
|
var keyLog string
|
||||||
for c.NextBlock() {
|
for c.NextBlock() {
|
||||||
switch c.Val() {
|
switch c.Val() {
|
||||||
case "client_auth":
|
case "client_auth":
|
||||||
@@ -54,6 +59,15 @@ func parseTLS(c *caddy.Controller) error {
|
|||||||
default:
|
default:
|
||||||
return c.Errf("unknown authentication type '%s'", authTypeArgs[0])
|
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:
|
default:
|
||||||
return c.Errf("unknown option '%s'", c.Val())
|
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.
|
// NewTLSConfigFromArgs only sets RootCAs, so we need to let ClientCAs refer to it.
|
||||||
tls.ClientCAs = tls.RootCAs
|
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
|
config.TLSConfig = tls
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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 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 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 {\nclient_auth require_and_verify\n}", false, "", ""},
|
||||||
|
{"tls test_cert.pem test_key.pem test_ca.pem {\nkeylog tls.log\n}", false, "", ""},
|
||||||
// negative
|
// negative
|
||||||
{"tls test_cert.pem test_key.pem test_ca.pem {\nunknown\n}", true, "", "unknown option"},
|
{"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.
|
// 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