fix: do not require a TLS client cert when InsecureSkipVerify is false
Co-authored-by: Tom Moulard <tom.moulard@traefik.io>
This commit is contained in:
parent
566b205758
commit
d3ff0c2cd4
21 changed files with 273 additions and 184 deletions
|
@ -398,7 +398,7 @@ spec:
|
||||||
info configuration.
|
info configuration.
|
||||||
properties:
|
properties:
|
||||||
issuer:
|
issuer:
|
||||||
description: TLSCLientCertificateDNInfo holds the client TLS
|
description: TLSClientCertificateDNInfo holds the client TLS
|
||||||
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
||||||
properties:
|
properties:
|
||||||
commonName:
|
commonName:
|
||||||
|
@ -425,7 +425,7 @@ spec:
|
||||||
serialNumber:
|
serialNumber:
|
||||||
type: boolean
|
type: boolean
|
||||||
subject:
|
subject:
|
||||||
description: TLSCLientCertificateDNInfo holds the client TLS
|
description: TLSClientCertificateDNInfo holds the client TLS
|
||||||
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
||||||
properties:
|
properties:
|
||||||
commonName:
|
commonName:
|
||||||
|
|
|
@ -840,7 +840,7 @@ spec:
|
||||||
info configuration.
|
info configuration.
|
||||||
properties:
|
properties:
|
||||||
issuer:
|
issuer:
|
||||||
description: TLSCLientCertificateDNInfo holds the client TLS
|
description: TLSClientCertificateDNInfo holds the client TLS
|
||||||
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
||||||
properties:
|
properties:
|
||||||
commonName:
|
commonName:
|
||||||
|
@ -867,7 +867,7 @@ spec:
|
||||||
serialNumber:
|
serialNumber:
|
||||||
type: boolean
|
type: boolean
|
||||||
subject:
|
subject:
|
||||||
description: TLSCLientCertificateDNInfo holds the client TLS
|
description: TLSClientCertificateDNInfo holds the client TLS
|
||||||
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
|
||||||
properties:
|
properties:
|
||||||
commonName:
|
commonName:
|
||||||
|
|
|
@ -273,7 +273,7 @@ func TestDo_dynamicConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
ForwardAuth: &dynamic.ForwardAuth{
|
ForwardAuth: &dynamic.ForwardAuth{
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
TLS: &dynamic.ClientTLS{
|
TLS: &types.ClientTLS{
|
||||||
CA: "ca.pem",
|
CA: "ca.pem",
|
||||||
CAOptional: true,
|
CAOptional: true,
|
||||||
Cert: "cert.pem",
|
Cert: "cert.pem",
|
||||||
|
@ -315,7 +315,7 @@ func TestDo_dynamicConfiguration(t *testing.T) {
|
||||||
NotAfter: true,
|
NotAfter: true,
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
Sans: true,
|
Sans: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -324,7 +324,7 @@ func TestDo_dynamicConfiguration(t *testing.T) {
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
package dynamic
|
package dynamic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ptypes "github.com/traefik/paerser/types"
|
ptypes "github.com/traefik/paerser/types"
|
||||||
"github.com/traefik/traefik/v2/pkg/ip"
|
"github.com/traefik/traefik/v2/pkg/ip"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
@ -131,7 +128,7 @@ type ErrorPage struct {
|
||||||
// ForwardAuth holds the http forward authentication configuration.
|
// ForwardAuth holds the http forward authentication configuration.
|
||||||
type ForwardAuth struct {
|
type ForwardAuth struct {
|
||||||
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||||
TLS *ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
TLS *types.ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||||
TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"`
|
TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"`
|
||||||
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"`
|
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"`
|
||||||
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"`
|
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"`
|
||||||
|
@ -402,16 +399,16 @@ type TLSClientCertificateInfo struct {
|
||||||
NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty" export:"true"`
|
NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty" export:"true"`
|
||||||
NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty" export:"true"`
|
NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty" export:"true"`
|
||||||
Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty" export:"true"`
|
Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty" export:"true"`
|
||||||
Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"`
|
Subject *TLSClientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"`
|
||||||
Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"`
|
Issuer *TLSClientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"`
|
||||||
SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"`
|
SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration.
|
// TLSClientCertificateDNInfo holds the client TLS certificate distinguished name info configuration.
|
||||||
// cf https://tools.ietf.org/html/rfc3739
|
// cf https://tools.ietf.org/html/rfc3739
|
||||||
type TLSCLientCertificateDNInfo struct {
|
type TLSClientCertificateDNInfo struct {
|
||||||
Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"`
|
Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"`
|
||||||
Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"`
|
Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"`
|
||||||
Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"`
|
Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"`
|
||||||
|
@ -425,83 +422,3 @@ type TLSCLientCertificateDNInfo struct {
|
||||||
|
|
||||||
// Users holds a list of users.
|
// Users holds a list of users.
|
||||||
type Users []string
|
type Users []string
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
|
||||||
|
|
||||||
// ClientTLS holds the TLS specific configurations as client
|
|
||||||
// CA, Cert and Key can be either path or file contents.
|
|
||||||
type ClientTLS struct {
|
|
||||||
CA string `json:"ca,omitempty" toml:"ca,omitempty" yaml:"ca,omitempty"`
|
|
||||||
CAOptional bool `json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true"`
|
|
||||||
Cert string `json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty"`
|
|
||||||
Key string `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"`
|
|
||||||
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateTLSConfig creates a TLS config from ClientTLS structures.
|
|
||||||
func (c *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
|
||||||
if c == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
caPool := x509.NewCertPool()
|
|
||||||
clientAuth := tls.NoClientCert
|
|
||||||
if c.CA != "" {
|
|
||||||
var ca []byte
|
|
||||||
if _, errCA := os.Stat(c.CA); errCA == nil {
|
|
||||||
ca, err = os.ReadFile(c.CA)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read CA. %w", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ca = []byte(c.CA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !caPool.AppendCertsFromPEM(ca) {
|
|
||||||
return nil, fmt.Errorf("failed to parse CA")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.CAOptional {
|
|
||||||
clientAuth = tls.VerifyClientCertIfGiven
|
|
||||||
} else {
|
|
||||||
clientAuth = tls.RequireAndVerifyClientCert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cert := tls.Certificate{}
|
|
||||||
_, errKeyIsFile := os.Stat(c.Key)
|
|
||||||
|
|
||||||
if !c.InsecureSkipVerify && (len(c.Cert) == 0 || len(c.Key) == 0) {
|
|
||||||
return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.Cert) > 0 && len(c.Key) > 0 {
|
|
||||||
if _, errCertIsFile := os.Stat(c.Cert); errCertIsFile == nil {
|
|
||||||
if errKeyIsFile == nil {
|
|
||||||
cert, err = tls.LoadX509KeyPair(c.Cert, c.Key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load TLS keypair: %w", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("tls cert is a file, but tls key is not")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if errKeyIsFile != nil {
|
|
||||||
cert, err = tls.X509KeyPair([]byte(c.Cert), []byte(c.Key))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load TLS keypair: %w", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("TLS key is a file, but tls cert is not")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
RootCAs: caPool,
|
|
||||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
|
||||||
ClientAuth: clientAuth,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -124,22 +124,6 @@ func (in *CircuitBreaker) DeepCopy() *CircuitBreaker {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *ClientTLS) DeepCopyInto(out *ClientTLS) {
|
|
||||||
*out = *in
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLS.
|
|
||||||
func (in *ClientTLS) DeepCopy() *ClientTLS {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(ClientTLS)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Compress) DeepCopyInto(out *Compress) {
|
func (in *Compress) DeepCopyInto(out *Compress) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -306,7 +290,7 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.TLS != nil {
|
if in.TLS != nil {
|
||||||
in, out := &in.TLS, &out.TLS
|
in, out := &in.TLS, &out.TLS
|
||||||
*out = new(ClientTLS)
|
*out = new(types.ClientTLS)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
if in.AuthResponseHeaders != nil {
|
if in.AuthResponseHeaders != nil {
|
||||||
|
@ -1536,17 +1520,17 @@ func (in *TCPWeightedRoundRobin) DeepCopy() *TCPWeightedRoundRobin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) {
|
func (in *TLSClientCertificateDNInfo) DeepCopyInto(out *TLSClientCertificateDNInfo) {
|
||||||
*out = *in
|
*out = *in
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateDNInfo.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSClientCertificateDNInfo.
|
||||||
func (in *TLSCLientCertificateDNInfo) DeepCopy() *TLSCLientCertificateDNInfo {
|
func (in *TLSClientCertificateDNInfo) DeepCopy() *TLSClientCertificateDNInfo {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(TLSCLientCertificateDNInfo)
|
out := new(TLSClientCertificateDNInfo)
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
@ -1556,12 +1540,12 @@ func (in *TLSClientCertificateInfo) DeepCopyInto(out *TLSClientCertificateInfo)
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.Subject != nil {
|
if in.Subject != nil {
|
||||||
in, out := &in.Subject, &out.Subject
|
in, out := &in.Subject, &out.Subject
|
||||||
*out = new(TLSCLientCertificateDNInfo)
|
*out = new(TLSClientCertificateDNInfo)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
if in.Issuer != nil {
|
if in.Issuer != nil {
|
||||||
in, out := &in.Issuer, &out.Issuer
|
in, out := &in.Issuer, &out.Issuer
|
||||||
*out = new(TLSCLientCertificateDNInfo)
|
*out = new(TLSClientCertificateDNInfo)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
ptypes "github.com/traefik/paerser/types"
|
ptypes "github.com/traefik/paerser/types"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDecodeConfiguration(t *testing.T) {
|
func TestDecodeConfiguration(t *testing.T) {
|
||||||
|
@ -366,7 +367,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
NotAfter: true,
|
NotAfter: true,
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -375,7 +376,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -501,7 +502,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"Middleware7": {
|
"Middleware7": {
|
||||||
ForwardAuth: &dynamic.ForwardAuth{
|
ForwardAuth: &dynamic.ForwardAuth{
|
||||||
Address: "foobar",
|
Address: "foobar",
|
||||||
TLS: &dynamic.ClientTLS{
|
TLS: &types.ClientTLS{
|
||||||
CA: "foobar",
|
CA: "foobar",
|
||||||
CAOptional: true,
|
CAOptional: true,
|
||||||
Cert: "foobar",
|
Cert: "foobar",
|
||||||
|
@ -844,7 +845,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
NotAfter: true,
|
NotAfter: true,
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -853,7 +854,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -986,7 +987,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"Middleware7": {
|
"Middleware7": {
|
||||||
ForwardAuth: &dynamic.ForwardAuth{
|
ForwardAuth: &dynamic.ForwardAuth{
|
||||||
Address: "foobar",
|
Address: "foobar",
|
||||||
TLS: &dynamic.ClientTLS{
|
TLS: &types.ClientTLS{
|
||||||
CA: "foobar",
|
CA: "foobar",
|
||||||
CAOptional: true,
|
CAOptional: true,
|
||||||
Cert: "foobar",
|
Cert: "foobar",
|
||||||
|
|
|
@ -72,9 +72,9 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.TLS != nil {
|
if config.TLS != nil {
|
||||||
tlsConfig, err := config.TLS.CreateTLSConfig()
|
tlsConfig, err := config.TLS.CreateTLSConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to create client TLS configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
|
|
@ -46,7 +46,7 @@ type DistinguishedNameOptions struct {
|
||||||
StateOrProvinceName bool
|
StateOrProvinceName bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDistinguishedNameOptions(info *dynamic.TLSCLientCertificateDNInfo) *DistinguishedNameOptions {
|
func newDistinguishedNameOptions(info *dynamic.TLSClientCertificateDNInfo) *DistinguishedNameOptions {
|
||||||
if info == nil {
|
if info == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,7 +376,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
desc: "No TLS, with subject info",
|
desc: "No TLS, with subject info",
|
||||||
config: dynamic.PassTLSClientCert{
|
config: dynamic.PassTLSClientCert{
|
||||||
Info: &dynamic.TLSClientCertificateInfo{
|
Info: &dynamic.TLSClientCertificateInfo{
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
Organization: true,
|
Organization: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -392,7 +392,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
config: dynamic.PassTLSClientCert{
|
config: dynamic.PassTLSClientCert{
|
||||||
PEM: false,
|
PEM: false,
|
||||||
Info: &dynamic.TLSClientCertificateInfo{
|
Info: &dynamic.TLSClientCertificateInfo{
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{},
|
Subject: &dynamic.TLSClientCertificateDNInfo{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -405,7 +405,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
Sans: true,
|
Sans: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
Country: true,
|
Country: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
|
@ -414,7 +414,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
Province: true,
|
Province: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
Country: true,
|
Country: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
|
@ -434,10 +434,10 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
Info: &dynamic.TLSClientCertificateInfo{
|
Info: &dynamic.TLSClientCertificateInfo{
|
||||||
NotAfter: true,
|
NotAfter: true,
|
||||||
Sans: true,
|
Sans: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Organization: true,
|
Organization: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -453,7 +453,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
Sans: true,
|
Sans: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -462,7 +462,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -484,7 +484,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
Sans: true,
|
Sans: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -493,7 +493,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
|
|
@ -165,7 +165,7 @@ func (p *Provider) getClientOpts() ([]client.Opt, error) {
|
||||||
|
|
||||||
conf, err := p.TLS.CreateTLSConfig(ctx)
|
conf, err := p.TLS.CreateTLSConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to create client TLS configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hostURL, err := client.ParseHostURL(p.Endpoint)
|
hostURL, err := client.ParseHostURL(p.Endpoint)
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (p *Provider) Init() error {
|
||||||
if p.TLS != nil {
|
if p.TLS != nil {
|
||||||
tlsConfig, err := p.TLS.CreateTLSConfig(context.Background())
|
tlsConfig, err := p.TLS.CreateTLSConfig(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create TLS configuration: %w", err)
|
return fmt.Errorf("unable to create client TLS configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.httpClient.Transport = &http.Transport{
|
p.httpClient.Transport = &http.Transport{
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
"github.com/traefik/traefik/v2/pkg/safe"
|
"github.com/traefik/traefik/v2/pkg/safe"
|
||||||
"github.com/traefik/traefik/v2/pkg/tls"
|
"github.com/traefik/traefik/v2/pkg/tls"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apiextensionv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
@ -481,7 +482,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *v1alp
|
||||||
return forwardAuth, nil
|
return forwardAuth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
forwardAuth.TLS = &dynamic.ClientTLS{
|
forwardAuth.TLS = &types.ClientTLS{
|
||||||
CAOptional: auth.TLS.CAOptional,
|
CAOptional: auth.TLS.CAOptional,
|
||||||
InsecureSkipVerify: auth.TLS.InsecureSkipVerify,
|
InsecureSkipVerify: auth.TLS.InsecureSkipVerify,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,14 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/traefik/paerser/types"
|
ptypes "github.com/traefik/paerser/types"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v2/pkg/provider"
|
"github.com/traefik/traefik/v2/pkg/provider"
|
||||||
crdfake "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
|
crdfake "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
|
||||||
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/k8s"
|
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/k8s"
|
||||||
"github.com/traefik/traefik/v2/pkg/tls"
|
"github.com/traefik/traefik/v2/pkg/tls"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
@ -3243,7 +3244,7 @@ func TestLoadIngressRoutes(t *testing.T) {
|
||||||
"default-forwardauth": {
|
"default-forwardauth": {
|
||||||
ForwardAuth: &dynamic.ForwardAuth{
|
ForwardAuth: &dynamic.ForwardAuth{
|
||||||
Address: "test.com",
|
Address: "test.com",
|
||||||
TLS: &dynamic.ClientTLS{
|
TLS: &types.ClientTLS{
|
||||||
CA: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
|
CA: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
|
||||||
Cert: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
|
Cert: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
|
||||||
Key: "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----",
|
Key: "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----",
|
||||||
|
@ -3612,17 +3613,17 @@ func TestLoadIngressRoutes(t *testing.T) {
|
||||||
MaxIdleConnsPerHost: 42,
|
MaxIdleConnsPerHost: 42,
|
||||||
DisableHTTP2: true,
|
DisableHTTP2: true,
|
||||||
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
||||||
DialTimeout: types.Duration(42 * time.Second),
|
DialTimeout: ptypes.Duration(42 * time.Second),
|
||||||
ResponseHeaderTimeout: types.Duration(42 * time.Second),
|
ResponseHeaderTimeout: ptypes.Duration(42 * time.Second),
|
||||||
IdleConnTimeout: types.Duration(42 * time.Millisecond),
|
IdleConnTimeout: ptypes.Duration(42 * time.Millisecond),
|
||||||
},
|
},
|
||||||
PeerCertURI: "foo://bar",
|
PeerCertURI: "foo://bar",
|
||||||
},
|
},
|
||||||
"default-test": {
|
"default-test": {
|
||||||
ServerName: "test",
|
ServerName: "test",
|
||||||
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
||||||
DialTimeout: types.Duration(30 * time.Second),
|
DialTimeout: ptypes.Duration(30 * time.Second),
|
||||||
IdleConnTimeout: types.Duration(90 * time.Second),
|
IdleConnTimeout: ptypes.Duration(90 * time.Second),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -170,7 +170,7 @@ func (p *Provider) createKVClient(ctx context.Context) (store.Store, error) {
|
||||||
var err error
|
var err error
|
||||||
storeConfig.TLS, err = p.TLS.CreateTLSConfig(ctx)
|
storeConfig.TLS, err = p.TLS.CreateTLSConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to create client TLS configuration: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -399,7 +399,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"Middleware08": {
|
"Middleware08": {
|
||||||
ForwardAuth: &dynamic.ForwardAuth{
|
ForwardAuth: &dynamic.ForwardAuth{
|
||||||
Address: "foobar",
|
Address: "foobar",
|
||||||
TLS: &dynamic.ClientTLS{
|
TLS: &types.ClientTLS{
|
||||||
CA: "foobar",
|
CA: "foobar",
|
||||||
CAOptional: true,
|
CAOptional: true,
|
||||||
Cert: "foobar",
|
Cert: "foobar",
|
||||||
|
@ -478,7 +478,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
NotAfter: true,
|
NotAfter: true,
|
||||||
NotBefore: true,
|
NotBefore: true,
|
||||||
Sans: true,
|
Sans: true,
|
||||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
Subject: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -487,7 +487,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSClientCertificateDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
|
|
@ -134,7 +134,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
||||||
}
|
}
|
||||||
TLSConfig, err := p.TLS.CreateTLSConfig(ctx)
|
TLSConfig, err := p.TLS.CreateTLSConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to create client TLS configuration: %w", err)
|
||||||
}
|
}
|
||||||
confg.HTTPClient = &http.Client{
|
confg.HTTPClient = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
|
|
13
pkg/types/fixtures/cert.pem
Normal file
13
pkg/types/fixtures/cert.pem
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB9jCCAV+gAwIBAgIQI3edJckNbicw4WIHs5Ws9TANBgkqhkiG9w0BAQsFADAS
|
||||||
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
||||||
|
iQKBgQCb8oWyME1QRBoMLFei3M8TVKwfZfW74cVjtcugCBMTTOTCouEIgjjmiMv6
|
||||||
|
FdMio2uBcgeD9R3dOtjjnA7N+xjwZ4vIPqDlJRE3YbfpV9igVX3sXU7ssHTSH0vs
|
||||||
|
R0TuYJwGReIFUnu5QIjGwVorodF+CQ8dTnyXVLeQVU9kvjohHwIDAQABo0swSTAO
|
||||||
|
BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw
|
||||||
|
ADAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADgYEADqylUQ/4
|
||||||
|
lrxh4h8UUQ2wKATQ2kG2YvMGlaIhr2vPZo2QDBlmL2xzai7YXX3+JZyM15TNCamn
|
||||||
|
WtFR7WQIOHzKA1GkR9WkaXKmFbJjhGMSZVCG6ghhTjzB+stBYZXhBsdjCJbkZWBu
|
||||||
|
OeI73oivo0MdI+4iCYCo7TnoY4PZGObwcgI=
|
||||||
|
-----END CERTIFICATE-----
|
16
pkg/types/fixtures/key.pem
Normal file
16
pkg/types/fixtures/key.pem
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJvyhbIwTVBEGgws
|
||||||
|
V6LczxNUrB9l9bvhxWO1y6AIExNM5MKi4QiCOOaIy/oV0yKja4FyB4P1Hd062OOc
|
||||||
|
Ds37GPBni8g+oOUlETdht+lX2KBVfexdTuywdNIfS+xHRO5gnAZF4gVSe7lAiMbB
|
||||||
|
Wiuh0X4JDx1OfJdUt5BVT2S+OiEfAgMBAAECgYA9+PbghQl0aFvhko2RDybLi86K
|
||||||
|
+73X2DTVFx3AjvTlqp0OLCQ5eWabVqmYzKuHDGJgoqwR6Irhq80dRpsriCm0YNui
|
||||||
|
mMV35bbimOKz9FoCTKx0ZB6xsqrVoFhjVmX3DOD9Txe41H42ZxmccOKZndR/QaXz
|
||||||
|
VV+1W/Wbz2VawnkyYQJBAMvF6w2eOJRRoN8e7GM7b7uqkupJPp9axgFREoJZb16W
|
||||||
|
mqXUZnH4Cydzc5keG4yknQRHdgz6RrQxnvR7GyKHLfUCQQDD6qG9D5BX0+mNW6TG
|
||||||
|
PRwW/L2qWgnmg9lxtSSQat9ZOnBhw2OLPi0zTu4p70oSmU67/YJr50HEoJpRccZJ
|
||||||
|
mnJDAkBdBTtY2xpe8qhqUjZ80hweYi5wzwDMQ+bRoQ2+/U6usjdkbgJaEm4dE0H4
|
||||||
|
6tqOqHKZCnokUHfIOEKkvjHT4DulAkBAgiJNSTGi6aDOLa28pGR6YS/mRo1Z/HH9
|
||||||
|
kcJ/VuFB1Q8p8Zb2QzvI2CVtY2AFbbtSBPALrXKnVqZZSNgcZiFXAkEAvcLKaEXE
|
||||||
|
haGMGwq2BLADPHqAR3hdCJL3ikMJwWUsTkTjm973iEIEZfF5j57EzRI4bASm4Zq5
|
||||||
|
Zt3BcblLODQ//w==
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -4,12 +4,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/traefik/traefik/v2/pkg/log"
|
"github.com/traefik/traefik/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// ClientTLS holds TLS specific configurations as client
|
// ClientTLS holds TLS specific configurations as client
|
||||||
// CA, Cert and Key can be either path or file contents.
|
// CA, Cert and Key can be either path or file contents.
|
||||||
type ClientTLS struct {
|
type ClientTLS struct {
|
||||||
|
@ -42,7 +45,7 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e
|
||||||
}
|
}
|
||||||
|
|
||||||
if !caPool.AppendCertsFromPEM(ca) {
|
if !caPool.AppendCertsFromPEM(ca) {
|
||||||
return nil, fmt.Errorf("failed to parse CA")
|
return nil, errors.New("failed to parse CA")
|
||||||
}
|
}
|
||||||
|
|
||||||
if clientTLS.CAOptional {
|
if clientTLS.CAOptional {
|
||||||
|
@ -52,34 +55,24 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !clientTLS.InsecureSkipVerify && (len(clientTLS.Cert) == 0 || len(clientTLS.Key) == 0) {
|
hasCert := len(clientTLS.Cert) > 0
|
||||||
return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created")
|
hasKey := len(clientTLS.Key) > 0
|
||||||
|
|
||||||
|
if hasCert != hasKey {
|
||||||
|
return nil, errors.New("both TLS cert and key must be defined")
|
||||||
}
|
}
|
||||||
|
|
||||||
cert := tls.Certificate{}
|
if !hasCert || !hasKey {
|
||||||
_, errKeyIsFile := os.Stat(clientTLS.Key)
|
return &tls.Config{
|
||||||
|
RootCAs: caPool,
|
||||||
|
InsecureSkipVerify: clientTLS.InsecureSkipVerify,
|
||||||
|
ClientAuth: clientAuth,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
if len(clientTLS.Cert) > 0 && len(clientTLS.Key) > 0 {
|
cert, err := loadKeyPair(clientTLS.Cert, clientTLS.Key)
|
||||||
var err error
|
|
||||||
if _, errCertIsFile := os.Stat(clientTLS.Cert); errCertIsFile == nil {
|
|
||||||
if errKeyIsFile == nil {
|
|
||||||
cert, err = tls.LoadX509KeyPair(clientTLS.Cert, clientTLS.Key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load TLS keypair: %w", err)
|
return nil, err
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("TLS cert is a file, but tls key is not")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if errKeyIsFile != nil {
|
|
||||||
cert, err = tls.X509KeyPair([]byte(clientTLS.Cert), []byte(clientTLS.Key))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load TLS keypair: %w", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("TLS key is a file, but tls cert is not")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
|
@ -89,3 +82,27 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e
|
||||||
ClientAuth: clientAuth,
|
ClientAuth: clientAuth,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadKeyPair(cert, key string) (tls.Certificate, error) {
|
||||||
|
keyPair, err := tls.X509KeyPair([]byte(cert), []byte(key))
|
||||||
|
if err == nil {
|
||||||
|
return keyPair, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(cert)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, errors.New("cert file does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(key)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, errors.New("key file does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPair, err = tls.LoadX509KeyPair(cert, key)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyPair, nil
|
||||||
|
}
|
||||||
|
|
123
pkg/types/tls_test.go
Normal file
123
pkg/types/tls_test.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go run $GOROOT/src/crypto/tls/generate_cert.go --rsa-bits 1024 --host localhost --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||||
|
var cert = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB9jCCAV+gAwIBAgIQI3edJckNbicw4WIHs5Ws9TANBgkqhkiG9w0BAQsFADAS
|
||||||
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
||||||
|
iQKBgQCb8oWyME1QRBoMLFei3M8TVKwfZfW74cVjtcugCBMTTOTCouEIgjjmiMv6
|
||||||
|
FdMio2uBcgeD9R3dOtjjnA7N+xjwZ4vIPqDlJRE3YbfpV9igVX3sXU7ssHTSH0vs
|
||||||
|
R0TuYJwGReIFUnu5QIjGwVorodF+CQ8dTnyXVLeQVU9kvjohHwIDAQABo0swSTAO
|
||||||
|
BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw
|
||||||
|
ADAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADgYEADqylUQ/4
|
||||||
|
lrxh4h8UUQ2wKATQ2kG2YvMGlaIhr2vPZo2QDBlmL2xzai7YXX3+JZyM15TNCamn
|
||||||
|
WtFR7WQIOHzKA1GkR9WkaXKmFbJjhGMSZVCG6ghhTjzB+stBYZXhBsdjCJbkZWBu
|
||||||
|
OeI73oivo0MdI+4iCYCo7TnoY4PZGObwcgI=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
var key = `-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJvyhbIwTVBEGgws
|
||||||
|
V6LczxNUrB9l9bvhxWO1y6AIExNM5MKi4QiCOOaIy/oV0yKja4FyB4P1Hd062OOc
|
||||||
|
Ds37GPBni8g+oOUlETdht+lX2KBVfexdTuywdNIfS+xHRO5gnAZF4gVSe7lAiMbB
|
||||||
|
Wiuh0X4JDx1OfJdUt5BVT2S+OiEfAgMBAAECgYA9+PbghQl0aFvhko2RDybLi86K
|
||||||
|
+73X2DTVFx3AjvTlqp0OLCQ5eWabVqmYzKuHDGJgoqwR6Irhq80dRpsriCm0YNui
|
||||||
|
mMV35bbimOKz9FoCTKx0ZB6xsqrVoFhjVmX3DOD9Txe41H42ZxmccOKZndR/QaXz
|
||||||
|
VV+1W/Wbz2VawnkyYQJBAMvF6w2eOJRRoN8e7GM7b7uqkupJPp9axgFREoJZb16W
|
||||||
|
mqXUZnH4Cydzc5keG4yknQRHdgz6RrQxnvR7GyKHLfUCQQDD6qG9D5BX0+mNW6TG
|
||||||
|
PRwW/L2qWgnmg9lxtSSQat9ZOnBhw2OLPi0zTu4p70oSmU67/YJr50HEoJpRccZJ
|
||||||
|
mnJDAkBdBTtY2xpe8qhqUjZ80hweYi5wzwDMQ+bRoQ2+/U6usjdkbgJaEm4dE0H4
|
||||||
|
6tqOqHKZCnokUHfIOEKkvjHT4DulAkBAgiJNSTGi6aDOLa28pGR6YS/mRo1Z/HH9
|
||||||
|
kcJ/VuFB1Q8p8Zb2QzvI2CVtY2AFbbtSBPALrXKnVqZZSNgcZiFXAkEAvcLKaEXE
|
||||||
|
haGMGwq2BLADPHqAR3hdCJL3ikMJwWUsTkTjm973iEIEZfF5j57EzRI4bASm4Zq5
|
||||||
|
Zt3BcblLODQ//w==
|
||||||
|
-----END PRIVATE KEY-----`
|
||||||
|
|
||||||
|
func TestClientTLS_CreateTLSConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
clientTLS ClientTLS
|
||||||
|
wantCertLen int
|
||||||
|
wantCALen int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Configure CA",
|
||||||
|
clientTLS: ClientTLS{CA: cert},
|
||||||
|
wantCALen: 1,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Configure the client keyPair from strings",
|
||||||
|
clientTLS: ClientTLS{Cert: cert, Key: key},
|
||||||
|
wantCertLen: 1,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Configure the client keyPair from files",
|
||||||
|
clientTLS: ClientTLS{Cert: "fixtures/cert.pem", Key: "fixtures/key.pem"},
|
||||||
|
wantCertLen: 1,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Configure InsecureSkipVerify",
|
||||||
|
clientTLS: ClientTLS{InsecureSkipVerify: true},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Return an error if only the client cert is provided",
|
||||||
|
clientTLS: ClientTLS{Cert: cert},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Return an error if only the client key is provided",
|
||||||
|
clientTLS: ClientTLS{Key: key},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Return an error if only the client cert is of type file",
|
||||||
|
clientTLS: ClientTLS{Cert: "fixtures/cert.pem", Key: key},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Return an error if only the client key is of type file",
|
||||||
|
clientTLS: ClientTLS{Cert: cert, Key: "fixtures/key.pem"},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Return an error if the client cert does not exist",
|
||||||
|
clientTLS: ClientTLS{Cert: "fixtures/cert2.pem", Key: "fixtures/key.pem"},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Return an error if the client key does not exist",
|
||||||
|
clientTLS: ClientTLS{Cert: "fixtures/cert.pem", Key: "fixtures/key2.pem"},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
tlsConfig, err := test.clientTLS.CreateTLSConfig(context.Background())
|
||||||
|
if test.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Len(t, tlsConfig.RootCAs.Subjects(), test.wantCALen)
|
||||||
|
assert.Len(t, tlsConfig.Certificates, test.wantCertLen)
|
||||||
|
assert.Equal(t, test.clientTLS.InsecureSkipVerify, tlsConfig.InsecureSkipVerify)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,22 @@ THE SOFTWARE.
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ClientTLS) DeepCopyInto(out *ClientTLS) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLS.
|
||||||
|
func (in *ClientTLS) DeepCopy() *ClientTLS {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ClientTLS)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Domain) DeepCopyInto(out *Domain) {
|
func (in *Domain) DeepCopyInto(out *Domain) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
Loading…
Reference in a new issue