Skip to content

Commit

Permalink
Merge #71548
Browse files Browse the repository at this point in the history
71548: security: prevent PEM decoding errors by careful newline insertation r=catj-cockroach a=catj-cockroach

Fixes #71588

> Previously, we assumed that the Tenant CA certificate and the CA
> certificate would not be the same, and that they would be saved as
> files with an end of file newline character.
> 
> This change adds support for not loading the Tenant CA Certificate
> if it will just be a second copy of the CA certificate.
> 
> This change also adds a helper function to concatenate certificates
> with a newline separating each, to ensure that the easiest way to
> make a certificate bundle is the correct way.
> 
> Release note: bugfix for certificate loading logic

The bug in question was introduced in #71248 (and back ported in #71402).

Co-authored-by: Cat J <87989074+catj-cockroach@users.noreply.github.com>
  • Loading branch information
craig[bot] and catj-cockroach committed Oct 15, 2021
2 parents ffcb7cb + 9495613 commit bbffd4c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 8 deletions.
20 changes: 12 additions & 8 deletions pkg/security/certificate_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1007,10 +1007,12 @@ func (cm *CertificateManager) GetTenantTLSConfig() (*tls.Config, error) {

caBlob := ca.FileContents

// CA used to validate certs provided by other SQL servers.
tenantCA, err := cm.getTenantCACertLocked()
if err == nil {
caBlob = append(caBlob, tenantCA.FileContents...)
if cm.tenantCACert != nil {
// If it's available, we also include the tenant CA.
tenantCA, err := cm.getTenantCACertLocked()
if err == nil {
caBlob = AppendCertificatesToBlob(caBlob, tenantCA.FileContents)
}
}

// Client cert presented to KV nodes.
Expand Down Expand Up @@ -1113,10 +1115,12 @@ func (cm *CertificateManager) GetUIClientTLSConfig() (*tls.Config, error) {

caBlob := uiCA.FileContents

// If it's available, we also include the tenant CA.
tenantCA, err := cm.getTenantCACertLocked()
if err == nil {
caBlob = append(caBlob, tenantCA.FileContents...)
if cm.tenantCACert != nil {
// If it's available, we also include the tenant CA.
tenantCA, err := cm.getTenantCACertLocked()
if err == nil {
caBlob = AppendCertificatesToBlob(caBlob, tenantCA.FileContents)
}
}

cfg, err := newUIClientTLSConfig(cm.tlsSettings, caBlob)
Expand Down
14 changes: 14 additions & 0 deletions pkg/security/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package security

import (
"bytes"
"context"
"crypto"
"crypto/rand"
Expand Down Expand Up @@ -561,3 +562,16 @@ func PEMContentsToX509(contents []byte) ([]*x509.Certificate, error) {

return certs, nil
}

// AppendCertificatesToBlob adds the passed PEM encoded certificates to the existing
// byte slice containing PEM encoded certificates, ensuring that there is a newline
// separating the original byte slice and each subsequent certificate byte slices.
func AppendCertificatesToBlob(certBlob []byte, newCerts ...[]byte) []byte {
return bytes.Join(
append(
[][]byte{certBlob},
newCerts...,
),
[]byte{'\n'},
)
}
43 changes: 43 additions & 0 deletions pkg/security/certs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
package security_test

import (
"bytes"
"context"
"crypto/x509"
gosql "database/sql"
"fmt"
"io/ioutil"
Expand All @@ -25,6 +27,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/rpc"
"github.com/cockroachdb/cockroach/pkg/security"
"github.com/cockroachdb/cockroach/pkg/security/securitytest"
"github.com/cockroachdb/cockroach/pkg/testutils"
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
Expand Down Expand Up @@ -635,3 +638,43 @@ func TestUseWrongSplitCACerts(t *testing.T) {
}
}
}

func TestAppendCertificateToBlob(t *testing.T) {
defer leaktest.AfterTest(t)()

caBlob, err := securitytest.Asset(filepath.Join(security.EmbeddedCertsDir, security.EmbeddedCACert))
if err != nil {
t.Fatal(err)
}
testCertPool := x509.NewCertPool()

certsToAdd := make([][]byte, 0, 3)

for _, certFilename := range []string{
// security.EmbeddedClientCACert,
// security.EmbeddedUICACert,
security.EmbeddedTenantCACert,
} {
newCertBlob, err := securitytest.Asset(filepath.Join(security.EmbeddedCertsDir, certFilename))
if err != nil {
t.Errorf("failed to read certificate \"%s\": %s", certFilename, err)
continue
}

certsToAdd = append(certsToAdd, bytes.TrimRight(newCertBlob, "\n"))

}

caBlob = security.AppendCertificatesToBlob(
bytes.TrimRight(caBlob, "\n"),
certsToAdd...,
)

if !testCertPool.AppendCertsFromPEM(caBlob) {
if testing.Verbose() {
t.Fatalf("appendCertificatesToBlob failed to properly concatenate the test certificates together:\n===\n%s===\n", caBlob)
} else {
t.Fatal("appendCertificatesToBlob failed to properly concatenate the test certificates together. Run with the verbose flag set to see the output.")
}
}
}

0 comments on commit bbffd4c

Please sign in to comment.