mirror of
https://github.com/coredns/coredns.git
synced 2025-11-11 14:32:25 -05:00
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:
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user