Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions internal/adc/translator/apisixtls.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ import (
"github.com/apache/apisix-ingress-controller/internal/controller/label"
"github.com/apache/apisix-ingress-controller/internal/id"
"github.com/apache/apisix-ingress-controller/internal/provider"
<<<<<<< HEAD
=======
sslutils "github.com/apache/apisix-ingress-controller/internal/ssl"
internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
>>>>>>> 351d20a5 (feat: add certificate conflict detection to admission webhooks (#2603))
)

func (t *Translator) TranslateApisixTls(tctx *provider.TranslateContext, tls *apiv2.ApisixTls) (*TranslateResult, error) {
Expand All @@ -43,7 +48,7 @@ func (t *Translator) TranslateApisixTls(tctx *provider.TranslateContext, tls *ap
}

// Extract cert and key from secret
cert, key, err := extractKeyPair(secret, true)
cert, key, err := sslutils.ExtractKeyPair(secret, true)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -80,7 +85,7 @@ func (t *Translator) TranslateApisixTls(tctx *provider.TranslateContext, tls *ap
return nil, fmt.Errorf("client CA secret %s not found", caSecretKey.String())
}

ca, _, err := extractKeyPair(caSecret, false)
ca, _, err := sslutils.ExtractKeyPair(caSecret, false)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion internal/adc/translator/apisixupstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/apache/apisix-ingress-controller/api/adc"
apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
"github.com/apache/apisix-ingress-controller/internal/provider"
sslutils "github.com/apache/apisix-ingress-controller/internal/ssl"
"github.com/apache/apisix-ingress-controller/internal/utils"
)

Expand Down Expand Up @@ -160,7 +161,7 @@ func translateApisixUpstreamClientTLS(tctx *provider.TranslateContext, au *apiv2
return errors.Errorf("sercret %s not found", secretNN)
}

cert, key, err := extractKeyPair(secret, true)
cert, key, err := sslutils.ExtractKeyPair(secret, true)
if err != nil {
return err
}
Expand Down
72 changes: 6 additions & 66 deletions internal/adc/translator/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
package translator

import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"slices"

"github.com/api7/gopkg/pkg/log"
"github.com/pkg/errors"
<<<<<<< HEAD
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
=======
>>>>>>> 351d20a5 (feat: add certificate conflict detection to admission webhooks (#2603))
"k8s.io/apimachinery/pkg/types"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"

Expand All @@ -36,6 +37,7 @@ import (
"github.com/apache/apisix-ingress-controller/internal/controller/label"
"github.com/apache/apisix-ingress-controller/internal/id"
"github.com/apache/apisix-ingress-controller/internal/provider"
sslutils "github.com/apache/apisix-ingress-controller/internal/ssl"
internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
"github.com/apache/apisix-ingress-controller/internal/utils"
)
Expand Down Expand Up @@ -100,7 +102,7 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g
log.Errorw("secret data is nil", zap.Any("secret", secret))
return nil, fmt.Errorf("no secret data found for %s/%s", ns, name)
}
cert, key, err := extractKeyPair(secret, true)
cert, key, err := sslutils.ExtractKeyPair(secret, true)
if err != nil {
log.Errorw("failed to extract key pair", zap.Error(err), zap.Any("secret", secret))
return nil, err
Expand All @@ -113,7 +115,7 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g
if listener.Hostname != nil && *listener.Hostname != "" {
sslObj.Snis = append(sslObj.Snis, string(*listener.Hostname))
} else {
hosts, err := extractHost(cert)
hosts, err := sslutils.ExtractHostsFromCertificate(cert)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -141,68 +143,6 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g
return sslObjs, nil
}

func extractHost(cert []byte) ([]string, error) {
block, _ := pem.Decode(cert)
if block == nil {
return nil, errors.New("parse certificate: not in PEM format")
}
der, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, errors.Wrap(err, "parse certificate")
}
hosts := make([]string, 0, len(der.DNSNames))
for _, dnsName := range der.DNSNames {
if dnsName != "*" {
hosts = append(hosts, dnsName)
}
}
return hosts, nil
}

func extractKeyPair(s *corev1.Secret, hasPrivateKey bool) ([]byte, []byte, error) {
if _, ok := s.Data["cert"]; ok {
return extractApisixSecretKeyPair(s, hasPrivateKey)
} else if _, ok := s.Data[corev1.TLSCertKey]; ok {
return extractKubeSecretKeyPair(s, hasPrivateKey)
} else if ca, ok := s.Data[corev1.ServiceAccountRootCAKey]; ok && !hasPrivateKey {
return ca, nil, nil
} else {
return nil, nil, errors.New("unknown secret format")
}
}

func extractApisixSecretKeyPair(s *corev1.Secret, hasPrivateKey bool) (cert []byte, key []byte, err error) {
var ok bool
cert, ok = s.Data["cert"]
if !ok {
return nil, nil, errors.New("missing cert field")
}

if hasPrivateKey {
key, ok = s.Data["key"]
if !ok {
return nil, nil, errors.New("missing key field")
}
}
return
}

func extractKubeSecretKeyPair(s *corev1.Secret, hasPrivateKey bool) (cert []byte, key []byte, err error) {
var ok bool
cert, ok = s.Data[corev1.TLSCertKey]
if !ok {
return nil, nil, errors.New("missing cert field")
}

if hasPrivateKey {
key, ok = s.Data[corev1.TLSPrivateKeyKey]
if !ok {
return nil, nil, errors.New("missing key field")
}
}
return
}

// fillPluginsFromGatewayProxy fill plugins from GatewayProxy to given plugins
func (t *Translator) fillPluginsFromGatewayProxy(plugins adctypes.GlobalRule, gatewayProxy *v1alpha1.GatewayProxy) {
if gatewayProxy == nil {
Expand Down
5 changes: 3 additions & 2 deletions internal/adc/translator/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,20 @@ import (
"github.com/apache/apisix-ingress-controller/internal/controller/label"
"github.com/apache/apisix-ingress-controller/internal/id"
"github.com/apache/apisix-ingress-controller/internal/provider"
sslutils "github.com/apache/apisix-ingress-controller/internal/ssl"
internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
)

func (t *Translator) translateIngressTLS(ingressTLS *networkingv1.IngressTLS, secret *corev1.Secret, labels map[string]string) (*adctypes.SSL, error) {
// extract the key pair from the secret
cert, key, err := extractKeyPair(secret, true)
cert, key, err := sslutils.ExtractKeyPair(secret, true)
if err != nil {
return nil, err
}

hosts := ingressTLS.Hosts
if len(hosts) == 0 {
certHosts, err := extractHost(cert)
certHosts, err := sslutils.ExtractHostsFromCertificate(cert)
if err != nil {
return nil, err
}
Expand Down
29 changes: 29 additions & 0 deletions internal/controller/indexer/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
IngressClassParametersRef = "ingressClassParametersRef"
ConsumerGatewayRef = "consumerGatewayRef"
PolicyTargetRefs = "targetRefs"
TLSHostIndexRef = "tlsHostRefs"
GatewayClassIndexRef = "gatewayClassRef"
ApisixUpstreamRef = "apisixUpstreamRef"
PluginConfigIndexRef = "pluginConfigRefs"
Expand Down Expand Up @@ -122,6 +123,16 @@ func setupGatewayIndexer(mgr ctrl.Manager) error {
); err != nil {
return err
}

if err := mgr.GetFieldIndexer().IndexField(
context.Background(),
&gatewayv1.Gateway{},
TLSHostIndexRef,
GatewayTLSHostIndexFunc,
); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -409,6 +420,15 @@ func setupIngressIndexer(mgr ctrl.Manager) error {
return err
}

if err := mgr.GetFieldIndexer().IndexField(
context.Background(),
&networkingv1.Ingress{},
TLSHostIndexRef,
IngressTLSHostIndexFunc,
); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -811,6 +831,15 @@ func setupApisixTlsIndexer(mgr ctrl.Manager) error {
return err
}

if err := mgr.GetFieldIndexer().IndexField(
context.Background(),
&apiv2.ApisixTls{},
TLSHostIndexRef,
ApisixTlsHostIndexFunc,
); err != nil {
return err
}

return nil
}

Expand Down
143 changes: 143 additions & 0 deletions internal/controller/indexer/ssl_host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package indexer

import (
"sort"

networkingv1 "k8s.io/api/networking/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"

apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
sslutil "github.com/apache/apisix-ingress-controller/internal/ssl"
)

var (
tlsHostIndexLogger = ctrl.Log.WithName("tls-host-indexer")
// Empty host is used to match the resource which does not specify any explicit host.
emptyHost = ""
)

// GatewayTLSHostIndexFunc indexes Gateways by their TLS SNI hosts.
func GatewayTLSHostIndexFunc(rawObj client.Object) []string {
gateway, ok := rawObj.(*gatewayv1.Gateway)
if !ok {
return nil
}
if len(gateway.Spec.Listeners) == 0 {
return nil
}

hosts := make(map[string]struct{})

for _, listener := range gateway.Spec.Listeners {
if listener.TLS == nil || len(listener.TLS.CertificateRefs) == 0 {
continue
}

hasExplicitHost := false
if listener.Hostname != nil {
candidates := sslutil.NormalizeHosts([]string{string(*listener.Hostname)})
for _, host := range candidates {
if host == "" {
continue
}
hasExplicitHost = true
hosts[host] = struct{}{}
}
}

if !hasExplicitHost {
hosts[emptyHost] = struct{}{}
}
}

tlsHostIndexLogger.Info("GatewayTLSHostIndexFunc", "hosts", hostSetToSlice(hosts), "len", len(hostSetToSlice(hosts)))

return hostSetToSlice(hosts)
}

// IngressTLSHostIndexFunc indexes Ingresses by their TLS SNI hosts.
func IngressTLSHostIndexFunc(rawObj client.Object) []string {
ingress, ok := rawObj.(*networkingv1.Ingress)
if !ok {
return nil
}
if len(ingress.Spec.TLS) == 0 {
return nil
}

hosts := make(map[string]struct{})
for _, tls := range ingress.Spec.TLS {
if tls.SecretName == "" {
continue
}

hasExplicitHost := false
candidates := sslutil.NormalizeHosts(tls.Hosts)
for _, host := range candidates {
if host == "" {
continue
}
hasExplicitHost = true
hosts[host] = struct{}{}
}

if !hasExplicitHost {
hosts[emptyHost] = struct{}{}
}
}

return hostSetToSlice(hosts)
}

// ApisixTlsHostIndexFunc indexes ApisixTls resources by their declared TLS hosts.
func ApisixTlsHostIndexFunc(rawObj client.Object) []string {
tls, ok := rawObj.(*apiv2.ApisixTls)
if !ok {
return nil
}
if len(tls.Spec.Hosts) == 0 {
return nil
}

hostSet := make(map[string]struct{}, len(tls.Spec.Hosts))
for _, host := range tls.Spec.Hosts {
for _, normalized := range sslutil.NormalizeHosts([]string{string(host)}) {
if normalized == "" {
continue
}
hostSet[normalized] = struct{}{}
}
}
return hostSetToSlice(hostSet)
}

func hostSetToSlice(set map[string]struct{}) []string {
if len(set) == 0 {
return nil
}
result := make([]string, 0, len(set))
for host := range set {
result = append(result, host)
}
sort.Strings(result)
return result
}
Loading