From 0eeae3db6700b6e6274af9073b696079ef61d71a Mon Sep 17 00:00:00 2001 From: Reto Lehmann Date: Tue, 7 Nov 2023 08:51:00 +0100 Subject: [PATCH] use Knative certificates for Serving encryption --- .github/workflows/kind-e2e.yaml | 30 +- cmd/activator/main.go | 7 +- cmd/controller/main.go | 13 +- config/core/300-knativecertificate.yaml | 14 + config/core/300-secret.yaml | 47 -- go.mod | 2 + go.sum | 2 - pkg/activator/certificate/cache.go | 11 +- pkg/apis/serving/v1/route_lifecycle.go | 5 - pkg/queue/certificate/watcher_test.go | 41 +- .../accessor/networking/certificate.go | 7 +- .../resources/certificate_test.go | 7 +- pkg/reconciler/domainmapping/table_test.go | 2 + pkg/reconciler/revision/controller.go | 11 +- pkg/reconciler/revision/cruds.go | 16 - .../revision/reconcile_resources.go | 40 +- .../revision/resources/certificate.go | 53 +++ .../revision/resources/certificate_test.go | 66 +++ pkg/reconciler/revision/revision.go | 26 +- pkg/reconciler/revision/revision_test.go | 1 + pkg/reconciler/route/domains/domains.go | 24 + pkg/reconciler/route/domains/domains_test.go | 124 +++++ pkg/reconciler/route/resources/certificate.go | 69 ++- .../route/resources/certificate_test.go | 71 ++- pkg/reconciler/route/resources/ingress.go | 27 +- pkg/reconciler/route/route.go | 108 ++++- pkg/reconciler/route/route_test.go | 151 +++++- pkg/reconciler/route/table_test.go | 23 +- pkg/testing/v1/route.go | 8 - test/config/tls/cert-secret.yaml | 30 -- test/config/tls/generate.sh | 68 --- test/e2e-common.sh | 18 +- test/e2e-tests.sh | 35 +- .../cluster_local_domain_tls_test.go | 155 +++++++ test/e2e/encryption.go | 96 ++++ test/e2e/httpproxy.go | 128 ++++++ test/e2e/service_to_service_test.go | 155 +------ test/e2e/subroutes_test.go | 9 +- test/ha/ha.go | 2 +- test/test_images/httpproxy/httpproxy.go | 11 +- .../cert-manager-latest/net-certmanager.yaml | 64 +-- third_party/kourier-latest/kourier.yaml | 2 +- .../pkg/apis/networking/register.go | 4 + .../networking/v1alpha1/ingress_helpers.go | 47 ++ .../certificates/reconciler/certificates.go | 211 --------- .../pkg/certificates/reconciler/controller.go | 92 ---- .../reconciler/controller_impl.go | 103 ----- .../pkg/certificates/reconciler/reconciler.go | 434 ------------------ .../networking/pkg/config/config.go | 20 +- .../core/v1/secret/filtered/secret.go | 65 --- vendor/modules.txt | 5 +- 51 files changed, 1332 insertions(+), 1428 deletions(-) create mode 100644 config/core/300-knativecertificate.yaml delete mode 100644 config/core/300-secret.yaml create mode 100644 pkg/reconciler/revision/resources/certificate.go create mode 100644 pkg/reconciler/revision/resources/certificate_test.go delete mode 100644 test/config/tls/cert-secret.yaml delete mode 100755 test/config/tls/generate.sh create mode 100644 test/e2e/clusterlocaldomaintls/cluster_local_domain_tls_test.go create mode 100644 test/e2e/encryption.go create mode 100644 test/e2e/httpproxy.go create mode 100644 vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_helpers.go delete mode 100644 vendor/knative.dev/networking/pkg/certificates/reconciler/certificates.go delete mode 100644 vendor/knative.dev/networking/pkg/certificates/reconciler/controller.go delete mode 100644 vendor/knative.dev/networking/pkg/certificates/reconciler/controller_impl.go delete mode 100644 vendor/knative.dev/networking/pkg/certificates/reconciler/reconciler.go delete mode 100644 vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered/secret.go diff --git a/.github/workflows/kind-e2e.yaml b/.github/workflows/kind-e2e.yaml index d27b5123b873..5040206cb42d 100644 --- a/.github/workflows/kind-e2e.yaml +++ b/.github/workflows/kind-e2e.yaml @@ -87,18 +87,18 @@ jobs: fail-fast: false # Keep running if one leg fails. matrix: k8s-version: - - v1.26.x - - v1.27.x +# - v1.26.x +# - v1.27.x - v1.28.x ingress: - kourier - kourier-tls - - istio - - istio-tls +# - istio +# - istio-tls # Disabled due to flakiness: https://github.com/knative/serving/issues/14637 # - istio-ambient - - contour +# - contour # Disabled due to consistent failures # - gateway_istio @@ -114,16 +114,16 @@ jobs: # test-flags: -enable-alpha # namespace-resources: httproute - - ingress: contour - namespace-resources: httpproxy - - - ingress: istio - namespace-resources: virtualservices - - - ingress: istio-tls - ingress-class: istio - namespace-resources: virtualservices - enable-tls: 1 +# - ingress: contour +# namespace-resources: httpproxy +# +# - ingress: istio +# namespace-resources: virtualservices +# +# - ingress: istio-tls +# ingress-class: istio +# namespace-resources: virtualservices +# enable-tls: 1 # Disabled due to flakiness: https://github.com/knative/serving/issues/14637 # - ingress: istio-ambient diff --git a/cmd/activator/main.go b/cmd/activator/main.go index b765ed1c2247..86626710988c 100644 --- a/cmd/activator/main.go +++ b/cmd/activator/main.go @@ -167,8 +167,11 @@ func main() { // At this moment activator with TLS does not disable HTTP. // See also https://github.com/knative/serving/issues/12808. if tlsEnabled { - logger.Info("Knative Internal TLS is enabled") - certCache = certificate.NewCertCache(ctx) + logger.Info("Knative system-internal-tls is enabled") + certCache, err = certificate.NewCertCache(ctx) + if err != nil { + logger.Fatalw("Failed to create certificate cache", zap.Error(err)) + } transport = pkgnet.NewProxyAutoTLSTransport(env.MaxIdleProxyConns, env.MaxIdleProxyConnsPerHost, certCache.TLSContext()) } diff --git a/cmd/controller/main.go b/cmd/controller/main.go index a0140315620f..440637d0b218 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -20,7 +20,6 @@ import ( // The set of controllers this controller process runs. "flag" - certificate "knative.dev/networking/pkg/certificates/reconciler" "knative.dev/pkg/reconciler" "knative.dev/pkg/signals" "knative.dev/serving/pkg/reconciler/configuration" @@ -32,18 +31,11 @@ import ( "knative.dev/serving/pkg/reconciler/serverlessservice" "knative.dev/serving/pkg/reconciler/service" - // This defines the shared main for injected controllers. - filteredFactory "knative.dev/pkg/client/injection/kube/informers/factory/filtered" "knative.dev/pkg/injection" "knative.dev/pkg/injection/sharedmain" - "knative.dev/serving/pkg/networking" "knative.dev/serving/pkg/reconciler/domainmapping" ) -const ( - secretLabelNamePostfix = "-ctrl" -) - var ctors = []injection.ControllerConstructor{ configuration.NewController, labeler.NewController, @@ -53,7 +45,6 @@ var ctors = []injection.ControllerConstructor{ service.NewController, gc.NewController, nscert.NewController, - certificate.NewControllerFactory(networking.ServingCertName), domainmapping.NewController, } @@ -62,7 +53,5 @@ func main() { "reconciliation-timeout", reconciler.DefaultTimeout, "The amount of time to give each reconciliation of a resource to complete before its context is canceled.") - labelName := networking.ServingCertName + secretLabelNamePostfix - ctx := filteredFactory.WithSelectors(signals.NewContext(), labelName) - sharedmain.MainWithContext(ctx, "controller", ctors...) + sharedmain.MainWithContext(signals.NewContext(), "controller", ctors...) } diff --git a/config/core/300-knativecertificate.yaml b/config/core/300-knativecertificate.yaml new file mode 100644 index 000000000000..b97c94623c9f --- /dev/null +++ b/config/core/300-knativecertificate.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.internal.knative.dev/v1alpha1 +kind: Certificate +metadata: + annotations: + networking.knative.dev/certificate.class: cert-manager.certificate.networking.knative.dev + labels: + networking.knative.dev/certificate-type: system-internal + name: routing-serving-certs + namespace: knative-serving +spec: + dnsNames: + - kn-routing + - data-plane.knative.dev # for reverse-compatibility with net-* implementations that do not work with multi-SANs + secretName: routing-serving-certs diff --git a/config/core/300-secret.yaml b/config/core/300-secret.yaml deleted file mode 100644 index 3b40510a2a7e..000000000000 --- a/config/core/300-secret.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2022 The Knative Authors -# -# Licensed 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 -# -# https://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. - -apiVersion: v1 -kind: Secret -metadata: - # Do not drop -ctrl-ca suffix as certificate creation requires it - # https://github.com/knative/networking/blob/main/pkg/certificates/reconciler/controller.go#L38 - name: serving-certs-ctrl-ca - namespace: knative-serving - labels: - serving-certs-ctrl: "data-plane" - networking.internal.knative.dev/certificate-uid: "serving-certs" -# The data is populated when knative-internal-tls is enabled. ---- -apiVersion: v1 -kind: Secret -metadata: - # this is the legacy secret - # we can drop this once all net-* implementations are using the new `routing-serving-certs` secret - name: knative-serving-certs - namespace: knative-serving - labels: - serving-certs-ctrl: "data-plane" - networking.internal.knative.dev/certificate-uid: "serving-certs" -# The data is populated when knative-internal-tls is enabled. ---- -apiVersion: v1 -kind: Secret -metadata: - name: routing-serving-certs - namespace: knative-serving - labels: - serving-certs-ctrl: "data-plane-routing" - networking.internal.knative.dev/certificate-uid: "serving-certs" -# The data is populated when knative-internal-tls is enabled. diff --git a/go.mod b/go.mod index 4fbb300612e4..1ba83971734f 100644 --- a/go.mod +++ b/go.mod @@ -156,3 +156,5 @@ require ( // TODO: https://github.com/knative/serving/issues/14597 replace github.com/gorilla/websocket => github.com/gorilla/websocket v1.5.0 + +replace knative.dev/networking => /Users/rlehmann/code/knative/networking diff --git a/go.sum b/go.sum index c1f2e69dbef1..a858978fa527 100644 --- a/go.sum +++ b/go.sum @@ -948,8 +948,6 @@ knative.dev/caching v0.0.0-20231109234001-c2b2543ace4a h1:DI0ZxUDWNFQbHUZYqTdR7d knative.dev/caching v0.0.0-20231109234001-c2b2543ace4a/go.mod h1:BlOGe4qgf8fOd0CziFWnblXFbwxqeRK3cBdPdzXj8kM= knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7 h1:HXf7M7n9jwn+Hp904r0HXRSymf+DLXSciFpXVpCg+Bs= knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7/go.mod h1:yk2OjGDsbEnQjfxdm0/HJKS2WqTLEFg/N6nUs6Rqx3Q= -knative.dev/networking v0.0.0-20231109233957-8f3c5211035b h1:xdj40UcZBX4QeZiOOuCZDDGLM7gB+Wg07AKT7KVFlD0= -knative.dev/networking v0.0.0-20231109233957-8f3c5211035b/go.mod h1:h98yk6yX/fCKx0t1uDNsA0zD1QM2k97P59/XMwOe944= knative.dev/pkg v0.0.0-20231115001034-97c7258e3a98 h1:uvOLwp5Ar7oJlaYEszh51CemuZc1sRRI14xzKhUEF3U= knative.dev/pkg v0.0.0-20231115001034-97c7258e3a98/go.mod h1:56Qcm0ai7xPWqGxpOnjRi4sAX9fZM9UDTk7fKyjUqZM= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= diff --git a/pkg/activator/certificate/cache.go b/pkg/activator/certificate/cache.go index 4140b71d58b7..4ece6408c63c 100644 --- a/pkg/activator/certificate/cache.go +++ b/pkg/activator/certificate/cache.go @@ -21,6 +21,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" + "fmt" "sync" "go.uber.org/zap" @@ -47,8 +48,8 @@ type CertCache struct { certificatesMux sync.RWMutex } -// NewCertCache starts secretInformer. -func NewCertCache(ctx context.Context) *CertCache { +// NewCertCache creates and starts the certificate cache that watches Activators certificate. +func NewCertCache(ctx context.Context) (*CertCache, error) { secretInformer := secretinformer.Get(ctx) cr := &CertCache{ @@ -58,8 +59,8 @@ func NewCertCache(ctx context.Context) *CertCache { secret, err := cr.secretInformer.Lister().Secrets(system.Namespace()).Get(netcfg.ServingRoutingCertName) if err != nil { - cr.logger.Warnw("failed to get secret", zap.Error(err)) - return nil + return nil, fmt.Errorf("failed to get activator certificate, secret %s/%s was not found: %w. Enabling system-internal-tls requires the secret to be present and populated with a valid certificate and CA", + system.Namespace(), netcfg.ServingRoutingCertName, err) } cr.updateCache(secret) @@ -72,7 +73,7 @@ func NewCertCache(ctx context.Context) *CertCache { }, }) - return cr + return cr, nil } func (cr *CertCache) handleCertificateAdd(added interface{}) { diff --git a/pkg/apis/serving/v1/route_lifecycle.go b/pkg/apis/serving/v1/route_lifecycle.go index 5a75031eb4f6..52653d114e20 100644 --- a/pkg/apis/serving/v1/route_lifecycle.go +++ b/pkg/apis/serving/v1/route_lifecycle.go @@ -195,11 +195,6 @@ const ( // RouteConditionCertificateProvisioned condition when it is set to True // because external-domain-tls was not enabled. ExternalDomainTLSNotEnabledMessage = "external-domain-tls is not enabled" - - // TLSNotEnabledForClusterLocalMessage is the message which is set on the - // RouteConditionCertificateProvisioned condition when it is set to True - // because the domain is cluster-local. - TLSNotEnabledForClusterLocalMessage = "TLS is not enabled for cluster-local" ) // MarkTLSNotEnabled sets RouteConditionCertificateProvisioned to true when diff --git a/pkg/queue/certificate/watcher_test.go b/pkg/queue/certificate/watcher_test.go index f5fea36e0381..6ecb94cd9f91 100644 --- a/pkg/queue/certificate/watcher_test.go +++ b/pkg/queue/certificate/watcher_test.go @@ -17,9 +17,15 @@ limitations under the License. package certificate import ( + "bytes" "context" + "crypto/rand" + "crypto/rsa" "crypto/tls" "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" "os" "testing" "time" @@ -94,26 +100,47 @@ func TestCertificateRotation(t *testing.T) { } func createAndSaveCertificate(san, dir string) error { - ca, err := certificates.CreateCACerts(1 * time.Hour) - if err != nil { - return err + cert := &x509.Certificate{ + SerialNumber: big.NewInt(2019), + Subject: pkix.Name{ + Organization: []string{"Knative"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: false, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, } + cert.DNSNames = []string{san} - caCert, caKey, err := ca.Parse() + pk, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { return err } - cert, err := certificates.CreateCert(caKey, caCert, 1*time.Hour, san) + certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &pk.PublicKey, pk) if err != nil { return err } - if err := os.WriteFile(dir+"/"+certificates.CertName, cert.CertBytes(), 0644); err != nil { + caPEM := new(bytes.Buffer) + pem.Encode(caPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + + caPrivKeyPEM := new(bytes.Buffer) + pem.Encode(caPrivKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(pk), + }) + + if err := os.WriteFile(dir+"/"+certificates.CertName, caPEM.Bytes(), 0644); err != nil { return err } - return os.WriteFile(dir+"/"+certificates.PrivateKeyName, cert.PrivateKeyBytes(), 0644) + return os.WriteFile(dir+"/"+certificates.PrivateKeyName, caPrivKeyPEM.Bytes(), 0644) } func getSAN(c *tls.Certificate) (string, error) { diff --git a/pkg/reconciler/accessor/networking/certificate.go b/pkg/reconciler/accessor/networking/certificate.go index d491e96869eb..d843175223c7 100644 --- a/pkg/reconciler/accessor/networking/certificate.go +++ b/pkg/reconciler/accessor/networking/certificate.go @@ -63,10 +63,15 @@ func ReconcileCertificate(ctx context.Context, owner kmeta.Accessor, desired *v1 return nil, kaccessor.NewAccessorError( fmt.Errorf("owner: %s with Type %T does not own Certificate: %q", owner.GetName(), owner, cert.Name), kaccessor.NotOwnResource) - } else if !equality.Semantic.DeepEqual(cert.Spec, desired.Spec) { + } else if !equality.Semantic.DeepEqual(cert.Spec, desired.Spec) || + !equality.Semantic.DeepEqual(cert.Annotations, desired.Annotations) || + !equality.Semantic.DeepEqual(cert.Labels, desired.Labels) { + // Don't modify the informers copy existing := cert.DeepCopy() existing.Spec = desired.Spec + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels cert, err = certAccessor.GetNetworkingClient().NetworkingV1alpha1().Certificates(existing.Namespace).Update(ctx, existing, metav1.UpdateOptions{}) if err != nil { recorder.Eventf(owner, corev1.EventTypeWarning, "UpdateFailed", diff --git a/pkg/reconciler/domainmapping/resources/certificate_test.go b/pkg/reconciler/domainmapping/resources/certificate_test.go index 7145bc0ca061..aa5aaded703a 100644 --- a/pkg/reconciler/domainmapping/resources/certificate_test.go +++ b/pkg/reconciler/domainmapping/resources/certificate_test.go @@ -22,6 +22,7 @@ import ( "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/networking/pkg/config" "knative.dev/networking/pkg/apis/networking" networkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" @@ -58,7 +59,8 @@ func TestMakeCertificate(t *testing.T) { Namespace: "the-namespace", Annotations: map[string]string{networking.CertificateClassAnnotationKey: certClass}, Labels: map[string]string{ - serving.DomainMappingUIDLabelKey: "mapping.com", + serving.DomainMappingUIDLabelKey: "mapping.com", + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: networkingv1alpha1.CertificateSpec{ @@ -96,7 +98,8 @@ func TestMakeCertificate(t *testing.T) { "others": "kept", }, Labels: map[string]string{ - serving.DomainMappingUIDLabelKey: "mapping.com", + serving.DomainMappingUIDLabelKey: "mapping.com", + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: networkingv1alpha1.CertificateSpec{ diff --git a/pkg/reconciler/domainmapping/table_test.go b/pkg/reconciler/domainmapping/table_test.go index 65cad5ca667f..0bec5f1bb581 100644 --- a/pkg/reconciler/domainmapping/table_test.go +++ b/pkg/reconciler/domainmapping/table_test.go @@ -967,6 +967,7 @@ func TestReconcileTLSEnabled(t *testing.T) { }, Labels: map[string]string{ serving.DomainMappingUIDLabelKey: "becomes.ready.run", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -1114,6 +1115,7 @@ func TestReconcileTLSEnabled(t *testing.T) { }, Labels: map[string]string{ serving.DomainMappingUIDLabelKey: "challenged.com", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ diff --git a/pkg/reconciler/revision/controller.go b/pkg/reconciler/revision/controller.go index 87a442a09cee..4318cec05968 100644 --- a/pkg/reconciler/revision/controller.go +++ b/pkg/reconciler/revision/controller.go @@ -26,6 +26,8 @@ import ( "golang.org/x/time/rate" cachingclient "knative.dev/caching/pkg/client/injection/client" imageinformer "knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image" + networkingclient "knative.dev/networking/pkg/client/injection/client" + certificateinformer "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/certificate" "knative.dev/pkg/changeset" kubeclient "knative.dev/pkg/client/injection/kube/client" deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment" @@ -73,15 +75,18 @@ func newControllerWithOptions( deploymentInformer := deploymentinformer.Get(ctx) imageInformer := imageinformer.Get(ctx) paInformer := painformer.Get(ctx) + certificateInformer := certificateinformer.Get(ctx) c := &Reconciler{ - kubeclient: kubeclient.Get(ctx), - client: servingclient.Get(ctx), - cachingclient: cachingclient.Get(ctx), + kubeclient: kubeclient.Get(ctx), + client: servingclient.Get(ctx), + networkingclient: networkingclient.Get(ctx), + cachingclient: cachingclient.Get(ctx), podAutoscalerLister: paInformer.Lister(), imageLister: imageInformer.Lister(), deploymentLister: deploymentInformer.Lister(), + certificateLister: certificateInformer.Lister(), } impl := revisionreconciler.NewImpl(ctx, c, func(impl *controller.Impl) controller.Options { diff --git a/pkg/reconciler/revision/cruds.go b/pkg/reconciler/revision/cruds.go index 100591179019..2fe0a0a9f161 100644 --- a/pkg/reconciler/revision/cruds.go +++ b/pkg/reconciler/revision/cruds.go @@ -21,7 +21,6 @@ import ( "fmt" appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,7 +30,6 @@ import ( "knative.dev/pkg/logging" autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1" v1 "knative.dev/serving/pkg/apis/serving/v1" - "knative.dev/serving/pkg/networking" "knative.dev/serving/pkg/reconciler/revision/config" "knative.dev/serving/pkg/reconciler/revision/resources" ) @@ -48,20 +46,6 @@ func (c *Reconciler) createDeployment(ctx context.Context, rev *v1.Revision) (*a return c.kubeclient.AppsV1().Deployments(deployment.Namespace).Create(ctx, deployment, metav1.CreateOptions{}) } -func (c *Reconciler) createSecret(ctx context.Context, ns *corev1.Namespace) (*corev1.Secret, error) { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: networking.ServingCertName, - Namespace: ns.Name, - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ns, corev1.SchemeGroupVersion.WithKind("Namespace"))}, - Labels: map[string]string{ - networking.ServingCertName + "-ctrl": "data-plane-user", - }, - }, - } - return c.kubeclient.CoreV1().Secrets(secret.Namespace).Create(ctx, secret, metav1.CreateOptions{}) -} - func (c *Reconciler) checkAndUpdateDeployment(ctx context.Context, rev *v1.Revision, have *appsv1.Deployment) (*appsv1.Deployment, error) { logger := logging.FromContext(ctx) cfgs := config.FromContext(ctx) diff --git a/pkg/reconciler/revision/reconcile_resources.go b/pkg/reconciler/revision/reconcile_resources.go index 1bef83c102b0..c40bf976f61b 100644 --- a/pkg/reconciler/revision/reconcile_resources.go +++ b/pkg/reconciler/revision/reconcile_resources.go @@ -21,6 +21,8 @@ import ( "fmt" "go.uber.org/zap" + kaccessor "knative.dev/serving/pkg/reconciler/accessor" + networkingaccessor "knative.dev/serving/pkg/reconciler/accessor/networking" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -28,6 +30,7 @@ import ( apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + networkingApi "knative.dev/networking/pkg/apis/networking" "knative.dev/networking/pkg/certificates" "knative.dev/pkg/kmeta" "knative.dev/pkg/kmp" @@ -35,6 +38,7 @@ import ( "knative.dev/pkg/logging/logkey" v1 "knative.dev/serving/pkg/apis/serving/v1" "knative.dev/serving/pkg/networking" + "knative.dev/serving/pkg/reconciler/revision/config" "knative.dev/serving/pkg/reconciler/revision/resources" resourcenames "knative.dev/serving/pkg/reconciler/revision/resources/names" ) @@ -206,31 +210,39 @@ func hasDeploymentTimedOut(deployment *appsv1.Deployment) bool { return false } -func (c *Reconciler) reconcileSecret(ctx context.Context, rev *v1.Revision) error { +func (c *Reconciler) reconcileQueueProxyCertificate(ctx context.Context, rev *v1.Revision) error { ns := rev.Namespace logger := logging.FromContext(ctx) - logger.Info("Reconciling Secret for system-internal-tls: ", networking.ServingCertName, " at namespace: ", ns) + logger.Infof("Reconciling queue-proxy Knative Certificate for system-internal-tls: %s/%s", ns, networking.ServingCertName) + certClass := config.FromContext(ctx).Network.DefaultCertificateClass + if class := networkingApi.GetCertificateClass(rev.Annotations); class != "" { + certClass = class + } + + desiredCert := resources.MakeQueueProxyCertificate(rev, certClass) + + _, err := networkingaccessor.ReconcileCertificate(ctx, rev, desiredCert, c) + if kaccessor.IsNotOwned(err) { + logger.Debugf("Skipping reconciling Knative certificate %s/%s as it already exists (from another Knative Service in the same namespace)", + ns, networking.ServingCertName) + } else if err != nil { + return fmt.Errorf("failed to reconcile Knative certificate %s/%s: %w", ns, networking.ServingCertName, err) + } + + // Verify the secret is created and has been added the certificates secret, err := c.kubeclient.CoreV1().Secrets(ns).Get(ctx, networking.ServingCertName, metav1.GetOptions{}) if apierrs.IsNotFound(err) { - namespace, err := c.kubeclient.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("failed to get Namespace %s: %w", ns, err) - } - if secret, err = c.createSecret(ctx, namespace); err != nil { - return fmt.Errorf("failed to create Secret %s/%s: %w", networking.ServingCertName, ns, err) - } - logger.Info("Created Secret: ", networking.ServingCertName, "at namespace: ", ns) + return fmt.Errorf("secret %s/%s is not ready yet: secret could not be found", ns, networking.ServingCertName) } else if err != nil { - return fmt.Errorf("failed to get secret %s/%s: %w", networking.ServingCertName, ns, err) + return fmt.Errorf("secret %s/%s is not ready yet: %w", ns, networking.ServingCertName, err) } - // Verify if secret has been added the data. if _, ok := secret.Data[certificates.CertName]; !ok { - return fmt.Errorf("public cert in the secret is not ready yet") + return fmt.Errorf("certificate in secret %s/%s is not ready yet: public cert not found", ns, networking.ServingCertName) } if _, ok := secret.Data[certificates.PrivateKeyName]; !ok { - return fmt.Errorf("private key in the secret is not ready yet") + return fmt.Errorf("certificate in secret %s/%s is not ready yet: private key not found", ns, networking.ServingCertName) } return nil diff --git a/pkg/reconciler/revision/resources/certificate.go b/pkg/reconciler/revision/resources/certificate.go new file mode 100644 index 000000000000..357814b16d82 --- /dev/null +++ b/pkg/reconciler/revision/resources/certificate.go @@ -0,0 +1,53 @@ +/* +Copyright 2023 The Knative Authors + +Licensed 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 resources + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/networking/pkg/apis/networking" + "knative.dev/networking/pkg/apis/networking/v1alpha1" + "knative.dev/networking/pkg/certificates" + "knative.dev/networking/pkg/config" + "knative.dev/pkg/kmeta" + v1 "knative.dev/serving/pkg/apis/serving/v1" + servingnetworking "knative.dev/serving/pkg/networking" +) + +// MakeQueueProxyCertificate creates a KnativeCertificate to be used by Queue-Proxy for system-internal-tls. +func MakeQueueProxyCertificate(rev *v1.Revision, certClass string) *v1alpha1.Certificate { + return &v1alpha1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: servingnetworking.ServingCertName, + Namespace: rev.Namespace, + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)}, + Annotations: map[string]string{ + networking.CertificateClassAnnotationKey: certClass, + }, + Labels: map[string]string{ + networking.CertificateTypeLabelKey: string(config.CertificateSystemInternal), + }, + }, + Spec: v1alpha1.CertificateSpec{ + DNSNames: []string{ + certificates.DataPlaneUserSAN(rev.Namespace), + // added for reverse-compatibility with net-* implementations that do not work with multi-SANs + certificates.LegacyFakeDnsName, + }, + SecretName: servingnetworking.ServingCertName, + }, + } +} diff --git a/pkg/reconciler/revision/resources/certificate_test.go b/pkg/reconciler/revision/resources/certificate_test.go new file mode 100644 index 000000000000..5f772f910ad1 --- /dev/null +++ b/pkg/reconciler/revision/resources/certificate_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2023 The Knative Authors + +Licensed 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 resources + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/networking/pkg/apis/networking" + "knative.dev/networking/pkg/apis/networking/v1alpha1" + "knative.dev/networking/pkg/certificates" + "knative.dev/networking/pkg/config" + "knative.dev/pkg/kmeta" + v1 "knative.dev/serving/pkg/apis/serving/v1" + servingnetworking "knative.dev/serving/pkg/networking" +) + +const ( + namespace = "test-ns" + domain = "example.com" + dnsName = "kn-user-test-ns" +) + +func TestMakeQueueProxyCertificate(t *testing.T) { + rev := &v1.Revision{ + ObjectMeta: metav1.ObjectMeta{Namespace: namespace}, + } + + want := &v1alpha1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: servingnetworking.ServingCertName, + Namespace: namespace, + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)}, + Annotations: map[string]string{ + networking.CertificateClassAnnotationKey: config.CertManagerCertificateClassName, + }, + Labels: map[string]string{ + networking.CertificateTypeLabelKey: string(config.CertificateSystemInternal), + }, + }, + Spec: v1alpha1.CertificateSpec{ + DNSNames: []string{dnsName, certificates.LegacyFakeDnsName}, + SecretName: servingnetworking.ServingCertName, + }, + } + + got := MakeQueueProxyCertificate(rev, config.CertManagerCertificateClassName) + if diff := cmp.Diff(want, got); diff != "" { + t.Error("MakeQueueProxyCertificate (-want, +got) =", diff) + } +} diff --git a/pkg/reconciler/revision/revision.go b/pkg/reconciler/revision/revision.go index d4228b8a8838..44a4e77e488a 100644 --- a/pkg/reconciler/revision/revision.go +++ b/pkg/reconciler/revision/revision.go @@ -25,17 +25,21 @@ import ( "github.com/google/go-containerregistry/pkg/authn/k8schain" "go.uber.org/zap" "go.uber.org/zap/zapcore" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" appsv1listers "k8s.io/client-go/listers/apps/v1" cachingclientset "knative.dev/caching/pkg/client/clientset/versioned" + networkingclientset "knative.dev/networking/pkg/client/clientset/versioned" + "knative.dev/networking/pkg/client/listers/networking/v1alpha1" clientset "knative.dev/serving/pkg/client/clientset/versioned" + revisionreconciler "knative.dev/serving/pkg/client/injection/reconciler/serving/v1/revision" cachinglisters "knative.dev/caching/pkg/client/listers/caching/v1alpha1" + networkinglisters "knative.dev/networking/pkg/client/listers/networking/v1alpha1" + "knative.dev/pkg/controller" "knative.dev/pkg/logging" pkgreconciler "knative.dev/pkg/reconciler" @@ -52,14 +56,16 @@ type resolver interface { // Reconciler implements controller.Reconciler for Revision resources. type Reconciler struct { - kubeclient kubernetes.Interface - client clientset.Interface - cachingclient cachingclientset.Interface + kubeclient kubernetes.Interface + client clientset.Interface + networkingclient networkingclientset.Interface + cachingclient cachingclientset.Interface // lister indexes properties about Revision podAutoscalerLister palisters.PodAutoscalerLister imageLister cachinglisters.ImageLister deploymentLister appsv1listers.DeploymentLister + certificateLister networkinglisters.CertificateLister resolver resolver } @@ -138,9 +144,9 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgrec logger.Debug("Revision meta: " + spew.Sdump(rev.ObjectMeta)) } - // Deploy certificate when system-internal-tls is enabled. + // Deploy Knative Certificate for queue-proxy when system-internal-tls is enabled. if config.FromContext(ctx).Network.SystemInternalTLSEnabled() { - if err := c.reconcileSecret(ctx, rev); err != nil { + if err := c.reconcileQueueProxyCertificate(ctx, rev); err != nil { return err } } @@ -184,3 +190,11 @@ func (c *Reconciler) ObserveDeletion(ctx context.Context, key types.NamespacedNa c.resolver.Forget(key) return nil } + +func (c *Reconciler) GetNetworkingClient() networkingclientset.Interface { + return c.networkingclient +} + +func (c *Reconciler) GetCertificateLister() v1alpha1.CertificateLister { + return c.certificateLister +} diff --git a/pkg/reconciler/revision/revision_test.go b/pkg/reconciler/revision/revision_test.go index 462cff4e8581..d93e654da6e3 100644 --- a/pkg/reconciler/revision/revision_test.go +++ b/pkg/reconciler/revision/revision_test.go @@ -28,6 +28,7 @@ import ( "go.uber.org/zap" fakecachingclient "knative.dev/caching/pkg/client/injection/client/fake" fakeimageinformer "knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image/fake" + _ "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/certificate/fake" fakekubeclient "knative.dev/pkg/client/injection/kube/client/fake" fakedeploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/fake" _ "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/fake" diff --git a/pkg/reconciler/route/domains/domains.go b/pkg/reconciler/route/domains/domains.go index 0ca76e1931a4..deebac2f77c4 100644 --- a/pkg/reconciler/route/domains/domains.go +++ b/pkg/reconciler/route/domains/domains.go @@ -24,11 +24,13 @@ import ( "text/template" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" netapi "knative.dev/networking/pkg/apis/networking" netv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" netcfg "knative.dev/networking/pkg/config" + "knative.dev/networking/pkg/ingress" "knative.dev/pkg/apis" pkgnet "knative.dev/pkg/network" "knative.dev/serving/pkg/apis/serving" @@ -63,6 +65,28 @@ func GetAllDomainsAndTags(ctx context.Context, r *v1.Route, names []string, visi return domainTagMap, nil } +// GetDomainsForVisibility return all domains for the specified visibility. +func GetDomainsForVisibility(ctx context.Context, targetName string, r *v1.Route, visibility netv1alpha1.IngressVisibility) (sets.String, error) { + hostname, err := HostnameFromTemplate(ctx, r.Name, targetName) + if err != nil { + return nil, err + } + + meta := r.ObjectMeta.DeepCopy() + isClusterLocal := visibility == netv1alpha1.IngressVisibilityClusterLocal + labels.SetVisibility(meta, isClusterLocal) + + domain, err := DomainNameFromTemplate(ctx, *meta, hostname) + if err != nil { + return nil, err + } + domains := []string{domain} + if isClusterLocal { + domains = ingress.ExpandedHosts(sets.NewString(domains...)).List() + } + return sets.NewString(domains...), err +} + // DomainNameFromTemplate generates domain name base on the template specified in the `config-network` ConfigMap. // name is the "subdomain" which will be referred as the "name" in the template func DomainNameFromTemplate(ctx context.Context, r metav1.ObjectMeta, name string) (string, error) { diff --git a/pkg/reconciler/route/domains/domains_test.go b/pkg/reconciler/route/domains/domains_test.go index 7f7617cf2705..6e80e811b0a9 100644 --- a/pkg/reconciler/route/domains/domains_test.go +++ b/pkg/reconciler/route/domains/domains_test.go @@ -21,6 +21,8 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/networking/pkg/apis/networking/v1alpha1" "github.com/google/go-cmp/cmp" "knative.dev/pkg/apis" @@ -271,6 +273,128 @@ func TestGetAllDomainsAndTags(t *testing.T) { }) } } + +func TestGetDomainsForVisibility(t *testing.T) { + tests := []struct { + name string + tag string + visibility v1alpha1.IngressVisibility + domainTemplate string + tagTemplate string + want sets.String + }{ + { + name: "default template - no tag - cluster-local", + tag: "", + visibility: v1alpha1.IngressVisibilityClusterLocal, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Name}}-{{.Tag}}", + want: sets.NewString( + "myroute.default", + "myroute.default.svc", + "myroute.default.svc.cluster.local", + ), + }, { + name: "default template - no tag - external-IP", + tag: "", + visibility: v1alpha1.IngressVisibilityExternalIP, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Name}}-{{.Tag}}", + want: sets.NewString( + "myroute.default.example.com", + ), + }, { + name: "default template - with tag - cluster-local", + tag: "test", + visibility: v1alpha1.IngressVisibilityClusterLocal, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Name}}-{{.Tag}}", + want: sets.NewString( + "myroute-test.default", + "myroute-test.default.svc", + "myroute-test.default.svc.cluster.local", + ), + }, { + name: "default template - with tag - external-IP", + tag: "test", + visibility: v1alpha1.IngressVisibilityExternalIP, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Name}}-{{.Tag}}", + want: sets.NewString( + "myroute-test.default.example.com", + ), + }, { + name: "alternative template - no tag - cluster-local", + tag: "", + visibility: v1alpha1.IngressVisibilityClusterLocal, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Tag}}-{{.Name}}", + want: sets.NewString( + "myroute.default", + "myroute.default.svc", + "myroute.default.svc.cluster.local", + ), + }, { + name: "alternative template - no tag - external-IP", + tag: "", + visibility: v1alpha1.IngressVisibilityExternalIP, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Tag}}-{{.Name}}", + want: sets.NewString( + "myroute.default.example.com", + ), + }, { + name: "alternative template - with tag - cluster-local", + tag: "test", + visibility: v1alpha1.IngressVisibilityClusterLocal, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Tag}}-{{.Name}}", + want: sets.NewString( + "test-myroute.default", + "test-myroute.default.svc", + "test-myroute.default.svc.cluster.local", + ), + }, { + name: "alternative template - with tag - external-IP", + tag: "test", + visibility: v1alpha1.IngressVisibilityExternalIP, + domainTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}", + tagTemplate: "{{.Tag}}-{{.Name}}", + want: sets.NewString( + "test-myroute.default.example.com", + ), + }, + } + + route := &v1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myroute", + Namespace: "default", + Labels: map[string]string{ + "route": "myapp", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + cfg := testConfig() + cfg.Network.DomainTemplate = tt.domainTemplate + cfg.Network.TagTemplate = tt.tagTemplate + ctx = config.ToContext(ctx, cfg) + + got, err := GetDomainsForVisibility(ctx, tt.tag, route, tt.visibility) + if err != nil { + t.Errorf("failed calling GetDomainsForVisibility: %v", err) + } + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Error("GetDomainsForVisibility() diff (-want +got):", diff) + } + }) + } +} + func TestIsClusterLocal(t *testing.T) { tests := []struct { name string diff --git a/pkg/reconciler/route/resources/certificate.go b/pkg/reconciler/route/resources/certificate.go index 959ad17a86d1..086f0b9d1e4f 100644 --- a/pkg/reconciler/route/resources/certificate.go +++ b/pkg/reconciler/route/resources/certificate.go @@ -21,7 +21,11 @@ import ( "hash/adler32" "sort" + "k8s.io/apimachinery/pkg/util/sets" "knative.dev/networking/pkg/apis/networking" + "knative.dev/networking/pkg/config" + "knative.dev/pkg/kmap" + "knative.dev/pkg/network" "knative.dev/serving/pkg/apis/serving" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,6 +35,10 @@ import ( "knative.dev/serving/pkg/reconciler/route/resources/names" ) +const ( + localDomainSuffix = "-local" +) + // MakeCertificate creates a Certificate, inheriting the certClass // annotations from the owner, as well as the namespaces. If owner // does not have a certClass, use the provided `certClass` parameter. @@ -45,7 +53,8 @@ func MakeCertificate(owner kmeta.OwnerRefableAccessor, ownerLabelKey string, dns networking.CertificateClassAnnotationKey: certClass, }, owner.GetAnnotations()), ExcludedAnnotations.Has), Labels: map[string]string{ - ownerLabelKey: owner.GetName(), + ownerLabelKey: owner.GetName(), + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: networkingv1alpha1.CertificateSpec{ @@ -72,17 +81,53 @@ func MakeCertificates(route *v1.Route, domainTagMap map[string]string, certClass certs := make([]*networkingv1alpha1.Certificate, 0, len(order)) for _, dnsName := range order { tag := domainTagMap[dnsName] - - // k8s supports cert name only up to 63 chars and so is constructed as route-[UID]-[tag digest] - // where route-[UID] will take 42 characters and leaves 20 characters for tag digest (need to include `-`). - // We use https://golang.org/pkg/hash/adler32/#Checksum to compute the digest which returns a uint32. - // We represent the digest in unsigned integer format with maximum value of 4,294,967,295 which are 10 digits. - // The "-[tag digest]" is computed only if there's a tag - certName := names.Certificate(route) - if tag != "" { - certName += fmt.Sprint("-", adler32.Checksum([]byte(tag))) - } - certs = append(certs, MakeCertificate(route, serving.RouteLabelKey, dnsName, certName, certClass, domain)) + certs = append(certs, MakeCertificate(route, serving.RouteLabelKey, dnsName, certNameFromRouteAndTag(route, tag), certClass, domain)) } return certs } + +// MakeClusterLocalCertificate creates a Knative Certificate +// for cluster-local-domain-tls. +func MakeClusterLocalCertificate(route *v1.Route, tag string, domains sets.String, certClass string) *networkingv1alpha1.Certificate { + domainsOrdered := make(sort.StringSlice, 0, len(domains)) + for dnsName := range domains { + domainsOrdered = append(domainsOrdered, dnsName) + } + domainsOrdered.Sort() + + certName := certNameFromRouteAndTag(route, tag) + localDomainSuffix + + return &networkingv1alpha1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: certName, + Namespace: route.GetNamespace(), + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(route)}, + Annotations: kmap.Filter(kmap.Union(map[string]string{ + networking.CertificateClassAnnotationKey: certClass, + }, route.GetAnnotations()), ExcludedAnnotations.Has), + Labels: map[string]string{ + serving.RouteLabelKey: route.GetName(), + networking.CertificateTypeLabelKey: string(config.CertificateClusterLocalDomain), + }, + }, + Spec: networkingv1alpha1.CertificateSpec{ + DNSNames: domainsOrdered, + Domain: "svc." + network.GetClusterDomainName(), + SecretName: certName, + }, + } +} + +// certNameFromRouteAndTag returns a possibly shortended certName as +// k8s supports cert name only up to 63 chars and so is constructed as route-[UID]-[tag digest] +// where route-[UID] will take 42 characters and leaves 20 characters for tag digest (need to include `-`). +// We use https://golang.org/pkg/hash/adler32/#Checksum to compute the digest which returns a uint32. +// We represent the digest in unsigned integer format with maximum value of 4,294,967,295 which are 10 digits. +// The "-[tag digest]" is computed only if there's a tag +func certNameFromRouteAndTag(route *v1.Route, tag string) string { + certName := names.Certificate(route) + if tag != "" { + certName += fmt.Sprint("-", adler32.Checksum([]byte(tag))) + } + return certName +} diff --git a/pkg/reconciler/route/resources/certificate_test.go b/pkg/reconciler/route/resources/certificate_test.go index ab86deb6d41d..23a06a9f9559 100644 --- a/pkg/reconciler/route/resources/certificate_test.go +++ b/pkg/reconciler/route/resources/certificate_test.go @@ -19,7 +19,9 @@ package resources import ( "testing" + "k8s.io/apimachinery/pkg/util/sets" "knative.dev/networking/pkg/apis/networking" + "knative.dev/networking/pkg/config" "knative.dev/serving/pkg/apis/serving" "knative.dev/pkg/kmeta" @@ -37,8 +39,9 @@ var ( "v1.default.example.com": "", "v1-current.default.example.com": "current", } - domain = "example.com" - route = Route("default", "route", WithRouteUID("12345")) + domain = "example.com" + localDomains = sets.NewString("hello.namespace", "hello.namespace.svc", "hello.namespace.svc.cluster.local") + route = Route("default", "route", WithRouteUID("12345")) ) func TestMakeCertificates(t *testing.T) { @@ -52,7 +55,8 @@ func TestMakeCertificates(t *testing.T) { networking.CertificateClassAnnotationKey: "foo-cert", }, Labels: map[string]string{ - serving.RouteLabelKey: "route", + serving.RouteLabelKey: "route", + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -70,7 +74,8 @@ func TestMakeCertificates(t *testing.T) { networking.CertificateClassAnnotationKey: "foo-cert", }, Labels: map[string]string{ - serving.RouteLabelKey: "route", + serving.RouteLabelKey: "route", + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -99,7 +104,8 @@ func TestMakeCertificates_FilterLastAppliedAnno(t *testing.T) { networking.CertificateClassAnnotationKey: "passdown-cert", }, Labels: map[string]string{ - serving.RouteLabelKey: "route", + serving.RouteLabelKey: "route", + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -117,7 +123,8 @@ func TestMakeCertificates_FilterLastAppliedAnno(t *testing.T) { networking.CertificateClassAnnotationKey: "passdown-cert", }, Labels: map[string]string{ - serving.RouteLabelKey: "route", + serving.RouteLabelKey: "route", + networking.CertificateTypeLabelKey: string(config.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -132,3 +139,55 @@ func TestMakeCertificates_FilterLastAppliedAnno(t *testing.T) { t.Error("MakeCertificate (-want, +got) =", diff) } } + +func TestMakeClusterLocalCertificateNoTag(t *testing.T) { + want := &netv1alpha1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-12345-local", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(route)}, + Annotations: map[string]string{ + networking.CertificateClassAnnotationKey: "cert-class", + }, + Labels: map[string]string{ + serving.RouteLabelKey: "route", + networking.CertificateTypeLabelKey: string(config.CertificateClusterLocalDomain), + }, + }, + Spec: netv1alpha1.CertificateSpec{ + DNSNames: localDomains.List(), + Domain: "svc.cluster.local", + SecretName: "route-12345-local", + }, + } + got := MakeClusterLocalCertificate(route, "", localDomains, "cert-class") + if diff := cmp.Diff(want, got); diff != "" { + t.Error("MakeCertificate (-want, +got) =", diff) + } +} + +func TestMakeClusterLocalCertificateWithTag(t *testing.T) { + want := &netv1alpha1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-12345-73204161-local", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(route)}, + Annotations: map[string]string{ + networking.CertificateClassAnnotationKey: "cert-class", + }, + Labels: map[string]string{ + serving.RouteLabelKey: "route", + networking.CertificateTypeLabelKey: string(config.CertificateClusterLocalDomain), + }, + }, + Spec: netv1alpha1.CertificateSpec{ + DNSNames: localDomains.List(), + Domain: "svc.cluster.local", + SecretName: "route-12345-73204161-local", + }, + } + got := MakeClusterLocalCertificate(route, "test", localDomains, "cert-class") + if diff := cmp.Diff(want, got); diff != "" { + t.Error("MakeCertificate (-want, +got) =", diff) + } +} diff --git a/pkg/reconciler/route/resources/ingress.go b/pkg/reconciler/route/resources/ingress.go index 4d866c5ea846..16e230409f63 100644 --- a/pkg/reconciler/route/resources/ingress.go +++ b/pkg/reconciler/route/resources/ingress.go @@ -26,11 +26,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/serving/pkg/reconciler/route/domains" "knative.dev/networking/pkg/apis/networking" netv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" netheader "knative.dev/networking/pkg/http/header" - ingress "knative.dev/networking/pkg/ingress" "knative.dev/pkg/kmeta" "knative.dev/pkg/logging" "knative.dev/serving/pkg/activator" @@ -39,8 +39,6 @@ import ( servingv1 "knative.dev/serving/pkg/apis/serving/v1" servingnetworking "knative.dev/serving/pkg/networking" "knative.dev/serving/pkg/reconciler/route/config" - "knative.dev/serving/pkg/reconciler/route/domains" - "knative.dev/serving/pkg/reconciler/route/resources/labels" "knative.dev/serving/pkg/reconciler/route/resources/names" "knative.dev/serving/pkg/reconciler/route/traffic" ) @@ -144,7 +142,7 @@ func makeIngressSpec( visibilities = append(visibilities, netv1alpha1.IngressVisibilityExternalIP) } for _, visibility := range visibilities { - domains, err := routeDomain(ctx, name, r, visibility) + domains, err := domains.GetDomainsForVisibility(ctx, name, r, visibility) if err != nil { return netv1alpha1.IngressSpec{}, err } @@ -205,27 +203,6 @@ func makeIngressSpec( }, nil } -func routeDomain(ctx context.Context, targetName string, r *servingv1.Route, visibility netv1alpha1.IngressVisibility) (sets.String, error) { - hostname, err := domains.HostnameFromTemplate(ctx, r.Name, targetName) - if err != nil { - return nil, err - } - - meta := r.ObjectMeta.DeepCopy() - isClusterLocal := visibility == netv1alpha1.IngressVisibilityClusterLocal - labels.SetVisibility(meta, isClusterLocal) - - domain, err := domains.DomainNameFromTemplate(ctx, *meta, hostname) - if err != nil { - return nil, err - } - domains := []string{domain} - if isClusterLocal { - domains = ingress.ExpandedHosts(sets.NewString(domains...)).List() - } - return sets.NewString(domains...), err -} - // MakeACMEIngressPaths returns a set of netv1alpha1.HTTPIngressPath // that can be used to perform ACME challenges. func MakeACMEIngressPaths(acmeChallenges []netv1alpha1.HTTP01Challenge, domains sets.String) ([]netv1alpha1.HTTPIngressPath, []string) { diff --git a/pkg/reconciler/route/route.go b/pkg/reconciler/route/route.go index 22b170c56a53..7cc5ac5ab0ca 100644 --- a/pkg/reconciler/route/route.go +++ b/pkg/reconciler/route/route.go @@ -137,10 +137,19 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconcil return err } - tls, acmeChallenges, err := c.tls(ctx, r.Status.URL.Host, r, traffic) + tls, acmeChallenges, err := c.externalDomainTLS(ctx, r.Status.URL.Host, r, traffic) if err != nil { return err } + + if config.FromContext(ctx).Network.ClusterLocalDomainTLS == netcfg.EncryptionEnabled { + internalTLS, err := c.clusterLocalDomainTLS(ctx, r, traffic) + if err != nil { + return err + } + tls = append(tls, internalTLS...) + } + // Reconcile ingress and its children resources. ingress, effectiveRO, err := c.reconcileIngress(ctx, r, traffic, tls, ingressClassForRoute(ctx, r), acmeChallenges...) if err != nil { @@ -180,7 +189,7 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconcil return nil } -func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic *traffic.Config) ([]netv1alpha1.IngressTLS, []netv1alpha1.HTTP01Challenge, error) { +func (c *Reconciler) externalDomainTLS(ctx context.Context, host string, r *v1.Route, traffic *traffic.Config) ([]netv1alpha1.IngressTLS, []netv1alpha1.HTTP01Challenge, error) { logger := logging.FromContext(ctx) tls := []netv1alpha1.IngressTLS{} @@ -195,8 +204,8 @@ func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic } for domain := range domainToTagMap { + // Ignore cluster local domains here, as their TLS is handled in clusterLocalDomainTLS() if domains.IsClusterLocal(domain) { - r.Status.MarkTLSNotEnabled(v1.TLSNotEnabledForClusterLocalMessage) delete(domainToTagMap, domain) } } @@ -285,28 +294,69 @@ func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic return acmeChallenges[i].URL.String() < acmeChallenges[j].URL.String() }) - orphanCerts, err := c.getOrphanRouteCerts(r, domainToTagMap) + orphanCerts, err := c.getOrphanRouteCerts(r, domainToTagMap, netcfg.CertificateExternalDomain) if err != nil { return nil, nil, err } - recorder := controller.GetEventRecorder(ctx) - for _, cert := range orphanCerts { - err = c.GetNetworkingClient().NetworkingV1alpha1().Certificates(cert.Namespace).Delete(ctx, cert.Name, metav1.DeleteOptions{}) + c.deleteOrphanedCerts(ctx, orphanCerts) + + return tls, acmeChallenges, nil +} + +func (c *Reconciler) clusterLocalDomainTLS(ctx context.Context, r *v1.Route, tc *traffic.Config) ([]netv1alpha1.IngressTLS, error) { + tls := []netv1alpha1.IngressTLS{} + usedDomains := make(map[string]string) + + for name := range tc.Targets { + localDomains, err := domains.GetDomainsForVisibility(ctx, name, r, netv1alpha1.IngressVisibilityClusterLocal) if err != nil { - recorder.Eventf(cert, corev1.EventTypeNormal, "DeleteFailed", - "Failed to delete orphaned Knative Certificate %s/%s: %v", cert.Namespace, cert.Name, err) + return nil, err + } + + desiredCert := resources.MakeClusterLocalCertificate(r, name, localDomains, certClass(ctx, r)) + cert, err := networkaccessor.ReconcileCertificate(ctx, r, desiredCert, c) + if err != nil { + if kaccessor.IsNotOwned(err) { + r.Status.MarkCertificateNotOwned(desiredCert.Name) + } else { + r.Status.MarkCertificateProvisionFailed(desiredCert.Name) + } + return nil, err + } + + if cert.IsReady() { + r.Status.Address.URL.Scheme = "https" + + // r.Status.URL contains the major domain, + // so only change if the cert is for the major domain + if localDomains.Has(r.Status.URL.Host) { + r.Status.URL.Scheme = "https" + } + + r.Status.MarkCertificateReady(cert.Name) + tls = append(tls, resources.MakeIngressTLS(cert, localDomains.List())) } else { - recorder.Eventf(cert, corev1.EventTypeNormal, "Deleted", - "Deleted orphaned Knative Certificate %s/%s", cert.Namespace, cert.Name) + r.Status.MarkCertificateNotReady(cert) + } + + for s, _ := range localDomains { + usedDomains[s] = s } } - return tls, acmeChallenges, nil + orphanCerts, err := c.getOrphanRouteCerts(r, usedDomains, netcfg.CertificateClusterLocalDomain) + if err != nil { + return nil, nil + } + + c.deleteOrphanedCerts(ctx, orphanCerts) + + return tls, nil } -// Returns a slice of certificates that used to belong route's old tags and are currently not in use. -func (c *Reconciler) getOrphanRouteCerts(r *v1.Route, domainToTagMap map[string]string) ([]*netv1alpha1.Certificate, error) { +// Returns a slice of certificates that used to belong route's old domains/tags for a specific visibility that are currently not in use. +func (c *Reconciler) getOrphanRouteCerts(r *v1.Route, domainToTagMap map[string]string, certificateType netcfg.CertificateType) ([]*netv1alpha1.Certificate, error) { labelSelector := kubelabels.SelectorFromSet(kubelabels.Set{ serving.RouteLabelKey: r.Name, }) @@ -318,21 +368,37 @@ func (c *Reconciler) getOrphanRouteCerts(r *v1.Route, domainToTagMap map[string] var unusedCerts []*netv1alpha1.Certificate for _, cert := range certs { - var shouldKeepCert bool - for _, dn := range cert.Spec.DNSNames { - if _, used := domainToTagMap[dn]; used { - shouldKeepCert = true + if v, ok := cert.ObjectMeta.Labels[networking.CertificateTypeLabelKey]; ok && v == string(certificateType) { + var shouldKeepCert bool + for _, dn := range cert.Spec.DNSNames { + if _, used := domainToTagMap[dn]; used { + shouldKeepCert = true + } } - } - if !shouldKeepCert { - unusedCerts = append(unusedCerts, cert) + if !shouldKeepCert { + unusedCerts = append(unusedCerts, cert) + } } } return unusedCerts, nil } +func (c *Reconciler) deleteOrphanedCerts(ctx context.Context, orphanCerts []*netv1alpha1.Certificate) { + recorder := controller.GetEventRecorder(ctx) + for _, cert := range orphanCerts { + err := c.GetNetworkingClient().NetworkingV1alpha1().Certificates(cert.Namespace).Delete(ctx, cert.Name, metav1.DeleteOptions{}) + if err != nil { + recorder.Eventf(cert, corev1.EventTypeNormal, "DeleteFailed", + "Failed to delete orphaned Knative Certificate %s/%s: %v", cert.Namespace, cert.Name, err) + } else { + recorder.Eventf(cert, corev1.EventTypeNormal, "Deleted", + "Deleted orphaned Knative Certificate %s/%s", cert.Namespace, cert.Name) + } + } +} + // configureTraffic attempts to configure traffic based on the RouteSpec. If there are missing // targets (e.g. Configurations without a Ready Revision, or Revision that isn't Ready or Inactive), // no traffic will be configured. diff --git a/pkg/reconciler/route/route_test.go b/pkg/reconciler/route/route_test.go index d90b9c9facef..2caacb5257ce 100644 --- a/pkg/reconciler/route/route_test.go +++ b/pkg/reconciler/route/route_test.go @@ -25,15 +25,16 @@ import ( // Inject the informers this controller depends on. fakenetworkingclient "knative.dev/networking/pkg/client/injection/client/fake" + fakecertificateinformer "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/certificate/fake" fakeingressinformer "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/ingress/fake" + fakekubeclient "knative.dev/pkg/client/injection/kube/client/fake" + fakeserviceinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/service/fake" fakeservingclient "knative.dev/serving/pkg/client/injection/client/fake" fakecfginformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/configuration/fake" fakerevisioninformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision/fake" fakerouteinformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/route/fake" - _ "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/certificate/fake" _ "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints/fake" - _ "knative.dev/pkg/client/injection/kube/informers/core/v1/service/fake" "github.com/google/go-cmp/cmp" "golang.org/x/sync/errgroup" @@ -1644,3 +1645,149 @@ func TestExternalDomainTLSEnabled(t *testing.T) { }) } } + +func TestCreateRouteWithClusterLocalDomainTLSEnabled(t *testing.T) { + ctx, _, ctl, watcher, cf := newTestSetup(t) + defer cf() + + // Enable cluster-local-domain-tls + watcher.OnChange(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: netcfg.ConfigMapName, + Namespace: system.Namespace(), + }, + Data: map[string]string{ + "cluster-local-domain-tls": "enabled", + }, + }) + + fakeRecorder := controller.GetEventRecorder(ctx).(*record.FakeRecorder) + + rev := Revision(testNamespace, "test-rev", MarkRevisionReady) + fakeservingclient.Get(ctx).ServingV1().Revisions(testNamespace).Create(ctx, rev, metav1.CreateOptions{}) + fakerevisioninformer.Get(ctx).Informer().GetIndexer().Add(rev) + + // A route targeting the revision + route := Route(testNamespace, "test-route", WithSpecTraffic(v1.TrafficTarget{ + RevisionName: "test-rev", + ConfigurationName: "test-config", + Percent: ptr.Int64(100), + }), WithRouteLabel(map[string]string{"route": "test-route"})) + fakeservingclient.Get(ctx).ServingV1().Routes(testNamespace).Create(ctx, route, metav1.CreateOptions{}) + // Since Reconcile looks in the lister, we need to add it to the informer + fakerouteinformer.Get(ctx).Informer().GetIndexer().Add(route) + + // reconcile once to get certificate object + ctl.Reconciler.Reconcile(ctx, KeyOrDie(route)) + + // mark the certificate as ready + cert, _ := fakenetworkingclient.Get(ctx).NetworkingV1alpha1().Certificates(testNamespace).Get(ctx, "route--local", metav1.GetOptions{}) + cert.Status.MarkReady() + fakenetworkingclient.Get(ctx).NetworkingV1alpha1().Certificates(testNamespace).Update(ctx, cert, metav1.UpdateOptions{}) + fakecertificateinformer.Get(ctx).Informer().GetIndexer().Add(cert) + + // Since Reconcile looks in the lister, we need to add the created objects to the informers to avoid "already exists" errors + svc, _ := fakekubeclient.Get(ctx).CoreV1().Services(testNamespace).List(ctx, metav1.ListOptions{}) + for _, s := range svc.Items { + fakeserviceinformer.Get(ctx).Informer().GetIndexer().Add(&s) + } + ing, _ := fakenetworkingclient.Get(ctx).NetworkingV1alpha1().Ingresses(testNamespace).List(ctx, metav1.ListOptions{}) + for _, i := range ing.Items { + fakeingressinformer.Get(ctx).Informer().GetIndexer().Add(&i) + } + + // reconcile again as now certificate is ready + ctl.Reconciler.Reconcile(ctx, KeyOrDie(route)) + + ci := getRouteIngressFromClient(ctx, t, route) + + domain := strings.Join([]string{route.Name, route.Namespace, defaultDomainSuffix}, ".") + hosts := []string{"test-route.test", "test-route.test.svc", pkgnet.GetServiceHostname("test-route", "test")} + + expectedSpec := v1alpha1.IngressSpec{ + HTTPOption: v1alpha1.HTTPOptionEnabled, + TLS: []v1alpha1.IngressTLS{{ + Hosts: hosts, + SecretName: "route--local", + SecretNamespace: testNamespace, + }, + }, + Rules: []v1alpha1.IngressRule{{ + Hosts: hosts, + Visibility: v1alpha1.IngressVisibilityClusterLocal, + HTTP: &v1alpha1.HTTPIngressRuleValue{ + Paths: []v1alpha1.HTTPIngressPath{{ + Splits: []v1alpha1.IngressBackendSplit{{ + IngressBackend: v1alpha1.IngressBackend{ + ServiceNamespace: testNamespace, + ServiceName: rev.Name, + ServicePort: intstr.FromInt(80), + }, + Percent: 100, + AppendHeaders: map[string]string{ + "Knative-Serving-Revision": "test-rev", + "Knative-Serving-Namespace": testNamespace, + }, + }}, + }}, + }, + }, { + Hosts: []string{ + domain, + }, + Visibility: v1alpha1.IngressVisibilityExternalIP, + HTTP: &v1alpha1.HTTPIngressRuleValue{ + Paths: []v1alpha1.HTTPIngressPath{{ + Splits: []v1alpha1.IngressBackendSplit{{ + IngressBackend: v1alpha1.IngressBackend{ + ServiceNamespace: testNamespace, + ServiceName: rev.Name, + ServicePort: intstr.FromInt(80), + }, + Percent: 100, + AppendHeaders: map[string]string{ + "Knative-Serving-Revision": "test-rev", + "Knative-Serving-Namespace": testNamespace, + }, + }}, + }}, + }, + }}, + } + if diff := cmp.Diff(expectedSpec, ci.Spec); diff != "" { + t.Error("Unexpected rule spec diff (-want +got):", diff) + } + + fakeingressinformer.Get(ctx).Informer().GetIndexer().Update(ci) + ctl.Reconciler.Reconcile(ctx, KeyOrDie(route)) + + // Look for the events. Events are delivered asynchronously so we need to use + // hooks here. Each hook tests for a specific event. + select { + case got := <-fakeRecorder.Events: + const want = `Normal Created Created placeholder service "test-route"` + if got != want { + t.Errorf("<-Events = %s wanted %s", got, want) + } + case <-time.After(3 * time.Second): + t.Error("Timed out waiting for expected events.") + } + select { + case got := <-fakeRecorder.Events: + const wantPrefix = `Normal Created Created Certificate test/route--local` + if !strings.HasPrefix(got, wantPrefix) { + t.Errorf("<-Events = %s wanted prefix %s", got, wantPrefix) + } + case <-time.After(3 * time.Second): + t.Error("Timed out waiting for expected events.") + } + select { + case got := <-fakeRecorder.Events: + const wantPrefix = `Normal Created Created Ingress "test-route"` + if !strings.HasPrefix(got, wantPrefix) { + t.Errorf("<-Events = %s wanted prefix %s", got, wantPrefix) + } + case <-time.After(3 * time.Second): + t.Error("Timed out waiting for expected events.") + } +} diff --git a/pkg/reconciler/route/table_test.go b/pkg/reconciler/route/table_test.go index d96b647aacd0..8bb0c23c183a 100644 --- a/pkg/reconciler/route/table_test.go +++ b/pkg/reconciler/route/table_test.go @@ -2526,7 +2526,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, }, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -2611,7 +2612,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef( Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")))}, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, Annotations: map[string]string{ netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, @@ -2631,7 +2633,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef( Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")))}, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, Annotations: map[string]string{ netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, @@ -2651,7 +2654,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef( Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")))}, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, Annotations: map[string]string{ netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, @@ -2741,7 +2745,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, }, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -2863,7 +2868,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, }, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, }, Spec: netv1alpha1.CertificateSpec{ @@ -3053,7 +3059,8 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef( Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")))}, Labels: map[string]string{ - serving.RouteLabelKey: "becomes-ready", + serving.RouteLabelKey: "becomes-ready", + netapi.CertificateTypeLabelKey: string(netcfg.CertificateExternalDomain), }, Annotations: map[string]string{ netapi.CertificateClassAnnotationKey: netcfg.CertManagerCertificateClassName, @@ -3175,7 +3182,7 @@ func TestReconcileEnableExternalDomainTLS(t *testing.T) { Object: Route("default", "becomes-local", WithConfigTarget("config"), WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration, - MarkTrafficAssigned, MarkIngressNotConfigured, WithRouteConditionsTLSNotEnabledForClusterLocalMessage, + MarkTrafficAssigned, MarkIngressNotConfigured, WithLocalDomain, WithAddress, WithInitRouteConditions, WithRouteLabel(map[string]string{netapi.VisibilityLabelKey: serving.VisibilityClusterLocal}), WithStatusTraffic( diff --git a/pkg/testing/v1/route.go b/pkg/testing/v1/route.go index 429f4f861cb5..9f2a60987414 100644 --- a/pkg/testing/v1/route.go +++ b/pkg/testing/v1/route.go @@ -180,14 +180,6 @@ func WithRouteConditionsExternalDomainTLSDisabled(rt *v1.Route) { rt.Status.MarkTLSNotEnabled(v1.ExternalDomainTLSNotEnabledMessage) } -// WithRouteConditionsTLSNotEnabledForClusterLocalMessage calls -// MarkTLSNotEnabled with TLSNotEnabledForClusterLocalMessage after initialized -// the Service's conditions. -func WithRouteConditionsTLSNotEnabledForClusterLocalMessage(rt *v1.Route) { - rt.Status.InitializeConditions() - rt.Status.MarkTLSNotEnabled(v1.TLSNotEnabledForClusterLocalMessage) -} - // WithRouteConditionsHTTPDowngrade calls MarkHTTPDowngrade after initialized the Service's conditions. func WithRouteConditionsHTTPDowngrade(rt *v1.Route) { rt.Status.InitializeConditions() diff --git a/test/config/tls/cert-secret.yaml b/test/config/tls/cert-secret.yaml deleted file mode 100644 index 7800c91f4728..000000000000 --- a/test/config/tls/cert-secret.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2022 The Knative Authors -# -# Licensed 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. - -apiVersion: v1 -kind: Secret -metadata: - name: ca-cert - namespace: serving-tests -data: - ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURSVENDQWkyZ0F3SUJBZ0lVS0lNbmZTUUpTRHgyVThWYU1UdjR2cnYwcnRrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd01qRWFNQmdHQTFVRUNnd1JTMjVoZEdsMlpTQkRiMjF0ZFc1cGRIa3hGREFTQmdOVkJBTU1DMlY0WVcxdwpiR1V1WTI5dE1CNFhEVEl6TURrd01UQXpNRGd4TkZvWERUTXpNRGd5T1RBek1EZ3hORm93TWpFYU1CZ0dBMVVFCkNnd1JTMjVoZEdsMlpTQkRiMjF0ZFc1cGRIa3hGREFTQmdOVkJBTU1DMlY0WVcxd2JHVXVZMjl0TUlJQklqQU4KQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBak83alk0bysvSTRZYnQ3ZjlwWWt3ZkJnSG4yQQpiSThvWWRxczg1UGlxaThsWm1lOXM4ZmhBcnJjbGJPcEZPTEwrTUJHWEVCSkxEMUNTN2VaWS9HQ0E1VkZYdkMzCkpjR0VjNmVZY2l0ZnNtdlh3bWk1bm1oVFovajdZSkNRNGZzS29KNkQ3Q1YzS2ZBVEhUTmsxUXVFbWpmUmRBNjEKS01tVkZSMVlpM0NWT1FNSzFhSHhRaEJ3TFRMbERQbVZkaHYvdDI0aDZFL0cwZHNTSGhodUI2aGgzOEoybFBPYwpyTjBOVFhFQ25JZ1hOZHNlMzVEVi9yQlZYUUhlQlRjQ1dqSElBdFQybUxPWWRxZ0dBdE1rYTZDRCtpaXVFemY4CjYyZGdOUjRoS1ErVTR4Q3FBOXNoRExqWVdKS2FMRFFPMFRKOXF5aTR4RTBZSVZETUFHMzhqakU3d1FJREFRQUIKbzFNd1VUQWRCZ05WSFE0RUZnUVVtTXVLSjEyaDBBQmRmT0hnRDhWU2ROVUpjQm93SHdZRFZSMGpCQmd3Rm9BVQptTXVLSjEyaDBBQmRmT0hnRDhWU2ROVUpjQm93RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCCkFRc0ZBQU9DQVFFQWlNRnByYVVqOWRrMDBpUlFRUi9vV1FHSks0c3VNWnJRT3hUNjMycThac1RmaGRoRWVwNUQKbk9wNCs0UnBJTWpPQVN0SnNIbWRNMXBYUkJiUFp4WnhQSjd4Z0pVQmdycE1JS3k2SmZoZU1aaGVnR1AvUmFFbwpXR3pPTzlSZER0Q3M4azRVUzVHeGJSUUVXWVV1dDlGUGhGL1RlaFdXS1RvOWFVMTUwakVyclNseFYwRkUxVzBtCitPUFBDOUVtV24vUHhVWXNKSitHalAwSENDTENMUU1tR2VZN0Y1TkVVWUtobFo4UnBISFdBaE54RXM1WSszUU4KakNVY1JvQ2RRaTBrS3NZYytheUp6Yi9hRVJQdTJlQStNVVpqM1pxMTlqenY4RzRseVlETmpleTZSdlBxV21XZQpHcmZpN2JpS21oSlNLWU5US1pRaHVWeTdEcUVkNUl5VWZnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= ---- -apiVersion: v1 -kind: Secret -metadata: - name: server-certs - namespace: knative-serving -data: - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQ3Bmd09tY21qMGZZNTQKSzg4SkxSWVh0MGt3Z3NNN2VTVEZGNWg5ZG5QYmk5KzIrRDRQYjgwN3F5cXZ3aEIzYTM0V215ZVpvOTAxS3BxeQp6TzNaR2VmVVd2dk1KVkVSUzhaRnVWVlJObTBDWWpsazhBUUtZSGloQVBXdFF0c0diVGhJVVM0cVZhc1M4U0tGCnFieGkwTG1WVDVlSTVseGNlL1pHaEtWS0psZmMzQ2NvSUovYW5YZXRCRHA4d1dUdEFDaUt6NVhpcnZuKzBnTjUKeVVnQmZIWHB2VE5qYkwyVFU4QXFhaThkdTNvS0dHaGo2Vkh1akVhTEZmVmdiUXNNMXUyMHkwcHpzcjIxZXlFZQovQXVMSGZXS0hsNmdieU9OUk1oRWJjTEpiRk83eXoxVkJnVUZzaTY2bDArRTRLeUJ3YW4zSTRsSnhUWDlheTdzCjVjSWV3dllSQWdNQkFBRUNnZ0VBRk5tNUd6cXJ5cGUvZE96K3RRUkUyWG9FbU9FZEh5M1I1aVQyNzBncERITHkKZkZHaFlNakNHSHIvNzBzU04zNlRyamc3OE55Rms4cS82UXJvbjNKZVlITW8wbjRzc05IL2ZBaEdoSTBMYitXaApaZGRZeCtZQ01Od3JYTHNDc3BHeEFNUnJ4MjF1ZEJBUnpSdUtQeVdxRHNJUnhCZUNlU1lPcjdJVm16K0lRM1RsCjlOajAyMUpCN0FNQmhpUlVQR2NJdTNOdk9UVGlNQnNBMFBNRCtsY1JJYXdOaW9XZWs3a1kxYXdWMlZycElwbDMKRkJvbThFK2pCSWxSbHVqcHBsMGlsKzR0Uy9xSHIwanZBQ2tEcUlaVTJ3QUlaRU5UNmFvRTZzWWxBdm9xN3R6WgpMaWVNNjh4cGVueEhpRXBZUDYwaFRlVUVrTEZ0V0ltNkg5Z0lxNFpJbVFLQmdRRHRSM1NJL3ZBQjU1V1BVMjluClF5R0VkZ3NoK05jNThXeWp1QVlLSUFBVStpUTJOSEIwK21KWGhxWmdab0RmNElZcVRXMjFZdXdQTVNQM1lrLzAKam1zcGpqQU1VYUd2U2JmZW1yQjc2KzJpeFhKUWduYzB6S0M4SEF0TXAyWHRITnp1N0VWODltRUdlRjJWYWluUwptZStLcVNMcCt4Rk9RekNaNTU5V2czSlRXd0tCZ1FDMjNudmxjTEh0KzZOMVZYWis4VFpQSitEQmYraUpGaStOCnZQRUFYTGlxRXM2TkV2RVNRWENJbjR0TS9LNnR6VVo4NlNtblEzWnpDdzdTR2ZwRWoxcEZhenZnNWc2bldvaFEKeFhRbDlaK0xrTUlNVkZjUHp2N29vYmJld2hjN3VHZUtsUzh4QXVRQ29qR0lyVnVTeHJCKzJmYWpFRVphWERRRgpEK0E0VG5DMEF3S0JnREg1YWI3c3lzbnI1NkVQTnBodUdDSVk1cEZhSXFKdWlhNFZ0UmZ1MDNXWGxZYmhGb3BjCnJkZTNZVlZPWVhoRmhweGtRR1lDbHF0QWMwM0lKQVRpOWdrRGpIVWJ3RXoxM3NjQWw0a1NFTFBHbUNRQ3htNlYKWm9rWDh2MUxDSG1mS1owY29Db0lqampab3dRMEh1amZTUWRRUmkwM2x0Wi82ajJQL2d3dmxTV2ZBb0dBUmVYQQpnYkNUc2gxUEcxZ3hWTkFxTEJOWUMxNHlxY043SmJna2Zqanl0TDgvaUJqSFlHejI4S0hiQXZCS2JGQmNNdjZFClYxU24vODBvT3hLRzRKSFN0Vm9KcHlzSVN0dUY0eW0xL1pSbkNWZXBkRUVBamlCV3V1Q2xIM3djbVUwZzlvMlQKWGk1MnJMTUdML0hqWUxUbFhYYyttSkZ0OHJmdmdrZlJNblI1QzdVQ2dZQnlmYXBaUmlrMkFuVHJGY1RldERScwpDSGFnbXkxZlowL0VuSjNxZlAwNWprTUo0bnFkM1I1WWFvRjNZNklKNFZyUXM0Wk9TbWgxaG9kTDhzcFBCVDFUCno4VUhMOFRhR1lxalJrL2RkZ0JiWjRjTU5ldmZoSTVsWU85TWIvR3pNSnNsSi9XN1RlSkZ3bXhFNzZwWXFsVXUKQ2k3M1ppekhWMTZ5cHpKZDVneUhZdz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURURENDQWpTZ0F3SUJBZ0lVWHh3K2hrNmE1Y1hWNW9QelRZR29pVzNQcFNFd0RRWUpLb1pJaHZjTkFRRUwKQlFBd01qRWFNQmdHQTFVRUNnd1JTMjVoZEdsMlpTQkRiMjF0ZFc1cGRIa3hGREFTQmdOVkJBTU1DMlY0WVcxdwpiR1V1WTI5dE1CNFhEVEl6TURrd01UQXpNRGd4TkZvWERUTXpNRGd5T1RBek1EZ3hORm93TWpFVU1CSUdBMVVFCkF3d0xaWGhoYlhCc1pTNWpiMjB4R2pBWUJnTlZCQW9NRVV0dVlYUnBkbVVnUTI5dGJYVnVhWFI1TUlJQklqQU4KQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcVg4RHBuSm85SDJPZUN2UENTMFdGN2RKTUlMRApPM2treFJlWWZYWnoyNHZmdHZnK0QyL05PNnNxcjhJUWQydCtGcHNubWFQZE5TcWFzc3p0MlJubjFGcjd6Q1ZSCkVVdkdSYmxWVVRadEFtSTVaUEFFQ21CNG9RRDFyVUxiQm0wNFNGRXVLbFdyRXZFaWhhbThZdEM1bFUrWGlPWmMKWEh2MlJvU2xTaVpYM053bktDQ2YycDEzclFRNmZNRms3UUFvaXMrVjRxNzUvdElEZWNsSUFYeDE2YjB6WTJ5OQprMVBBS21vdkhidDZDaGhvWStsUjdveEdpeFgxWUcwTEROYnR0TXRLYzdLOXRYc2hIdndMaXgzMWloNWVvRzhqCmpVVElSRzNDeVd4VHU4czlWUVlGQmJJdXVwZFBoT0NzZ2NHcDl5T0pTY1UxL1dzdTdPWENIc0wyRVFJREFRQUIKbzFvd1dEQVdCZ05WSFJFRUR6QU5nZ3RyYm1GMGFYWmxMbVJsZGpBZEJnTlZIUTRFRmdRVWQrZXVqbzV3cTVIWgpjTEhTUU1VSURKOUJ2akl3SHdZRFZSMGpCQmd3Rm9BVW1NdUtKMTJoMEFCZGZPSGdEOFZTZE5VSmNCb3dEUVlKCktvWklodmNOQVFFTEJRQURnZ0VCQUJqK2JxNFE5bWYrRzZudVREdUpGUnlRUmNTcEpYa1p0VWc0R0pqMFFrVHAKN2J6MWFVTWRrdHJuY1lqZVk0QkpMdm9Ia3V1QVFlMUZlZmVyWmtnb3hVZkorU2syMUdhMEVGTEEvVzdQeElNZAo5MmVyNHQ3M0xGa1hVV1c2N3JoZ2Q5b2thcVA5bjBZSms2VlF2M0hoTVl3Wk1iSi82dXQ0c1kwN0lxYUpRUysrCllhK3RVK1dVOUhSQjAvRXBTRXlHSVB0ZWFPNi9XTjJzTVVCSVBzOWR2dW12SHROYVpMUnYxdWRHMnN3TTdPZkgKeVJEVFJ0Y05XSkxlc1RhY01BYlZUa1dGUHNJKzR3bXFJMW5FRVZBQ2loVi9sZUlHQmxYNGdKK0FJN0d5MEdkLwpnaXlranlmZmFkb1JRSjhyK0RuOEMzeDVTNUVDSWhtK2lSei9oZkhWalpFPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== diff --git a/test/config/tls/generate.sh b/test/config/tls/generate.sh deleted file mode 100755 index 1196211dd9dc..000000000000 --- a/test/config/tls/generate.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2022 The Knative Authors -# -# Licensed 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. - -# This script generates test/config/tls/cert-secret.yaml. - -san="knative.dev" - -# Create CA key and cert -openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -subj '/O=Knative Community/CN=example.com' -keyout rootCAKey.pem -out rootCACert.pem - -# Create server key -openssl req -out tls.csr -newkey rsa:2048 -nodes -keyout tls.key -subj "/CN=example.com/O=Knative Community" -addext "subjectAltName = DNS:$san" - -# Create server certs -openssl x509 -req -extfile <(printf "subjectAltName=DNS:$san") -days 3650 -in tls.csr -CA rootCACert.pem -CAkey rootCAKey.pem -CAcreateserial -out tls.crt - -CA_CERT=$(cat rootCACert.pem | base64 | tr -d '\n') -TLS_KEY=$(cat tls.key | base64 | tr -d '\n') -TLS_CERT=$(cat tls.crt | base64 | tr -d '\n') - -cat < cert-secret.yaml -# Copyright 2022 The Knative Authors -# -# Licensed 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. - -apiVersion: v1 -kind: Secret -metadata: - name: ca-cert - namespace: serving-tests -data: - ca.crt: ${CA_CERT} ---- -apiVersion: v1 -kind: Secret -metadata: - name: server-certs - namespace: knative-serving -data: - tls.key: ${TLS_KEY} - tls.crt: ${TLS_CERT} -EOF - -# Clean up -rm -f rootCACert.pem rootCAKey.pem tls.key tls.crt tls.csr rootCACert.srl diff --git a/test/e2e-common.sh b/test/e2e-common.sh index 1946824d6635..4461a61d357c 100644 --- a/test/e2e-common.sh +++ b/test/e2e-common.sh @@ -319,10 +319,6 @@ function install() { YTT_FILES+=("${REPO_ROOT_DIR}/test/config/resource-quota/resource-quota.yaml") fi - if (( ENABLE_TLS )); then - YTT_FILES+=("${REPO_ROOT_DIR}/test/config/tls/cert-secret.yaml") - fi - local ytt_result=$(mktemp) local ytt_post_install_result=$(mktemp) local ytt_flags="" @@ -375,16 +371,14 @@ function install() { fi if (( ENABLE_TLS )); then - echo "Patch to config-network to enable internal encryption" - toggle_feature system-internal-tls Enabled config-network + echo "Patch config-network to enable encryption features" + toggle_feature system-internal-tls enabled config-network + + # This is currently only supported by kourier if [[ "$INGRESS_CLASS" == "kourier.ingress.networking.knative.dev" ]]; then - echo "Point Kourier local gateway to custom server certificates" - toggle_feature cluster-cert-secret server-certs config-kourier - # This needs to match the name of Secret in test/config/tls/cert-secret.yaml - export CA_CERT=ca-cert - # This needs to match $san from test/config/tls/generate.sh - export SERVER_NAME=knative.dev + toggle_feature cluster-local-domain-tls enabled config-network fi + echo "Restart activator to mount the certificates" kubectl delete pod -n ${SYSTEM_NAMESPACE} -l app=activator kubectl wait --timeout=60s --for=condition=Available deployment -n ${SYSTEM_NAMESPACE} activator diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh index eff5a2e51faf..56b9c661eec0 100755 --- a/test/e2e-tests.sh +++ b/test/e2e-tests.sh @@ -63,23 +63,26 @@ if (( SHORT )); then GO_TEST_FLAGS+=" -short" fi +#go_test_e2e -timeout=30m \ +# ${GO_TEST_FLAGS} \ +# ./test/conformance/api/... \ +# ./test/conformance/runtime/... \ +# ./test/e2e \ +# ${E2E_TEST_FLAGS} || failed=1 +# +#toggle_feature tag-header-based-routing Enabled +#go_test_e2e -timeout=2m ./test/e2e/tagheader ${E2E_TEST_FLAGS} || failed=1 +#toggle_feature tag-header-based-routing Disabled +# +#toggle_feature allow-zero-initial-scale true config-autoscaler || fail_test +#go_test_e2e -timeout=2m ./test/e2e/initscale ${E2E_TEST_FLAGS} || failed=1 +#toggle_feature allow-zero-initial-scale false config-autoscaler || fail_test +# +#go_test_e2e -timeout=2m ./test/e2e/domainmapping ${E2E_TEST_FLAGS} || failed=1 -go_test_e2e -timeout=30m \ - ${GO_TEST_FLAGS} \ - ./test/conformance/api/... \ - ./test/conformance/runtime/... \ - ./test/e2e \ - ${E2E_TEST_FLAGS} || failed=1 - -toggle_feature tag-header-based-routing Enabled -go_test_e2e -timeout=2m ./test/e2e/tagheader ${E2E_TEST_FLAGS} || failed=1 -toggle_feature tag-header-based-routing Disabled - -toggle_feature allow-zero-initial-scale true config-autoscaler || fail_test -go_test_e2e -timeout=2m ./test/e2e/initscale ${E2E_TEST_FLAGS} || failed=1 -toggle_feature allow-zero-initial-scale false config-autoscaler || fail_test - -go_test_e2e -timeout=2m ./test/e2e/domainmapping ${E2E_TEST_FLAGS} || failed=1 +toggle_feature cluster-local-domain-tls enabled config-network || fail_test +go_test_e2e -timeout=2m ./test/e2e/clusterlocaldomaintls ${E2E_TEST_FLAGS} || failed=1 +toggle_feature cluster-local-domain-tls disabled config-network || fail_test toggle_feature system-internal-tls enabled config-network || fail_test toggle_feature "logging.enable-request-log" true config-observability || fail_test diff --git a/test/e2e/clusterlocaldomaintls/cluster_local_domain_tls_test.go b/test/e2e/clusterlocaldomaintls/cluster_local_domain_tls_test.go new file mode 100644 index 000000000000..a8e97a8f99de --- /dev/null +++ b/test/e2e/clusterlocaldomaintls/cluster_local_domain_tls_test.go @@ -0,0 +1,155 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2023 The Knative Authors + +Licensed 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 clusterlocaldomaintls + +import ( + "net/url" + "strings" + "testing" + + netapi "knative.dev/networking/pkg/apis/networking" + "knative.dev/serving/pkg/apis/serving" + rtesting "knative.dev/serving/pkg/testing/v1" + "knative.dev/serving/test" + "knative.dev/serving/test/e2e" + v1test "knative.dev/serving/test/v1" +) + +var dnsVariants = []struct { + name string + suffix string +}{ + {"fqdn", ""}, + {"short", ".cluster.local"}, + {"shortest", ".svc.cluster.local"}, +} + +func TestClusterLocalDomainTLSClusterLocalVisibility(t *testing.T) { + if !test.ServingFlags.EnableAlphaFeatures { + t.Skip("Alpha features not enabled") + } + + if !(strings.Contains(test.ServingFlags.IngressClass, "kourier")) { + t.Skip("Skip this test for non-kourier ingress.") + } + + t.Parallel() + clients := test.Setup(t) + names := test.ResourceNames{ + Service: test.ObjectNameForTest(t), + Image: test.HelloWorld, + } + + test.EnsureTearDown(t, clients, &names) + + withInternalVisibility := rtesting.WithServiceLabel(netapi.VisibilityLabelKey, serving.VisibilityClusterLocal) + t.Log("Creating a new service with cluster-local visibility") + resources, err := v1test.CreateServiceReady(t, clients, &names, withInternalVisibility) + if err != nil { + t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err) + } + + // After the service is created, we need to wait for the CA to be populated, + // then use that secret in the ProxyImage to trust the cluster-local https connection + secret, err := e2e.GetCASecret(clients) + if err != nil { + t.Fatal(err.Error()) + } + + svcUrl := resources.Route.Status.URL.URL() + if svcUrl.Scheme != "https" { + t.Fatalf("URL scheme of service %v was not https", names.Service) + } + + // Check access via https on all cluster-local-domains + for _, dns := range dnsVariants { + helloworldURL := &url.URL{ + Scheme: svcUrl.Scheme, + Host: strings.TrimSuffix(svcUrl.Host, dns.suffix), + Path: svcUrl.Path, + } + t.Run(dns.name, func(t *testing.T) { + t.Parallel() + e2e.TestProxyToHelloworld(t, clients, helloworldURL, false, false, secret) + }) + } +} + +func TestClusterLocalDomainTLSClusterExternalVisibility(t *testing.T) { + if !test.ServingFlags.EnableAlphaFeatures { + t.Skip("Alpha features not enabled") + } + + if !(strings.Contains(test.ServingFlags.IngressClass, "kourier")) { + t.Skip("Skip this test for non-kourier ingress.") + } + + t.Parallel() + clients := test.Setup(t) + names := test.ResourceNames{ + Service: test.ObjectNameForTest(t), + Image: test.HelloWorld, + } + + test.EnsureTearDown(t, clients, &names) + + t.Log("Creating a new service with external visibility") + resources, err := v1test.CreateServiceReady(t, clients, &names) + if err != nil { + t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err) + } + + // After the service is created, we need to wait for the CA to be populated, + // then use that secret in the ProxyImage to trust the cluster-local https connection + secret, err := e2e.GetCASecret(clients) + if err != nil { + t.Fatal(err.Error()) + } + + externalURL := resources.Route.Status.URL.URL() + internalURL := resources.Route.Status.Address.URL + + if internalURL.Scheme != "https" { + t.Fatalf("Internal URL scheme of service %v was not https", names.Service) + } + + if externalURL.Scheme != "http" { + t.Fatalf("External URL scheme of service %v was not http", names.Service) + } + + // Check normal access on external domain + t.Run("external-access", func(t *testing.T) { + t.Parallel() + e2e.TestProxyToHelloworld(t, clients, externalURL, false, true, secret) + }) + + // Check access via https on all cluster-local-domains + for _, dns := range dnsVariants { + helloworldURL := &url.URL{ + Scheme: internalURL.Scheme, + Host: strings.TrimSuffix(internalURL.Host, dns.suffix), + Path: internalURL.Path, + } + t.Run(dns.name, func(t *testing.T) { + t.Parallel() + e2e.TestProxyToHelloworld(t, clients, helloworldURL, false, false, secret) + }) + } +} diff --git a/test/e2e/encryption.go b/test/e2e/encryption.go new file mode 100644 index 000000000000..886d3a697ed5 --- /dev/null +++ b/test/e2e/encryption.go @@ -0,0 +1,96 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2023 The Knative Authors + +Licensed 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 e2e + +import ( + "context" + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "knative.dev/networking/pkg/certificates" + netcfg "knative.dev/networking/pkg/config" + "knative.dev/pkg/system" + "knative.dev/serving/test" +) + +// this needs to match the ClusterIssuer in third_parth/cert-manager-latest/net-certmanager.yaml +const ( + certManagerCASecret = "knative-selfsigned-ca" + certManagerNamespace = "cert-manager" +) + +// GetCASecret returns the Secret that is used by the CA to issue KnativeCertificates. +// Note: this process can be omitted when https://github.com/knative/serving/issues/14196 is implemented, +// then httpproxy.go should get the CA to trust from the Addressable. +func GetCASecret(clients *test.Clients) (*corev1.Secret, error) { + cm, err := clients.KubeClient.CoreV1().ConfigMaps(system.Namespace()). + Get(context.Background(), netcfg.ConfigMapName, metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get ConfigMap config-network: %w", err) + } + + // CA is only needed when encryption on the cluster is enabled + if !strings.EqualFold(cm.Data[netcfg.ClusterLocalDomainTLSKey], string(netcfg.EncryptionEnabled)) { + return nil, nil + } + + class := getCertificateClass(cm) + switch class { + case netcfg.CertManagerCertificateClassName: + return getCertManagerCA(clients) + default: + return nil, fmt.Errorf("invalid %s: %s", netcfg.DefaultCertificateClassKey, class) + } +} + +// getCertificateClass returns the currently configured certificate-class. +func getCertificateClass(cm *corev1.ConfigMap) string { + // if not specified, we fall back to our default, which is cert-manager + if class, ok := cm.Data[netcfg.DefaultCertificateClassKey]; ok { + return class + } + + return netcfg.CertManagerCertificateClassName +} + +func getCertManagerCA(clients *test.Clients) (*corev1.Secret, error) { + var secret *corev1.Secret + err := wait.PollImmediate(test.PollInterval, test.PollTimeout, func() (bool, error) { + caSecret, err := clients.KubeClient.CoreV1().Secrets(certManagerNamespace).Get(context.Background(), certManagerCASecret, metav1.GetOptions{}) + if err != nil { + return false, err + } + // CA not yet populated + if len(caSecret.Data[certificates.CertName]) == 0 { + return false, nil + } + + secret = caSecret + return true, nil + }) + if err != nil { + return nil, fmt.Errorf("error while waiting for cert-manager self-signed CA to be popluated: %w", err) + } + + return secret, nil +} diff --git a/test/e2e/httpproxy.go b/test/e2e/httpproxy.go new file mode 100644 index 000000000000..55c3916844a8 --- /dev/null +++ b/test/e2e/httpproxy.go @@ -0,0 +1,128 @@ +package e2e + +import ( + "context" + "net" + "net/http" + "net/url" + "strconv" + "testing" + + corev1 "k8s.io/api/core/v1" + "knative.dev/networking/pkg/certificates" + pkgTest "knative.dev/pkg/test" + "knative.dev/pkg/test/ingress" + "knative.dev/pkg/test/spoof" + rtesting "knative.dev/serving/pkg/testing/v1" + "knative.dev/serving/test" + v1test "knative.dev/serving/test/v1" +) + +const ( + targetHostEnv = "TARGET_HOST" + gatewayHostEnv = "GATEWAY_HOST" + helloworldResponse = "Hello World! How about some tasty noodles?" +) + +func TestProxyToHelloworld(t *testing.T, clients *test.Clients, helloworldURL *url.URL, inject bool, accessibleExternal bool, caSecret *corev1.Secret) { + // Create envVars to be used in httpproxy app. + envVars := []corev1.EnvVar{{ + Name: targetHostEnv, + Value: helloworldURL.Hostname(), + }} + + // When resolvable domain is not set for external access test, use gateway for the endpoint as services like sslip.io may be flaky. + // ref: https://github.com/knative/serving/issues/5389 + if !test.ServingFlags.ResolvableDomain && accessibleExternal { + gatewayTarget, mapper, err := ingress.GetIngressEndpoint(context.Background(), clients.KubeClient, pkgTest.Flags.IngressEndpoint) + if err != nil { + t.Fatal("Failed to get gateway IP:", err) + } + envVars = append(envVars, corev1.EnvVar{ + Name: gatewayHostEnv, + Value: net.JoinHostPort(gatewayTarget, mapper("80")), + }) + } + + // HTTPProxy needs to trust the CA that signed the cluster-local-domain certificates of the target service + if caSecret != nil && !accessibleExternal { + envVars = append(envVars, []corev1.EnvVar{{Name: "CA_CERT", Value: string(caSecret.Data[certificates.CertName])}}...) + } + + // Set up httpproxy app. + t.Log("Creating a Service for the httpproxy test app.") + names := test.ResourceNames{ + Service: test.ObjectNameForTest(t), + Image: test.HTTPProxy, + } + + test.EnsureTearDown(t, clients, &names) + + serviceOptions := []rtesting.ServiceOption{ + rtesting.WithEnv(envVars...), + rtesting.WithConfigAnnotations(map[string]string{ + "sidecar.istio.io/inject": strconv.FormatBool(inject), + }), + } + + resources, err := v1test.CreateServiceReady(t, clients, &names, serviceOptions...) + + if err != nil { + t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err) + } + + url := resources.Route.Status.URL.URL() + if _, err = pkgTest.CheckEndpointState( + context.Background(), + clients.KubeClient, + t.Logf, + url, + spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(helloworldResponse)), + "HTTPProxy", + test.ServingFlags.ResolvableDomain, + test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS), + ); err != nil { + t.Fatal("Failed to start endpoint of httpproxy:", err) + } + t.Log("httpproxy is ready.") + + // When we're testing with resolvable domains, we fail earlier trying + // to resolve the cluster local domain. + if !accessibleExternal && test.ServingFlags.ResolvableDomain { + return + } + + // if the service is not externally available, + // the gateway does not expose a https port, so we need to call the http port + if !accessibleExternal && helloworldURL.Scheme == "https" { + helloworldURL.Scheme = "http" + } + + // As a final check (since we know they are both up), check that if we can + // (or cannot) access the helloworld app externally. + response, err := sendRequest(t, clients, test.ServingFlags.ResolvableDomain, helloworldURL) + if err != nil { + t.Fatal("Unexpected error when sending request to helloworld:", err) + } + expectedStatus := http.StatusNotFound + if accessibleExternal { + expectedStatus = http.StatusOK + } + if got, want := response.StatusCode, expectedStatus; got != want { + t.Errorf("helloworld response StatusCode = %v, want %v", got, want) + } +} + +func sendRequest(t *testing.T, clients *test.Clients, resolvableDomain bool, url *url.URL) (*spoof.Response, error) { + t.Logf("The domain of request is %s.", url.Hostname()) + client, err := pkgTest.NewSpoofingClient(context.Background(), clients.KubeClient, t.Logf, url.Hostname(), resolvableDomain, test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS)) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, url.String(), nil) + if err != nil { + return nil, err + } + return client.Do(req) +} diff --git a/test/e2e/service_to_service_test.go b/test/e2e/service_to_service_test.go index 5787bcac4c71..52a25babdaf3 100644 --- a/test/e2e/service_to_service_test.go +++ b/test/e2e/service_to_service_test.go @@ -20,22 +20,13 @@ limitations under the License. package e2e import ( - "context" - "net" - "net/http" "net/url" - "os" "strconv" "strings" "testing" - corev1 "k8s.io/api/core/v1" netapi "knative.dev/networking/pkg/apis/networking" - ptr "knative.dev/pkg/ptr" - pkgTest "knative.dev/pkg/test" - ingress "knative.dev/pkg/test/ingress" "knative.dev/pkg/test/logstream" - "knative.dev/pkg/test/spoof" "knative.dev/serving/pkg/apis/autoscaling" "knative.dev/serving/pkg/apis/serving" rtesting "knative.dev/serving/pkg/testing/v1" @@ -43,14 +34,6 @@ import ( v1test "knative.dev/serving/test/v1" ) -const ( - targetHostEnv = "TARGET_HOST" - gatewayHostEnv = "GATEWAY_HOST" - helloworldResponse = "Hello World! How about some tasty noodles?" - caCertDirectory = "/var/lib/knative/ca" - caCertPath = caCertDirectory + "/ca.crt" -) - // testCases for table-driven testing. var testCases = []struct { // name of the test case, which will be inserted in names of routes, configurations, etc. @@ -79,117 +62,6 @@ var testInjection = []struct { {"both-enabled", true, true}, } -func sendRequest(t *testing.T, clients *test.Clients, resolvableDomain bool, url *url.URL) (*spoof.Response, error) { - t.Logf("The domain of request is %s.", url.Hostname()) - client, err := pkgTest.NewSpoofingClient(context.Background(), clients.KubeClient, t.Logf, url.Hostname(), resolvableDomain, test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS)) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, url.String(), nil) - if err != nil { - return nil, err - } - return client.Do(req) -} - -func testProxyToHelloworld(t *testing.T, clients *test.Clients, helloworldURL *url.URL, inject bool, accessibleExternal bool) { - // Create envVars to be used in httpproxy app. - envVars := []corev1.EnvVar{{ - Name: targetHostEnv, - Value: helloworldURL.Hostname(), - }} - - // When resolvable domain is not set for external access test, use gateway for the endpoint as services like sslip.io may be flaky. - // ref: https://github.com/knative/serving/issues/5389 - if !test.ServingFlags.ResolvableDomain && accessibleExternal { - gatewayTarget, mapper, err := ingress.GetIngressEndpoint(context.Background(), clients.KubeClient, pkgTest.Flags.IngressEndpoint) - if err != nil { - t.Fatal("Failed to get gateway IP:", err) - } - envVars = append(envVars, corev1.EnvVar{ - Name: gatewayHostEnv, - Value: net.JoinHostPort(gatewayTarget, mapper("80")), - }) - } - - caSecretName := os.Getenv("CA_CERT") - - // External services use different TLS certificates than cluster-local services. - // Not passing CA_CERT will make the httpproxy use plain http to connect to the - // target service. - if caSecretName != "" && !accessibleExternal { - envVars = append(envVars, []corev1.EnvVar{{Name: "CA_CERT", Value: caCertPath}, - {Name: "SERVER_NAME", Value: os.Getenv("SERVER_NAME")}}...) - } - - // Set up httpproxy app. - t.Log("Creating a Service for the httpproxy test app.") - names := test.ResourceNames{ - Service: test.ObjectNameForTest(t), - Image: test.HTTPProxy, - } - - test.EnsureTearDown(t, clients, &names) - - serviceOptions := []rtesting.ServiceOption{ - rtesting.WithEnv(envVars...), - rtesting.WithConfigAnnotations(map[string]string{ - "sidecar.istio.io/inject": strconv.FormatBool(inject), - }), - } - - if caSecretName != "" && !accessibleExternal { - serviceOptions = append(serviceOptions, rtesting.WithVolume("ca-certs", caCertDirectory, corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: caSecretName, - Optional: ptr.Bool(false), - }}), - ) - } - - resources, err := v1test.CreateServiceReady(t, clients, &names, serviceOptions...) - - if err != nil { - t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err) - } - - url := resources.Route.Status.URL.URL() - if _, err = pkgTest.CheckEndpointState( - context.Background(), - clients.KubeClient, - t.Logf, - url, - spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(helloworldResponse)), - "HTTPProxy", - test.ServingFlags.ResolvableDomain, - test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS), - ); err != nil { - t.Fatal("Failed to start endpoint of httpproxy:", err) - } - t.Log("httpproxy is ready.") - - // When we're testing with resolvable domains, we fail earlier trying - // to resolve the cluster local domain. - if !accessibleExternal && test.ServingFlags.ResolvableDomain { - return - } - - // As a final check (since we know they are both up), check that if we can - // (or cannot) access the helloworld app externally. - response, err := sendRequest(t, clients, test.ServingFlags.ResolvableDomain, helloworldURL) - if err != nil { - t.Fatal("Unexpected error when sending request to helloworld:", err) - } - expectedStatus := http.StatusNotFound - if accessibleExternal { - expectedStatus = http.StatusOK - } - if got, want := response.StatusCode, expectedStatus; got != want { - t.Errorf("helloworld response StatusCode = %v, want %v", got, want) - } -} - // In this test, we set up two apps: helloworld and httpproxy. // helloworld is a simple app that displays a plaintext string. // httpproxy is a proxy that redirects request to internal service of helloworld app @@ -229,6 +101,13 @@ func TestServiceToServiceCall(t *testing.T) { } t.Logf("helloworld internal domain is %s.", resources.Route.Status.URL.Host) + // if cluster-local-domain-tls is enabled, this will return the CA used to sign the certificates. + // TestProxyToHelloworld will use this CA to verify the https connection + secret, err := GetCASecret(clients) + if err != nil { + t.Fatal(err.Error()) + } + // helloworld app and its route are ready. Running the test cases now. for _, tc := range testCases { helloworldURL := &url.URL{ @@ -243,7 +122,7 @@ func TestServiceToServiceCall(t *testing.T) { cancel := logstream.Start(t) defer cancel() } - testProxyToHelloworld(t, clients, helloworldURL, true /*inject*/, false /*accessible externally*/) + TestProxyToHelloworld(t, clients, helloworldURL, true, false, secret) }) } } @@ -279,8 +158,15 @@ func testSvcToSvcCallViaActivator(t *testing.T, clients *test.Clients, injectA b t.Fatal("Never got Activator endpoints in the service:", err) } + // if cluster-local-domain-tls is enabled, this will return the CA used to sign the certificates. + // TestProxyToHelloworld will use this CA to verify the https connection + secret, err := GetCASecret(clients) + if err != nil { + t.Fatal(err.Error()) + } + // Send request to helloworld app via httpproxy service - testProxyToHelloworld(t, clients, resources.Route.Status.URL.URL(), injectA, false /*accessible externally*/) + TestProxyToHelloworld(t, clients, resources.Route.Status.URL.URL(), injectA, false, secret) } // Same test as TestServiceToServiceCall but before sending requests @@ -340,6 +226,13 @@ func TestCallToPublicService(t *testing.T) { {"external_address", resources.Route.Status.URL.URL(), true}, } + // if cluster-local-domain-tls is enabled, this will return the CA used to sign the certificates. + // TestProxyToHelloworld will use this CA to verify the https connection + secret, err := GetCASecret(clients) + if err != nil { + t.Fatal(err.Error()) + } + for _, tc := range gatewayTestCases { tc := tc t.Run(tc.name, func(t *testing.T) { @@ -348,7 +241,7 @@ func TestCallToPublicService(t *testing.T) { cancel := logstream.Start(t) defer cancel() } - testProxyToHelloworld(t, clients, tc.url, false /*inject*/, tc.accessibleExternally) + TestProxyToHelloworld(t, clients, tc.url, false, tc.accessibleExternally, secret) }) } } diff --git a/test/e2e/subroutes_test.go b/test/e2e/subroutes_test.go index b86fcc27cd38..a66a42ee361d 100644 --- a/test/e2e/subroutes_test.go +++ b/test/e2e/subroutes_test.go @@ -76,6 +76,13 @@ func TestSubrouteLocalSTS(t *testing.T) { // We can't use a longer more descript t.Log("helloworld internal domain is ", resources.Route.Status.URL.Host) + // if cluster-local-domain-tls is enabled, this will return the CA used to sign the certificates. + // TestProxyToHelloworld will use this CA to verify the https connection + secret, err := GetCASecret(clients) + if err != nil { + t.Fatal(err.Error()) + } + // helloworld app and its route are ready. Running the test cases now. for _, tc := range testCases { domain := fmt.Sprintf("%s-%s", tag, resources.Route.Status.Address.URL.Host) @@ -83,7 +90,7 @@ func TestSubrouteLocalSTS(t *testing.T) { // We can't use a longer more descript helloworldURL.Host = strings.TrimSuffix(domain, tc.suffix) t.Run(tc.name, func(t *testing.T) { t.Parallel() - testProxyToHelloworld(t, clients, helloworldURL, true, false) + TestProxyToHelloworld(t, clients, helloworldURL, true, false, secret) }) } } diff --git a/test/ha/ha.go b/test/ha/ha.go index 3183f433fe68..6be34ab0e078 100644 --- a/test/ha/ha.go +++ b/test/ha/ha.go @@ -37,7 +37,7 @@ import ( const ( // NumControllerReconcilers is the number of controllers run by ./cmd/controller/main.go. // It is exported so the tests from cmd/controller/main.go can ensure we keep it in sync. - NumControllerReconcilers = 10 + NumControllerReconcilers = 9 ) func createPizzaPlanetService(t *testing.T, fopt ...rtesting.ServiceOption) (test.ResourceNames, *v1test.ResourceObjects) { diff --git a/test/test_images/httpproxy/httpproxy.go b/test/test_images/httpproxy/httpproxy.go index ba2c0a93e2bd..e36e9cdfd4d2 100644 --- a/test/test_images/httpproxy/httpproxy.go +++ b/test/test_images/httpproxy/httpproxy.go @@ -103,19 +103,12 @@ func newTLSEnabledTransport() http.RoundTripper { } transport.TLSClientConfig = &tls.Config{ RootCAs: rootCAs, - // If SERVER_NAME is not set the empty value will make the - // TLS client infer the ServerName from the hostname. - ServerName: os.Getenv("SERVER_NAME"), } } return transport } -func createRootCAs(caCertFile string) (*x509.CertPool, error) { - pemData, err := os.ReadFile(caCertFile) - if err != nil { - return nil, err - } +func createRootCAs(caCert string) (*x509.CertPool, error) { rootCAs, err := x509.SystemCertPool() if rootCAs == nil || err != nil { if err != nil { @@ -123,7 +116,7 @@ func createRootCAs(caCertFile string) (*x509.CertPool, error) { } rootCAs = x509.NewCertPool() } - if !rootCAs.AppendCertsFromPEM(pemData) { + if !rootCAs.AppendCertsFromPEM([]byte(caCert)) { return nil, errors.New("failed to add the certificate to the root CA") } return rootCAs, nil diff --git a/third_party/cert-manager-latest/net-certmanager.yaml b/third_party/cert-manager-latest/net-certmanager.yaml index 5e79a2538fe9..46ef4899a7fe 100644 --- a/third_party/cert-manager-latest/net-certmanager.yaml +++ b/third_party/cert-manager-latest/net-certmanager.yaml @@ -19,7 +19,7 @@ metadata: name: knative-serving-certmanager labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving serving.knative.dev/controller: "true" networking.knative.dev/certificate-provider: cert-manager @@ -52,7 +52,7 @@ metadata: name: config.webhook.net-certmanager.networking.internal.knative.dev labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager webhooks: @@ -93,7 +93,7 @@ metadata: namespace: knative-serving labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager @@ -119,7 +119,7 @@ metadata: namespace: knative-serving labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager data: @@ -138,23 +138,32 @@ data: # These sample configuration options may be copied out of # this block and unindented to actually change the configuration. - # issuerRef is a reference to the issuer for cluster external certificates used for ingress. + # issuerRef is a reference to the issuer for external-domain certificates used for ingress. # IssuerRef should be either `ClusterIssuer` or `Issuer`. # Please refer `IssuerRef` in https://github.com/cert-manager/cert-manager/tree/master/pkg/apis/certmanager/v1/types_certificate.go # for more details about IssuerRef configuration. - # If the issuerRef is not specified, the self-signed `knative-internal-encryption-ca` ClusterIssuer is used. + # If the issuerRef is not specified, the self-signed `knative-selfsigned-issuer` ClusterIssuer is used. issuerRef: | kind: ClusterIssuer name: letsencrypt-issuer - # clusterInternalIssuerRef is a reference to the issuer for cluster internal certificates used for ingress. - # ClusterInternalIssuerRef should be either `ClusterIssuer` or `Issuer`. + # clusterLocalIssuerRef is a reference to the issuer for cluster-local-domain certificates used for ingress. + # clusterLocalIssuerRef should be either `ClusterIssuer` or `Issuer`. # Please refer `IssuerRef` in https://github.com/cert-manager/cert-manager/tree/master/pkg/apis/certmanager/v1/types_certificate.go # for more details about ClusterInternalIssuerRef configuration. - # If the clusterInternalIssuerRef is not specified, the self-signed `knative-internal-encryption-ca` ClusterIssuer is used. - clusterInternalIssuerRef: | + # If the clusterLocalIssuerRef is not specified, the self-signed `knative-selfsigned-issuer` ClusterIssuer is used. + clusterLocalIssuerRef: | kind: ClusterIssuer - name: knative-internal-encryption-issuer + name: your-company-issuer + + # systemInternalIssuerRef is a reference to the issuer for certificates for system-internal-tls certificates used by Knative internal components. + # systemInternalIssuerRef should be either `ClusterIssuer` or `Issuer`. + # Please refer `IssuerRef` in https://github.com/cert-manager/cert-manager/tree/master/pkg/apis/certmanager/v1/types_certificate.go + # for more details about ClusterInternalIssuerRef configuration. + # If the systemInternalIssuerRef is not specified, the self-signed `knative-selfsigned-issuer` ClusterIssuer is used. + systemInternalIssuerRef: | + kind: ClusterIssuer + name: knative-selfsigned-issuer --- # Copyright 2020 The Knative Authors @@ -178,7 +187,7 @@ metadata: namespace: knative-serving labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager spec: @@ -190,7 +199,7 @@ spec: labels: app: net-certmanager-controller app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving spec: serviceAccountName: controller @@ -198,7 +207,7 @@ spec: - name: controller # This is the Go import path for the binary that is containerized # and substituted here. - image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/controller@sha256:057fdce28a97fae03635bc534d90ed2141a21e224aa6c63f3d633cfc4f8626cb + image: quay.io/rlehmann/net-certmanager-controller:latest resources: requests: cpu: 30m @@ -239,7 +248,7 @@ metadata: labels: app: net-certmanager-controller app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager name: net-certmanager-controller @@ -277,37 +286,40 @@ metadata: name: selfsigned-cluster-issuer labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager + knative.dev/issuer-install: "true" spec: selfSigned: {} --- apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: - name: knative-internal-encryption-issuer + name: knative-selfsigned-issuer labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager + knative.dev/issuer-install: "true" spec: ca: - secretName: knative-internal-encryption-ca + secretName: knative-selfsigned-ca --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: knative-internal-encryption-ca + name: knative-selfsigned-ca namespace: cert-manager # If you want to use it as a ClusterIssuer the secret must be in the cert-manager namespace. labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager + knative.dev/issuer-install: "true" spec: - secretName: knative-internal-encryption-ca + secretName: knative-selfsigned-ca commonName: knative.dev usages: - server auth @@ -338,7 +350,7 @@ metadata: namespace: knative-serving labels: app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager spec: @@ -351,7 +363,7 @@ spec: labels: app: net-certmanager-webhook app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving role: net-certmanager-webhook spec: @@ -360,7 +372,7 @@ spec: - name: webhook # This is the Go import path for the binary that is containerized # and substituted here. - image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/webhook@sha256:26b1036a9abced551cfea51fef5d7d0ac3dfd006b768f5d47ae44e0a2095ec09 + image: ko.local/knative.dev/net-certmanager/cmd/webhook:76f5f6a8cefa969814fd875c33de356e476804d5e81a08da9e18de407a305e3c resources: requests: cpu: 20m @@ -426,7 +438,7 @@ metadata: labels: role: net-certmanager-webhook app.kubernetes.io/component: net-certmanager - app.kubernetes.io/version: "20231120-67dcf8f7" + app.kubernetes.io/version: "20231122-c699f692-dirty" app.kubernetes.io/name: knative-serving networking.knative.dev/certificate-provider: cert-manager spec: diff --git a/third_party/kourier-latest/kourier.yaml b/third_party/kourier-latest/kourier.yaml index 4d4391ffceab..0568bfa5a441 100644 --- a/third_party/kourier-latest/kourier.yaml +++ b/third_party/kourier-latest/kourier.yaml @@ -343,7 +343,7 @@ spec: app: net-kourier-controller spec: containers: - - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:137b0d6e24a2d65cebd25f67af9b83fe4c6d820ca3080b2537bfa28d309351ba + - image: quay.io/rlehmann/net-kourier:latest name: controller env: - name: CERTS_SECRET_NAMESPACE diff --git a/vendor/knative.dev/networking/pkg/apis/networking/register.go b/vendor/knative.dev/networking/pkg/apis/networking/register.go index e88e9b5c0455..4a7a82bc4fb5 100644 --- a/vendor/knative.dev/networking/pkg/apis/networking/register.go +++ b/vendor/knative.dev/networking/pkg/apis/networking/register.go @@ -119,6 +119,10 @@ const ( // already using labels for domain, it probably best to keep this // consistent. VisibilityLabelKey = PublicGroupName + "/visibility" + + // CertificateTypeLabelKey is the label to indicate the type of Knative certificate + // used for Knative Serving encryption functionality. + CertificateTypeLabelKey = PublicGroupName + "/certificate-type" ) // Pseudo-constants diff --git a/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_helpers.go b/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_helpers.go new file mode 100644 index 000000000000..e8ea7c17b5bf --- /dev/null +++ b/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_helpers.go @@ -0,0 +1,47 @@ +/* +Copyright 2023 The Knative Authors + +Licensed 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 v1alpha1 + +import ( + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +// GetIngressTLSForVisibility returns a list of `Spec.TLS` where the `Hosts` field matches +// to `Spec.Rules.Hosts` and where the Rules have the defined ingress visibility. +// This method can be used in net-* implementations to select the correct `IngressTLS` entries +// for cluster-local and cluster-external gateways/listeners. +func (i *Ingress) GetIngressTLSForVisibility(visibility IngressVisibility) []IngressTLS { + ingressTLS := make([]IngressTLS, 0, len(i.Spec.TLS)) + + if i.Spec.TLS == nil || len(i.Spec.TLS) == 0 { + return ingressTLS + } + + for _, r := range i.Spec.Rules { + if r.Visibility == visibility { + for _, t := range i.Spec.TLS { + // Check if hosts slices are equal ignoring the order + if cmp.Diff(r.Hosts, t.Hosts, cmpopts.SortSlices(func(a, b string) bool { return a < b })) == "" { + ingressTLS = append(ingressTLS, t) + } + } + } + } + + return ingressTLS +} diff --git a/vendor/knative.dev/networking/pkg/certificates/reconciler/certificates.go b/vendor/knative.dev/networking/pkg/certificates/reconciler/certificates.go deleted file mode 100644 index bf8c26d207f8..000000000000 --- a/vendor/knative.dev/networking/pkg/certificates/reconciler/certificates.go +++ /dev/null @@ -1,211 +0,0 @@ -/* -Copyright 2021 The Knative Authors - -Licensed 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 reconciler - -import ( - "bytes" - "context" - "crypto/rsa" - "crypto/x509" - "fmt" - "time" - - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/client-go/kubernetes" - listerv1 "k8s.io/client-go/listers/core/v1" - pkgreconciler "knative.dev/pkg/reconciler" - "knative.dev/pkg/system" - - "knative.dev/networking/pkg/certificates" -) - -const ( - caExpirationInterval = time.Hour * 24 * 365 * 10 // 10 years - expirationInterval = time.Hour * 24 * 30 // 30 days - rotationThreshold = 24 * time.Hour - - // certificates used by trusted data routing elements such as activator, ingress gw - dataPlaneRoutingSecretType = "data-plane-routing" - - // certificates used by entities acting as senders and receivers (users) of the data-plane such as queue - dataPlaneUserSecretType = "data-plane-user" - - // Deprecated used by any data plane element - dataPlaneDeprecatedSecretType = "data-plane" -) - -// Reconciler reconciles a SampleSource object -type reconciler struct { - client kubernetes.Interface - secretLister listerv1.SecretLister - caSecretName string - secretTypeLabelName string - enqueueAfter func(key types.NamespacedName, delay time.Duration) - - logger *zap.SugaredLogger -} - -// Check that our Reconciler implements Interface -var _ Interface = (*reconciler)(nil) - -// ReconcileKind implements Interface.ReconcileKind. -func (r *reconciler) ReconcileKind(ctx context.Context, secret *corev1.Secret) pkgreconciler.Event { - // This should not happen, but it happens :) https://github.com/knative/pkg/issues/1891 - if !r.shouldReconcile(secret) { - r.logger.Infof("Skipping reconciling secret %s/%s", secret.Namespace, secret.Name) - return nil - } - r.logger.Infof("Updating secret %s/%s", secret.Namespace, secret.Name) - - // Reconcile CA secret first - caSecret, err := r.secretLister.Secrets(system.Namespace()).Get(r.caSecretName) - if apierrors.IsNotFound(err) { - // The secret should be created explicitly by a higher-level system - // that's responsible for install/updates. We simply populate the - // secret information. - return nil - } else if err != nil { - r.logger.Errorf("Error accessing CA certificate secret %s/%s: %v", system.Namespace(), r.caSecretName, err) - return err - } - caCert, caPk, err := parseAndValidateSecret(caSecret, nil) - if err != nil { - r.logger.Infof("CA cert invalid: %v", err) - - // We need to generate a new CA cert, then shortcircuit the reconciler - keyPair, err := certificates.CreateCACerts(caExpirationInterval) - if err != nil { - return fmt.Errorf("cannot generate the CA cert: %w", err) - } - return r.commitUpdatedSecret(ctx, caSecret, keyPair, nil) - } - - // Reconcile the provided secret - var sans []string - switch secret.Labels[r.secretTypeLabelName] { - case dataPlaneRoutingSecretType: - sans = []string{certificates.DataPlaneRoutingSAN, certificates.LegacyFakeDnsName} - case dataPlaneUserSecretType: - sans = []string{certificates.DataPlaneUserSAN(secret.Namespace), certificates.LegacyFakeDnsName} - case dataPlaneDeprecatedSecretType: - sans = []string{certificates.LegacyFakeDnsName} - default: - return fmt.Errorf("unknown cert type: %v", r.secretTypeLabelName) - } - - cert, _, err := parseAndValidateSecret(secret, caSecret.Data[certificates.CertName], sans...) - if err != nil { - r.logger.Infof("Secret %s/%s invalid: %v", secret.Namespace, secret.Name, err) - // Check the secret to reconcile type - - var keyPair *certificates.KeyPair - keyPair, err = certificates.CreateCert(caPk, caCert, expirationInterval, sans...) - if err != nil { - return fmt.Errorf("cannot generate the cert: %w", err) - } - err = r.commitUpdatedSecret(ctx, secret, keyPair, caSecret.Data[certificates.CertName]) - if err != nil { - return err - } - cert, _, err = certificates.ParseCert(keyPair.CertBytes(), keyPair.PrivateKeyBytes()) - if err != nil { - return err - } - } - - r.enqueueBeforeExpiration(secret, cert) - - return nil -} - -// All sans provided are required to be lower case -func parseAndValidateSecret(secret *corev1.Secret, caCert []byte, sans ...string) (*x509.Certificate, *rsa.PrivateKey, error) { - certBytes, ok := secret.Data[certificates.CertName] - if !ok { - return nil, nil, fmt.Errorf("missing cert bytes in %q", certificates.CertName) - } - pkBytes, ok := secret.Data[certificates.PrivateKeyName] - if !ok { - return nil, nil, fmt.Errorf("missing pk bytes in %q", certificates.PrivateKeyName) - } - if caCert != nil { - ca, ok := secret.Data[certificates.CaCertName] - if !ok { - return nil, nil, fmt.Errorf("missing ca cert bytes in %q", certificates.CaCertName) - } - if !bytes.Equal(ca, caCert) { - return nil, nil, fmt.Errorf("ca cert bytes changed in %q", certificates.CaCertName) - } - } - - cert, caPk, err := certificates.ParseCert(certBytes, pkBytes) - if err != nil { - return nil, nil, err - } - if err := certificates.CheckExpiry(cert, rotationThreshold); err != nil { - return nil, nil, err - } - - sanSet := sets.NewString(sans...) - certSet := sets.NewString(cert.DNSNames...) - if !sanSet.Equal(certSet) { - return nil, nil, fmt.Errorf("unexpected SANs") - } - - return cert, caPk, nil -} - -func (r *reconciler) enqueueBeforeExpiration(secret *corev1.Secret, cert *x509.Certificate) { - when := cert.NotAfter.Add(-rotationThreshold).Add(1 * time.Second) // Make sure to enqueue it after the rotation threshold - r.enqueueAfter(types.NamespacedName{ - Namespace: secret.Namespace, - Name: secret.Name, - }, time.Until(when)) -} - -func (r *reconciler) commitUpdatedSecret(ctx context.Context, secret *corev1.Secret, keyPair *certificates.KeyPair, caCert []byte) error { - // Don't modify the informer copy. - secret = secret.DeepCopy() - - secret.Data = make(map[string][]byte, 3) - secret.Data[certificates.CertName] = keyPair.CertBytes() - secret.Data[certificates.PrivateKeyName] = keyPair.PrivateKeyBytes() - secret.Data[certificates.SecretCertKey] = keyPair.CertBytes() - secret.Data[certificates.SecretPKKey] = keyPair.PrivateKeyBytes() - if caCert != nil { - secret.Data[certificates.SecretCaCertKey] = caCert - secret.Data[certificates.CaCertName] = caCert - } - - _, err := r.client.CoreV1().Secrets(secret.Namespace).Update(ctx, secret, metav1.UpdateOptions{}) - return err -} - -func (r *reconciler) shouldReconcile(secret *corev1.Secret) bool { - // Is CA secret? - if secret.Name == r.caSecretName && secret.Namespace == system.Namespace() { - return false - } - - _, hasLabel := secret.Labels[r.secretTypeLabelName] - return hasLabel -} diff --git a/vendor/knative.dev/networking/pkg/certificates/reconciler/controller.go b/vendor/knative.dev/networking/pkg/certificates/reconciler/controller.go deleted file mode 100644 index 43f475773207..000000000000 --- a/vendor/knative.dev/networking/pkg/certificates/reconciler/controller.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2021 The Knative Authors - -Licensed 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 reconciler - -import ( - "context" - - v1 "k8s.io/client-go/informers/core/v1" - "k8s.io/client-go/tools/cache" - kubeclient "knative.dev/pkg/client/injection/kube/client" - "knative.dev/pkg/injection" - "knative.dev/pkg/system" - - "knative.dev/pkg/configmap" - "knative.dev/pkg/controller" - "knative.dev/pkg/logging" - - pkgreconciler "knative.dev/pkg/reconciler" - - secretfilteredinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered" - filteredFactory "knative.dev/pkg/client/injection/kube/informers/factory/filtered" -) - -const ( - caSecretNamePostfix = "-ctrl-ca" - secretLabelNamePostfix = "-ctrl" -) - -// NewControllerFactory generates a ControllerConstructor for the control certificates reconciler. -func NewControllerFactory(componentName string) injection.ControllerConstructor { - return func( - ctx context.Context, - cmw configmap.Watcher, - ) *controller.Impl { - - caSecretName := componentName + caSecretNamePostfix - labelName := componentName + secretLabelNamePostfix - - ctx = filteredFactory.WithSelectors(ctx, labelName) - secretInformer := getSecretInformer(ctx) - - r := &reconciler{ - client: kubeclient.Get(ctx), - - secretLister: secretInformer.Lister(), - caSecretName: caSecretName, - secretTypeLabelName: labelName, - - logger: logging.FromContext(ctx), - } - - impl := NewFilteredImpl(ctx, r, secretInformer) - r.enqueueAfter = impl.EnqueueKeyAfter - - logging.FromContext(ctx).Info("Setting up event handlers") - - // If the ca secret changes, global resync - secretInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ - FilterFunc: controller.FilterWithNameAndNamespace(system.Namespace(), caSecretName), - Handler: controller.HandleAll(func(i interface{}) { - impl.FilteredGlobalResync(pkgreconciler.LabelExistsFilterFunc(labelName), secretInformer.Informer()) - }), - }) - - // Enqueue only secrets with expected label - secretInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ - FilterFunc: pkgreconciler.LabelExistsFilterFunc(labelName), - Handler: controller.HandleAll(impl.Enqueue), - }) - - return impl - } -} - -func getSecretInformer(ctx context.Context) v1.SecretInformer { - untyped := ctx.Value(filteredFactory.LabelKey{}) // This should always be not nil and have exactly one selector - return secretfilteredinformer.Get(ctx, untyped.([]string)[0]) -} diff --git a/vendor/knative.dev/networking/pkg/certificates/reconciler/controller_impl.go b/vendor/knative.dev/networking/pkg/certificates/reconciler/controller_impl.go deleted file mode 100644 index 908f33f6645a..000000000000 --- a/vendor/knative.dev/networking/pkg/certificates/reconciler/controller_impl.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2022 The Knative Authors - -Licensed 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 reconciler - -import ( - context "context" - fmt "fmt" - reflect "reflect" - strings "strings" - - zap "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - watch "k8s.io/apimachinery/pkg/watch" - informersv1 "k8s.io/client-go/informers/core/v1" - scheme "k8s.io/client-go/kubernetes/scheme" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" - record "k8s.io/client-go/tools/record" - client "knative.dev/pkg/client/injection/kube/client" - controller "knative.dev/pkg/controller" - logging "knative.dev/pkg/logging" - logkey "knative.dev/pkg/logging/logkey" -) - -const ( - defaultControllerAgentName = "secret-controller" - defaultFinalizerName = "secrets.core" -) - -// NewFilteredImpl returns a controller.Impl that handles queuing and feeding work from -// the queue through an implementation of controller.Reconciler, delegating to -// the provided Interface and optional Finalizer methods. OptionsFn is used to return -// controller.ControllerOptions to be used by the internal reconciler. -func NewFilteredImpl(ctx context.Context, r Interface, secretInformer informersv1.SecretInformer, options ...controller.Options) *controller.Impl { - logger := logging.FromContext(ctx) - - // Check the options function input. It should be 0 or 1. - if len(options) > 1 { - logger.Fatal("Up to one options function is supported, found: ", len(options)) - } - - lister := secretInformer.Lister() - - agentName := defaultControllerAgentName - recorder := createRecorder(ctx, agentName) - - rec := NewReconciler(ctx, logger, client.Get(ctx), lister, recorder, r, options...) - - ctrType := reflect.TypeOf(r).Elem() - ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) - ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") - - logger = logger.With( - zap.String(logkey.ControllerType, ctrTypeName), - zap.String(logkey.Kind, "core.Secret"), - ) - - impl := controller.NewContext(ctx, rec, controller.ControllerOptions{WorkQueueName: ctrTypeName, Logger: logger}) - - return impl -} - -func createRecorder(ctx context.Context, agentName string) record.EventRecorder { - logger := logging.FromContext(ctx) - - recorder := controller.GetEventRecorder(ctx) - if recorder == nil { - // Create event broadcaster - logger.Debug("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - watches := []watch.Interface{ - eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), - eventBroadcaster.StartRecordingToSink( - &v1.EventSinkImpl{Interface: client.Get(ctx).CoreV1().Events("")}), - } - recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) - go func() { - <-ctx.Done() - for _, w := range watches { - w.Stop() - } - }() - } - - return recorder -} - -func init() { - scheme.AddToScheme(scheme.Scheme) -} diff --git a/vendor/knative.dev/networking/pkg/certificates/reconciler/reconciler.go b/vendor/knative.dev/networking/pkg/certificates/reconciler/reconciler.go deleted file mode 100644 index d036ba0f0115..000000000000 --- a/vendor/knative.dev/networking/pkg/certificates/reconciler/reconciler.go +++ /dev/null @@ -1,434 +0,0 @@ -/* -Copyright 2022 The Knative Authors - -Licensed 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. -*/ - -// Code generated by injection-gen. DO NOT EDIT. - -package reconciler - -import ( - context "context" - json "encoding/json" - fmt "fmt" - - zap "go.uber.org/zap" - v1 "k8s.io/api/core/v1" - errors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - sets "k8s.io/apimachinery/pkg/util/sets" - kubernetes "k8s.io/client-go/kubernetes" - corev1 "k8s.io/client-go/listers/core/v1" - cache "k8s.io/client-go/tools/cache" - record "k8s.io/client-go/tools/record" - controller "knative.dev/pkg/controller" - logging "knative.dev/pkg/logging" - pkgreconciler "knative.dev/pkg/reconciler" -) - -// Interface defines the strongly typed interfaces to be implemented by a -// controller reconciling v1.Secret. -type Interface interface { - // ReconcileKind implements custom logic to reconcile v1.Secret. Any changes - // to the objects .Status or .Finalizers will be propagated to the stored - // object. It is recommended that implementors do not call any update calls - // for the Kind inside of ReconcileKind, it is the responsibility of the calling - // controller to propagate those properties. The resource passed to ReconcileKind - // will always have an empty deletion timestamp. - ReconcileKind(ctx context.Context, o *v1.Secret) pkgreconciler.Event -} - -// Finalizer defines the strongly typed interfaces to be implemented by a -// controller finalizing v1.Secret. -type Finalizer interface { - // FinalizeKind implements custom logic to finalize v1.Secret. Any changes - // to the objects .Status or .Finalizers will be ignored. Returning a nil or - // Normal type reconciler.Event will allow the finalizer to be deleted on - // the resource. The resource passed to FinalizeKind will always have a set - // deletion timestamp. - FinalizeKind(ctx context.Context, o *v1.Secret) pkgreconciler.Event -} - -// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a -// controller reconciling v1.Secret if they want to process resources for which -// they are not the leader. -type ReadOnlyInterface interface { - // ObserveKind implements logic to observe v1.Secret. - // This method should not write to the API. - ObserveKind(ctx context.Context, o *v1.Secret) pkgreconciler.Event -} - -// state is used to track the state of a reconciler in a single run. -type state struct { - // key is the original reconciliation key from the queue. - key string - // namespace is the namespace split from the reconciliation key. - namespace string - // name is the name split from the reconciliation key. - name string - // reconciler is the reconciler. - reconciler Interface - // roi is the read only interface cast of the reconciler. - roi ReadOnlyInterface - // isROI (Read Only Interface) the reconciler only observes reconciliation. - isROI bool - // isLeader the instance of the reconciler is the elected leader. - isLeader bool -} - -type doReconcile func(ctx context.Context, o *v1.Secret) pkgreconciler.Event - -// reconcilerImpl implements controller.Reconciler for v1.Secret resources. -type reconcilerImpl struct { - // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware. - pkgreconciler.LeaderAwareFuncs - - // Client is used to write back status updates. - Client kubernetes.Interface - - // Listers index properties about resources. - Lister corev1.SecretLister - - // Recorder is an event recorder for recording Event resources to the - // Kubernetes API. - Recorder record.EventRecorder - - // configStore allows for decorating a context with config maps. - // +optional - configStore pkgreconciler.ConfigStore - - // reconciler is the implementation of the business logic of the resource. - reconciler Interface - - // finalizerName is the name of the finalizer to reconcile. - finalizerName string -} - -// Check that our Reconciler implements controller.Reconciler. -var _ controller.Reconciler = (*reconcilerImpl)(nil) - -// Check that our generated Reconciler is always LeaderAware. -var _ pkgreconciler.LeaderAware = (*reconcilerImpl)(nil) - -func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubernetes.Interface, lister corev1.SecretLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { - // Check the options function input. It should be 0 or 1. - if len(options) > 1 { - logger.Fatal("Up to one options struct is supported, found: ", len(options)) - } - - // Fail fast when users inadvertently implement the other LeaderAware interface. - // For the typed reconcilers, Promote shouldn't take any arguments. - if _, ok := r.(pkgreconciler.LeaderAware); ok { - logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) - } - - rec := &reconcilerImpl{ - LeaderAwareFuncs: pkgreconciler.LeaderAwareFuncs{ - PromoteFunc: func(bkt pkgreconciler.Bucket, enq func(pkgreconciler.Bucket, types.NamespacedName)) error { - all, err := lister.List(labels.Everything()) - if err != nil { - return err - } - for _, elt := range all { - // TODO: Consider letting users specify a filter in options. - enq(bkt, types.NamespacedName{ - Namespace: elt.GetNamespace(), - Name: elt.GetName(), - }) - } - return nil - }, - }, - Client: client, - Lister: lister, - Recorder: recorder, - reconciler: r, - finalizerName: defaultFinalizerName, - } - - for _, opts := range options { - if opts.ConfigStore != nil { - rec.configStore = opts.ConfigStore - } - if opts.FinalizerName != "" { - rec.finalizerName = opts.FinalizerName - } - if opts.DemoteFunc != nil { - rec.DemoteFunc = opts.DemoteFunc - } - } - - return rec -} - -// Reconcile implements controller.Reconciler -func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { - logger := logging.FromContext(ctx) - - // Initialize the reconciler state. This will convert the namespace/name - // string into a distinct namespace and name, determine if this instance of - // the reconciler is the leader, and any additional interfaces implemented - // by the reconciler. Returns an error is the resource key is invalid. - s, err := newState(key, r) - if err != nil { - logger.Error("Invalid resource key: ", key) - return nil - } - - // If we are not the leader, and we don't implement either ReadOnly - // observer interfaces, then take a fast-path out. - if s.isNotLeaderNorObserver() { - return controller.NewSkipKey(key) - } - - // If configStore is set, attach the frozen configuration to the context. - if r.configStore != nil { - ctx = r.configStore.ToContext(ctx) - } - - // Add the recorder to context. - ctx = controller.WithEventRecorder(ctx, r.Recorder) - - // Get the resource with this namespace/name. - - getter := r.Lister.Secrets(s.namespace) - - original, err := getter.Get(s.name) - - if errors.IsNotFound(err) { - // The resource may no longer exist, in which case we stop processing and call - // the ObserveDeletion handler if appropriate. - logger.Debugf("Resource %q no longer exists", key) - if del, ok := r.reconciler.(pkgreconciler.OnDeletionInterface); ok { - return del.ObserveDeletion(ctx, types.NamespacedName{ - Namespace: s.namespace, - Name: s.name, - }) - } - return nil - } else if err != nil { - return err - } - - // Don't modify the informers copy. - resource := original.DeepCopy() - - var reconcileEvent pkgreconciler.Event - - name, do := s.reconcileMethodFor(resource) - // Append the target method to the logger. - logger = logger.With(zap.String("targetMethod", name)) - switch name { - case pkgreconciler.DoReconcileKind: - // Set and update the finalizer on resource if r.reconciler - // implements Finalizer. - if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { - return fmt.Errorf("failed to set finalizers: %w", err) - } - - // Reconcile this copy of the resource and then write back any status - // updates regardless of whether the reconciliation errored out. - reconcileEvent = do(ctx, resource) - - case pkgreconciler.DoFinalizeKind: - // For finalizing reconcilers, if this resource being marked for deletion - // and reconciled cleanly (nil or normal event), remove the finalizer. - reconcileEvent = do(ctx, resource) - - if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { - return fmt.Errorf("failed to clear finalizers: %w", err) - } - - case pkgreconciler.DoObserveKind: - // Observe any changes to this resource, since we are not the leader. - reconcileEvent = do(ctx, resource) - - } - - // Report the reconciler event, if any. - if reconcileEvent != nil { - var event *pkgreconciler.ReconcilerEvent - if pkgreconciler.EventAs(reconcileEvent, &event) { - logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) - r.Recorder.Event(resource, event.EventType, event.Reason, event.Error()) - - // the event was wrapped inside an error, consider the reconciliation as failed - if _, isEvent := reconcileEvent.(*pkgreconciler.ReconcilerEvent); !isEvent { - return reconcileEvent - } - return nil - } - - if controller.IsSkipKey(reconcileEvent) { - // This is a wrapped error, don't emit an event. - } else if ok, _ := controller.IsRequeueKey(reconcileEvent); ok { - // This is a wrapped error, don't emit an event. - } else { - logger.Errorw("Returned an error", zap.Error(reconcileEvent)) - r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) - } - return reconcileEvent - } - - return nil -} - -// updateFinalizersFiltered will update the Finalizers of the resource. -// TODO: this method could be generic and sync all finalizers. For now it only -// updates defaultFinalizerName or its override. -func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1.Secret, desiredFinalizers sets.String) (*v1.Secret, error) { - // Don't modify the informers copy. - existing := resource.DeepCopy() - - var finalizers []string - - // If there's nothing to update, just return. - existingFinalizers := sets.NewString(existing.Finalizers...) - - if desiredFinalizers.Has(r.finalizerName) { - if existingFinalizers.Has(r.finalizerName) { - // Nothing to do. - return resource, nil - } - // Add the finalizer. - finalizers = append(existing.Finalizers, r.finalizerName) - } else { - if !existingFinalizers.Has(r.finalizerName) { - // Nothing to do. - return resource, nil - } - // Remove the finalizer. - existingFinalizers.Delete(r.finalizerName) - finalizers = existingFinalizers.List() - } - - mergePatch := map[string]interface{}{ - "metadata": map[string]interface{}{ - "finalizers": finalizers, - "resourceVersion": existing.ResourceVersion, - }, - } - - patch, err := json.Marshal(mergePatch) - if err != nil { - return resource, err - } - - patcher := r.Client.CoreV1().Secrets(resource.Namespace) - - resourceName := resource.Name - updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{}) - if err != nil { - r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed", - "Failed to update finalizers for %q: %v", resourceName, err) - } else { - r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate", - "Updated %q finalizers", resource.GetName()) - } - return updated, err -} - -func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1.Secret) (*v1.Secret, error) { - if _, ok := r.reconciler.(Finalizer); !ok { - return resource, nil - } - - finalizers := sets.NewString(resource.Finalizers...) - - // If this resource is not being deleted, mark the finalizer. - if resource.GetDeletionTimestamp().IsZero() { - finalizers.Insert(r.finalizerName) - } - - // Synchronize the finalizers filtered by r.finalizerName. - return r.updateFinalizersFiltered(ctx, resource, finalizers) -} - -func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1.Secret, reconcileEvent pkgreconciler.Event) (*v1.Secret, error) { - if _, ok := r.reconciler.(Finalizer); !ok { - return resource, nil - } - if resource.GetDeletionTimestamp().IsZero() { - return resource, nil - } - - finalizers := sets.NewString(resource.Finalizers...) - - if reconcileEvent != nil { - var event *pkgreconciler.ReconcilerEvent - if pkgreconciler.EventAs(reconcileEvent, &event) { - if event.EventType == v1.EventTypeNormal { - finalizers.Delete(r.finalizerName) - } - } - } else { - finalizers.Delete(r.finalizerName) - } - - // Synchronize the finalizers filtered by r.finalizerName. - return r.updateFinalizersFiltered(ctx, resource, finalizers) -} - -func newState(key string, r *reconcilerImpl) (*state, error) { - // Convert the namespace/name string into a distinct namespace and name. - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - return nil, fmt.Errorf("invalid resource key: %s", key) - } - - roi, isROI := r.reconciler.(ReadOnlyInterface) - - isLeader := r.IsLeaderFor(types.NamespacedName{ - Namespace: namespace, - Name: name, - }) - - return &state{ - key: key, - namespace: namespace, - name: name, - reconciler: r.reconciler, - roi: roi, - isROI: isROI, - isLeader: isLeader, - }, nil -} - -// isNotLeaderNorObserver checks to see if this reconciler with the current -// state is enabled to do any work or not. -// isNotLeaderNorObserver returns true when there is no work possible for the -// reconciler. -func (s *state) isNotLeaderNorObserver() bool { - if !s.isLeader && !s.isROI { - // If we are not the leader, and we don't implement the ReadOnly - // interface, then take a fast-path out. - return true - } - return false -} - -func (s *state) reconcileMethodFor(o *v1.Secret) (string, doReconcile) { - if o.GetDeletionTimestamp().IsZero() { - if s.isLeader { - return pkgreconciler.DoReconcileKind, s.reconciler.ReconcileKind - } else if s.isROI { - return pkgreconciler.DoObserveKind, s.roi.ObserveKind - } - } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { - return pkgreconciler.DoFinalizeKind, fin.FinalizeKind - } - return "unknown", nil -} diff --git a/vendor/knative.dev/networking/pkg/config/config.go b/vendor/knative.dev/networking/pkg/config/config.go index 028937067b70..b9fca8585c24 100644 --- a/vendor/knative.dev/networking/pkg/config/config.go +++ b/vendor/knative.dev/networking/pkg/config/config.go @@ -67,12 +67,6 @@ const ( // Certificate reconciler. CertManagerCertificateClassName = "cert-manager.certificate.networking.knative.dev" - // ServingInternalCertName is the name of secret contains certificates in serving - // system namespace. - // - // Deprecated: ServingInternalCertName is deprecated. Use ServingRoutingCertName instead. - ServingInternalCertName = "knative-serving-certs" - // ServingRoutingCertName is the name of secret contains certificates for Routing data in serving // system namespace. (Used by Ingress GWs and Activator) ServingRoutingCertName = "routing-serving-certs" @@ -148,6 +142,20 @@ const ( SystemInternalTLSKey = "system-internal-tls" ) +// CertificateType indicates the type of Knative Certificate. +type CertificateType string + +const ( + // CertificateSystemInternal defines a certificate used for `system-internal-tls`. + CertificateSystemInternal CertificateType = "system-internal" + + // CertificateClusterLocalDomain defines a certificate used for `cluster-local-domain-tls`. + CertificateClusterLocalDomain CertificateType = "cluster-local-domain" + + // CertificateExternalDomain defines a cerificate used for `external-domain-tls`. + CertificateExternalDomain CertificateType = "external-domain" +) + // EncryptionConfig indicates the encryption configuration // used for TLS connections. type EncryptionConfig string diff --git a/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered/secret.go b/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered/secret.go deleted file mode 100644 index 80d46c400c37..000000000000 --- a/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered/secret.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2022 The Knative Authors - -Licensed 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. -*/ - -// Code generated by injection-gen. DO NOT EDIT. - -package filtered - -import ( - context "context" - - v1 "k8s.io/client-go/informers/core/v1" - filtered "knative.dev/pkg/client/injection/kube/informers/factory/filtered" - controller "knative.dev/pkg/controller" - injection "knative.dev/pkg/injection" - logging "knative.dev/pkg/logging" -) - -func init() { - injection.Default.RegisterFilteredInformers(withInformer) -} - -// Key is used for associating the Informer inside the context.Context. -type Key struct { - Selector string -} - -func withInformer(ctx context.Context) (context.Context, []controller.Informer) { - untyped := ctx.Value(filtered.LabelKey{}) - if untyped == nil { - logging.FromContext(ctx).Panic( - "Unable to fetch labelkey from context.") - } - labelSelectors := untyped.([]string) - infs := []controller.Informer{} - for _, selector := range labelSelectors { - f := filtered.Get(ctx, selector) - inf := f.Core().V1().Secrets() - ctx = context.WithValue(ctx, Key{Selector: selector}, inf) - infs = append(infs, inf.Informer()) - } - return ctx, infs -} - -// Get extracts the typed informer from the context. -func Get(ctx context.Context, selector string) v1.SecretInformer { - untyped := ctx.Value(Key{Selector: selector}) - if untyped == nil { - logging.FromContext(ctx).Panicf( - "Unable to fetch k8s.io/client-go/informers/core/v1.SecretInformer with selector %s from context.", selector) - } - return untyped.(v1.SecretInformer) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index d9e7805b9ef0..883b785a80e6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1300,14 +1300,13 @@ knative.dev/caching/pkg/client/listers/caching/v1alpha1 # knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7 ## explicit; go 1.18 knative.dev/hack -# knative.dev/networking v0.0.0-20231109233957-8f3c5211035b +# knative.dev/networking v0.0.0-20231109233957-8f3c5211035b => /Users/rlehmann/code/knative/networking ## explicit; go 1.18 knative.dev/networking/config knative.dev/networking/pkg knative.dev/networking/pkg/apis/networking knative.dev/networking/pkg/apis/networking/v1alpha1 knative.dev/networking/pkg/certificates -knative.dev/networking/pkg/certificates/reconciler knative.dev/networking/pkg/client/clientset/versioned knative.dev/networking/pkg/client/clientset/versioned/fake knative.dev/networking/pkg/client/clientset/versioned/scheme @@ -1375,7 +1374,6 @@ knative.dev/pkg/client/injection/kube/informers/core/v1/pod/filtered knative.dev/pkg/client/injection/kube/informers/core/v1/pod/filtered/fake knative.dev/pkg/client/injection/kube/informers/core/v1/secret knative.dev/pkg/client/injection/kube/informers/core/v1/secret/fake -knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered knative.dev/pkg/client/injection/kube/informers/core/v1/service knative.dev/pkg/client/injection/kube/informers/core/v1/service/fake knative.dev/pkg/client/injection/kube/informers/factory @@ -1471,3 +1469,4 @@ sigs.k8s.io/structured-merge-diff/v4/value sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 # github.com/gorilla/websocket => github.com/gorilla/websocket v1.5.0 +# knative.dev/networking => /Users/rlehmann/code/knative/networking