From 62aa34b392fee5f01e3ada1b16c47fa068df289f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= Date: Thu, 19 Mar 2026 13:53:47 +0100 Subject: [PATCH] Strip text from x509 certificate before creating the secret (bsc#1259720) Some characters in the secret content are supposed to mess up with podman secret's internal JSON input. Stripping the text form from the certificate sanitizes the input. --- mgradm/shared/podman/ssl.go | 5 +- shared/ssl/ssl.go | 69 ++++++++++++++------- shared/ssl/ssl_test.go | 11 ++++ uyuni-tools.changes.cbosdo.5.1-cert-cleanup | 2 + 4 files changed, 62 insertions(+), 25 deletions(-) create mode 100644 uyuni-tools.changes.cbosdo.5.1-cert-cleanup diff --git a/mgradm/shared/podman/ssl.go b/mgradm/shared/podman/ssl.go index 0e7d53181de..ee7ea2ba50f 100644 --- a/mgradm/shared/podman/ssl.go +++ b/mgradm/shared/podman/ssl.go @@ -70,8 +70,8 @@ func prepareThirdPartyCertificate( // Create secrets for CA return shared_podman.CreateTLSSecrets( - caSecretName, path.Join(tempDir, "ca.crt"), - certSecretName, path.Join(tempDir, "server.crt"), + caSecretName, caPath, + certSecretName, serverCertPath, keySecretName, pair.Key, ) } @@ -468,6 +468,7 @@ func reuseExistingCertificatesFromMounts( log.Info().Msgf(L("Certificate file %s not found."), crtCheckPath) return false, nil } + cert = ssl.StripTextFromCertificate(string(cert)) if err = os.WriteFile(serverCert, cert, 0444); err != nil { return true, utils.Error(err, L("cannot write existing server certificate")) } diff --git a/shared/ssl/ssl.go b/shared/ssl/ssl.go index 3f509c8fa23..7bd3e2c6f55 100644 --- a/shared/ssl/ssl.go +++ b/shared/ssl/ssl.go @@ -112,28 +112,58 @@ func findServerCert(certs []certificate) (*certificate, error) { return nil, errors.New(L("expected to find a certificate, got none")) } -func readCertificates(path string) ([]certificate, error) { - fd, err := os.Open(path) +// processOpenSSL is the generic core that handles the piping logic. +func processOpenSSL(r io.Reader) ([][]byte, error) { + data, err := io.ReadAll(r) if err != nil { - return []certificate{}, utils.Errorf(err, L("Failed to read certificate file %s"), path) + return nil, err } - certs := []certificate{} + var results [][]byte + rest := data + for { - log.Debug().Msgf("Running openssl x509 on %s", path) + var block *pem.Block + block, rest = pem.Decode(rest) + if block == nil { + break // No more PEM blocks found + } + + // Re-encode just this one block to pass to OpenSSL + singleCert := pem.EncodeToMemory(block) + cmd := exec.Command("openssl", "x509") - cmd.Stdin = fd + cmd.Stdin = bytes.NewReader(singleCert) out, err := cmd.Output() + if err == nil && len(out) > 0 { + results = append(results, out) + } - if err != nil { - // openssl got an invalid certificate or the end of the file + if len(rest) == 0 { break } + } + + return results, nil +} - // Extract data from the certificate - cert, err := extractCertificateData(out) +func readCertificates(path string) ([]certificate, error) { + fd, err := os.Open(path) + if err != nil { + return []certificate{}, utils.Errorf(err, L("Failed to read certificate file %s"), path) + } + defer fd.Close() + + rawCerts, err := processOpenSSL(fd) + if err != nil { + return nil, err + } + + var certs []certificate + for _, raw := range rawCerts { + cert, err := extractCertificateData(raw) if err != nil { - return []certificate{}, err + return nil, err } certs = append(certs, cert) } @@ -336,19 +366,12 @@ func GetRsaKey(keyContent string, password string) []byte { // StripTextFromCertificate removes the optional text part of an x509 certificate. func StripTextFromCertificate(certContent string) []byte { - cmd := exec.Command("openssl", "x509") - stdin, err := cmd.StdinPipe() - if err != nil { - log.Fatal().Err(err).Msg(L("Failed to open openssl x509 process input stream")) + reader := bytes.NewBufferString(certContent) + rawCerts, err := processOpenSSL(reader) + if err != nil || len(rawCerts) == 0 { + return nil } - if _, err := io.WriteString(stdin, certContent); err != nil { - log.Fatal().Err(err).Msg(L("Failed to write SSL certificate to input stream")) - } - out, err := cmd.Output() - if err != nil { - log.Fatal().Err(err).Msg(L("failed to strip text part from CA certificate")) - } - return out + return bytes.Join(rawCerts, []byte("")) } var newRunner = utils.NewRunner diff --git a/shared/ssl/ssl_test.go b/shared/ssl/ssl_test.go index 680589337a3..4bbd89d7ad5 100644 --- a/shared/ssl/ssl_test.go +++ b/shared/ssl/ssl_test.go @@ -5,6 +5,7 @@ package ssl import ( + "bytes" "fmt" "os" "path" @@ -247,3 +248,13 @@ func TestSHA256Fingerprints(t *testing.T) { "7C:30:C7:12:03:64:D0:B3:FF:AA:B2:7C:1E:F1:E9:60:75:C8:A7:81:63:F0:6D:E9:EB:88:D6:2E:F2:32:B2:69", fingerprints[1]) } + +func TestStripTextFromCertificate(t *testing.T) { + cert := testutils.ReadFile(t, "testdata/chain2/spacewalk.crt") + actual := StripTextFromCertificate(cert) + testutils.AssertEquals(t, "Unexpected number of certificates starts", + 3, bytes.Count(actual, []byte("-----BEGIN CERTIFICATE-----\n"))) + testutils.AssertEquals(t, "Unexpected number of certificates ends", + 3, bytes.Count(actual, []byte("\n-----END CERTIFICATE-----\n"))) + testutils.AssertTrue(t, "The stripped cert shouldn't contain text", !bytes.Contains(actual, []byte("Data:"))) +} diff --git a/uyuni-tools.changes.cbosdo.5.1-cert-cleanup b/uyuni-tools.changes.cbosdo.5.1-cert-cleanup new file mode 100644 index 00000000000..4281dbc8815 --- /dev/null +++ b/uyuni-tools.changes.cbosdo.5.1-cert-cleanup @@ -0,0 +1,2 @@ +- Strip text from x509 certs before creating the secret + (bsc#1259720)