fix(nodes): validate every certificate in the node mTLS trust bundle (#6188)

* fix(nodes): validate every certificate in the node mTLS trust bundle

AppendCertsFromPEM reports success once a single certificate parses, so a
trust bundle whose later entries are damaged or truncated was accepted with
those entries silently absent from the pool. Parse and validate every PEM
block instead, and reject the bundle if any of them is malformed.

* fix(mtls): reject malformed certificate bundle layout

---------

Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
This commit is contained in:
n0ctal
2026-08-16 00:31:31 +05:00
committed by GitHub
parent 338822ab07
commit bab39393f1
3 changed files with 116 additions and 14 deletions
+38 -2
View File
@@ -1,11 +1,14 @@
package service
import (
"bytes"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"strings"
"sync"
@@ -189,9 +192,42 @@ func (s *SettingService) NodeMtlsClientCAPool() (*x509.CertPool, error) {
if caPem == "" {
return nil, nil
}
certs, err := parseCertificateBundlePEM([]byte(caPem))
if err != nil {
return nil, fmt.Errorf("nodeMtlsClientCAPem is not a valid certificate bundle: %w", err)
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM([]byte(caPem)) {
return nil, common.NewError("nodeMtlsClientCAPem is not a valid certificate")
for _, cert := range certs {
pool.AddCert(cert)
}
return pool, nil
}
// parseCertificateBundlePEM avoids AppendCertsFromPEM because that helper can
// silently accept a bundle after parsing only its first certificate.
func parseCertificateBundlePEM(bundle []byte) ([]*x509.Certificate, error) {
rest := bytes.TrimSpace(bundle)
if len(rest) == 0 {
return nil, errors.New("certificate bundle is empty")
}
certs := make([]*x509.Certificate, 0, 1)
for len(rest) > 0 {
if !bytes.HasPrefix(rest, []byte("-----BEGIN CERTIFICATE-----")) {
return nil, errors.New("certificate bundle contains malformed or non-PEM data")
}
block, next := pem.Decode(rest)
if block == nil {
return nil, errors.New("certificate bundle contains malformed or non-PEM data")
}
if block.Type != "CERTIFICATE" {
return nil, errors.New("certificate bundle contains a non-certificate PEM block")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, errors.New("certificate bundle contains an invalid certificate")
}
certs = append(certs, cert)
rest = bytes.TrimSpace(next)
}
return certs, nil
}