diff --git a/go.mod b/go.mod index e57c441f10ca..fb1340e27311 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module knative.dev/serving go 1.24.0 +replace knative.dev/networking => ../networking + require ( github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20210609063737-0067dc6dcea2 github.com/cert-manager/cert-manager v1.16.3 diff --git a/pkg/reconciler/autoscaling/kpa/kpa.go b/pkg/reconciler/autoscaling/kpa/kpa.go index 6a54b8e18855..a18fd2ff08cc 100644 --- a/pkg/reconciler/autoscaling/kpa/kpa.go +++ b/pkg/reconciler/autoscaling/kpa/kpa.go @@ -124,12 +124,13 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, pa *autoscalingv1alpha1. } mode := nv1alpha1.SKSOperationModeProxy + netCfg := config.FromContext(ctx).Network switch { - // When activator CA is enabled, force activator always in path. + // When activator CA is enabled, force activator always in path unless explicitly allowed. // TODO: This is a temporary state and to be fixed. // See also issues/11906 and issues/12797. - case config.FromContext(ctx).Network.SystemInternalTLSEnabled(): + case netCfg.SystemInternalTLSEnabled() && !netCfg.SystemInternalTLSAllowServeMode: mode = nv1alpha1.SKSOperationModeProxy // If the want == -1 and PA is inactive that implies the autoscaler diff --git a/pkg/reconciler/autoscaling/kpa/kpa_test.go b/pkg/reconciler/autoscaling/kpa/kpa_test.go index bb9187d9d39e..d2e196976adc 100644 --- a/pkg/reconciler/autoscaling/kpa/kpa_test.go +++ b/pkg/reconciler/autoscaling/kpa/kpa_test.go @@ -135,6 +135,14 @@ func activatorCertsNetConfig() *netcfg.Config { return nc } +func activatorCertsWithServeModeNetConfig() *netcfg.Config { + nc, _ := netcfg.NewConfigFromMap(map[string]string{ + netcfg.SystemInternalTLSKey: "enabled", + netcfg.SystemInternalTLSAllowServeModeKey: "enabled", + }) + return nc +} + func defaultConfig() *config.Config { ac, _ := asconfig.NewConfigFromMap(defaultConfigMapData()) deploymentConfig, _ := deployment.NewConfigFromMap(map[string]string{ @@ -1192,6 +1200,40 @@ func TestReconcile(t *testing.T) { WantUpdates: []clientgotesting.UpdateActionImpl{{ Object: defaultProxySKS, }}, + }, { + Name: "TLS enabled with allows-serve-mode true, switch to Serve mode", + Key: key, + Ctx: context.WithValue(context.WithValue(context.Background(), netConfigKey{}, activatorCertsWithServeModeNetConfig()), deciderKey{}, + decider(testNamespace, testRevision, defaultScale, /* desiredScale */ + 1 /* ebc */)), + Objects: []runtime.Object{ + kpa(testNamespace, testRevision, WithPASKSReady, WithTraffic, markScaleTargetInitialized, + WithPAMetricsService(privateSvc), withScales(1, defaultScale), + WithPAStatusService(testRevision), WithObservedGeneration(1)), + defaultProxySKS, + metric(testNamespace, testRevision), + defaultDeployment, + defaultReady, + }, + WantUpdates: []clientgotesting.UpdateActionImpl{{ + Object: defaultSKS, + }}, + }, { + Name: "TLS enabled with allows-serve-mode true, already in Serve mode", + Key: key, + Ctx: context.WithValue(context.WithValue(context.Background(), netConfigKey{}, activatorCertsWithServeModeNetConfig()), deciderKey{}, + decider(testNamespace, testRevision, defaultScale, /* desiredScale */ + 1 /* ebc */)), + Objects: []runtime.Object{ + kpa(testNamespace, testRevision, WithPASKSReady, WithTraffic, markScaleTargetInitialized, + WithPAMetricsService(privateSvc), withScales(1, defaultScale), + WithPAStatusService(testRevision), WithObservedGeneration(1)), + defaultSKS, + metric(testNamespace, testRevision), + defaultDeployment, + defaultReady, + }, + // No update - already in Serve mode }} table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler { diff --git a/test/e2e/systeminternaltls/system_internal_tls_test.go b/test/e2e/systeminternaltls/system_internal_tls_test.go index c19b539da2a8..dcf32d6934aa 100644 --- a/test/e2e/systeminternaltls/system_internal_tls_test.go +++ b/test/e2e/systeminternaltls/system_internal_tls_test.go @@ -32,7 +32,9 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" "knative.dev/networking/pkg/apis/networking" + nv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" "knative.dev/networking/pkg/certificates" "knative.dev/networking/pkg/config" "knative.dev/pkg/system" @@ -373,3 +375,109 @@ func TestGracefulShutdownWithTLS(t *testing.T) { t.Log("All requests completed successfully - PreStop hook worked correctly with TLS enabled") } + +// TestSystemInternalTLSWithServeMode tests that when system-internal-tls-allow-serve-mode is enabled, +// the SKS transitions to Serve mode and traffic works correctly with TLS. +func TestSystemInternalTLSWithServeMode(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 as we only need to check one ingress with TLS support.") + } + + clients := test.Setup(t) + + // Enable system-internal-tls-allow-serve-mode + t.Log("Enabling system-internal-tls-allow-serve-mode") + systemNS := os.Getenv(system.NamespaceEnvKey) + cm, err := clients.KubeClient.CoreV1().ConfigMaps(systemNS).Get( + context.Background(), "config-network", v1.GetOptions{}) + if err != nil { + t.Fatalf("Failed to get config-network ConfigMap: %v", err) + } + + originalData := cm.Data["system-internal-tls-allow-serve-mode"] + if cm.Data == nil { + cm.Data = make(map[string]string) + } + cm.Data["system-internal-tls-allow-serve-mode"] = "enabled" + _, err = clients.KubeClient.CoreV1().ConfigMaps(systemNS).Update( + context.Background(), cm, v1.UpdateOptions{}) + if err != nil { + t.Fatalf("Failed to update config-network ConfigMap: %v", err) + } + + // Restore original config on cleanup + test.EnsureCleanup(t, func() { + cm, err := clients.KubeClient.CoreV1().ConfigMaps(systemNS).Get( + context.Background(), "config-network", v1.GetOptions{}) + if err != nil { + t.Logf("Warning: Failed to get config-network ConfigMap during cleanup: %v", err) + return + } + if originalData == "" { + delete(cm.Data, "system-internal-tls-allow-serve-mode") + } else { + cm.Data["system-internal-tls-allow-serve-mode"] = originalData + } + if _, err := clients.KubeClient.CoreV1().ConfigMaps(systemNS).Update( + context.Background(), cm, v1.UpdateOptions{}); err != nil { + t.Logf("Warning: Failed to restore config-network ConfigMap: %v", err) + } + }) + + names := test.ResourceNames{ + Service: test.ObjectNameForTest(t), + Image: test.HelloWorld, + } + + test.EnsureTearDown(t, clients, &names) + + t.Log("Creating a new Service with min-scale=1 and target-burst-capacity=0") + resources, err := v1test.CreateServiceReady(t, clients, &names, + rtesting.WithConfigAnnotations(map[string]string{ + // These annotations ensure the service will be in Serve mode: + // - min-scale=1 ensures want > 0 + // - target-burst-capacity=0 ensures excess burst capacity >= 0 + autoscaling.MinScaleAnnotationKey: "1", + autoscaling.TargetBurstCapacityKey: "0", + })) + if err != nil { + t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err) + } + + // Wait for SKS to be in Serve mode + t.Log("Waiting for SKS to transition to Serve mode") + var sksMode nv1alpha1.ServerlessServiceOperationMode + if err := wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute, true, func(context.Context) (bool, error) { + sks, err := clients.NetworkingClient.ServerlessServices.Get( + context.Background(), resources.Revision.Name, v1.GetOptions{}) + if err != nil { + return false, nil //nolint:nilerr + } + sksMode = sks.Spec.Mode + t.Logf("Current SKS mode: %s", sksMode) + return sksMode == nv1alpha1.SKSOperationModeServe, nil + }); err != nil { + t.Fatalf("SKS did not transition to Serve mode (current mode: %s): %v", sksMode, err) + } + + t.Log("SKS is in Serve mode, verifying service works correctly") + url := resources.Route.Status.URL.URL() + if _, err := pkgTest.CheckEndpointState( + context.Background(), + clients.KubeClient, + t.Logf, + url, + spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(test.HelloWorldText)), + "HelloWorldText", + test.ServingFlags.ResolvableDomain, + test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS), + ); err != nil { + t.Fatalf("The endpoint %s didn't serve the expected text %q: %v", url, test.HelloWorldText, err) + } + + t.Log("Service works correctly in Serve mode with system-internal-tls enabled") +} diff --git a/vendor/knative.dev/networking/config/config-network.yaml b/vendor/knative.dev/networking/config/config-network.yaml index 7d5c93945d44..c6ecbeea75ba 100644 --- a/vendor/knative.dev/networking/config/config-network.yaml +++ b/vendor/knative.dev/networking/config/config-network.yaml @@ -22,7 +22,7 @@ metadata: app.kubernetes.io/component: networking app.kubernetes.io/version: devel annotations: - knative.dev/example-checksum: "0573e07d" + knative.dev/example-checksum: "e93fd63e" data: _example: | ################################ @@ -149,6 +149,19 @@ data: # for now. Use with caution. system-internal-tls: "Disabled" + # system-internal-tls-allow-serve-mode controls whether Serve mode is allowed + # when system-internal-tls is enabled. By default, enabling system-internal-tls + # forces all traffic to use Proxy mode because not all Knative ingress implementations + # support Serve mode with TLS enabled. Set this to "Enabled" only if your ingress + # implementation supports direct TLS connections to application containers in Serve mode. + # + # Possible values for this flag are: + # - Disabled (default): system-internal-tls forces Proxy mode for compatibility. + # - Enabled: Serve mode is allowed even with TLS enabled (requires ingress support). + # NOTE: This flag is in an alpha state and is mostly here to enable internal testing + # for now. Use with caution. + system-internal-tls-allow-serve-mode: "Disabled" + # Controls the behavior of the HTTP endpoint for the Knative ingress. # It requires auto-tls to be enabled. # - Enabled: The Knative ingress will be able to serve HTTP connection. diff --git a/vendor/knative.dev/networking/pkg/config/config.go b/vendor/knative.dev/networking/pkg/config/config.go index b9fca8585c24..2827fb3c3a37 100644 --- a/vendor/knative.dev/networking/pkg/config/config.go +++ b/vendor/knative.dev/networking/pkg/config/config.go @@ -140,6 +140,12 @@ const ( // SystemInternalTLSKey is the name of the configuration whether // traffic between Knative system components is encrypted or not. SystemInternalTLSKey = "system-internal-tls" + + // SystemInternalTLSAllowServeModeKey is the name of the configuration that + // specifies whether Serve mode is allowed when system-internal-tls is enabled. + // When false (default), system-internal-tls forces Proxy mode. + // When true, Serve mode is allowed even with TLS enabled. + SystemInternalTLSAllowServeModeKey = "system-internal-tls-allow-serve-mode" ) // CertificateType indicates the type of Knative Certificate. @@ -303,26 +309,32 @@ type Config struct { // SystemInternalTLS specifies whether knative internal traffic is encrypted or not. SystemInternalTLS EncryptionConfig + // SystemInternalTLSAllowServeMode specifies whether Serve mode is allowed + // when SystemInternalTLS is enabled. When false (default), enabling TLS + // forces Proxy mode. When true, Serve mode is permitted even with TLS enabled. + SystemInternalTLSAllowServeMode bool + // ClusterLocalDomainTLS specifies whether cluster-local traffic is encrypted or not. ClusterLocalDomainTLS EncryptionConfig } func defaultConfig() *Config { return &Config{ - DefaultIngressClass: IstioIngressClassName, - DefaultCertificateClass: CertManagerCertificateClassName, - DomainTemplate: DefaultDomainTemplate, - TagTemplate: DefaultTagTemplate, - AutoTLS: false, - ExternalDomainTLS: false, - NamespaceWildcardCertSelector: nil, - HTTPProtocol: HTTPEnabled, - AutocreateClusterDomainClaims: false, - DefaultExternalScheme: "http", - MeshCompatibilityMode: MeshCompatibilityModeAuto, - InternalEncryption: false, - SystemInternalTLS: EncryptionDisabled, - ClusterLocalDomainTLS: EncryptionDisabled, + DefaultIngressClass: IstioIngressClassName, + DefaultCertificateClass: CertManagerCertificateClassName, + DomainTemplate: DefaultDomainTemplate, + TagTemplate: DefaultTagTemplate, + AutoTLS: false, + ExternalDomainTLS: false, + NamespaceWildcardCertSelector: nil, + HTTPProtocol: HTTPEnabled, + AutocreateClusterDomainClaims: false, + DefaultExternalScheme: "http", + MeshCompatibilityMode: MeshCompatibilityModeAuto, + InternalEncryption: false, + SystemInternalTLS: EncryptionDisabled, + SystemInternalTLSAllowServeMode: false, + ClusterLocalDomainTLS: EncryptionDisabled, } } @@ -455,6 +467,10 @@ func NewConfigFromMap(data map[string]string) (*Config, error) { ClusterLocalDomainTLSKey, data[ClusterLocalDomainTLSKey]) } + if val, ok := data[SystemInternalTLSAllowServeModeKey]; ok { + nc.SystemInternalTLSAllowServeMode = strings.EqualFold(val, "enabled") + } + return nc, nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index a9c18790a800..22ce693e050a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1475,7 +1475,7 @@ knative.dev/caching/pkg/client/listers/caching/v1alpha1 # knative.dev/hack v0.0.0-20251021013703-4fae78067103 ## explicit; go 1.24 knative.dev/hack -# knative.dev/networking v0.0.0-20251021092443-0bde19154dce +# knative.dev/networking v0.0.0-20251021092443-0bde19154dce => ../networking ## explicit; go 1.24.0 knative.dev/networking/config knative.dev/networking/pkg @@ -1662,3 +1662,4 @@ sigs.k8s.io/structured-merge-diff/v4/value ## explicit; go 1.22 sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 +# knative.dev/networking => ../networking