Common tls (#474)
* Add common TLS config routines These routines can be used to load TLS configs based upon the args in the Corefile. * Add common routine for Corefile arg handling Add the NewTLSConfigFromArgs routine so that you can just pass in the Corefile args and get a tls.Config. This ensures the parameters are handled consistently across middleware. * Change to varargs style params Change to use args ...string instead of []string. Add documentation of what each call means.
This commit is contained in:
parent
53ac25d1c3
commit
b10a4f9075
3 changed files with 273 additions and 0 deletions
|
@ -3,6 +3,7 @@ package test
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TempFile will create a temporary file on disk and returns the name and a cleanup function to remove it later.
|
// TempFile will create a temporary file on disk and returns the name and a cleanup function to remove it later.
|
||||||
|
@ -17,3 +18,90 @@ func TempFile(dir, content string) (string, func(), error) {
|
||||||
rmFunc := func() { os.Remove(f.Name()) }
|
rmFunc := func() { os.Remove(f.Name()) }
|
||||||
return f.Name(), rmFunc, nil
|
return f.Name(), rmFunc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WritePEMFiles creates a tmp dir with ca.pem, cert.pem, and key.pem and the func to remove it
|
||||||
|
func WritePEMFiles(dir string) (string, func(), error) {
|
||||||
|
tempDir, err := ioutil.TempDir(dir, "go-test-pemfiles")
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC9zCCAd+gAwIBAgIJALGtqdMzpDemMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
|
||||||
|
BAMMB2t1YmUtY2EwHhcNMTYxMDE5MTU1NDI0WhcNNDQwMzA2MTU1NDI0WjASMRAw
|
||||||
|
DgYDVQQDDAdrdWJlLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||||
|
pa4Wu/WkpJNRr8pMVE6jjwzNUOx5mIyoDr8WILSxVQcEeyVPPmAqbmYXtVZO11p9
|
||||||
|
jTzoEqF7Kgts3HVYGCk5abqbE14a8Ru/DmV5avU2hJ/NvSjtNi/O+V6SzCbg5yR9
|
||||||
|
lBR53uADDlzuJEQT9RHq7A5KitFkx4vUcXnjOQCbDogWFoYuOgNEwJPy0Raz3NJc
|
||||||
|
ViVfDqSJ0QHg02kCOMxcGFNRQ9F5aoW7QXZXZXD0tn3wLRlu4+GYyqt8fw5iNdLJ
|
||||||
|
t79yKp8I+vMTmMPz4YKUO+eCl5EY10Qs7wvoG/8QNbjH01BRN3L8iDT2WfxdvjTu
|
||||||
|
1RjPxFL92i+B7HZO7jGLfQIDAQABo1AwTjAdBgNVHQ4EFgQUZTrg+Xt87tkxDhlB
|
||||||
|
gKk9FdTOW3IwHwYDVR0jBBgwFoAUZTrg+Xt87tkxDhlBgKk9FdTOW3IwDAYDVR0T
|
||||||
|
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApB7JFVrZpGSOXNO3W7SlN6OCPXv9
|
||||||
|
C7rIBc8rwOrzi2mZWcBmWheQrqBo8xHif2rlFNVQxtq3JcQ8kfg/m1fHeQ/Ygzel
|
||||||
|
Z+U1OqozynDySBZdNn9i+kXXgAUCqDPp3hEQWe0os/RRpIwo9yOloBxdiX6S0NIf
|
||||||
|
VB8n8kAynFPkH7pYrGrL1HQgDFCSfa4tUJ3+9sppnCu0pNtq5AdhYx9xFb2sn+8G
|
||||||
|
xGbtCkhVk2VQ+BiCWnjYXJ6ZMzabP7wiOFDP9Pvr2ik22PRItsW/TLfHFXM1jDmc
|
||||||
|
I1rs/VUGKzcJGVIWbHrgjP68CTStGAvKgbsTqw7aLXTSqtPw88N9XVSyRg==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
path := filepath.Join(tempDir, "ca.pem")
|
||||||
|
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
data = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIICozCCAYsCCQCRlf5BrvPuqjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdr
|
||||||
|
dWJlLWNhMB4XDTE2MTAxOTE2MDUxOFoXDTE3MTAxOTE2MDUxOFowFTETMBEGA1UE
|
||||||
|
AwwKa3ViZS1hZG1pbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTw
|
||||||
|
a7wCFoiCad/N53aURfjrme+KR7FS0yf5Ur9OR/oM3BoS9stYu5Flzr35oL5T6t5G
|
||||||
|
c2ey78mUs/Cs07psnjUdKH55bDpJSdG7zW9mXNyeLwIefFcj/38SS5NBSotmLo8u
|
||||||
|
scJMGXeQpCQtfVuVJSP2bfU5u5d0KTLSg/Cor6UYonqrRB82HbOuuk8Wjaww4VHo
|
||||||
|
nCq7X8o948V6HN5ZibQOgMMo+nf0wORREHBjvwc4W7ewbaTcfoe1VNAo/QnkqxTF
|
||||||
|
ueMb2HxgghArqQSK8b44O05V0zrde25dVnmnte6sPjcV0plqMJ37jViISxsOPUFh
|
||||||
|
/ZW7zbIM/7CMcDekCiECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAYZE8OxwRR7GR
|
||||||
|
kdd5aIriDwWfcl56cq5ICyx87U8hAZhBxk46a6a901LZPzt3xKyWIFQSRj/NYiQ+
|
||||||
|
/thjGLZI2lhkVgYtyAD4BNxDiuppQSCbkjY9tLVDdExGttEVN7+UYDWJBHy6X16Y
|
||||||
|
xSG9FE3Dvp9LI89Nq8E3dRh+Q8wu52q9HaQXjS5YtzQOtDFKPBkihXu/c6gEHj4Y
|
||||||
|
bZVk8rFiH8/CvcQxAuvNI3VVCFUKd2LeQtqwYQQ//qoiuA15krTq5Ut9eXJ8zxAw
|
||||||
|
zhDEPP4FhY+Sz+y1yWirphl7A1aZwhXVPcfWIGqpQ3jzNwUeocbH27kuLh+U4hQo
|
||||||
|
qeg10RdFnw==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
path = filepath.Join(tempDir, "cert.pem")
|
||||||
|
if err = ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpgIBAAKCAQEAxPBrvAIWiIJp383ndpRF+OuZ74pHsVLTJ/lSv05H+gzcGhL2
|
||||||
|
y1i7kWXOvfmgvlPq3kZzZ7LvyZSz8KzTumyeNR0ofnlsOklJ0bvNb2Zc3J4vAh58
|
||||||
|
VyP/fxJLk0FKi2Yujy6xwkwZd5CkJC19W5UlI/Zt9Tm7l3QpMtKD8KivpRiieqtE
|
||||||
|
HzYds666TxaNrDDhUeicKrtfyj3jxXoc3lmJtA6Awyj6d/TA5FEQcGO/Bzhbt7Bt
|
||||||
|
pNx+h7VU0Cj9CeSrFMW54xvYfGCCECupBIrxvjg7TlXTOt17bl1Weae17qw+NxXS
|
||||||
|
mWownfuNWIhLGw49QWH9lbvNsgz/sIxwN6QKIQIDAQABAoIBAQDCXq9V7ZGjxWMN
|
||||||
|
OkFaLVkqJg3V91puztoMt+xNV8t+JTcOnOzrIXZuOFbl9PwLHPPP0SSRkm9LOvKl
|
||||||
|
dU26zv0OWureeKSymia7U2mcqyC3tX+bzc7WinbeSYZBnc0e7AjD1EgpBcaU1TLL
|
||||||
|
agIxY3A2oD9CKmrVPhZzTIZf/XztqTYjhvs5I2kBeT0imdYGpXkdndRyGX4I5/JQ
|
||||||
|
fnp3Czj+AW3zX7RvVnXOh4OtIAcfoG9xoNyD5LOSlJkkX0MwTS8pEBeZA+A4nb+C
|
||||||
|
ivjnOSgXWD+liisI+LpBgBbwYZ/E49x5ghZYrJt8QXSk7Bl/+UOyv6XZAm2mev6j
|
||||||
|
RLAZtoABAoGBAP2P+1PoKOwsk+d/AmHqyTCUQm0UG18LOLB/5PyWfXs/6caDmdIe
|
||||||
|
DZWeZWng1jUQLEadmoEw/CBY5+tPfHlzwzMNhT7KwUfIDQCIBoS7dzHYnwrJ3VZh
|
||||||
|
qYA05cuGHAAHqwb6UWz3y6Pa4AEVSHX6CM83CAi9jdWZ1rdZybWG+qYBAoGBAMbV
|
||||||
|
FsR/Ft+tK5ALgXGoG83TlmxzZYuZ1SnNje1OSdCQdMFCJB10gwoaRrw1ICzi40Xk
|
||||||
|
ydJwV1upGz1om9ReDAD1zQM9artmQx6+TVLiVPALuARdZE70+NrA6w3ZvxUgJjdN
|
||||||
|
ngvXUr+8SdvaYUAwFu7BulfJlwXjUS711hHW/KQhAoGBALY41QuV2mLwHlLNie7I
|
||||||
|
hlGtGpe9TXZeYB0nrG6B0CfU5LJPPSotguG1dXhDpm138/nDpZeWlnrAqdsHwpKd
|
||||||
|
yPhVjR51I7XsZLuvBdA50Q03egSM0c4UXXXPjh1XgaPb3uMi3YWMBwL4ducQXoS6
|
||||||
|
bb5M9C8j2lxZNF+L3VPhbxwBAoGBAIEWDvX7XKpTDxkxnxRfA84ZNGusb5y2fsHp
|
||||||
|
Bd+vGBUj8+kUO8Yzwm9op8vA4ebCVrMl2jGZZd3IaDryE1lIxZpJ+pPD5+tKdQEc
|
||||||
|
o67P6jz+HrYWu+zW9klvPit71qasfKMi7Rza6oo4f+sQWFsH3ZucgpJD+pyD/Ez0
|
||||||
|
pcpnPRaBAoGBANT/xgHBfIWt4U2rtmRLIIiZxKr+3mGnQdpA1J2BCh+/6AvrEx//
|
||||||
|
E/WObVJXDnBdViu0L9abE9iaTToBVri4cmlDlZagLuKVR+TFTCN/DSlVZTDkqkLI
|
||||||
|
8chzqtkH6b2b2R73hyRysWjsomys34ma3mEEPTX/aXeAF2MSZ/EWT9yL
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
path = filepath.Join(tempDir, "key.pem")
|
||||||
|
if err = ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rmFunc := func() { os.RemoveAll(tempDir) }
|
||||||
|
return tempDir, rmFunc, nil
|
||||||
|
}
|
||||||
|
|
104
middleware/tls/tls.go
Normal file
104
middleware/tls/tls.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTLSConfigFromArgs returns a TLS config based upon the passed
|
||||||
|
// in list of arguments. Typically these come straight from the
|
||||||
|
// Corefile.
|
||||||
|
// no args
|
||||||
|
// - creates a Config with no cert and using system CAs
|
||||||
|
// - use for a client that talks to a server with a public signed cert (CA installed in system)
|
||||||
|
// - the client will not be authenticated by the server since there is no cert
|
||||||
|
// one arg: the path to CA PEM file
|
||||||
|
// - creates a Config with no cert using a specific CA
|
||||||
|
// - use for a client that talks to a server with a private signed cert (CA not installed in system)
|
||||||
|
// - the client will not be authenticated by the server since there is no cert
|
||||||
|
// two args: path to cert PEM file, the path to private key PEM file
|
||||||
|
// - creates a Config with a cert, using system CAs to validate the other end
|
||||||
|
// - use for:
|
||||||
|
// - a server; or,
|
||||||
|
// - a client that talks to a server with a public cert and needs certificate-based authentication
|
||||||
|
// - the other end will authenticate this end via the provided cert
|
||||||
|
// - the cert of the other end will be verified via system CAs
|
||||||
|
// three args: path to cert PEM file, path to client private key PEM file, path to CA PEM file
|
||||||
|
// - creates a Config with the cert, using specified CA to validate the other end
|
||||||
|
// - use for:
|
||||||
|
// - a server; or,
|
||||||
|
// - a client that talks to a server with a privately signed cert and needs certificate-based
|
||||||
|
// authentication
|
||||||
|
// - the other end will authenticate this end via the provided cert
|
||||||
|
// - this end will verify the other end's cert using the specified CA
|
||||||
|
func NewTLSConfigFromArgs(args ...string) (*tls.Config, error) {
|
||||||
|
var err error
|
||||||
|
var c *tls.Config
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
// No client cert, use system CA
|
||||||
|
c, err = NewTLSClientConfig("")
|
||||||
|
case 1:
|
||||||
|
// No client cert, use specified CA
|
||||||
|
c, err = NewTLSClientConfig(args[0])
|
||||||
|
case 2:
|
||||||
|
// Client cert, use system CA
|
||||||
|
c, err = NewTLSConfig(args[0], args[1], "")
|
||||||
|
case 3:
|
||||||
|
// Client cert, use specified CA
|
||||||
|
c, err = NewTLSConfig(args[0], args[1], args[2])
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Maximum of three arguments allowed for TLS config, found %d", len(args))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSConfig returns a TLS config that includes a certificate
|
||||||
|
// Use for server TLS config or when using a client certificate
|
||||||
|
// If caPath is empty, system CAs will be used
|
||||||
|
func NewTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not load TLS cert: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
roots, err := loadRoots(caPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: roots}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSClientConfig returns a TLS config for a client connection
|
||||||
|
// If caPath is empty, system CAs will be used
|
||||||
|
func NewTLSClientConfig(caPath string) (*tls.Config, error) {
|
||||||
|
roots, err := loadRoots(caPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tls.Config{RootCAs: roots}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRoots(caPath string) (*x509.CertPool, error) {
|
||||||
|
if caPath == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
pem, err := ioutil.ReadFile(caPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error reading %s: %s", caPath, err)
|
||||||
|
}
|
||||||
|
ok := roots.AppendCertsFromPEM(pem)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Could not read root certs: %s", err)
|
||||||
|
}
|
||||||
|
return roots, nil
|
||||||
|
}
|
81
middleware/tls/tls_test.go
Normal file
81
middleware/tls/tls_test.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/miekg/coredns/middleware/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPEMFiles(t *testing.T) (rmFunc func(), cert, key, ca string) {
|
||||||
|
tempDir, rmFunc, err := test.WritePEMFiles("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not write PEM files: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert = filepath.Join(tempDir, "cert.pem")
|
||||||
|
key = filepath.Join(tempDir, "key.pem")
|
||||||
|
ca = filepath.Join(tempDir, "ca.pem")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewTLSConfig(t *testing.T) {
|
||||||
|
rmFunc, cert, key, ca := getPEMFiles(t)
|
||||||
|
defer rmFunc()
|
||||||
|
|
||||||
|
_, err := NewTLSConfig(cert, key, ca)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to create TLSConfig: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewTLSClientConfig(t *testing.T) {
|
||||||
|
rmFunc, _, _, ca := getPEMFiles(t)
|
||||||
|
defer rmFunc()
|
||||||
|
|
||||||
|
_, err := NewTLSClientConfig(ca)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to create TLSConfig: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewTLSConfigFromArgs(t *testing.T) {
|
||||||
|
rmFunc, cert, key, ca := getPEMFiles(t)
|
||||||
|
defer rmFunc()
|
||||||
|
|
||||||
|
_, err := NewTLSConfigFromArgs()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to create TLSConfig: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := NewTLSConfigFromArgs(ca)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to create TLSConfig: %s", err)
|
||||||
|
}
|
||||||
|
if c.RootCAs == nil {
|
||||||
|
t.Error("RootCAs should not be nil when one arg passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = NewTLSConfigFromArgs(cert,key)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to create TLSConfig: %s", err)
|
||||||
|
}
|
||||||
|
if c.RootCAs != nil {
|
||||||
|
t.Error("RootCAs should be nil when two args passed")
|
||||||
|
}
|
||||||
|
if len(c.Certificates) != 1 {
|
||||||
|
t.Error("Certificates should have a single entry when two args passed")
|
||||||
|
}
|
||||||
|
args := []string{cert,key,ca}
|
||||||
|
c, err = NewTLSConfigFromArgs(args...)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to create TLSConfig: %s", err)
|
||||||
|
}
|
||||||
|
if c.RootCAs == nil {
|
||||||
|
t.Error("RootCAs should not be nil when three args passed")
|
||||||
|
}
|
||||||
|
if len(c.Certificates) != 1 {
|
||||||
|
t.Error("Certificateis should have a single entry when three args passed")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue