Allow specifying the name of the header for forwarding client certificates
This commit is contained in:
parent
a687343fed
commit
affd8733a0
3 changed files with 60 additions and 13 deletions
|
@ -32,12 +32,13 @@ const (
|
||||||
defaultAuthTLSDepth = 1
|
defaultAuthTLSDepth = 1
|
||||||
defaultAuthVerifyClient = "on"
|
defaultAuthVerifyClient = "on"
|
||||||
|
|
||||||
annotationAuthTLSSecret = "auth-tls-secret" //#nosec G101
|
annotationAuthTLSSecret = "auth-tls-secret" //#nosec G101
|
||||||
annotationAuthTLSVerifyClient = "auth-tls-verify-client"
|
annotationAuthTLSVerifyClient = "auth-tls-verify-client"
|
||||||
annotationAuthTLSVerifyDepth = "auth-tls-verify-depth"
|
annotationAuthTLSVerifyDepth = "auth-tls-verify-depth"
|
||||||
annotationAuthTLSErrorPage = "auth-tls-error-page"
|
annotationAuthTLSErrorPage = "auth-tls-error-page"
|
||||||
annotationAuthTLSPassCertToUpstream = "auth-tls-pass-certificate-to-upstream" //#nosec G101
|
annotationAuthTLSPassCertToUpstream = "auth-tls-pass-certificate-to-upstream" //#nosec G101
|
||||||
annotationAuthTLSMatchCN = "auth-tls-match-cn"
|
annotationAuthTLSPassCertToUpstreamHeader = "auth-tls-pass-certificate-to-upstream-header" //#nosec G101
|
||||||
|
annotationAuthTLSMatchCN = "auth-tls-match-cn"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -80,6 +81,12 @@ var authTLSAnnotations = parser.Annotation{
|
||||||
Risk: parser.AnnotationRiskLow,
|
Risk: parser.AnnotationRiskLow,
|
||||||
Documentation: `This annotation defines if the received certificates should be passed or not to the upstream server in the header "ssl-client-cert"`,
|
Documentation: `This annotation defines if the received certificates should be passed or not to the upstream server in the header "ssl-client-cert"`,
|
||||||
},
|
},
|
||||||
|
annotationAuthTLSPassCertToUpstreamHeader: {
|
||||||
|
Validator: parser.ValidateNull,
|
||||||
|
Scope: parser.AnnotationScopeLocation,
|
||||||
|
Risk: parser.AnnotationRiskLow,
|
||||||
|
Documentation: `This annotation defines the header name in which the client certificate will be passed to the upstream server`,
|
||||||
|
},
|
||||||
annotationAuthTLSMatchCN: {
|
annotationAuthTLSMatchCN: {
|
||||||
Validator: parser.ValidateRegex(commonNameRegex, true),
|
Validator: parser.ValidateRegex(commonNameRegex, true),
|
||||||
Scope: parser.AnnotationScopeLocation,
|
Scope: parser.AnnotationScopeLocation,
|
||||||
|
@ -93,12 +100,13 @@ var authTLSAnnotations = parser.Annotation{
|
||||||
// and the configured ValidationDepth
|
// and the configured ValidationDepth
|
||||||
type Config struct {
|
type Config struct {
|
||||||
resolver.AuthSSLCert
|
resolver.AuthSSLCert
|
||||||
VerifyClient string `json:"verify_client"`
|
VerifyClient string `json:"verify_client"`
|
||||||
ValidationDepth int `json:"validationDepth"`
|
ValidationDepth int `json:"validationDepth"`
|
||||||
ErrorPage string `json:"errorPage"`
|
ErrorPage string `json:"errorPage"`
|
||||||
PassCertToUpstream bool `json:"passCertToUpstream"`
|
PassCertToUpstream bool `json:"passCertToUpstream"`
|
||||||
MatchCN string `json:"matchCN"`
|
PassCertToUpstreamHeader string `json:"passCertToUpstreamHeader"`
|
||||||
AuthTLSError string
|
MatchCN string `json:"matchCN"`
|
||||||
|
AuthTLSError string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two Config types
|
// Equal tests for equality between two Config types
|
||||||
|
@ -125,6 +133,10 @@ func (assl1 *Config) Equal(assl2 *Config) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if assl1.PassCertToUpstreamHeader != assl2.PassCertToUpstreamHeader {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +212,14 @@ func (a authTLS) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||||
config.PassCertToUpstream = false
|
config.PassCertToUpstream = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.PassCertToUpstreamHeader, err = parser.GetStringAnnotation(annotationAuthTLSPassCertToUpstreamHeader, ing, a.annotationConfig.Annotations)
|
||||||
|
if err != nil {
|
||||||
|
if ing_errors.IsValidationError(err) {
|
||||||
|
return &Config{}, err
|
||||||
|
}
|
||||||
|
config.PassCertToUpstreamHeader = "ssl-client-cert"
|
||||||
|
}
|
||||||
|
|
||||||
config.MatchCN, err = parser.GetStringAnnotation(annotationAuthTLSMatchCN, ing, a.annotationConfig.Annotations)
|
config.MatchCN, err = parser.GetStringAnnotation(annotationAuthTLSMatchCN, ing, a.annotationConfig.Annotations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ing_errors.IsValidationError(err) {
|
if ing_errors.IsValidationError(err) {
|
||||||
|
|
|
@ -132,6 +132,9 @@ func TestAnnotations(t *testing.T) {
|
||||||
if u.PassCertToUpstream != false {
|
if u.PassCertToUpstream != false {
|
||||||
t.Errorf("expected %v but got %v", false, u.PassCertToUpstream)
|
t.Errorf("expected %v but got %v", false, u.PassCertToUpstream)
|
||||||
}
|
}
|
||||||
|
if u.PassCertToUpstreamHeader != "ssl-client-cert" {
|
||||||
|
t.Errorf("expected %v but got %v", "ssl-client-cert", u.PassCertToUpstreamHeader)
|
||||||
|
}
|
||||||
if u.MatchCN != "" {
|
if u.MatchCN != "" {
|
||||||
t.Errorf("expected empty string, but got %v", u.MatchCN)
|
t.Errorf("expected empty string, but got %v", u.MatchCN)
|
||||||
}
|
}
|
||||||
|
@ -140,6 +143,7 @@ func TestAnnotations(t *testing.T) {
|
||||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2"
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2"
|
||||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error"
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error"
|
||||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true"
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true"
|
||||||
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstreamHeader)] = "X-SSL-CERT"
|
||||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)] = "CN=(hello-app|ok|goodbye)"
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)] = "CN=(hello-app|ok|goodbye)"
|
||||||
|
|
||||||
ing.SetAnnotations(data)
|
ing.SetAnnotations(data)
|
||||||
|
@ -169,6 +173,9 @@ func TestAnnotations(t *testing.T) {
|
||||||
if u.PassCertToUpstream != true {
|
if u.PassCertToUpstream != true {
|
||||||
t.Errorf("expected %v but got %v", true, u.PassCertToUpstream)
|
t.Errorf("expected %v but got %v", true, u.PassCertToUpstream)
|
||||||
}
|
}
|
||||||
|
if u.PassCertToUpstreamHeader != "X-SSL-CERT" {
|
||||||
|
t.Errorf("expected %v but got %v", "X-SSL-CERT", u.PassCertToUpstreamHeader)
|
||||||
|
}
|
||||||
if u.MatchCN != "CN=(hello-app|ok|goodbye)" {
|
if u.MatchCN != "CN=(hello-app|ok|goodbye)" {
|
||||||
t.Errorf("expected %v but got %v", "CN=(hello-app|ok|goodbye)", u.MatchCN)
|
t.Errorf("expected %v but got %v", "CN=(hello-app|ok|goodbye)", u.MatchCN)
|
||||||
}
|
}
|
||||||
|
@ -235,6 +242,14 @@ func TestInvalidAnnotations(t *testing.T) {
|
||||||
}
|
}
|
||||||
delete(data, parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream))
|
delete(data, parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream))
|
||||||
|
|
||||||
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstreamHeader)] = 1
|
||||||
|
ing.SetAnnotations(data)
|
||||||
|
_, err = NewParser(fakeSecret).Parse(ing)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error with ingress but got nil")
|
||||||
|
}
|
||||||
|
delete(data, parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstreamHeader))
|
||||||
|
|
||||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)] = "<script>nope</script>"
|
data[parser.GetAnnotationWithPrefix(annotationAuthTLSMatchCN)] = "<script>nope</script>"
|
||||||
ing.SetAnnotations(data)
|
ing.SetAnnotations(data)
|
||||||
_, err = NewParser(fakeSecret).Parse(ing)
|
_, err = NewParser(fakeSecret).Parse(ing)
|
||||||
|
@ -263,6 +278,9 @@ func TestInvalidAnnotations(t *testing.T) {
|
||||||
if u.PassCertToUpstream != false {
|
if u.PassCertToUpstream != false {
|
||||||
t.Errorf("expected %v but got %v", false, u.PassCertToUpstream)
|
t.Errorf("expected %v but got %v", false, u.PassCertToUpstream)
|
||||||
}
|
}
|
||||||
|
if u.PassCertToUpstreamHeader != "ssl-client-cert" {
|
||||||
|
t.Errorf("expected %v but got %v", "ssl-client-cert", u.PassCertToUpstreamHeader)
|
||||||
|
}
|
||||||
if u.MatchCN != "" {
|
if u.MatchCN != "" {
|
||||||
t.Errorf("expected empty string but got %v", u.MatchCN)
|
t.Errorf("expected empty string but got %v", u.MatchCN)
|
||||||
}
|
}
|
||||||
|
@ -333,6 +351,15 @@ func TestEquals(t *testing.T) {
|
||||||
}
|
}
|
||||||
cfg2.PassCertToUpstream = true
|
cfg2.PassCertToUpstream = true
|
||||||
|
|
||||||
|
// Different Pass to Upstream Header
|
||||||
|
cfg1.PassCertToUpstreamHeader = "ssl-client-cert"
|
||||||
|
cfg2.PassCertToUpstreamHeader = "X-SSL-CERT"
|
||||||
|
result = cfg1.Equal(cfg2)
|
||||||
|
if result != false {
|
||||||
|
t.Errorf("Expected false")
|
||||||
|
}
|
||||||
|
cfg2.PassCertToUpstreamHeader = "ssl-client-cert"
|
||||||
|
|
||||||
// Equal Configs
|
// Equal Configs
|
||||||
result = cfg1.Equal(cfg2)
|
result = cfg1.Equal(cfg2)
|
||||||
if result != true {
|
if result != true {
|
||||||
|
|
|
@ -1182,7 +1182,7 @@ stream {
|
||||||
# Pass the extracted client certificate to the auth provider
|
# Pass the extracted client certificate to the auth provider
|
||||||
{{ if not (empty $server.CertificateAuth.CAFileName) }}
|
{{ if not (empty $server.CertificateAuth.CAFileName) }}
|
||||||
{{ if $server.CertificateAuth.PassCertToUpstream }}
|
{{ if $server.CertificateAuth.PassCertToUpstream }}
|
||||||
proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
|
proxy_set_header {{ $server.CertificateAuth.PassCertToUpstreamHeader }} $ssl_client_escaped_cert;
|
||||||
{{ end }}
|
{{ end }}
|
||||||
proxy_set_header ssl-client-verify $ssl_client_verify;
|
proxy_set_header ssl-client-verify $ssl_client_verify;
|
||||||
proxy_set_header ssl-client-subject-dn $ssl_client_s_dn;
|
proxy_set_header ssl-client-subject-dn $ssl_client_s_dn;
|
||||||
|
|
Loading…
Reference in a new issue