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 22, 2020
1 parent dd51af0 commit d3c914d
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 3 deletions.
10 changes: 8 additions & 2 deletions 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)
sw.SetInvalid("Spec.Virtualhost.TLS Secret %q fallback certificate is invalid: %s", b.FallbackCertificate, err)
return
}

if !b.delegationPermitted(*b.FallbackCertificate, proxy.Namespace) {
sw.SetInvalid("Spec.VirtualHost.TLS fallback Secret %q is not configured for certificate delegation", b.FallbackCertificate)
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
2 changes: 1 addition & 1 deletion internal/dag/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2653,7 +2653,7 @@ func TestDAGStatus(t *testing.T) {
},
objs: []interface{}{fallbackCertificate, fallbackSecret, sec1, s4},
want: map[k8s.FullName]Status{
{Name: fallbackCertificate.Name, Namespace: fallbackCertificate.Namespace}: {Object: fallbackCertificate, Status: "invalid", Description: "Spec.Virtualhost.TLS fallback certificate Secret \"invalid/invalid\" is invalid: Secret not found", Vhost: "example.com"},
{Name: fallbackCertificate.Name, Namespace: fallbackCertificate.Namespace}: {Object: fallbackCertificate, Status: "invalid", Description: "Spec.Virtualhost.TLS Secret \"invalid/invalid\" fallback certificate is invalid: Secret not found", Vhost: "example.com"},
},
},
"fallback certificate requested but cert not configured in contour": {
Expand Down
63 changes: 63 additions & 0 deletions internal/featuretests/fallbackcert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func TestFallbackCertificate(t *testing.T) {
}},
}},
})

rh.OnAdd(proxy1)

// We should start with a single generic HTTPS service.
Expand Down Expand Up @@ -124,6 +125,27 @@ 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)

// Now we should still have the generic HTTPS service filter,
// but also the fallback certificate filter.
c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{
Expand All @@ -145,6 +167,47 @@ func TestFallbackCertificate(t *testing.T) {
),
})

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{
TypeUrl: listenerType,
Resources: resources(t,
&v2.Listener{
Name: "ingress_https",
Address: envoy.SocketAddress("0.0.0.0", 8443),
ListenerFilters: envoy.ListenerFilters(
envoy.TLSInspector(),
),
FilterChains: appendFilterChains(
filterchaintls("fallback.example.com", sec1,
httpsFilterFor("fallback.example.com"),
nil, "h2", "http/1.1"),
filterchaintlsfallback(fallbackSecret, nil, "h2", "http/1.1"),
),
},
),
})

// Invalid HTTPProxy with FallbackCertificate enabled along with ClientValidation
proxy3 := fixture.NewProxy("simple").WithSpec(
projcontour.HTTPProxySpec{
Expand Down

0 comments on commit d3c914d

Please sign in to comment.