Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion pkg/virtualkubelet/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
73 changes: 73 additions & 0 deletions pkg/virtualkubelet/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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)
})
}
}
Loading