Skip to content

Commit 487380f

Browse files
ronethingbackport-bot[bot]
authored andcommitted
fix: generate unique SSL IDs to prevent certificate conflicts across different hosts (#2592)
Signed-off-by: Ashing Zheng <[email protected]> (cherry picked from commit 5f0d1af1dea737f8c509ee6f52b34f55bccd5b2d)
1 parent 6b98bdb commit 487380f

File tree

10 files changed

+315
-55
lines changed

10 files changed

+315
-55
lines changed

api/adc/types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,19 @@ func (n Upstream) MarshalJSON() ([]byte, error) {
472472
return json.Marshal((Alias)(n))
473473
}
474474

475+
func ComposeSSLName(kind, namespace, name string) string {
476+
p := make([]byte, 0, len(kind)+len(namespace)+len(name)+2)
477+
buf := bytes.NewBuffer(p)
478+
479+
buf.WriteString(kind)
480+
buf.WriteByte('_')
481+
buf.WriteString(namespace)
482+
buf.WriteByte('_')
483+
buf.WriteString(name)
484+
485+
return buf.String()
486+
}
487+
475488
// ComposeRouteName uses namespace, name and rule name to compose
476489
// the route name.
477490
func ComposeRouteName(namespace, name string, rule string) string {

internal/adc/translator/apisixtls.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/apache/apisix-ingress-controller/internal/controller/label"
2828
"github.com/apache/apisix-ingress-controller/internal/id"
2929
"github.com/apache/apisix-ingress-controller/internal/provider"
30+
internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
3031
)
3132

3233
func (t *Translator) TranslateApisixTls(tctx *provider.TranslateContext, tls *apiv2.ApisixTls) (*TranslateResult, error) {
@@ -57,7 +58,7 @@ func (t *Translator) TranslateApisixTls(tctx *provider.TranslateContext, tls *ap
5758
// Create SSL object
5859
ssl := &adctypes.SSL{
5960
Metadata: adctypes.Metadata{
60-
ID: id.GenID(tls.Namespace + "_" + tls.Name),
61+
ID: id.GenID(adctypes.ComposeSSLName(internaltypes.KindApisixTls, tls.Namespace, tls.Name)),
6162
Labels: label.GenLabel(tls),
6263
},
6364
Certificates: []adctypes.Certificate{

internal/adc/translator/gateway.go

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"encoding/json"
2323
"encoding/pem"
2424
"fmt"
25-
"slices"
2625

2726
"github.com/api7/gopkg/pkg/log"
2827
"github.com/pkg/errors"
@@ -52,7 +51,6 @@ func (t *Translator) TranslateGateway(tctx *provider.TranslateContext, obj *gate
5251
result.SSL = append(result.SSL, ssl...)
5352
}
5453
}
55-
result.SSL = mergeSSLWithSameID(result.SSL)
5654

5755
rk := utils.NamespacedNameKind(obj)
5856
gatewayProxy, ok := tctx.GatewayProxies[rk]
@@ -82,7 +80,7 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g
8280
sslObjs := make([]*adctypes.SSL, 0)
8381
switch *listener.TLS.Mode {
8482
case gatewayv1.TLSModeTerminate:
85-
for _, ref := range listener.TLS.CertificateRefs {
83+
for refIndex, ref := range listener.TLS.CertificateRefs {
8684
ns := obj.GetNamespace()
8785
if ref.Namespace != nil {
8886
ns = string(*ref.Namespace)
@@ -123,9 +121,14 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g
123121
}
124122
sslObj.Snis = append(sslObj.Snis, hosts...)
125123
}
124+
<<<<<<< HEAD
126125
// Note: use cert as id to avoid duplicate certificate across ssl objects
127126
sslObj.ID = id.GenID(string(cert))
128127
log.Debugw("generated ssl id", zap.String("ssl id", sslObj.ID), zap.String("secret", secret.Namespace+"/"+secret.Name))
128+
=======
129+
sslObj.ID = id.GenID(fmt.Sprintf("%s_%s_%d", adctypes.ComposeSSLName(internaltypes.KindGateway, obj.Namespace, obj.Name), listener.Name, refIndex))
130+
t.Log.V(1).Info("generated ssl id", "ssl id", sslObj.ID, "secret", secretNN.String())
131+
>>>>>>> 5f0d1af1 (fix: generate unique SSL IDs to prevent certificate conflicts across different hosts (#2592))
129132
sslObj.Labels = label.GenLabel(obj)
130133
sslObjs = append(sslObjs, sslObj)
131134
}
@@ -242,47 +245,3 @@ func (t *Translator) fillPluginMetadataFromGatewayProxy(pluginMetadata adctypes.
242245
pluginMetadata[pluginName] = pluginConfig
243246
}
244247
}
245-
246-
// mergeSSLWithSameID merge ssl with same id
247-
func mergeSSLWithSameID(sslList []*adctypes.SSL) []*adctypes.SSL {
248-
if len(sslList) <= 1 {
249-
return sslList
250-
}
251-
252-
// create a map to store ssl with same id
253-
sslMap := make(map[string]*adctypes.SSL)
254-
for _, ssl := range sslList {
255-
if existing, exists := sslMap[ssl.ID]; exists {
256-
// if ssl with same id exists, merge their snis
257-
// use map to deduplicate
258-
sniMap := make(map[string]struct{})
259-
// add existing snis
260-
for _, sni := range existing.Snis {
261-
sniMap[sni] = struct{}{}
262-
}
263-
// add new snis
264-
for _, sni := range ssl.Snis {
265-
sniMap[sni] = struct{}{}
266-
}
267-
// rebuild deduplicated snis list
268-
newSnis := make([]string, 0, len(sniMap))
269-
for sni := range sniMap {
270-
newSnis = append(newSnis, sni)
271-
}
272-
273-
slices.Sort(newSnis)
274-
// update existing ssl object
275-
existing.Snis = newSnis
276-
} else {
277-
slices.Sort(ssl.Snis)
278-
// if new ssl id, add to map
279-
sslMap[ssl.ID] = ssl
280-
}
281-
}
282-
283-
mergedSSL := make([]*adctypes.SSL, 0, len(sslMap))
284-
for _, ssl := range sslMap {
285-
mergedSSL = append(mergedSSL, ssl)
286-
}
287-
return mergedSSL
288-
}

internal/adc/translator/ingress.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333
internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
3434
)
3535

36-
func (t *Translator) translateIngressTLS(ingressTLS *networkingv1.IngressTLS, secret *corev1.Secret, labels map[string]string) (*adctypes.SSL, error) {
36+
func (t *Translator) translateIngressTLS(namespace, name string, tlsIndex int, ingressTLS *networkingv1.IngressTLS, secret *corev1.Secret, labels map[string]string) (*adctypes.SSL, error) {
3737
// extract the key pair from the secret
3838
cert, key, err := extractKeyPair(secret, true)
3939
if err != nil {
@@ -64,7 +64,7 @@ func (t *Translator) translateIngressTLS(ingressTLS *networkingv1.IngressTLS, se
6464
},
6565
Snis: hosts,
6666
}
67-
ssl.ID = id.GenID(string(cert))
67+
ssl.ID = id.GenID(fmt.Sprintf("%s_%d", adctypes.ComposeSSLName(internaltypes.KindIngress, namespace, name), tlsIndex))
6868

6969
return ssl, nil
7070
}
@@ -75,7 +75,7 @@ func (t *Translator) TranslateIngress(tctx *provider.TranslateContext, obj *netw
7575
labels := label.GenLabel(obj)
7676

7777
// handle TLS configuration, convert to SSL objects
78-
for _, tls := range obj.Spec.TLS {
78+
for tlsIndex, tls := range obj.Spec.TLS {
7979
if tls.SecretName == "" {
8080
continue
8181
}
@@ -86,7 +86,7 @@ func (t *Translator) TranslateIngress(tctx *provider.TranslateContext, obj *netw
8686
if secret == nil {
8787
continue
8888
}
89-
ssl, err := t.translateIngressTLS(&tls, secret, labels)
89+
ssl, err := t.translateIngressTLS(obj.Namespace, obj.Name, tlsIndex, &tls, secret, labels)
9090
if err != nil {
9191
return nil, err
9292
}

internal/provider/init/init.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package init
1919

2020
import (
21+
"github.com/go-logr/logr"
22+
2123
"github.com/apache/apisix-ingress-controller/internal/controller/status"
2224
"github.com/apache/apisix-ingress-controller/internal/manager/readiness"
2325
"github.com/apache/apisix-ingress-controller/internal/provider"

internal/provider/register.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"fmt"
2222
"net/http"
2323

24+
"github.com/go-logr/logr"
25+
2426
"github.com/apache/apisix-ingress-controller/internal/controller/status"
2527
"github.com/apache/apisix-ingress-controller/internal/manager/readiness"
2628
)

test/e2e/crds/v2/tls.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,111 @@ spec:
310310
assert.Equal(GinkgoT(), int64(10), *tls[0].Client.Depth, "client depth should be 10")
311311
assert.Contains(GinkgoT(), tls[0].Client.SkipMtlsURIRegex, skipMtlsUriRegex, "skip_mtls_uri_regex should be set")
312312
})
313+
<<<<<<< HEAD
314+
=======
315+
316+
It("ApisixTls and Ingress with same certificate but different hosts", func() {
317+
By("create shared TLS secret")
318+
err := s.NewKubeTlsSecret("shared-tls-secret", Cert, Key)
319+
Expect(err).NotTo(HaveOccurred(), "creating shared TLS secret")
320+
321+
const apisixTlsSpec = `
322+
apiVersion: apisix.apache.org/v2
323+
kind: ApisixTls
324+
metadata:
325+
name: test-apisixtls-shared
326+
spec:
327+
ingressClassName: %s
328+
hosts:
329+
- api6.com
330+
secret:
331+
name: shared-tls-secret
332+
namespace: %s
333+
`
334+
335+
By("apply ApisixTls with api6.com")
336+
var apisixTls apiv2.ApisixTls
337+
tlsSpec := fmt.Sprintf(apisixTlsSpec, s.Namespace(), s.Namespace())
338+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "test-apisixtls-shared"}, &apisixTls, tlsSpec)
339+
340+
const ingressYamlWithTLS = `
341+
apiVersion: networking.k8s.io/v1
342+
kind: Ingress
343+
metadata:
344+
name: test-ingress-tls-shared
345+
spec:
346+
ingressClassName: %s
347+
tls:
348+
- hosts:
349+
- api7.com
350+
secretName: shared-tls-secret
351+
rules:
352+
- host: api7.com
353+
http:
354+
paths:
355+
- path: /
356+
pathType: Prefix
357+
backend:
358+
service:
359+
name: httpbin-service-e2e-test
360+
port:
361+
number: 80
362+
`
363+
364+
By("apply Ingress with api7.com using same certificate")
365+
err = s.CreateResourceFromString(fmt.Sprintf(ingressYamlWithTLS, s.Namespace()))
366+
Expect(err).NotTo(HaveOccurred(), "creating Ingress")
367+
368+
By("verify two SSL objects exist in control plane")
369+
Eventually(func() bool {
370+
tls, err := s.DefaultDataplaneResource().SSL().List(context.Background())
371+
if err != nil {
372+
return false
373+
}
374+
return len(tls) == 2
375+
}).WithTimeout(30 * time.Second).ProbeEvery(1 * time.Second).Should(BeTrue())
376+
377+
tls, err := s.DefaultDataplaneResource().SSL().List(context.Background())
378+
assert.Nil(GinkgoT(), err, "list tls error")
379+
assert.Len(GinkgoT(), tls, 2, "should have exactly 2 SSL objects")
380+
381+
By("verify SSL objects have different IDs and SNIs")
382+
sniFound := make(map[string]bool)
383+
384+
for i := range tls {
385+
// Check certificate content is the same
386+
assert.Len(GinkgoT(), tls[i].Certificates, 1, "each SSL should have 1 certificate")
387+
assert.Equal(GinkgoT(), Cert, tls[i].Certificates[0].Certificate, "certificate should match")
388+
389+
// Track SNIs
390+
for _, sni := range tls[i].Snis {
391+
sniFound[sni] = true
392+
}
393+
}
394+
395+
By("verify both hosts are covered")
396+
assert.True(GinkgoT(), sniFound["api6.com"], "api6.com should be in SNIs")
397+
assert.True(GinkgoT(), sniFound["api7.com"], "api7.com should be in SNIs")
398+
399+
By("test HTTPS request to api6.com")
400+
Eventually(func() int {
401+
return s.NewAPISIXHttpsClient("api6.com").
402+
GET("/get").
403+
WithHost("api6.com").
404+
Expect().
405+
Raw().StatusCode
406+
}).WithTimeout(30 * time.Second).ProbeEvery(1 * time.Second).Should(Equal(http.StatusOK))
407+
408+
By("test HTTPS request to api7.com")
409+
Eventually(func() int {
410+
return s.NewAPISIXHttpsClient("api7.com").
411+
GET("/get").
412+
WithHost("api7.com").
413+
Expect().
414+
Raw().StatusCode
415+
}).WithTimeout(30 * time.Second).ProbeEvery(1 * time.Second).Should(Equal(http.StatusOK))
416+
})
417+
418+
>>>>>>> 5f0d1af1 (fix: generate unique SSL IDs to prevent certificate conflicts across different hosts (#2592))
313419
})
314420
})

test/e2e/gatewayapi/gateway.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ spec:
284284
Eventually(func() error {
285285
tls, err := s.DefaultDataplaneResource().SSL().List(context.Background())
286286
Expect(err).NotTo(HaveOccurred(), "list ssl")
287-
if len(tls) != 1 {
288-
return fmt.Errorf("expect 1 ssl, got %d", len(tls))
287+
if len(tls) != 2 {
288+
return fmt.Errorf("expect 2 ssl, got %d", len(tls))
289289
}
290290
if len(tls[0].Certificates) != 1 {
291291
return fmt.Errorf("expect 1 certificate, got %d", len(tls[0].Certificates))
@@ -305,7 +305,7 @@ spec:
305305
Eventually(func() string {
306306
tls, err := s.DefaultDataplaneResource().SSL().List(context.Background())
307307
Expect(err).NotTo(HaveOccurred(), "list ssl")
308-
if len(tls) < 1 {
308+
if len(tls) != 2 {
309309
return ""
310310
}
311311
if len(tls[0].Certificates) < 1 {

0 commit comments

Comments
 (0)