cloudflare/cloudflared · error

unable to get x509 system cert pool

Error message

unable to get x509 system cert pool

What it means

CreateTunnelConfig in tlsconfig/origin_ca.go builds a *tls.Config for origin connections. When no explicit CA cert is supplied, it falls back to the operating system's root certificate store via x509.SystemCertPool(). If Go cannot load the system pool, the call fails and this wrapped error is returned instead of a TLS config.

Source

Thrown at tlsconfig/origin_ca.go:94

func CreateTunnelConfig(caCert string, serverName string) (*tls.Config, error) {
	tlsConfig := &tls.Config{ServerName: serverName}
	if caCert != "" {
		caCertPEM, err := os.ReadFile(caCert) //nolint:gosec
		if err != nil {
			return nil, fmt.Errorf("read CA certificate %s: %w", caCert, err)
		}

		rootCAPool := x509.NewCertPool()
		if !rootCAPool.AppendCertsFromPEM(caCertPEM) {
			return nil, fmt.Errorf("parse CA certificate %s", caCert)
		}
		tlsConfig.RootCAs = rootCAPool
	}

	if tlsConfig.RootCAs == nil {
		rootCAPool, err := x509.SystemCertPool()
		if err != nil {
			return nil, errors.Wrap(err, "unable to get x509 system cert pool")
		}
		cfRootCA, err := GetCloudflareRootCA()
		if err != nil {
			return nil, errors.Wrap(err, "could not append Cloudflare Root CAs to cloudflared certificate pool")
		}
		for _, cert := range cfRootCA {
			rootCAPool.AddCert(cert)
		}
		tlsConfig.RootCAs = rootCAPool
	}

	if tlsConfig.ServerName == "" && !tlsConfig.InsecureSkipVerify {
		return nil, fmt.Errorf("either ServerName or InsecureSkipVerify must be specified in the tls.Config")
	}
	return tlsConfig, nil
}

func loadOriginCertPool(originCAPoolPEM []byte, log *zerolog.Logger) (*x509.CertPool, error) {

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Install the OS CA bundle (e.g. apt-get install ca-certificates, apk add ca-certificates) or use a base image that includes it.
  2. Check SSL_CERT_FILE and SSL_CERT_DIR environment variables point to a readable PEM bundle and unset them if wrong.
  3. Pass an explicit CA file to CreateTunnelConfig so tlsConfig.RootCAs is populated and the system pool is never loaded.
  4. Verify with `trust list` (p11-kit) or `ls /etc/ssl/certs` that the trust store exists and is non-empty.

Example fix

// before
tlsConfig, err := tlsconfig.CreateTunnelConfig("", "origin.example.com")
// after (provide explicit CA so the system pool is not needed)
tlsConfig, err := tlsconfig.CreateTunnelConfig("/etc/cloudflared/origin-ca.pem", "origin.example.com")
Defensive patterns

Strategy: validation

Validate before calling

if _, err := x509.SystemCertPool(); err != nil {
    // fall back to bundled roots or fail fast with guidance
    return fmt.Errorf("system trust store unavailable: %w; install ca-certificates or pass an explicit CA pool", err)
}

Try / catch

pooled, err := tlsconfig.CreateTunnelConfig(caCert, serverName)
if err != nil && strings.Contains(err.Error(), "x509 system cert pool") {
    log.Error().Err(err).Msg("system CA bundle missing; install ca-certificates or set --origin-ca-pool")
}

Prevention

When it happens

Trigger: Calling tlsconfig.CreateTunnelConfig(caCert="", serverName=...) when x509.SystemCertPool() returns an error — typically because the OS trust store is missing or unreadable (empty/missing /etc/ssl/certs, broken SSL_CERT_FILE/SSL_CERT_DIR env vars, minimal container images without ca-certificates).

Common situations: Running cloudflared or embedding cloudflared in a scratch/Distroless Docker image with no ca-certificates package; deleting or corrupting the system CA bundle; setting SSL_CERT_FILE/SSL_CERT_DIR to a nonexistent path; older Go versions on Windows where SystemCertPool was unsupported.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/e505902aa74aa519. Report an issue: GitHub.