Skip to content

Commit 78d7c1b

Browse files
authored
Merge pull request #17 from datum-cloud/split-listener-certificates
Program downstream gateways to use a separate secret for each listener certificate
2 parents da1f56a + 627b4d2 commit 78d7c1b

File tree

4 files changed

+107
-18
lines changed

4 files changed

+107
-18
lines changed

internal/controller/gateway_controller.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434

3535
"go.datum.net/network-services-operator/internal/config"
3636
downstreamclient "go.datum.net/network-services-operator/internal/downstreamclient"
37+
"go.datum.net/network-services-operator/internal/util/resourcename"
3738
)
3839

3940
const gatewayControllerFinalizer = "gateway.networking.datumapis.com/gateway-controller"
@@ -104,13 +105,15 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req mcreconcile.Reque
104105
downstreamStrategy := downstreamclient.NewMappedNamespaceResourceStrategy(req.ClusterName, cl.GetClient(), r.DownstreamCluster.GetClient())
105106

106107
if !gateway.DeletionTimestamp.IsZero() {
107-
if result := r.finalizeGateway(ctx, cl.GetClient(), &gateway, downstreamStrategy); result.ShouldReturn() {
108-
return result.Complete(ctx)
109-
}
108+
if controllerutil.ContainsFinalizer(&gateway, gatewayControllerFinalizer) {
109+
if result := r.finalizeGateway(ctx, cl.GetClient(), &gateway, downstreamStrategy); result.ShouldReturn() {
110+
return result.Complete(ctx)
111+
}
110112

111-
controllerutil.RemoveFinalizer(&gateway, gatewayControllerFinalizer)
112-
if err := cl.GetClient().Update(ctx, &gateway); err != nil {
113-
return ctrl.Result{}, fmt.Errorf("failed to remove finalizer from gateway: %w", err)
113+
controllerutil.RemoveFinalizer(&gateway, gatewayControllerFinalizer)
114+
if err := cl.GetClient().Update(ctx, &gateway); err != nil {
115+
return ctrl.Result{}, fmt.Errorf("failed to remove finalizer from gateway: %w", err)
116+
}
114117
}
115118

116119
return ctrl.Result{}, nil
@@ -198,12 +201,6 @@ func (r *GatewayReconciler) ensureDownstreamGateway(
198201
}
199202
var listeners []gatewayv1.Listener
200203
for _, l := range upstreamGateway.Spec.Listeners {
201-
202-
// TODO(jreese) this approach actually leads to request coalescing, resulting
203-
// in the `OverlappingTLSConfig` condition being set to true on the downstream
204-
// gateway, and HTTP2 disabled. We may need to either handle our own
205-
// certificate requests for each listener, or create a gateway for each
206-
// unique hostname that needs TLS.
207204
if l.TLS != nil && l.TLS.Options[certificateIssuerTLSOption] != "" {
208205
if r.Config.Gateway.PerGatewayCertificateIssuer {
209206
downstreamGateway.Annotations["cert-manager.io/issuer"] = downstreamGateway.Name
@@ -230,7 +227,7 @@ func (r *GatewayReconciler) ensureDownstreamGateway(
230227
// See: https://cert-manager.io/docs/usage/certificate/#cleaning-up-secrets-when-certificates-are-deleted
231228
CertificateRefs: []gatewayv1.SecretObjectReference{
232229
{
233-
Name: gatewayv1.ObjectName(downstreamGateway.Name),
230+
Name: gatewayv1.ObjectName(resourcename.GetValidDNS1123Name(fmt.Sprintf("%s-%s", downstreamGateway.Name, l.Name))),
234231
},
235232
},
236233
}
@@ -267,7 +264,7 @@ func (r *GatewayReconciler) ensureDownstreamGateway(
267264
// See: https://cert-manager.io/docs/usage/certificate/#cleaning-up-secrets-when-certificates-are-deleted
268265
CertificateRefs: []gatewayv1.SecretObjectReference{
269266
{
270-
Name: gatewayv1.ObjectName(downstreamGateway.Name),
267+
Name: gatewayv1.ObjectName(resourcename.GetValidDNS1123Name(fmt.Sprintf("%s-%s", downstreamGateway.Name, name))),
271268
},
272269
},
273270
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package resourcename
2+
3+
import (
4+
"crypto/md5"
5+
"fmt"
6+
7+
validation "k8s.io/apimachinery/pkg/util/validation"
8+
)
9+
10+
// GetValidDNS1123Name returns a name compliant with Kubernetes DNS-1123
11+
// subdomain length (253).
12+
// It truncates and appends an MD5 hash if the input name is too long.
13+
func GetValidDNS1123Name(name string) string {
14+
return optionalTruncateAndHash(name, validation.DNS1123SubdomainMaxLength)
15+
}
16+
17+
// GetValidDNS1035Name returns a name compliant with Kubernetes DNS-1035
18+
// label length (63).
19+
// It truncates and appends an MD5 hash if the input name is too long.
20+
func GetValidDNS1035Name(name string) string {
21+
return optionalTruncateAndHash(name, validation.DNS1035LabelMaxLength)
22+
}
23+
24+
// optionalTruncateAndHash ensures a name fits maxLength. If longer, it
25+
// truncates the name and appends an MD5 hash of the original name. Panics if
26+
// maxLength is too small for the hash suffix (33 chars).
27+
func optionalTruncateAndHash(name string, maxLength int) string {
28+
if len(name) <= maxLength {
29+
return name
30+
}
31+
32+
hash := md5.Sum([]byte(name))
33+
34+
prefixLen := maxLength - 33
35+
if prefixLen <= 0 {
36+
panic(fmt.Sprintf("maxLength is too small: %d", maxLength))
37+
}
38+
39+
if len(name) > prefixLen {
40+
name = name[0:prefixLen]
41+
}
42+
43+
return fmt.Sprintf("%s-%x", name, hash)
44+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package resourcename
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestOptionalTruncateAndHash(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
fn func(string) string
12+
input string
13+
want string
14+
}{
15+
{
16+
name: "DNS1123 no truncation",
17+
fn: GetValidDNS1123Name,
18+
input: "short",
19+
want: "short",
20+
},
21+
{
22+
name: "DNS1123 truncation",
23+
fn: GetValidDNS1123Name,
24+
input: "long" + strings.Repeat("a", 253),
25+
want: "long" + strings.Repeat("a", 253-33-4) + "-ac7d16f7520c0ebcf3269a2d2dbda4da",
26+
},
27+
{
28+
name: "DNS1035 no truncation",
29+
fn: GetValidDNS1035Name,
30+
input: "short",
31+
want: "short",
32+
},
33+
{
34+
name: "DNS1035 truncation",
35+
fn: GetValidDNS1035Name,
36+
input: "long" + strings.Repeat("a", 63),
37+
want: "long" + strings.Repeat("a", 63-33-4) + "-95b923195bff3e889498eca200310834",
38+
},
39+
}
40+
41+
for _, tt := range tests {
42+
t.Run(tt.name, func(t *testing.T) {
43+
if got := tt.fn(tt.input); got != tt.want {
44+
t.Errorf("got %q, want %q", got, tt.want)
45+
}
46+
})
47+
}
48+
}

test/e2e/gateway/chainsaw-test.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ spec:
329329
certificateRefs:
330330
- group: ""
331331
kind: Secret
332-
name: test-gateway
332+
name: test-gateway-https-test-e2e
333333
mode: Terminate
334334
- allowedRoutes:
335335
namespaces:
@@ -349,7 +349,7 @@ spec:
349349
certificateRefs:
350350
- group: ""
351351
kind: Secret
352-
name: test-gateway
352+
name: test-gateway-https-0
353353
mode: Terminate
354354
- allowedRoutes:
355355
namespaces:
@@ -369,7 +369,7 @@ spec:
369369
certificateRefs:
370370
- group: ""
371371
kind: Secret
372-
name: test-gateway
372+
name: test-gateway-https-1
373373
mode: Terminate
374374
- allowedRoutes:
375375
namespaces:
@@ -389,7 +389,7 @@ spec:
389389
certificateRefs:
390390
- group: ""
391391
kind: Secret
392-
name: test-gateway
392+
name: test-gateway-https-2
393393
mode: Terminate
394394
status:
395395
conditions:

0 commit comments

Comments
 (0)