Skip to content

Commit 3ee8837

Browse files
authored
chore(tests): test Gateway with HTTPS wildcard domains (#6310)
1 parent 0acb4da commit 3ee8837

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

test/integration/isolated/ctx.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package isolated
44

55
import (
66
"context"
7+
"crypto/x509"
78
"net/url"
89
"strings"
910
"testing"
@@ -202,3 +203,19 @@ func GetIngressClassFromCtx(ctx context.Context) string {
202203
}
203204
return r.(string)
204205
}
206+
207+
type _certPool struct{}
208+
209+
// SetCertPoolInCtx sets the cert pool in the context.
210+
func SetCertPoolInCtx(ctx context.Context, certPool *x509.CertPool) context.Context {
211+
return setInCtx(ctx, _certPool{}, certPool)
212+
}
213+
214+
// GetCertPoolFromCtx gets the cert pool from the context.
215+
func GetCertPoolFromCtx(ctx context.Context) *x509.CertPool {
216+
cp := ctx.Value(_certPool{})
217+
if cp == nil {
218+
return nil
219+
}
220+
return cp.(*x509.CertPool)
221+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
//go:build integration_tests
2+
3+
package isolated
4+
5+
import (
6+
"context"
7+
"crypto/x509"
8+
"fmt"
9+
"net/http"
10+
"testing"
11+
12+
"github.com/google/uuid"
13+
"github.com/kong/kubernetes-testing-framework/pkg/clusters"
14+
"github.com/kong/kubernetes-testing-framework/pkg/utils/kubernetes/generators"
15+
"github.com/samber/lo"
16+
"github.com/stretchr/testify/require"
17+
corev1 "k8s.io/api/core/v1"
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
k8stypes "k8s.io/apimachinery/pkg/types"
20+
"sigs.k8s.io/e2e-framework/pkg/envconf"
21+
"sigs.k8s.io/e2e-framework/pkg/features"
22+
gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned"
23+
24+
"github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
25+
"github.com/kong/kubernetes-ingress-controller/v3/test"
26+
"github.com/kong/kubernetes-ingress-controller/v3/test/helpers/certificate"
27+
"github.com/kong/kubernetes-ingress-controller/v3/test/integration/consts"
28+
"github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers"
29+
"github.com/kong/kubernetes-ingress-controller/v3/test/internal/testlabels"
30+
)
31+
32+
func TestGatewayHTTPSMultipleCertificates(t *testing.T) {
33+
const (
34+
wildcardExample = "*.example.com"
35+
nameWildcardExample = "example"
36+
testExampleURL = "https://test.example.com"
37+
38+
wildCardOneInternalExample = "*.one.internal.example.com"
39+
nameWildcardOneInternalExample = "one-internal-example"
40+
testOneInternalExampleURL = "https://test.one.internal.example.com"
41+
)
42+
43+
f := features.
44+
New("essentials").
45+
WithLabel(testlabels.NetworkingFamily, testlabels.NetworkingFamilyGatewayAPI).
46+
WithLabel(testlabels.Kind, testlabels.KindHTTPRoute).
47+
Setup(SkipIfRouterNotExpressions).
48+
WithSetup("deploy kong addon into cluster", featureSetup(
49+
withControllerManagerOpts(helpers.ControllerManagerOptAdditionalWatchNamespace("default")),
50+
withKongProxyEnvVars(map[string]string{
51+
"PROXY_LISTEN": `0.0.0.0:8443 http2 ssl`, // Ensure that only HTTPS is available.
52+
}),
53+
)).
54+
Assess(
55+
"deploying gateway with configuration (certificates and routing) to cluster",
56+
func(ctx context.Context, t *testing.T, _ *envconf.Config) context.Context {
57+
cleaner := GetFromCtxForT[*clusters.Cleaner](ctx, t)
58+
cluster := GetClusterFromCtx(ctx)
59+
namespace := GetNamespaceForT(ctx, t)
60+
61+
certPool := x509.NewCertPool()
62+
ctx = SetCertPoolInCtx(ctx, certPool)
63+
t.Log("deploying secrets with certificates")
64+
createK8sCertSecret := func(
65+
ctx context.Context, t *testing.T, cluster clusters.Cluster, nn k8stypes.NamespacedName, certPool *x509.CertPool, domainName string,
66+
) {
67+
t.Helper()
68+
certificateCrt, firstCertificateKey := certificate.MustGenerateSelfSignedCertPEMFormat(
69+
certificate.WithCommonName(domainName),
70+
certificate.WithDNSNames(domainName),
71+
)
72+
certificateSecret := &corev1.Secret{
73+
ObjectMeta: metav1.ObjectMeta{
74+
Name: nn.Name,
75+
},
76+
Type: corev1.SecretTypeTLS,
77+
Data: map[string][]byte{
78+
"tls.crt": certificateCrt,
79+
"tls.key": firstCertificateKey,
80+
},
81+
}
82+
_, err := cluster.Client().CoreV1().Secrets(nn.Namespace).Create(ctx, certificateSecret, metav1.CreateOptions{})
83+
require.NoError(t, err)
84+
require.True(t, certPool.AppendCertsFromPEM(certificateCrt))
85+
}
86+
createK8sCertSecret(
87+
ctx, t, cluster, k8stypes.NamespacedName{Namespace: namespace, Name: nameWildcardExample}, certPool, wildcardExample,
88+
)
89+
createK8sCertSecret(
90+
ctx, t, cluster, k8stypes.NamespacedName{Namespace: namespace, Name: nameWildcardOneInternalExample}, certPool, wildCardOneInternalExample,
91+
)
92+
93+
t.Log("getting a gateway client")
94+
gatewayClient, err := gatewayclient.NewForConfig(cluster.Config())
95+
require.NoError(t, err)
96+
97+
t.Log("deploying a new gatewayClass")
98+
gatewayClassName := uuid.NewString()
99+
gwc, err := helpers.DeployGatewayClass(ctx, gatewayClient, gatewayClassName)
100+
require.NoError(t, err)
101+
cleaner.Add(gwc)
102+
103+
t.Log("deploying a new gateway")
104+
createHTTPSListener := func(t *testing.T, domainName string, certSecretName string) gatewayapi.Listener {
105+
t.Helper()
106+
return gatewayapi.Listener{
107+
Name: gatewayapi.SectionName(fmt.Sprintf("https-%s", certSecretName)),
108+
Protocol: gatewayapi.HTTPSProtocolType,
109+
Port: gatewayapi.PortNumber(443),
110+
Hostname: lo.ToPtr(gatewayapi.Hostname(domainName)),
111+
TLS: &gatewayapi.GatewayTLSConfig{
112+
Mode: lo.ToPtr(gatewayapi.TLSModeTerminate),
113+
CertificateRefs: []gatewayapi.SecretObjectReference{
114+
{
115+
Name: gatewayapi.ObjectName(certSecretName),
116+
Kind: lo.ToPtr(gatewayapi.Kind("Secret")),
117+
},
118+
},
119+
},
120+
}
121+
}
122+
gatewayName := uuid.NewString()
123+
gateway, err := helpers.DeployGateway(ctx, gatewayClient, namespace, gatewayClassName, func(gw *gatewayapi.Gateway) {
124+
gw.Name = gatewayName
125+
gw.Spec.Listeners = []gatewayapi.Listener{
126+
createHTTPSListener(t, wildcardExample, nameWildcardExample),
127+
createHTTPSListener(t, wildCardOneInternalExample, nameWildcardOneInternalExample),
128+
}
129+
})
130+
require.NoError(t, err)
131+
cleaner.Add(gateway)
132+
133+
t.Log("deploying a minimal HTTP container")
134+
deployment := generators.NewDeploymentForContainer(
135+
generators.NewContainer("echo", test.EchoImage, test.EchoHTTPPort),
136+
)
137+
deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
138+
Name: "POD_NAME",
139+
Value: "echo",
140+
})
141+
deployment, err = cluster.Client().AppsV1().Deployments(namespace).Create(ctx, deployment, metav1.CreateOptions{})
142+
require.NoError(t, err)
143+
144+
t.Logf("exposing deployment %q via service", deployment.Name)
145+
service := generators.NewServiceForDeployment(deployment, corev1.ServiceTypeClusterIP)
146+
_, err = cluster.Client().CoreV1().Services(namespace).Create(ctx, service, metav1.CreateOptions{})
147+
require.NoError(t, err)
148+
149+
t.Logf("creating a HTTPRoute to access deployment %q via Kong", deployment.Name)
150+
httpRoute := &gatewayapi.HTTPRoute{
151+
ObjectMeta: metav1.ObjectMeta{
152+
Name: "echo-httproute",
153+
},
154+
Spec: gatewayapi.HTTPRouteSpec{
155+
CommonRouteSpec: gatewayapi.CommonRouteSpec{
156+
ParentRefs: []gatewayapi.ParentReference{{
157+
Name: gatewayapi.ObjectName(gateway.Name),
158+
}},
159+
},
160+
Hostnames: []gatewayapi.Hostname{
161+
gatewayapi.Hostname(wildcardExample),
162+
gatewayapi.Hostname(wildCardOneInternalExample),
163+
},
164+
Rules: []gatewayapi.HTTPRouteRule{
165+
{
166+
BackendRefs: []gatewayapi.HTTPBackendRef{
167+
{
168+
BackendRef: gatewayapi.BackendRef{
169+
BackendObjectReference: gatewayapi.BackendObjectReference{
170+
Name: gatewayapi.ObjectName(service.Name),
171+
Port: lo.ToPtr(gatewayapi.PortNumber(test.EchoHTTPPort)),
172+
Kind: lo.ToPtr(gatewayapi.Kind("Service")),
173+
},
174+
},
175+
},
176+
},
177+
},
178+
},
179+
},
180+
}
181+
_, err = gatewayClient.GatewayV1().HTTPRoutes(namespace).Create(ctx, httpRoute, metav1.CreateOptions{})
182+
require.NoError(t, err)
183+
184+
return ctx
185+
},
186+
).
187+
Assess(
188+
"verifying that certs match and HTTPS traffic is routed to the service",
189+
func(ctx context.Context, t *testing.T, _ *envconf.Config) context.Context {
190+
verifyEventuallyGet := func(url string) {
191+
helpers.EventuallyGETPath(
192+
t,
193+
GetHTTPSURLFromCtx(ctx),
194+
url,
195+
"",
196+
GetCertPoolFromCtx(ctx),
197+
http.StatusOK,
198+
"echo",
199+
nil,
200+
consts.IngressWait,
201+
consts.WaitTick,
202+
)
203+
}
204+
205+
t.Logf("verifying that %s is routed by %s", testExampleURL, wildcardExample)
206+
verifyEventuallyGet(testExampleURL)
207+
t.Logf("verifying that %s is routed by %s", testOneInternalExampleURL, wildCardOneInternalExample)
208+
verifyEventuallyGet(testOneInternalExampleURL)
209+
210+
return ctx
211+
},
212+
).
213+
Teardown(featureTeardown())
214+
215+
tenv.Test(t, f.Feature())
216+
}

0 commit comments

Comments
 (0)