Skip to content

Commit

Permalink
update TLS fallbackCertificate logic to require tlscertificatedelegat…
Browse files Browse the repository at this point in the history
…ion to each namespace where a root HTTPProxy enables fallbackCertificate for a fqdn.

Signed-off-by: Steve Sloka <slokas@vmware.com>
  • Loading branch information
stevesloka committed May 21, 2020
1 parent faf7340 commit d803875
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 1 deletion.
8 changes: 7 additions & 1 deletion internal/dag/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,17 @@ func (b *Builder) computeHTTPProxy(proxy *projcontour.HTTPProxy) {
return
}

sec, err = b.lookupSecret(k8s.FullName{Name: b.FallbackCertificate.Name, Namespace: b.FallbackCertificate.Namespace}, validSecret)
sec, err = b.lookupSecret(*b.FallbackCertificate, validSecret)
if err != nil {
sw.SetInvalid("Spec.Virtualhost.TLS fallback certificate Secret %q is invalid: %s", b.FallbackCertificate, err)
return
}

if !b.delegationPermitted(*b.FallbackCertificate, proxy.Namespace) {
sw.SetInvalid("Spec.VirtualHost.TLS fallback secret %s is not configured for certificate delegation", b.FallbackCertificate.Name)
return
}

svhost.FallbackCertificate = sec
}

Expand Down
177 changes: 177 additions & 0 deletions internal/dag/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ func TestDAGInsert(t *testing.T) {
},
}

sec4 := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
Namespace: "root",
},
Type: v1.SecretTypeTLS,
Data: secretdata(CERTIFICATE, RSA_PRIVATE_KEY),
}

fallbackCertificateSecret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "fallbacksecret",
Expand All @@ -76,6 +85,15 @@ func TestDAGInsert(t *testing.T) {
Data: secretdata(CERTIFICATE, RSA_PRIVATE_KEY),
}

fallbackCertificateSecretRootNamespace := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "fallbacksecret",
Namespace: "root",
},
Type: v1.SecretTypeTLS,
Data: secretdata(CERTIFICATE, RSA_PRIVATE_KEY),
}

cert1 := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "ca",
Expand Down Expand Up @@ -6438,6 +6456,165 @@ func TestDAGInsert(t *testing.T) {
},
),
},
"httpproxy with fallback certificate enabled - cert delegation not configured": {
fallbackCertificateName: "fallbacksecret",
fallbackCertificateNamespace: "root",
objs: []interface{}{
sec4,
s9,
fallbackCertificateSecret,
&projcontour.HTTPProxy{
ObjectMeta: metav1.ObjectMeta{
Name: "nginx",
Namespace: "default",
},
Spec: projcontour.HTTPProxySpec{
VirtualHost: &projcontour.VirtualHost{
Fqdn: "example.com",
TLS: &projcontour.TLS{
SecretName: sec1.Name,
EnableFallbackCertificate: true,
},
},
Routes: []projcontour.Route{{
Services: []projcontour.Service{{
Name: "nginx",
Port: 80,
}},
}},
},
},
},
want: listeners(),
},
"httpproxy with fallback certificate enabled - cert delegation configured all namespaces": {
fallbackCertificateName: "fallbacksecret",
fallbackCertificateNamespace: "root",
objs: []interface{}{
sec1,
s9,
fallbackCertificateSecretRootNamespace,
&projcontour.TLSCertificateDelegation{
ObjectMeta: metav1.ObjectMeta{
Name: "fallbackcertdelegation",
Namespace: "root",
},
Spec: projcontour.TLSCertificateDelegationSpec{
Delegations: []projcontour.CertificateDelegation{{
SecretName: "fallbacksecret",
TargetNamespaces: []string{"*"},
}},
},
},
&projcontour.HTTPProxy{
ObjectMeta: metav1.ObjectMeta{
Name: "nginx",
Namespace: "default",
},
Spec: projcontour.HTTPProxySpec{
VirtualHost: &projcontour.VirtualHost{
Fqdn: "example.com",
TLS: &projcontour.TLS{
SecretName: sec1.Name,
EnableFallbackCertificate: true,
},
},
Routes: []projcontour.Route{{
Services: []projcontour.Service{{
Name: "nginx",
Port: 80,
}},
}},
},
},
},
want: listeners(
&Listener{
Port: 80,
VirtualHosts: virtualhosts(
virtualhost("example.com", routeUpgrade("/", service(s9))),
),
},
&Listener{
Port: 443,
VirtualHosts: virtualhosts(
&SecureVirtualHost{
VirtualHost: VirtualHost{
Name: "example.com",
routes: routes(routeUpgrade("/", service(s9))),
},
MinProtoVersion: envoy_api_v2_auth.TlsParameters_TLSv1_1,
Secret: secret(sec1),
FallbackCertificate: secret(fallbackCertificateSecretRootNamespace),
},
),
},
),
},
"httpproxy with fallback certificate enabled - cert delegation configured single namespaces": {
fallbackCertificateName: "fallbacksecret",
fallbackCertificateNamespace: "root",
objs: []interface{}{
sec1,
s9,
fallbackCertificateSecretRootNamespace,
&projcontour.TLSCertificateDelegation{
ObjectMeta: metav1.ObjectMeta{
Name: "fallbackcertdelegation",
Namespace: "root",
},
Spec: projcontour.TLSCertificateDelegationSpec{
Delegations: []projcontour.CertificateDelegation{{
SecretName: "fallbacksecret",
TargetNamespaces: []string{"default"},
}},
},
},
&projcontour.HTTPProxy{
ObjectMeta: metav1.ObjectMeta{
Name: "nginx",
Namespace: "default",
},
Spec: projcontour.HTTPProxySpec{
VirtualHost: &projcontour.VirtualHost{
Fqdn: "example.com",
TLS: &projcontour.TLS{
SecretName: sec1.Name,
EnableFallbackCertificate: true,
},
},
Routes: []projcontour.Route{{
Services: []projcontour.Service{{
Name: "nginx",
Port: 80,
}},
}},
},
},
},
want: listeners(
&Listener{
Port: 80,
VirtualHosts: virtualhosts(
virtualhost("example.com", routeUpgrade("/", service(s9))),
),
},
&Listener{
Port: 443,
VirtualHosts: virtualhosts(
&SecureVirtualHost{
VirtualHost: VirtualHost{
Name: "example.com",
routes: routes(routeUpgrade("/", service(s9))),
},
MinProtoVersion: envoy_api_v2_auth.TlsParameters_TLSv1_1,
Secret: secret(sec1),
FallbackCertificate: secret(fallbackCertificateSecretRootNamespace),
},
),
},
),
},
"httpproxy with fallback certificate enabled - no tls secret": {
fallbackCertificateName: "fallbacksecret",
fallbackCertificateNamespace: "default",
Expand Down
64 changes: 64 additions & 0 deletions internal/featuretests/fallbackcert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,70 @@ func TestFallbackCertificate(t *testing.T) {

rh.OnUpdate(proxy1, proxy2)

// Invalid since there's no TLSCertificateDelegation configured
c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{
Resources: nil,
TypeUrl: listenerType,
})

certDelegationAll := &projcontour.TLSCertificateDelegation{
ObjectMeta: metav1.ObjectMeta{
Name: "fallbackcertdelegation",
Namespace: "admin",
},
Spec: projcontour.TLSCertificateDelegationSpec{
Delegations: []projcontour.CertificateDelegation{{
SecretName: "fallbacksecret",
TargetNamespaces: []string{"*"},
}},
},
}

rh.OnAdd(certDelegationAll)

c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{
Resources: resources(t,
&v2.Listener{
Name: "ingress_https",
Address: envoy.SocketAddress("0.0.0.0", 8443),
ListenerFilters: envoy.ListenerFilters(
envoy.TLSInspector(),
),
FilterChains: filterchaintlsfallback("fallback.example.com", sec1, fallbackSecret,
envoy.HTTPConnectionManagerBuilder().
RouteConfigName("https/fallback.example.com").
MetricsPrefix(contour.ENVOY_HTTPS_LISTENER).
AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")).
Get(),
nil,
"h2", "http/1.1"),
},
),
TypeUrl: listenerType,
})

rh.OnDelete(certDelegationAll)

c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{
Resources: nil,
TypeUrl: listenerType,
})

certDelegationSingle := &projcontour.TLSCertificateDelegation{
ObjectMeta: metav1.ObjectMeta{
Name: "fallbackcertdelegation",
Namespace: "admin",
},
Spec: projcontour.TLSCertificateDelegationSpec{
Delegations: []projcontour.CertificateDelegation{{
SecretName: "fallbacksecret",
TargetNamespaces: []string{"default"},
}},
},
}

rh.OnAdd(certDelegationSingle)

c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{
Resources: resources(t,
&v2.Listener{
Expand Down

0 comments on commit d803875

Please sign in to comment.