diff --git a/pkg/controllers/signer.go b/pkg/controllers/signer.go index b25f8c3a0a..0414cff6b6 100644 --- a/pkg/controllers/signer.go +++ b/pkg/controllers/signer.go @@ -6,10 +6,11 @@ import ( "errors" "fmt" "math/rand" - "strings" + "regexp" "time" privateca "cloud.google.com/go/security/privateca/apiv1" + casapi "cloud.google.com/go/security/privateca/apiv1/privatecapb" cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" issuerapi "github.com/cert-manager/issuer-lib/api/v1alpha1" controllers "github.com/cert-manager/issuer-lib/controllers" @@ -17,7 +18,6 @@ import ( "github.com/google/uuid" "github.com/spf13/viper" "google.golang.org/api/option" - casapi "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" durationpb "google.golang.org/protobuf/types/known/durationpb" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" @@ -194,25 +194,44 @@ func (c *GoogleCAS) createCasClient(ctx context.Context, resourceNamespace strin // Additionally, for each PEM block, all whitespace is trimmed and a single new line is // appended, in case software consuming the resulting secret writes the PEM blocks // directly into a config file without parsing them. +// +// Unfortunatly, Google CAS API can send multiple certs within the same pemCertificateChain +// element, so we (Arkea) have to parse and re-order this + func extractCertAndCA(resp *casapi.Certificate) (cert []byte, ca []byte, err error) { 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}`) - // Write the leaf to the buffer - certBuf.WriteString(strings.TrimSpace(resp.PemCertificate)) - certBuf.WriteRune('\n') + // 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) // Write any remaining certificates except for the root-most one - for _, c := range resp.PemCertificateChain[:len(resp.PemCertificateChain)-1] { - certBuf.WriteString(strings.TrimSpace(c)) + 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') } // Return the root-most certificate in the CA field. - return certBuf.Bytes(), []byte( - strings.TrimSpace( - resp.PemCertificateChain[len(resp.PemCertificateChain)-1], - ) + "\n"), nil + return certBuf.Bytes(), []byte(certs[len(certs)-1] + "\n"), nil } diff --git a/pkg/controllers/signer_test.go b/pkg/controllers/signer_test.go index ce2066be44..23afdf8045 100644 --- a/pkg/controllers/signer_test.go +++ b/pkg/controllers/signer_test.go @@ -22,7 +22,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" + "google.golang.org/api/privateca/v1" "github.com/jetstack/google-cas-issuer/api/v1beta1" ) @@ -153,6 +153,38 @@ intermediate1 []byte(`-----BEGIN CERTIFICATE----- root -----END CERTIFICATE----- +`), + nil, + }, + }, + { + name: "Extract the root cert if several certificates are passed within the last element of the certificate chain", + input: &privateca.Certificate{ + PemCertificate: `-----BEGIN CERTIFICATE----- +leaf +-----END CERTIFICATE-----`, + PemCertificateChain: []string{`-----BEGIN CERTIFICATE----- +intermediate2 +-----END CERTIFICATE-----`, `-----BEGIN CERTIFICATE----- +intermediate1 +-----END CERTIFICATE-----\r\n-----BEGIN CERTIFICATE----- +root +-----END CERTIFICATE-----`}, + }, + expected: expected{ + []byte(`-----BEGIN CERTIFICATE----- +leaf +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +intermediate2 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +intermediate1 +-----END CERTIFICATE----- +`), + []byte(`-----BEGIN CERTIFICATE----- +root +-----END CERTIFICATE----- `), nil, },