Skip to content

Commit

Permalink
Add support for internal-encryption in net-istio
Browse files Browse the repository at this point in the history
  • Loading branch information
ReToCode committed Feb 20, 2023
1 parent ef720f6 commit eb52d02
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 22 deletions.
23 changes: 23 additions & 0 deletions config/700-istio-secret.yaml
Original file line number Diff line number Diff line change
@@ -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: knative-serving-certs
namespace: istio-system
labels:
serving-certs-ctrl: "data-plane"
networking.internal.knative.dev/certificate-uid: "serving-certs"
# The data is populated when internal-encryption is enabled.
39 changes: 35 additions & 4 deletions pkg/reconciler/serverlessservice/resources/destinationrule.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ limitations under the License.
package resources

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

istiov1alpha3 "istio.io/api/networking/v1alpha3"
"istio.io/client-go/pkg/apis/networking/v1alpha3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/networking/pkg/apis/networking/v1alpha1"
"knative.dev/pkg/kmeta"
pkgnetwork "knative.dev/pkg/network"
Expand All @@ -29,11 +28,17 @@ import (
const (
subsetNormal = "normal"
subsetDirect = "direct"

// has to match config/700-istio-secret.yaml
knativeServingCertsSecret = "knative-serving-certs"

// has to match https://github.com/knative-sandbox/control-protocol/blob/main/pkg/certificates/constants.go#L21
knativeFakeDnsName = "data-plane.knative.dev"
)

// MakeDestinationRule creates a DestinationRule that defines a "normal" and a "direct"
// MakeMeshAddressableDestinationRule creates a DestinationRule that defines a "normal" and a "direct"
// loadbalancer for the service in question, to allow for pod addressability, even in mesh.
func MakeDestinationRule(sks *v1alpha1.ServerlessService) *v1alpha3.DestinationRule {
func MakeMeshAddressableDestinationRule(sks *v1alpha1.ServerlessService) *v1alpha3.DestinationRule {
ns := sks.Namespace
name := sks.Status.PrivateServiceName
host := pkgnetwork.GetServiceHostname(name, ns)
Expand Down Expand Up @@ -68,3 +73,29 @@ func MakeDestinationRule(sks *v1alpha1.ServerlessService) *v1alpha3.DestinationR
},
}
}

// MakeInternalEncryptionDestinationRule creates a DestinationRule that enables upstream TLS
// on the sks PrivateService host
func MakeInternalEncryptionDestinationRule(sks *v1alpha1.ServerlessService) *v1alpha3.DestinationRule {
ns := sks.Namespace
name := sks.Status.ServiceName
host := pkgnetwork.GetServiceHostname(name, ns)

return &v1alpha3.DestinationRule{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(sks)},
},
Spec: istiov1alpha3.DestinationRule{
Host: host,
TrafficPolicy: &istiov1alpha3.TrafficPolicy{
Tls: &istiov1alpha3.ClientTLSSettings{
Mode: istiov1alpha3.ClientTLSSettings_SIMPLE,
CredentialName: knativeServingCertsSecret,
SubjectAltNames: []string{knativeFakeDnsName},
},
},
},
}
}
29 changes: 22 additions & 7 deletions pkg/reconciler/serverlessservice/serverlessservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package serverlessservice

import (
"context"
"errors"
"fmt"

istioclientset "knative.dev/net-istio/pkg/client/istio/clientset/versioned"
Expand Down Expand Up @@ -48,24 +49,38 @@ var (

// Reconcile compares the actual state with the desired, and attempts to converge the two.
func (r *reconciler) ReconcileKind(ctx context.Context, sks *netv1alpha1.ServerlessService) pkgreconciler.Event {
if !config.FromContext(ctx).Network.EnableMeshPodAddressability {
networkCfg := config.FromContext(ctx).Network
if !networkCfg.EnableMeshPodAddressability && !networkCfg.InternalEncryption {
// Just ignore if we're disabled.
return nil
}

if networkCfg.EnableMeshPodAddressability && networkCfg.InternalEncryption {
return errors.New("failed to reconcile VirtualService. EnableMeshPodAddressability and InternalEncryption cannot both be enabled")
}

if sks.Status.PrivateServiceName == "" {
// No private service yet, nothing to do here.
return nil
}

vs := resources.MakeVirtualService(sks)
if _, err := istioaccessor.ReconcileVirtualService(ctx, sks, vs, r); err != nil {
return fmt.Errorf("failed to reconcile VirtualService: %w", err)
if networkCfg.EnableMeshPodAddressability {
vs := resources.MakeVirtualService(sks)
if _, err := istioaccessor.ReconcileVirtualService(ctx, sks, vs, r); err != nil {
return fmt.Errorf("failed to reconcile VirtualService: %w", err)
}

dr := resources.MakeMeshAddressableDestinationRule(sks)
if _, err := istioaccessor.ReconcileDestinationRule(ctx, sks, dr, r); err != nil {
return fmt.Errorf("failed to reconcile DestinationRule: %w", err)
}
}

dr := resources.MakeDestinationRule(sks)
if _, err := istioaccessor.ReconcileDestinationRule(ctx, sks, dr, r); err != nil {
return fmt.Errorf("failed to reconcile DestinationRule: %w", err)
if networkCfg.InternalEncryption {
dr := resources.MakeInternalEncryptionDestinationRule(sks)
if _, err := istioaccessor.ReconcileDestinationRule(ctx, sks, dr, r); err != nil {
return fmt.Errorf("failed to reconcile DestinationRule: %w", err)
}
}

return nil
Expand Down
146 changes: 135 additions & 11 deletions pkg/reconciler/serverlessservice/serverlessservice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func sks(name string) *netv1alpha1.ServerlessService {
Name: name,
},
Status: netv1alpha1.ServerlessServiceStatus{
ServiceName: name + "-foo",
PrivateServiceName: name + "-foo",
},
}
Expand All @@ -68,11 +69,48 @@ func vs(name string) *istiov1alpha3.VirtualService {
return resources.MakeVirtualService(sks(name))
}

func dr(name string) *istiov1alpha3.DestinationRule {
return resources.MakeDestinationRule(sks(name))
func meshAddressableDr(name string) *istiov1alpha3.DestinationRule {
return resources.MakeMeshAddressableDestinationRule(sks(name))
}

func TestReconcile(t *testing.T) {
func internalEncryptionDr(name string) *istiov1alpha3.DestinationRule {
return resources.MakeInternalEncryptionDestinationRule(sks(name))
}

func TestReconcileInvalidConfig(t *testing.T) {
table := TableTest{{
Name: "invalid network configuration",
Key: "testing/test",
Objects: []runtime.Object{
sks("test"),
},
WantErr: true,
WantEvents: []string{
Eventf(corev1.EventTypeWarning, "InternalError", "failed to reconcile VirtualService. EnableMeshPodAddressability and InternalEncryption cannot both be enabled"),
},
}}
table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler {
r := &reconciler{
istioclient: istioclient.Get(ctx),
virtualServiceLister: listers.GetVirtualServiceLister(),
destinationRuleLister: listers.GetDestinationRuleLister(),
}
return sksreconciler.NewReconciler(ctx, logging.FromContext(ctx), fakenetworkingclient.Get(ctx),
listers.GetServerlessServiceLister(), controller.GetEventRecorder(ctx), r, controller.Options{
ConfigStore: &testConfigStore{
config: &config.Config{
Istio: &config.Istio{},
Network: &netconfig.Config{
EnableMeshPodAddressability: true,
InternalEncryption: true,
},
},
},
})
}))
}

func TestReconcileMeshAddressable(t *testing.T) {
table := TableTest{{
Name: "bad workqueue key",
Key: "too/many/parts",
Expand All @@ -85,7 +123,7 @@ func TestReconcile(t *testing.T) {
Objects: []runtime.Object{
sks("test"),
vs("test"),
dr("test"),
meshAddressableDr("test"),
},
CmpOpts: defaultCmpOpts,
}, {
Expand All @@ -96,7 +134,7 @@ func TestReconcile(t *testing.T) {
},
WantCreates: []runtime.Object{
vs("test"),
dr("test"),
meshAddressableDr("test"),
},
WantEvents: []string{
Eventf(corev1.EventTypeNormal, "Created", "Created VirtualService %q", "test-foo"),
Expand All @@ -108,7 +146,7 @@ func TestReconcile(t *testing.T) {
Key: "testing/test",
Objects: []runtime.Object{
sks("test"),
dr("test"),
meshAddressableDr("test"),
},
WantCreates: []runtime.Object{
vs("test"),
Expand All @@ -125,7 +163,7 @@ func TestReconcile(t *testing.T) {
vs("test"),
},
WantCreates: []runtime.Object{
dr("test"),
meshAddressableDr("test"),
},
WantEvents: []string{
Eventf(corev1.EventTypeNormal, "Created", "Created DestinationRule %q", "test-foo"),
Expand All @@ -142,15 +180,15 @@ func TestReconcile(t *testing.T) {
return virtualService
}(),
func() *istiov1alpha3.DestinationRule {
destinationRule := dr("test")
destinationRule := meshAddressableDr("test")
destinationRule.Spec.Host = "foo"
return destinationRule
}(),
},
WantUpdates: []clientgotesting.UpdateActionImpl{{
Object: vs("test"),
}, {
Object: dr("test"),
Object: meshAddressableDr("test"),
}},
WantEvents: []string{
Eventf(corev1.EventTypeNormal, "Updated", "Updated VirtualService %s", "testing/test-foo"),
Expand All @@ -166,7 +204,7 @@ func TestReconcile(t *testing.T) {
},
Objects: []runtime.Object{
sks("test"),
dr("test"),
meshAddressableDr("test"),
},
WantCreates: []runtime.Object{
vs("test"),
Expand All @@ -188,7 +226,7 @@ func TestReconcile(t *testing.T) {
vs("test"),
},
WantCreates: []runtime.Object{
dr("test"),
meshAddressableDr("test"),
},
WantEvents: []string{
Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create DestinationRule %s: inducing failure for create destinationrules", "testing/test-foo"),
Expand Down Expand Up @@ -217,6 +255,92 @@ func TestReconcile(t *testing.T) {
}))
}

func TestReconcileInternalEncryption(t *testing.T) {
table := TableTest{{
Name: "bad workqueue key",
Key: "too/many/parts",
}, {
Name: "key not found",
Key: "foo/not-found",
}, {
Name: "stable state",
Key: "testing/test",
Objects: []runtime.Object{
sks("test"),
internalEncryptionDr("test"),
},
CmpOpts: defaultCmpOpts,
}, {
Name: "create DestinationRule",
Key: "testing/test",
Objects: []runtime.Object{
sks("test"),
},
WantCreates: []runtime.Object{
internalEncryptionDr("test"),
},
WantEvents: []string{
Eventf(corev1.EventTypeNormal, "Created", "Created DestinationRule %q", "test-foo"),
},
CmpOpts: defaultCmpOpts,
}, {
Name: "fix DestinationRule",
Key: "testing/test",
Objects: []runtime.Object{
sks("test"),
func() *istiov1alpha3.DestinationRule {
destinationRule := internalEncryptionDr("test")
destinationRule.Spec.Host = "foo"
return destinationRule
}(),
},
WantUpdates: []clientgotesting.UpdateActionImpl{{
Object: internalEncryptionDr("test"),
}},
WantEvents: []string{
Eventf(corev1.EventTypeNormal, "Updated", "Updated DestinationRule %s", "testing/test-foo"),
},
CmpOpts: defaultCmpOpts,
}, {
Name: "failure for DestinationRule",
Key: "testing/test",
WantErr: true,
WithReactors: []clientgotesting.ReactionFunc{
InduceFailure("create", "destinationrules"),
},
Objects: []runtime.Object{
sks("test"),
},
WantCreates: []runtime.Object{
internalEncryptionDr("test"),
},
WantEvents: []string{
Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create DestinationRule %s: inducing failure for create destinationrules", "testing/test-foo"),
Eventf(corev1.EventTypeWarning, "InternalError", "failed to reconcile DestinationRule: failed to create DestinationRule: inducing failure for create destinationrules"),
},
CmpOpts: defaultCmpOpts,
}}
table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler {
r := &reconciler{
istioclient: istioclient.Get(ctx),
virtualServiceLister: listers.GetVirtualServiceLister(),
destinationRuleLister: listers.GetDestinationRuleLister(),
}

return sksreconciler.NewReconciler(ctx, logging.FromContext(ctx), fakenetworkingclient.Get(ctx),
listers.GetServerlessServiceLister(), controller.GetEventRecorder(ctx), r, controller.Options{
ConfigStore: &testConfigStore{
config: &config.Config{
Istio: &config.Istio{},
Network: &netconfig.Config{
InternalEncryption: true,
},
},
},
})
}))
}

type testConfigStore struct {
config *config.Config
}
Expand Down

0 comments on commit eb52d02

Please sign in to comment.