mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-24 03:47:15 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user