Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Certificate chain is not split correctly #148

Open
ZzubbuzZ opened this issue Apr 9, 2024 · 1 comment · May be fixed by #159
Open

Certificate chain is not split correctly #148

ZzubbuzZ opened this issue Apr 9, 2024 · 1 comment · May be fixed by #159

Comments

@ZzubbuzZ
Copy link

ZzubbuzZ commented Apr 9, 2024

Description:

We are using google CAS as a delagated certificate authority and our complete certificate chain is :
[Root CA cert - RCA] -> [Intermediate CA cert - ICA] -> [GoogleCAS CA cert - CASCA] -> [Leaf certificate - CERT]
In certificate secrets, the certificate chain is not split properly :

  • tls.crt contains CASCA and CERT in a single encoded string using a mix of "\r\n" and as new line separator.
    • CERT : this part of the string uses "\r\n" as new line separator and contains the leaf certificate.
    • CASCA : this part of the string uses "\n" as new line separator and contains the Google CAS certificate authaurity public certificate.
  • ca.crt contains RCA + ICA in a single encoded string with "\r\n" as new line separator (except at the end of the string where only "\n" is used.

What's expected:

  • From what we undestood from cert-manager FAQ, the secret should contains :

    • tls.crt with the full certificate chain within (except the CA cert)
    • ca.crt should only contain the root certificate
  • All encoded string should be using the same new line separator (might be an external issue without impact to google-cas-issuer, but...??)

What's happening:

As the web server using the leaf certificate publishes cert.crt as certificate chain, our TLS handshake is timeout, as the client does not trust the ICA, but only the RCA.
If we modify the client config and we add the ICA to its truststore, the TLS handshake ends succesfully, and the TLS connexion is established.

Versions affected:

google-cas-issuer: 0.8.0
cert-manager: 1.14.4

How to reproduce:

Create the CA chain described, and you should reproduce the issue.

@ZzubbuzZ
Copy link
Author

ZzubbuzZ commented Apr 30, 2024

The google private-certificate GO api provide, as fullchain certificate, an array of certificates (as string). In this issue's context, the latest element of this array contains 2 certificates in one string.

I'm writing an issue on google side regarding this topic, but it might be a good idea to "sanitize" received certificate chain. Here is a snippet of code that fix current issue in my case in signer.go :

import (
  "regex"
  (...)
  "k8s.io/klog/v2"
)

(...)

func extractCertAndCA(resp *casapi.Certificate) (cert []byte, ca []byte, err error) {
	klog.Info("Starting extractCertAndCA")
	defer klog.Info("Quit extractCertAndCA")
	if resp == nil {
		return nil, nil, errors.New("extractCertAndCA: certificate response is nil")
	}

	certBuf := &bytes.Buffer{}
	var certs []string

	re := regexp.MustCompile(`(?sU)-{5}BEGIN CERTIFICATE(?:.+)END CERTIFICATE-{5}`)

	// parse the certificate and store it in certs slice
	match := re.FindString(resp.PemCertificate)
	if match == "" {
		return nil, nil, errors.New("extractCertAndCA: leaf certificate is not properly parsed")
	}
	certs = append(certs, match)

	klog.Info("The original Certificate Chain length is ", len(resp.PemCertificateChain))
	// Write any remaining certificates except for the root-most one
	// for _, c := range resp.PemCertificateChain[:len(resp.PemCertificateChain)-1] {
	for _, casCert := range resp.PemCertificateChain {
		match := re.FindAllString(casCert, -1)
		if len(match) == 0 {
			return nil, nil, errors.New("extractCertAndCA: the certificate chain is not properly parsed")
		}
		// Append all matched certs from the certificate chain to the certs slice
		certs = append(certs, match...)
	}

	for _, cert := range certs[:len(certs)-1] {
		// For all the certificate chain, but the most root one (CA cert)
		// We write it to the cert buffer
		certBuf.WriteString(cert)
		certBuf.WriteRune('\n')
	}

	klog.Info("Finally, the Certificate Chain length is ", len(certs)-1, ", including the leaf certificate")
	// Return the root-most certificate in the CA field.
	return certBuf.Bytes(), []byte(certs[len(certs)-1] + "\n"), nil
}

ZzubbuzZ pushed a commit to ZzubbuzZ/google-cas-issuer that referenced this issue May 7, 2024
@ZzubbuzZ ZzubbuzZ linked a pull request May 7, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant