From 388b34c9ad0b6423d0fc43eb562d8d61f346ae92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:59:35 +0000 Subject: [PATCH 1/2] Initial plan From eacd2e7edde21af73ee1c46c4863628eb6a2d545 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:12:27 +0000 Subject: [PATCH 2/2] Fix malformed oneliner ca.crt in projected volumes Add normalizePEMCertificate() to convert single-line PEM certs (produced by tools like helm --set that collapse newlines) back to proper multi-line PEM format before storing in projected volume data. Fixes: Malformed oneliner in ca.crt for projected volumes Agent-Logs-Url: https://github.com/interlink-hq/interLink/sessions/37d0f278-c6f8-4a1f-b1ac-9d76be1ed15b Co-authored-by: dciangot <4144326+dciangot@users.noreply.github.com> --- pkg/virtualkubelet/execute.go | 27 ++++++++++- pkg/virtualkubelet/execute_test.go | 73 ++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/pkg/virtualkubelet/execute.go b/pkg/virtualkubelet/execute.go index 98737d37..be18b5cc 100644 --- a/pkg/virtualkubelet/execute.go +++ b/pkg/virtualkubelet/execute.go @@ -715,6 +715,31 @@ func addKubernetesServicesEnvVars(ctx context.Context, config Config, pod *v1.Po pod.Name, " k8s addr ", config.KubernetesAPIAddr, " k8s port ", config.KubernetesAPIPort) } +// normalizePEMCertificate converts a single-line "oneliner" PEM certificate to proper +// multi-line format. Some tools (e.g., helm --set) may store PEM certificates on a single +// line with spaces instead of newlines, resulting in a malformed cert like: +// +// -----BEGIN CERTIFICATE----- MIIB... -----END CERTIFICATE----- +// +// This function restores the newlines around PEM header and footer markers. +func normalizePEMCertificate(cert string) string { + if !strings.Contains(cert, "-----BEGIN ") || !strings.Contains(cert, "-----END ") { + // Not a complete PEM certificate, return as-is. + return cert + } + if strings.Contains(cert, "\n") { + // Already multi-line, return as-is. + return cert + } + // The cert is a "oneliner": spaces were used instead of newlines around markers. + // Trim surrounding whitespace, then convert "----- " (end of header line) and + // " -----" (start of footer line) to newlines. + cert = strings.TrimSpace(cert) + result := strings.ReplaceAll(cert, "----- ", "-----\n") + result = strings.ReplaceAll(result, " -----", "\n-----") + return result +} + // Handle projected sources and fills the projectedVolume object. func remoteExecutionHandleProjectedSource( ctx context.Context, p *Provider, pod *v1.Pod, source v1.VolumeProjection, projectedVolume *v1.ConfigMap, @@ -779,7 +804,7 @@ func remoteExecutionHandleProjectedSource( name: my-config */ const kubeCaCrt = "kube-root-ca.crt" - overrideCaCrt := p.config.KubernetesAPICaCrt + overrideCaCrt := normalizePEMCertificate(p.config.KubernetesAPICaCrt) if source.ConfigMap.Name == kubeCaCrt && overrideCaCrt != "" { log.G(ctx).Debug("handling special case of Kubernetes API kube-root-ca.crt, override found, using provided ca.crt:, ", overrideCaCrt) if len(source.ConfigMap.Items) == 0 { diff --git a/pkg/virtualkubelet/execute_test.go b/pkg/virtualkubelet/execute_test.go index 5d76b9a0..b699b870 100644 --- a/pkg/virtualkubelet/execute_test.go +++ b/pkg/virtualkubelet/execute_test.go @@ -358,6 +358,28 @@ func TestRemoteExecutionHandleProjectedSourceConfigMap(t *testing.T) { "ca.crt": "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----\n", }, }, + { + name: "kube-root-ca.crt without items with oneliner override is normalized", + configMapName: "kube-root-ca.crt", + configMapData: nil, + sourceItems: nil, + overrideCaCrt: "-----BEGIN CERTIFICATE----- MIIBIjAN -----END CERTIFICATE-----", + expectedData: map[string]string{ + "ca.crt": "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----", + }, + }, + { + name: "kube-root-ca.crt with items with oneliner override is normalized", + configMapName: "kube-root-ca.crt", + configMapData: nil, + sourceItems: []v1.KeyToPath{ + {Key: "ca.crt", Path: "ca.crt"}, + }, + overrideCaCrt: "-----BEGIN CERTIFICATE----- MIIBIjAN -----END CERTIFICATE-----", + expectedData: map[string]string{ + "ca.crt": "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----", + }, + }, { name: "missing key in items returns error", configMapName: "my-config", @@ -419,3 +441,54 @@ func TestRemoteExecutionHandleProjectedSourceConfigMap(t *testing.T) { }) } } + +func TestNormalizePEMCertificate(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "already multi-line cert is returned as-is", + input: "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----\n", + expected: "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----\n", + }, + { + name: "oneliner cert is normalized to multi-line", + input: "-----BEGIN CERTIFICATE----- MIIBIjAN -----END CERTIFICATE-----", + expected: "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----", + }, + { + name: "oneliner cert with multi-segment base64 is normalized", + input: "-----BEGIN CERTIFICATE----- MIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8A -----END CERTIFICATE-----", + expected: "-----BEGIN CERTIFICATE-----\nMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8A\n-----END CERTIFICATE-----", + }, + { + name: "leading and trailing whitespace is trimmed", + input: " -----BEGIN CERTIFICATE----- MIIBIjAN -----END CERTIFICATE----- ", + expected: "-----BEGIN CERTIFICATE-----\nMIIBIjAN\n-----END CERTIFICATE-----", + }, + { + name: "non-PEM value is returned unchanged", + input: "not-a-certificate", + expected: "not-a-certificate", + }, + { + name: "incomplete PEM (missing END marker) is returned unchanged", + input: "-----BEGIN CERTIFICATE----- MIIBIjAN", + expected: "-----BEGIN CERTIFICATE----- MIIBIjAN", + }, + { + name: "empty string is returned unchanged", + input: "", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := normalizePEMCertificate(tt.input) + assert.Equal(t, tt.expected, got) + }) + } +}