make sure client CA and auth type are set if CA is explicitly specified. (#2825)
* make sure client CA and auth type are set if CA is explicitly specified. added some simple tests to confirm the effect. * test certificates (forgot to add them in the previous commit) * made client auth policy configurable with new client_auth option. README has been updated accordingly. * fix editorial in README
This commit is contained in:
parent
5565ca1c03
commit
a6d9adbf4a
6 changed files with 160 additions and 1 deletions
|
@ -24,6 +24,16 @@ tls CERT KEY [CA]
|
||||||
|
|
||||||
Parameter CA is optional. If not set, system CAs can be used to verify the client certificate
|
Parameter CA is optional. If not set, system CAs can be used to verify the client certificate
|
||||||
|
|
||||||
|
~~~ txt
|
||||||
|
tls CERT KEY [CA] {
|
||||||
|
client_auth nocert|request|require|verify_if_given|require_and_verify
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
If client_auth option is specified, it controls the client authentication policy.
|
||||||
|
The option value corresponds to the [ClientAuthType values of the Go tls package](https://golang.org/pkg/crypto/tls/#ClientAuthType): NoClientCert, RequestClientCert, RequireAnyClientCert, VerifyClientCertIfGiven, and RequireAndVerifyClientCert, respectively.
|
||||||
|
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.
|
||||||
|
|
||||||
## 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
|
||||||
|
|
20
plugin/tls/test_ca.pem
Normal file
20
plugin/tls/test_ca.pem
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDPzCCAiegAwIBAgIJAPjCWTu1wGapMA0GCSqGSIb3DQEBCwUAMDUxCzAJBgNV
|
||||||
|
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQKDAhJbmZvYmxveDAg
|
||||||
|
Fw0xOTA1MTEwMDI3NDRaGA8yMTE5MDQxNzAwMjc0NFowNTELMAkGA1UEBhMCVVMx
|
||||||
|
EzARBgNVBAgMCkNhbGlmb3JuaWExETAPBgNVBAoMCEluZm9ibG94MIIBIjANBgkq
|
||||||
|
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAYiw1UjlYj+nITRUlj5hA7j8U2qWcyN
|
||||||
|
YcDfqQnt173Z8yR7NJokqt3Bd3PlrBZS2XtYSNohxRr4qeJu/g7UBre/fSEU/ZOM
|
||||||
|
Gl7NjBGKQEymJ0d8rBg52iiGNwU+ERI9pcQRA6DCEjVbOmjDiUd5yzuVotG/Sxep
|
||||||
|
GUJ2puJ0p0gWCMEL9sdqY6HHd/hdj6B6+u2xD9NUCkX9pLC7CPFJHnP0vLO4WIWL
|
||||||
|
z5C7yzpeLO9r7Nfnu+2HcRLmuFZVPNxkMq7UymqR1w5ZYJQ5p9E7pyxDVXxHnTqQ
|
||||||
|
yLaAS2/9umrOwVnD1NaN3OdAhDedXbH0cF08GcIQD9rnlkLMW4CKtwIDAQABo1Aw
|
||||||
|
TjAdBgNVHQ4EFgQUHcxJPBmHF0nSv+FJJI/kwrSThf8wHwYDVR0jBBgwFoAUHcxJ
|
||||||
|
PBmHF0nSv+FJJI/kwrSThf8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
||||||
|
AQEAByItgyhlXDv2wnnMVXHHlUCbsKCOtBJZ8EumvKjeOx5G4gqJpQIQPNeBv1Od
|
||||||
|
QT7d15HfT7RQqHSL0uAoGuNuyGjZGWWbLMkVt8T0tXY2v9Dd8eWC/lFaaA0vkqTG
|
||||||
|
GpADSmH+SoFAdPPcYN/sXmEHvZcIQ0wUxuF48ZMwOh7ZOcrZggxlA9+BKHU4fO03
|
||||||
|
o7krzpQZQmEDXNN8bt1R0DIhVADw/G2oJAzK0LGhh4eu6hj6k/cAWS6ujRBGqN0Z
|
||||||
|
fURCrMEyjzbNybhkU1KqSr7eSJOWkl4UJ5Ns/dt9/yw2BBrKH3Mijch7UA8mlbEE
|
||||||
|
29M28u2W7GMXLSSwmtCqDBRNhg==
|
||||||
|
-----END CERTIFICATE-----
|
20
plugin/tls/test_cert.pem
Normal file
20
plugin/tls/test_cert.pem
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDPzCCAiegAwIBAgIJAPezzzshGRiTMA0GCSqGSIb3DQEBCwUAMDUxCzAJBgNV
|
||||||
|
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQKDAhJbmZvYmxveDAg
|
||||||
|
Fw0xOTA1MTEwMDI2MjNaGA8yMTE5MDQxNzAwMjYyM1owNTELMAkGA1UEBhMCVVMx
|
||||||
|
EzARBgNVBAgMCkNhbGlmb3JuaWExETAPBgNVBAoMCEluZm9ibG94MIIBIjANBgkq
|
||||||
|
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAYiw1UjlYj+nITRUlj5hA7j8U2qWcyN
|
||||||
|
YcDfqQnt173Z8yR7NJokqt3Bd3PlrBZS2XtYSNohxRr4qeJu/g7UBre/fSEU/ZOM
|
||||||
|
Gl7NjBGKQEymJ0d8rBg52iiGNwU+ERI9pcQRA6DCEjVbOmjDiUd5yzuVotG/Sxep
|
||||||
|
GUJ2puJ0p0gWCMEL9sdqY6HHd/hdj6B6+u2xD9NUCkX9pLC7CPFJHnP0vLO4WIWL
|
||||||
|
z5C7yzpeLO9r7Nfnu+2HcRLmuFZVPNxkMq7UymqR1w5ZYJQ5p9E7pyxDVXxHnTqQ
|
||||||
|
yLaAS2/9umrOwVnD1NaN3OdAhDedXbH0cF08GcIQD9rnlkLMW4CKtwIDAQABo1Aw
|
||||||
|
TjAdBgNVHQ4EFgQUHcxJPBmHF0nSv+FJJI/kwrSThf8wHwYDVR0jBBgwFoAUHcxJ
|
||||||
|
PBmHF0nSv+FJJI/kwrSThf8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
||||||
|
AQEAQyN9nLImdtufuSjXcrCJ3alt/vffHJIzlPgDsNw8+tjI7aRX7CzuurOOEQUC
|
||||||
|
fJ9A6O+dat5k5yqVb9hDcD42HXtOjRQDYpQ6dOGirLFThIFSMC/7RiqHk0YtxojM
|
||||||
|
ZNBbgXo4o1d+P9b25oc/+pRDzlOvqNL7IzW/LDHnJ4j6tBNguujCB5QFUF5dOa1z
|
||||||
|
UR5rupMvv2KpEgRcfW/d3kwcAxH9nI0SHKJenhtweyajUgInK88TC+aT4909c2XA
|
||||||
|
EADYyWxj1DMz3/sMpvGegHsfTPegNoDgz2yEKdu53dr4BUpF6E+eoCX9Hv78SWH3
|
||||||
|
/rAlkbffzCL5d+I8y0jzEpLEqA==
|
||||||
|
-----END CERTIFICATE-----
|
28
plugin/tls/test_key.pem
Normal file
28
plugin/tls/test_key.pem
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsBiLDVSOViP6c
|
||||||
|
hNFSWPmEDuPxTapZzI1hwN+pCe3XvdnzJHs0miSq3cF3c+WsFlLZe1hI2iHFGvip
|
||||||
|
4m7+DtQGt799IRT9k4waXs2MEYpATKYnR3ysGDnaKIY3BT4REj2lxBEDoMISNVs6
|
||||||
|
aMOJR3nLO5Wi0b9LF6kZQnam4nSnSBYIwQv2x2pjocd3+F2PoHr67bEP01QKRf2k
|
||||||
|
sLsI8Ukec/S8s7hYhYvPkLvLOl4s72vs1+e77YdxEua4VlU83GQyrtTKapHXDllg
|
||||||
|
lDmn0TunLENVfEedOpDItoBLb/26as7BWcPU1o3c50CEN51dsfRwXTwZwhAP2ueW
|
||||||
|
QsxbgIq3AgMBAAECggEAF3FCnYHltoQTxnqnF+S+JAvvbjvaQiCJB9BD6oJK4kKi
|
||||||
|
B+tpytJSuuI7ci7eFqR4J+ESN+NaBMVXK7eKzp5wsHWr575xYNkRl6phsnvVbkvD
|
||||||
|
vMiWKdGnWJ57I9ZYDfWBZyyf8PGgYODajMwoEXYnF9YH30dcHTydM68GAloL8Zu9
|
||||||
|
CtGCmlu4TER0BvG+rK2OD5lt8ORK56eMwzTTqMy0hCkP5VEq8j9RmekEzrgtWKm8
|
||||||
|
OI3i8VnpOA0RCVhJ0q5a5jt/xbKRjFNsUNmy9HBRYg7Iw3SCEHmDtz1R9A9rvaJC
|
||||||
|
WXqwKbGZPY8W69h8BhKcJ5RrKt2PZyJxw+LB610XSQKBgQDR/LIGXdJR/90epiGC
|
||||||
|
p68W9Vc3eWxJlAtLDQCSULphLi6j7D+jesmhD3z2woBPjxkd4TaZa2t94Q1MzSeC
|
||||||
|
ON/Aux1huto9ddxvijUQJN3Ep4zPkHdNzHfRwIZsgGH8u77VY/5I4V7IgxKjWlJ6
|
||||||
|
Ii8ez8xpWj1rnQ0azSaYIcVl7QKBgQDRt+J+iRjKxHWuXoBFfv8oMfl+iYaMdJxu
|
||||||
|
PELWb3RLsZ92hobSAmNR/gC3T7p8NFJlQVCoxZr8zt/Rvqh4aK3aSOuKeUvYAjs1
|
||||||
|
/YbPcdSn6uTTIOi6CcHaJ8ZUXNvY5FuoT0+Q9Eb8fw5NGzxsgsfhScELLgbFKb5E
|
||||||
|
Tkw43ZqeswKBgQCxXBgZnIEaVVw0mOlQ68TNRWfnKR23f92SBGdpLdpeXp1yQwb1
|
||||||
|
U66d5PENkvbBPAJg5GozZzGhXsbXCajHKraCmQiWFTZkFvqbE0cCXcEaatJaNpEu
|
||||||
|
GvdRKKXhWwZoa0MiBZUvhXuDLII/iviCxAC8q5LhoSCjlkENVB22/T83eQKBgQC4
|
||||||
|
c3wRALG+fWZns5QsC5ONnc6rXXfqhxGi3vuGMMbfYF05WP6xLQp/7eBhWg1R+o7R
|
||||||
|
oc24cvxrB+TRTFhOdvsZtvL7es2bMfMz/EUapSp9edpCW3p1Temi30LPplByhf6b
|
||||||
|
nQ4FFuRsZa+FX8QYSDpWypCwLY4k0R8YYqklhrrcgwKBgFiM/GnRc230nj0GGWf1
|
||||||
|
+Ve2M/TQCgS6ufr2F0vU7QkEWfeiN9iunhmhsggqWxOEOU77FhCkQRtztm93hG0K
|
||||||
|
eKoHNh/1HvHGBWsR0TaMDw3n8t7Yg5NmQb617nBELZbxxpd358muLiHDoix86W9Q
|
||||||
|
xM6hB159G1gOEJsi8exm5AlZ
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -1,6 +1,8 @@
|
||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ctls "crypto/tls"
|
||||||
|
|
||||||
"github.com/coredns/coredns/core/dnsserver"
|
"github.com/coredns/coredns/core/dnsserver"
|
||||||
"github.com/coredns/coredns/plugin"
|
"github.com/coredns/coredns/plugin"
|
||||||
"github.com/coredns/coredns/plugin/pkg/tls"
|
"github.com/coredns/coredns/plugin/pkg/tls"
|
||||||
|
@ -16,6 +18,14 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(c *caddy.Controller) error {
|
func setup(c *caddy.Controller) error {
|
||||||
|
err := parseTLS(c)
|
||||||
|
if err != nil {
|
||||||
|
return plugin.Error("tls", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTLS(c *caddy.Controller) error {
|
||||||
config := dnsserver.GetConfig(c)
|
config := dnsserver.GetConfig(c)
|
||||||
|
|
||||||
if config.TLSConfig != nil {
|
if config.TLSConfig != nil {
|
||||||
|
@ -27,10 +37,39 @@ func setup(c *caddy.Controller) error {
|
||||||
if len(args) < 2 || len(args) > 3 {
|
if len(args) < 2 || len(args) > 3 {
|
||||||
return plugin.Error("tls", c.ArgErr())
|
return plugin.Error("tls", c.ArgErr())
|
||||||
}
|
}
|
||||||
|
clientAuth := ctls.NoClientCert
|
||||||
|
for c.NextBlock() {
|
||||||
|
switch c.Val() {
|
||||||
|
case "client_auth":
|
||||||
|
authTypeArgs := c.RemainingArgs()
|
||||||
|
if len(authTypeArgs) != 1 {
|
||||||
|
return c.ArgErr()
|
||||||
|
}
|
||||||
|
switch authTypeArgs[0] {
|
||||||
|
case "nocert":
|
||||||
|
clientAuth = ctls.NoClientCert
|
||||||
|
case "request":
|
||||||
|
clientAuth = ctls.RequestClientCert
|
||||||
|
case "require":
|
||||||
|
clientAuth = ctls.RequireAnyClientCert
|
||||||
|
case "verify_if_given":
|
||||||
|
clientAuth = ctls.VerifyClientCertIfGiven
|
||||||
|
case "require_and_verify":
|
||||||
|
clientAuth = ctls.RequireAndVerifyClientCert
|
||||||
|
default:
|
||||||
|
return c.Errf("unknown authentication type '%s'", authTypeArgs[0])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return c.Errf("unknown option '%s'", c.Val())
|
||||||
|
}
|
||||||
|
}
|
||||||
tls, err := tls.NewTLSConfigFromArgs(args...)
|
tls, err := tls.NewTLSConfigFromArgs(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugin.Error("tls", err)
|
return err
|
||||||
}
|
}
|
||||||
|
tls.ClientAuth = clientAuth
|
||||||
|
// NewTLSConfigFromArgs only sets RootCAs, so we need to let ClientCAs refer to it.
|
||||||
|
tls.ClientCAs = tls.RootCAs
|
||||||
config.TLSConfig = tls
|
config.TLSConfig = tls
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coredns/coredns/core/dnsserver"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +19,11 @@ func TestTLS(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
// positive
|
// positive
|
||||||
// negative
|
// 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.
|
||||||
|
{"tls test_cert.pem test_key.pem test_ca.pem {\nclient_auth\n}", true, "", "Wrong argument"},
|
||||||
|
{"tls test_cert.pem test_key.pem test_ca.pem {\nclient_auth none bogus\n}", true, "", "Wrong argument"},
|
||||||
|
{"tls test_cert.pem test_key.pem test_ca.pem {\nclient_auth bogus\n}", true, "", "unknown authentication type"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
@ -38,3 +46,37 @@ func TestTLS(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLSClientAuthentication(t *testing.T) {
|
||||||
|
// Invalid configurations are tested in the general test case. In this test we only look into specific details of valid client_auth options.
|
||||||
|
tests := []struct {
|
||||||
|
option string // tls plugin option(s)
|
||||||
|
expectedType tls.ClientAuthType // expected authentication type.
|
||||||
|
}{
|
||||||
|
// By default, or if 'nocert' is specified, no cert should be requested.
|
||||||
|
// Other cases should be a straightforward mapping from the keyword to the type value.
|
||||||
|
{"", tls.NoClientCert},
|
||||||
|
{"{\nclient_auth nocert\n}", tls.NoClientCert},
|
||||||
|
{"{\nclient_auth request\n}", tls.RequestClientCert},
|
||||||
|
{"{\nclient_auth require\n}", tls.RequireAnyClientCert},
|
||||||
|
{"{\nclient_auth verify_if_given\n}", tls.VerifyClientCertIfGiven},
|
||||||
|
{"{\nclient_auth require_and_verify\n}", tls.RequireAndVerifyClientCert},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
input := "tls test_cert.pem test_key.pem test_ca.pem " + test.option
|
||||||
|
c := caddy.NewTestController("dns", input)
|
||||||
|
err := setup(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test %d: TLS config is unexpectedly rejected: %v", i, err)
|
||||||
|
continue // there's no point in the rest of the tests.
|
||||||
|
}
|
||||||
|
cfg := dnsserver.GetConfig(c)
|
||||||
|
if cfg.TLSConfig.ClientCAs == nil {
|
||||||
|
t.Errorf("Test %d: Client CA is not configured", i)
|
||||||
|
}
|
||||||
|
if cfg.TLSConfig.ClientAuth != test.expectedType {
|
||||||
|
t.Errorf("Test %d: Unexpected client auth type: %d", i, cfg.TLSConfig.ClientAuth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue