diff --git a/.github/workflows/kind-e2e.yaml b/.github/workflows/kind-e2e.yaml index 76f3b71ec4..65188b1486 100644 --- a/.github/workflows/kind-e2e.yaml +++ b/.github/workflows/kind-e2e.yaml @@ -31,6 +31,10 @@ jobs: istio-version: - latest + encryption: + - disabled + - system-internal-tls + istio-profile: - no-mesh - ambient @@ -90,6 +94,21 @@ jobs: echo "GATEWAY_NAMESPACE_OVERRIDE=istio-system" >> $GITHUB_ENV + - name: Generate certificates and enable system-internal-tls + if: matrix.encryption == 'system-internal-tls' && matrix.istio-profile == 'no-mesh' + run: | + set -o pipefail + + echo ">> Deploy certificate for upstream traffic" + ./test/generate-upstream-cert.sh + + echo ">> Setting environment variables for upstream tls" + echo "UPSTREAM_TLS_CERT=serving-certs" >> $GITHUB_ENV + echo "SERVER_NAME=kn-user-serving-tests" >> $GITHUB_ENV + + echo ">> Enabling system-internal-tls in config-network" + kubectl apply -f test/config/system-internal-tls + - name: Upload Test Images run: | # Build and Publish our test images to the docker daemon. @@ -108,7 +127,7 @@ jobs: - name: Run e2e Tests run: | set -x - + # Exclude the control-plane node, which doesn't seem to expose the nodeport service. IPS=( $(kubectl get nodes -lkubernetes.io/hostname!=kind-control-plane -ojsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}') ) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index a2e0ffdfaf..ba0a4183ee 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -82,8 +82,8 @@ istioctl install -y Run the following command to install Knative ```shell -kubectl apply --filename https://storage.googleapis.com/knative-nightly/serving/latest/serving-crds.yaml -kubectl apply --filename https://storage.googleapis.com/knative-nightly/serving/latest/serving-core.yaml +kubectl apply -f https://github.com/knative/serving/releases/latest/download/serving-crds.yaml +kubectl apply -f https://github.com/knative/serving/releases/latest/download/serving-core.yaml ``` ### Install Knative net-istio @@ -93,3 +93,18 @@ Run the following command to install net-istio components ```shell ko apply -f config/ ``` + +### System Internal TLS (optional) + +If you want to work with `system-internal-tls` enabled you can either: + +* Install `Knative Serving` to automatically generate the certificates. The CA will be injected in [700-istio-secret.yaml](./config/700-istio-secret.yaml). +* Or use [./test/generate-upstream-cert.sh)](./test/generate-upstream-cert.sh) to manually generate the secrets. + +You can then enable `system-internal-tls` in `config-network` like in [our test resources](./test/config/system-internal-tls/config-network.yaml) +and specify the following environment variables before you run the e2e/conformance tests: + +```bash +export UPSTREAM_TLS_CERT=serving-certs +export SERVER_NAME=kn-user-serving-tests +``` diff --git a/cmd/controller/main.go b/cmd/controller/main.go index d50872813e..808ad732f2 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -32,6 +32,7 @@ func main() { // resilient to clusters containing malformed resources. v1beta1.VirtualServiceUnmarshaler.AllowUnknownFields = true v1beta1.GatewayUnmarshaler.AllowUnknownFields = true + v1beta1.DestinationRuleUnmarshaler.AllowUnknownFields = true ctx := informerfiltering.GetContextWithFilteringLabelSelector(signals.NewContext()) sharedmain.MainWithContext(ctx, "net-istio-controller", ingress.NewController, serverlessservice.NewController) diff --git a/config/700-istio-secret.yaml b/config/700-istio-secret.yaml new file mode 100644 index 0000000000..259efea991 --- /dev/null +++ b/config/700-istio-secret.yaml @@ -0,0 +1,23 @@ +# 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 +# +# 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: + name: routing-serving-certs + namespace: istio-system + labels: + serving-certs-ctrl: "data-plane-routing" + networking.internal.knative.dev/certificate-uid: "serving-certs" +# The data is populated when system-internal-tls is enabled. diff --git a/pkg/reconciler/ingress/controller.go b/pkg/reconciler/ingress/controller.go index 9cd04fcadf..5971e4b01b 100644 --- a/pkg/reconciler/ingress/controller.go +++ b/pkg/reconciler/ingress/controller.go @@ -22,6 +22,7 @@ import ( "go.uber.org/zap" v1 "k8s.io/client-go/informers/core/v1" istioclient "knative.dev/net-istio/pkg/client/istio/injection/client" + destinationruleinformer "knative.dev/net-istio/pkg/client/istio/injection/informers/networking/v1beta1/destinationrule" gatewayinformer "knative.dev/net-istio/pkg/client/istio/injection/informers/networking/v1beta1/gateway" virtualserviceinformer "knative.dev/net-istio/pkg/client/istio/injection/informers/networking/v1beta1/virtualservice" "knative.dev/net-istio/pkg/reconciler/ingress/config" @@ -82,18 +83,20 @@ func newControllerWithOptions( ctx = AnnotateLoggerWithName(ctx, controllerAgentName) logger := logging.FromContext(ctx) virtualServiceInformer := virtualserviceinformer.Get(ctx) + destinationRuleInformer := destinationruleinformer.Get(ctx) gatewayInformer := gatewayinformer.Get(ctx) secretInformer := getSecretInformer(ctx) serviceInformer := serviceinformer.Get(ctx) ingressInformer := ingressinformer.Get(ctx) c := &Reconciler{ - kubeclient: kubeclient.Get(ctx), - istioClientSet: istioclient.Get(ctx), - virtualServiceLister: virtualServiceInformer.Lister(), - gatewayLister: gatewayInformer.Lister(), - secretLister: secretInformer.Lister(), - svcLister: serviceInformer.Lister(), + kubeclient: kubeclient.Get(ctx), + istioClientSet: istioclient.Get(ctx), + virtualServiceLister: virtualServiceInformer.Lister(), + destinationRuleLister: destinationRuleInformer.Lister(), + gatewayLister: gatewayInformer.Lister(), + secretLister: secretInformer.Lister(), + svcLister: serviceInformer.Lister(), } myFilterFunc := reconciler.AnnotationFilterFunc(networking.IngressClassAnnotationKey, netconfig.IstioIngressClassName, true) @@ -123,6 +126,11 @@ func newControllerWithOptions( Handler: controller.HandleAll(impl.EnqueueControllerOf), }) + destinationRuleInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterController(&v1alpha1.Ingress{}), + Handler: controller.HandleAll(impl.EnqueueControllerOf), + }) + endpointsInformer := endpointsinformer.Get(ctx) podInformer := podinformer.Get(ctx) resyncOnIngressReady := func(ing *v1alpha1.Ingress) { diff --git a/pkg/reconciler/ingress/ingress.go b/pkg/reconciler/ingress/ingress.go index f6d981adc5..0255d624c8 100644 --- a/pkg/reconciler/ingress/ingress.go +++ b/pkg/reconciler/ingress/ingress.go @@ -24,6 +24,7 @@ import ( "github.com/google/go-cmp/cmp" "go.uber.org/zap" "google.golang.org/protobuf/testing/protocmp" + pkgnetwork "knative.dev/pkg/network" istiov1beta1 "istio.io/api/networking/v1beta1" "istio.io/client-go/pkg/apis/networking/v1beta1" @@ -66,11 +67,12 @@ const ( type Reconciler struct { kubeclient kubernetes.Interface - istioClientSet istioclientset.Interface - virtualServiceLister istiolisters.VirtualServiceLister - gatewayLister istiolisters.GatewayLister - secretLister corev1listers.SecretLister - svcLister corev1listers.ServiceLister + istioClientSet istioclientset.Interface + virtualServiceLister istiolisters.VirtualServiceLister + destinationRuleLister istiolisters.DestinationRuleLister + gatewayLister istiolisters.GatewayLister + secretLister corev1listers.SecretLister + svcLister corev1listers.ServiceLister tracker tracker.Interface @@ -78,10 +80,11 @@ type Reconciler struct { } var ( - _ ingressreconciler.Interface = (*Reconciler)(nil) - _ ingressreconciler.Finalizer = (*Reconciler)(nil) - _ coreaccessor.SecretAccessor = (*Reconciler)(nil) - _ istioaccessor.VirtualServiceAccessor = (*Reconciler)(nil) + _ ingressreconciler.Interface = (*Reconciler)(nil) + _ ingressreconciler.Finalizer = (*Reconciler)(nil) + _ coreaccessor.SecretAccessor = (*Reconciler)(nil) + _ istioaccessor.VirtualServiceAccessor = (*Reconciler)(nil) + _ istioaccessor.DestinationRuleAccessor = (*Reconciler)(nil) ) // ReconcileKind compares the actual state with the desired, and attempts to @@ -186,6 +189,13 @@ func (r *Reconciler) reconcileIngress(ctx context.Context, ing *v1alpha1.Ingress } gatewayNames[v1alpha1.IngressVisibilityExternalIP].Insert(resources.GetQualifiedGatewayNames(ingressGateways)...) + if config.FromContext(ctx).Network.SystemInternalTLSEnabled() { + logger.Info("reconciling DestinationRules for system-internal-tls") + if err := r.reconcileDestinationRules(ctx, ing); err != nil { + return err + } + } + vses, err := resources.MakeVirtualServices(ing, gatewayNames) if err != nil { return err @@ -347,6 +357,47 @@ func (r *Reconciler) reconcileVirtualServices(ctx context.Context, ing *v1alpha1 return nil } +func (r *Reconciler) reconcileDestinationRules(ctx context.Context, ing *v1alpha1.Ingress) error { + var drs = sets.New[string]() + for _, rule := range ing.Spec.Rules { + for _, path := range rule.HTTP.Paths { + // Currently DomainMappings point to the cluster local domain on the local gateway. + // As there is no encryption there yet (https://github.com/knative/serving/issues/13472), + // we cannot use upstream TLS here, so we need to skip it for DomainMappings + if path.RewriteHost != "" { + continue + } + + for _, split := range path.Splits { + svc, err := r.svcLister.Services(split.ServiceNamespace).Get(split.ServiceName) + if err != nil { + return fmt.Errorf("failed to get service: %w", err) + } + + http2 := false + for _, port := range svc.Spec.Ports { + if port.Name == "http2" || port.Name == "h2c" { + http2 = true + } + } + + hostname := pkgnetwork.GetServiceHostname(split.ServiceName, split.ServiceNamespace) + + // skip duplicate entries, as we only need one DR per unique upstream k8s service + if !drs.Has(hostname) { + dr := resources.MakeInternalEncryptionDestinationRule(hostname, ing, http2) + if _, err := istioaccessor.ReconcileDestinationRule(ctx, ing, dr, r); err != nil { + return fmt.Errorf("failed to reconcile DestinationRule: %w", err) + } + drs.Insert(hostname) + } + } + } + } + + return nil +} + func (r *Reconciler) FinalizeKind(ctx context.Context, ing *v1alpha1.Ingress) pkgreconciler.Event { logger := logging.FromContext(ctx) istiocfg := config.FromContext(ctx).Istio @@ -437,6 +488,10 @@ func (r *Reconciler) GetVirtualServiceLister() istiolisters.VirtualServiceLister return r.virtualServiceLister } +func (r *Reconciler) GetDestinationRuleLister() istiolisters.DestinationRuleLister { + return r.destinationRuleLister +} + // qualifiedGatewayNamesFromContext get gateway names from context func qualifiedGatewayNamesFromContext(ctx context.Context) map[v1alpha1.IngressVisibility]sets.Set[string] { ci := config.FromContext(ctx).Istio diff --git a/pkg/reconciler/ingress/ingress_test.go b/pkg/reconciler/ingress/ingress_test.go index 680d756618..32e262767b 100644 --- a/pkg/reconciler/ingress/ingress_test.go +++ b/pkg/reconciler/ingress/ingress_test.go @@ -27,6 +27,7 @@ import ( // Inject our fakes istioclient "knative.dev/net-istio/pkg/client/istio/injection/client" fakeistioclient "knative.dev/net-istio/pkg/client/istio/injection/client/fake" + _ "knative.dev/net-istio/pkg/client/istio/injection/informers/networking/v1beta1/destinationrule/fake" _ "knative.dev/net-istio/pkg/client/istio/injection/informers/networking/v1beta1/gateway/fake" _ "knative.dev/net-istio/pkg/client/istio/injection/informers/networking/v1beta1/virtualservice/fake" fakenetworkingclient "knative.dev/networking/pkg/client/injection/client/fake" @@ -117,6 +118,48 @@ var ( Selector: selector, }, } + ingressServiceHTTP1 = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service", + Namespace: testNS, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 8080, + }, + }, + }, + } + ingressService2HTTP1 = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service-2", + Namespace: testNS, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 8080, + }, + }, + }, + } + ingressServiceHTTP2 = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service", + Namespace: testNS, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http2", + Port: 8080, + }, + }, + }, + } wildcardTLSServer = &istiov1beta1.Server{ Hosts: []string{"*.example.com"}, Port: &istiov1beta1.Port{ @@ -143,7 +186,7 @@ var ( v1alpha1.IngressVisibilityExternalIP: sets.New(config.KnativeIngressGateway), } gateways = map[v1alpha1.IngressVisibility]sets.Set[string]{ - v1alpha1.IngressVisibilityExternalIP: sets.New("knative-test-gateway", config.KnativeIngressGateway), + v1alpha1.IngressVisibilityExternalIP: sets.New[string]("knative-test-gateway", config.KnativeIngressGateway), } perIngressGatewayName = resources.GatewayName(ingressWithTLS("reconciling-ingress", ingressTLS), ingressService) ) @@ -462,7 +505,209 @@ func TestReconcile(t *testing.T) { })) } -func TestReconcile_EnableAutoTLS(t *testing.T) { +func TestReconcile_EnableSystemInternalTLS(t *testing.T) { + table := TableTest{{ + Name: "create DestinationRules single split http1", + SkipNamespaceValidation: true, + Objects: []runtime.Object{ + ing("reconcile-virtualservice"), + ingressServiceHTTP1, + gateway("knative-ingress-gateway", system.Namespace(), []*istiov1beta1.Server{irrelevantServer1}), + gateway("knative-test-gateway", system.Namespace(), []*istiov1beta1.Server{irrelevantServer1}), + }, + WantCreates: []runtime.Object{ + resources.MakeInternalEncryptionDestinationRule("test-service.test-ns.svc.cluster.local", ing("reconcile-virtualservice"), false), + resources.MakeMeshVirtualService(insertProbe(ing("reconcile-virtualservice")), gateways), + resources.MakeIngressVirtualService(insertProbe(ing("reconcile-virtualservice")), + makeGatewayMap([]string{"knative-testing/knative-test-gateway", "knative-testing/" + config.KnativeIngressGateway}, nil)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: ingressWithStatus("reconcile-virtualservice", + v1alpha1.IngressStatus{ + PublicLoadBalancer: &v1alpha1.LoadBalancerStatus{ + Ingress: []v1alpha1.LoadBalancerIngressStatus{ + {DomainInternal: pkgnet.GetServiceHostname("test-ingressgateway", "istio-system")}, + }, + }, + PrivateLoadBalancer: &v1alpha1.LoadBalancerStatus{ + Ingress: []v1alpha1.LoadBalancerIngressStatus{ + {MeshOnly: true}, + }, + }, + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: v1alpha1.IngressConditionLoadBalancerReady, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }, { + Type: v1alpha1.IngressConditionNetworkConfigured, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }, { + Type: v1alpha1.IngressConditionReady, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }}, + }, + }, + ), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", "reconcile-virtualservice"), + Eventf(corev1.EventTypeNormal, "Created", "Created DestinationRule %q", "test-service.test-ns.svc.cluster.local"), + Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "reconcile-virtualservice-mesh"), + Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "reconcile-virtualservice-ingress"), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchAddFinalizerAction("reconcile-virtualservice", "ingresses.networking.internal.knative.dev"), + }, + PostConditions: []func(*testing.T, *TableRow){proberCalledTimes(1)}, + Key: "test-ns/reconcile-virtualservice", + CmpOpts: defaultCmpOptsList, + }, { + Name: "create DestinationRules single split http2", + SkipNamespaceValidation: true, + Objects: []runtime.Object{ + ing("reconcile-virtualservice"), + ingressServiceHTTP2, + gateway("knative-ingress-gateway", system.Namespace(), []*istiov1beta1.Server{irrelevantServer1}), + gateway("knative-test-gateway", system.Namespace(), []*istiov1beta1.Server{irrelevantServer1}), + }, + WantCreates: []runtime.Object{ + resources.MakeInternalEncryptionDestinationRule("test-service.test-ns.svc.cluster.local", ing("reconcile-virtualservice"), true), + resources.MakeMeshVirtualService(insertProbe(ing("reconcile-virtualservice")), gateways), + resources.MakeIngressVirtualService(insertProbe(ing("reconcile-virtualservice")), + makeGatewayMap([]string{"knative-testing/knative-test-gateway", "knative-testing/" + config.KnativeIngressGateway}, nil)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: ingressWithStatus("reconcile-virtualservice", + v1alpha1.IngressStatus{ + PublicLoadBalancer: &v1alpha1.LoadBalancerStatus{ + Ingress: []v1alpha1.LoadBalancerIngressStatus{ + {DomainInternal: pkgnet.GetServiceHostname("test-ingressgateway", "istio-system")}, + }, + }, + PrivateLoadBalancer: &v1alpha1.LoadBalancerStatus{ + Ingress: []v1alpha1.LoadBalancerIngressStatus{ + {MeshOnly: true}, + }, + }, + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: v1alpha1.IngressConditionLoadBalancerReady, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }, { + Type: v1alpha1.IngressConditionNetworkConfigured, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }, { + Type: v1alpha1.IngressConditionReady, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }}, + }, + }, + ), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", "reconcile-virtualservice"), + Eventf(corev1.EventTypeNormal, "Created", "Created DestinationRule %q", "test-service.test-ns.svc.cluster.local"), + Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "reconcile-virtualservice-mesh"), + Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "reconcile-virtualservice-ingress"), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchAddFinalizerAction("reconcile-virtualservice", "ingresses.networking.internal.knative.dev"), + }, + PostConditions: []func(*testing.T, *TableRow){proberCalledTimes(1)}, + Key: "test-ns/reconcile-virtualservice", + CmpOpts: defaultCmpOptsList, + }, { + Name: "create DestinationRules multiple splits", + SkipNamespaceValidation: true, + Objects: []runtime.Object{ + ingWithMultipleSplitsWithStatus("reconcile-virtualservice", v1alpha1.IngressStatus{}), + ingressServiceHTTP1, + ingressService2HTTP1, + gateway("knative-ingress-gateway", system.Namespace(), []*istiov1beta1.Server{irrelevantServer1}), + gateway("knative-test-gateway", system.Namespace(), []*istiov1beta1.Server{irrelevantServer1}), + }, + WantCreates: []runtime.Object{ + resources.MakeInternalEncryptionDestinationRule("test-service.test-ns.svc.cluster.local", ing("reconcile-virtualservice"), false), + resources.MakeInternalEncryptionDestinationRule("test-service-2.test-ns.svc.cluster.local", ing("reconcile-virtualservice"), false), + resources.MakeMeshVirtualService(insertProbe(ingWithMultipleSplitsWithStatus("reconcile-virtualservice", v1alpha1.IngressStatus{})), gateways), + resources.MakeIngressVirtualService(insertProbe(ingWithMultipleSplitsWithStatus("reconcile-virtualservice", v1alpha1.IngressStatus{})), + makeGatewayMap([]string{"knative-testing/knative-test-gateway", "knative-testing/" + config.KnativeIngressGateway}, nil)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: ingWithMultipleSplitsWithStatus("reconcile-virtualservice", + v1alpha1.IngressStatus{ + PublicLoadBalancer: &v1alpha1.LoadBalancerStatus{ + Ingress: []v1alpha1.LoadBalancerIngressStatus{ + {DomainInternal: pkgnet.GetServiceHostname("test-ingressgateway", "istio-system")}, + }, + }, + PrivateLoadBalancer: &v1alpha1.LoadBalancerStatus{ + Ingress: []v1alpha1.LoadBalancerIngressStatus{ + {MeshOnly: true}, + }, + }, + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: v1alpha1.IngressConditionLoadBalancerReady, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }, { + Type: v1alpha1.IngressConditionNetworkConfigured, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }, { + Type: v1alpha1.IngressConditionReady, + Status: corev1.ConditionTrue, + Severity: apis.ConditionSeverityError, + }}, + }, + }, + ), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", "reconcile-virtualservice"), + Eventf(corev1.EventTypeNormal, "Created", "Created DestinationRule %q", "test-service.test-ns.svc.cluster.local"), + Eventf(corev1.EventTypeNormal, "Created", "Created DestinationRule %q", "test-service-2.test-ns.svc.cluster.local"), + Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "reconcile-virtualservice-mesh"), + Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "reconcile-virtualservice-ingress"), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchAddFinalizerAction("reconcile-virtualservice", "ingresses.networking.internal.knative.dev"), + }, + PostConditions: []func(*testing.T, *TableRow){proberCalledTimes(1)}, + Key: "test-ns/reconcile-virtualservice", + CmpOpts: defaultCmpOptsList, + }, + } + + table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler { + r := &Reconciler{ + kubeclient: kubeclient.Get(ctx), + istioClientSet: istioclient.Get(ctx), + virtualServiceLister: listers.GetVirtualServiceLister(), + destinationRuleLister: listers.GetDestinationRuleLister(), + gatewayLister: listers.GetGatewayLister(), + svcLister: listers.GetK8sServiceLister(), + statusManager: ctx.Value(FakeStatusManagerKey).(status.Manager), + } + + testConfig := ReconcilerTestConfig() + testConfig.Network.SystemInternalTLS = netconfig.EncryptionEnabled + return ingressreconciler.NewReconciler(ctx, logging.FromContext(ctx), fakenetworkingclient.Get(ctx), + listers.GetIngressLister(), controller.GetEventRecorder(ctx), r, netconfig.IstioIngressClassName, controller.Options{ + ConfigStore: &testConfigStore{ + config: testConfig, + }}) + })) +} + +func TestReconcile_ExternalDomainTLS(t *testing.T) { table := TableTest{{ Name: "create Ingress Gateway to match newly created Ingress", SkipNamespaceValidation: true, @@ -979,13 +1224,14 @@ func TestReconcile_EnableAutoTLS(t *testing.T) { } r := &Reconciler{ - kubeclient: kubeclient.Get(ctx), - istioClientSet: istioclient.Get(ctx), - virtualServiceLister: listers.GetVirtualServiceLister(), - gatewayLister: listers.GetGatewayLister(), - secretLister: listers.GetSecretLister(), - svcLister: listers.GetK8sServiceLister(), - tracker: &NullTracker{}, + kubeclient: kubeclient.Get(ctx), + istioClientSet: istioclient.Get(ctx), + virtualServiceLister: listers.GetVirtualServiceLister(), + destinationRuleLister: listers.GetDestinationRuleLister(), + gatewayLister: listers.GetGatewayLister(), + secretLister: listers.GetSecretLister(), + svcLister: listers.GetK8sServiceLister(), + tracker: &NullTracker{}, statusManager: &fakestatusmanager.FakeStatusManager{ FakeIsReady: func(ctx context.Context, ing *v1alpha1.Ingress) (bool, error) { return true, nil @@ -1006,8 +1252,8 @@ func TestReconcile_EnableAutoTLS(t *testing.T) { }}, }, Network: &netconfig.Config{ - HTTPProtocol: netconfig.HTTPDisabled, - AutoTLS: true, + HTTPProtocol: netconfig.HTTPDisabled, + ExternalDomainTLS: true, }, }, }, @@ -1160,7 +1406,7 @@ func ReconcilerTestConfig() *config.Config { }}, }, Network: &netconfig.Config{ - AutoTLS: false, + ExternalDomainTLS: false, }, } } @@ -1195,6 +1441,17 @@ func ing(name string) *v1alpha1.Ingress { return ingressWithStatus(name, v1alpha1.IngressStatus{}) } +func ingWithMultipleSplitsWithStatus(name string, status v1alpha1.IngressStatus) *v1alpha1.Ingress { + ing := ingressWithStatus(name, status).DeepCopy() + split1 := ing.Spec.Rules[0].HTTP.Paths[0].Splits[0] + split1.Percent = 50 + split2 := split1.DeepCopy() + split2.ServiceName = ingressService2HTTP1.Name + ing.Spec.Rules[0].HTTP.Paths[0].Splits = []v1alpha1.IngressBackendSplit{split1, *split2} + ing.Spec.Rules[1].HTTP.Paths[0].Splits = []v1alpha1.IngressBackendSplit{split1, *split2} + return ing +} + func ingressWithFinalizers(name string, tls []v1alpha1.IngressTLS, finalizers []string, deletionTime *metav1.Time) *v1alpha1.Ingress { ingress := ingressWithTLS(name, tls) ingress.ObjectMeta.Finalizers = finalizers diff --git a/pkg/reconciler/ingress/resources/destinationrule.go b/pkg/reconciler/ingress/resources/destinationrule.go new file mode 100644 index 0000000000..6bbfa1bad5 --- /dev/null +++ b/pkg/reconciler/ingress/resources/destinationrule.go @@ -0,0 +1,72 @@ +/* +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 ( + istiov1beta1 "istio.io/api/networking/v1beta1" + "istio.io/client-go/pkg/apis/networking/v1beta1" + 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/kmap" + "knative.dev/pkg/kmeta" +) + +// MakeInternalEncryptionDestinationRule creates a DestinationRule that enables upstream TLS +// on for the specified host +func MakeInternalEncryptionDestinationRule(host string, ing *v1alpha1.Ingress, http2 bool) *v1beta1.DestinationRule { + dr := &v1beta1.DestinationRule{ + ObjectMeta: metav1.ObjectMeta{ + Name: host, + Namespace: ing.Namespace, + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(ing)}, + Annotations: ing.GetAnnotations(), + }, + Spec: istiov1beta1.DestinationRule{ + Host: host, + TrafficPolicy: &istiov1beta1.TrafficPolicy{ + Tls: &istiov1beta1.ClientTLSSettings{ + Mode: istiov1beta1.ClientTLSSettings_SIMPLE, + CredentialName: config.ServingRoutingCertName, + SubjectAltNames: []string{ + // SAN used by Activator + certificates.DataPlaneRoutingSAN, + // SAN used by Queue-Proxy in target namespace + certificates.DataPlaneUserSAN(ing.Namespace), + }, + }, + }, + }, + } + + // Populate the Ingress labels. + dr.Labels = kmap.Filter(ing.GetLabels(), func(k string) bool { + return k != RouteLabelKey && k != RouteNamespaceLabelKey + }) + dr.Labels[networking.IngressLabelKey] = ing.Name + + if http2 { + dr.Spec.TrafficPolicy.ConnectionPool = &istiov1beta1.ConnectionPoolSettings{ + Http: &istiov1beta1.ConnectionPoolSettings_HTTPSettings{ + H2UpgradePolicy: istiov1beta1.ConnectionPoolSettings_HTTPSettings_UPGRADE}, + } + } + + return dr +} diff --git a/pkg/reconciler/ingress/resources/destinationrule_test.go b/pkg/reconciler/ingress/resources/destinationrule_test.go new file mode 100644 index 0000000000..6e6f61f35b --- /dev/null +++ b/pkg/reconciler/ingress/resources/destinationrule_test.go @@ -0,0 +1,120 @@ +/* +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" + "google.golang.org/protobuf/testing/protocmp" + istiov1beta1 "istio.io/api/networking/v1beta1" + "istio.io/client-go/pkg/apis/networking/v1beta1" + 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" +) + +var ( + host = "myservice-private.svc.cluster.local" + ing = &v1alpha1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-ingress", + Namespace: "my-namespace", + Annotations: map[string]string{ + "my-annotation": "my-value", + }, + Labels: map[string]string{ + "my-label": "my-value-ignored", + RouteLabelKey: "my-route", + RouteNamespaceLabelKey: "my-route-namespace", + }, + }, + } +) + +func TestMakeInternalEncryptionDestinationRuleHttp1(t *testing.T) { + dr := MakeInternalEncryptionDestinationRule(host, ing, false) + expected := &v1beta1.DestinationRule{ + ObjectMeta: metav1.ObjectMeta{ + Name: host, + Namespace: ing.Namespace, + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(ing)}, + Annotations: map[string]string{ + "my-annotation": "my-value", + }, + Labels: map[string]string{ + networking.IngressLabelKey: "my-ingress", + RouteLabelKey: "my-route", + RouteNamespaceLabelKey: "my-route-namespace", + }, + }, + Spec: istiov1beta1.DestinationRule{ + Host: host, + TrafficPolicy: &istiov1beta1.TrafficPolicy{ + Tls: &istiov1beta1.ClientTLSSettings{ + Mode: istiov1beta1.ClientTLSSettings_SIMPLE, + CredentialName: config.ServingRoutingCertName, + SubjectAltNames: []string{certificates.DataPlaneRoutingSAN, certificates.DataPlaneUserSAN(ing.Namespace)}, + }, + }, + }, + } + + if diff := cmp.Diff(expected, dr, protocmp.Transform()); diff != "" { + t.Error("Unexpected DestinationRule (-want +got):", diff) + } +} + +func TestMakeInternalEncryptionDestinationRuleHttp2(t *testing.T) { + dr := MakeInternalEncryptionDestinationRule(host, ing, true) + expected := &v1beta1.DestinationRule{ + ObjectMeta: metav1.ObjectMeta{ + Name: host, + Namespace: ing.Namespace, + OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(ing)}, + Annotations: map[string]string{ + "my-annotation": "my-value", + }, + Labels: map[string]string{ + networking.IngressLabelKey: "my-ingress", + RouteLabelKey: "my-route", + RouteNamespaceLabelKey: "my-route-namespace", + }, + }, + Spec: istiov1beta1.DestinationRule{ + Host: host, + TrafficPolicy: &istiov1beta1.TrafficPolicy{ + Tls: &istiov1beta1.ClientTLSSettings{ + Mode: istiov1beta1.ClientTLSSettings_SIMPLE, + CredentialName: config.ServingRoutingCertName, + SubjectAltNames: []string{certificates.DataPlaneRoutingSAN, certificates.DataPlaneUserSAN(ing.Namespace)}, + }, + ConnectionPool: &istiov1beta1.ConnectionPoolSettings{ + Http: &istiov1beta1.ConnectionPoolSettings_HTTPSettings{ + H2UpgradePolicy: istiov1beta1.ConnectionPoolSettings_HTTPSettings_UPGRADE}, + }, + }, + }, + } + + if diff := cmp.Diff(expected, dr, protocmp.Transform()); diff != "" { + t.Error("Unexpected DestinationRule (-want +got):", diff) + } +} diff --git a/test/config/system-internal-tls/config-network.yaml b/test/config/system-internal-tls/config-network.yaml new file mode 100644 index 0000000000..b94580ecc3 --- /dev/null +++ b/test/config/system-internal-tls/config-network.yaml @@ -0,0 +1,21 @@ +# Copyright 2020 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: ConfigMap +metadata: + name: config-network + namespace: knative-serving +data: + system-internal-tls: "enabled" diff --git a/test/generate-upstream-cert.sh b/test/generate-upstream-cert.sh new file mode 100755 index 0000000000..220eeac311 --- /dev/null +++ b/test/generate-upstream-cert.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# 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. + +ISTIO_NAMESPACE=istio-system +TEST_NAMESPACE=serving-tests +out_dir="$(mktemp -d /tmp/certs-XXX)" +activatorSAN="kn-routing" +serviceSAN="kn-user-$TEST_NAMESPACE" + +kubectl create ns $TEST_NAMESPACE +kubectl create ns $ISTIO_NAMESPACE + +# Generate Root key and cert. +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=Example/CN=Example' -keyout "${out_dir}"/root.key -out "${out_dir}"/root.crt + +# Create activator key + cert +openssl req -out "${out_dir}"/activator-tls.csr -newkey rsa:2048 -nodes -keyout "${out_dir}"/activator-tls.key -subj "/CN=Example/O=Example" -addext "subjectAltName = DNS:$activatorSAN" +openssl x509 -req -extfile <(printf "subjectAltName=DNS:$activatorSAN") -days 365 -in "${out_dir}"/activator-tls.csr -CA "${out_dir}"/root.crt -CAkey "${out_dir}"/root.key -CAcreateserial -out "${out_dir}"/activator-tls.crt + +# Create test service key + cert +openssl req -out "${out_dir}"/service-tls.csr -newkey rsa:2048 -nodes -keyout "${out_dir}"/service-tls.key -subj "/CN=Example/O=Example" -addext "subjectAltName = DNS:$serviceSAN" +openssl x509 -req -extfile <(printf "subjectAltName=DNS:$serviceSAN") -days 365 -in "${out_dir}"/service-tls.csr -CA "${out_dir}"/root.crt -CAkey "${out_dir}"/root.key -CAcreateserial -out "${out_dir}"/service-tls.crt + +# Override certificate in istio-system namespace with the generated CA +# Delete it first, otherwise istio reconciliation does not work properly +kubectl delete -n ${ISTIO_NAMESPACE} secret routing-serving-certs +kubectl create -n ${ISTIO_NAMESPACE} secret generic routing-serving-certs \ + --from-file=ca.crt="${out_dir}"/root.crt \ + --dry-run=client -o yaml | \ + sed '/^metadata:/a\ \ labels: {"networking.internal.knative.dev/certificate-uid":"test-id"}' | kubectl apply -f - + +# Create test service secret for system-internal-tls +kubectl create -n ${TEST_NAMESPACE} secret tls serving-certs \ + --key="${out_dir}"/service-tls.key \ + --cert="${out_dir}"/service-tls.crt --dry-run=client -o yaml | kubectl apply -f - diff --git a/vendor/knative.dev/networking/pkg/certificates/certs.go b/vendor/knative.dev/networking/pkg/certificates/certs.go new file mode 100644 index 0000000000..16cca6047a --- /dev/null +++ b/vendor/knative.dev/networking/pkg/certificates/certs.go @@ -0,0 +1,184 @@ +/* +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 certificates + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "time" +) + +var randReader = rand.Reader +var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) + +// Create template common to all certificates +func createCertTemplate(expirationInterval time.Duration, sans []string) (*x509.Certificate, error) { + serialNumber, err := rand.Int(randReader, serialNumberLimit) + if err != nil { + return nil, fmt.Errorf("failed to generate serial number: %w", err) + } + + tmpl := x509.Certificate{ + SerialNumber: serialNumber, + SignatureAlgorithm: x509.SHA256WithRSA, + NotBefore: time.Now(), + NotAfter: time.Now().Add(expirationInterval), + BasicConstraintsValid: true, + DNSNames: sans, + } + return &tmpl, nil +} + +// Create cert template suitable for CA and hence signing +func createCACertTemplate(expirationInterval time.Duration) (*x509.Certificate, error) { + rootCert, err := createCertTemplate(expirationInterval, []string{}) + if err != nil { + return nil, err + } + // Make it into a CA cert and change it so we can use it to sign certs + rootCert.IsCA = true + rootCert.KeyUsage = x509.KeyUsageCertSign + rootCert.Subject = pkix.Name{ + Organization: []string{Organization}, + } + return rootCert, nil +} + +// Create cert template that we can use on the client/server for TLS +func createTransportCertTemplate(expirationInterval time.Duration, sans []string) (*x509.Certificate, error) { + cert, err := createCertTemplate(expirationInterval, sans) + if err != nil { + return nil, err + } + cert.KeyUsage = x509.KeyUsageDigitalSignature + cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} + cert.Subject = pkix.Name{ + Organization: []string{Organization}, + CommonName: "control-protocol-certificate", + } + return cert, err +} + +func createCert(template, parent *x509.Certificate, pub, parentPriv interface{}) (certPEM *pem.Block, err error) { + certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, parentPriv) + if err != nil { + return + } + _, err = x509.ParseCertificate(certDER) + if err != nil { + return + } + certPEM = &pem.Block{Type: "CERTIFICATE", Bytes: certDER} + return +} + +// CreateCACerts generates the root CA cert +func CreateCACerts(expirationInterval time.Duration) (*KeyPair, error) { + caKeyPair, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, fmt.Errorf("error generating random key: %w", err) + } + + rootCertTmpl, err := createCACertTemplate(expirationInterval) + if err != nil { + return nil, fmt.Errorf("error generating CA cert: %w", err) + } + + caCertPem, err := createCert(rootCertTmpl, rootCertTmpl, &caKeyPair.PublicKey, caKeyPair) + if err != nil { + return nil, fmt.Errorf("error signing the CA cert: %w", err) + } + caPrivateKeyPem := &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(caKeyPair), + } + return NewKeyPair(caPrivateKeyPem, caCertPem), nil +} + +// Deprecated: CreateControlPlaneCert generates the certificate for the client +func CreateControlPlaneCert(_ context.Context, caKey *rsa.PrivateKey, caCertificate *x509.Certificate, expirationInterval time.Duration) (*KeyPair, error) { + return CreateCert(caKey, caCertificate, expirationInterval, LegacyFakeDnsName) +} + +// Deprecated: CreateDataPlaneCert generates the certificate for the server +func CreateDataPlaneCert(_ context.Context, caKey *rsa.PrivateKey, caCertificate *x509.Certificate, expirationInterval time.Duration) (*KeyPair, error) { + return CreateCert(caKey, caCertificate, expirationInterval, LegacyFakeDnsName) +} + +// CreateCert generates the certificate for use by client and server +func CreateCert(caKey *rsa.PrivateKey, caCertificate *x509.Certificate, expirationInterval time.Duration, sans ...string) (*KeyPair, error) { + + // Then create the private key for the serving cert + keyPair, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, fmt.Errorf("error generating random key: %w", err) + } + + certTemplate, err := createTransportCertTemplate(expirationInterval, sans) + if err != nil { + return nil, fmt.Errorf("failed to create the certificate template: %w", err) + } + + // create a certificate which wraps the public key, sign it with the CA private key + certPEM, err := createCert(certTemplate, caCertificate, &keyPair.PublicKey, caKey) + if err != nil { + return nil, fmt.Errorf("error signing certificate template: %w", err) + } + + privateKeyPEM := &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keyPair), + } + return NewKeyPair(privateKeyPEM, certPEM), nil +} + +// ParseCert parses a certificate/private key pair from serialized pem blocks +func ParseCert(certPemBytes []byte, privateKeyPemBytes []byte) (*x509.Certificate, *rsa.PrivateKey, error) { + certBlock, _ := pem.Decode(certPemBytes) + if certBlock == nil { + return nil, nil, fmt.Errorf("decoding the cert block returned nil") + } + if certBlock.Type != "CERTIFICATE" { + return nil, nil, fmt.Errorf("bad pem block, expecting type 'CERTIFICATE', found %q", certBlock.Type) + } + cert, err := x509.ParseCertificate(certBlock.Bytes) + if err != nil { + return nil, nil, err + } + + pkBlock, _ := pem.Decode(privateKeyPemBytes) + if pkBlock == nil { + return nil, nil, fmt.Errorf("decoding the pk block returned nil") + } + if pkBlock.Type != "RSA PRIVATE KEY" { + return nil, nil, fmt.Errorf("bad pem block, expecting type 'RSA PRIVATE KEY', found %q", pkBlock.Type) + } + pk, err := x509.ParsePKCS1PrivateKey(pkBlock.Bytes) + return cert, pk, err +} + +// CheckExpiry checks the expiration of the certificate +func CheckExpiry(cert *x509.Certificate, rotationThreshold time.Duration) error { + if time.Now().Add(rotationThreshold).After(cert.NotAfter) { + return fmt.Errorf("certificate is going to expire %v", cert.NotAfter) + } + return nil +} diff --git a/vendor/knative.dev/networking/pkg/certificates/constants.go b/vendor/knative.dev/networking/pkg/certificates/constants.go new file mode 100644 index 0000000000..195e269c12 --- /dev/null +++ b/vendor/knative.dev/networking/pkg/certificates/constants.go @@ -0,0 +1,58 @@ +/* +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 certificates + +import "strings" + +const ( + Organization = "knative.dev" + + // nolint:all + LegacyFakeDnsName = "data-plane." + Organization + + // nolint:all + // Deprecated: FakeDnsName is deprecated. + // Please use the DataPlaneRoutingSAN for calls to the Activator + // and the DataPlaneUserSAN function for calls to a Knative-Service via Queue-Proxy. + FakeDnsName = LegacyFakeDnsName + + dataPlaneUserPrefix = "kn-user-" + DataPlaneRoutingSAN = "kn-routing" + + // These keys are meant to line up with cert-manager, see + // https://cert-manager.io/docs/usage/certificate/#additional-certificate-output-formats + CaCertName = "ca.crt" + CertName = "tls.crt" + PrivateKeyName = "tls.key" + + // These should be able to be deprecated some time in the future when the new names are fully adopted + // #nosec + // Deprecated: please use CaCertName instead. + SecretCaCertKey = "ca-cert.pem" + // #nosec + // Deprecated: please use CertName instead. + SecretCertKey = "public-cert.pem" + // #nosec + // Deprecated: please use PrivateKeyName instead. + SecretPKKey = "private-key.pem" +) + +// DataPlaneUserSAN constructs a SAN for a data-plane-user certificate in the +// target namespace of a Knative Service. +func DataPlaneUserSAN(namespace string) string { + return dataPlaneUserPrefix + strings.ToLower(namespace) +} diff --git a/vendor/knative.dev/networking/pkg/certificates/key_pair.go b/vendor/knative.dev/networking/pkg/certificates/key_pair.go new file mode 100644 index 0000000000..67abc42dd4 --- /dev/null +++ b/vendor/knative.dev/networking/pkg/certificates/key_pair.go @@ -0,0 +1,60 @@ +/* +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 certificates + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" +) + +type KeyPair struct { + privateKeyBlock *pem.Block + privateKeyPemBytes []byte + + certBlock *pem.Block + certPemBytes []byte +} + +func NewKeyPair(privateKey *pem.Block, cert *pem.Block) *KeyPair { + return &KeyPair{ + privateKeyBlock: privateKey, + privateKeyPemBytes: pem.EncodeToMemory(privateKey), + certBlock: cert, + certPemBytes: pem.EncodeToMemory(cert), + } +} + +func (kh *KeyPair) PrivateKey() *pem.Block { + return kh.privateKeyBlock +} + +func (kh *KeyPair) PrivateKeyBytes() []byte { + return kh.privateKeyPemBytes +} + +func (kh *KeyPair) Cert() *pem.Block { + return kh.certBlock +} + +func (kh *KeyPair) CertBytes() []byte { + return kh.certPemBytes +} + +func (kh *KeyPair) Parse() (*x509.Certificate, *rsa.PrivateKey, error) { + return ParseCert(kh.certPemBytes, kh.privateKeyPemBytes) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ddf379ac7d..982836f552 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -926,6 +926,7 @@ 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/client/clientset/versioned knative.dev/networking/pkg/client/clientset/versioned/fake knative.dev/networking/pkg/client/clientset/versioned/scheme