slackhq/nebula · error

host certificate is a CA certificate

Error message

host certificate is a CA certificate

What it means

loadCertificate rejects certificates where c.IsCA() is true: a CA certificate is a signing identity, not a host identity, and cannot be used as pki.cert. This prevents operators from accidentally booting a node with the CA's own certificate and key material.

Source

Thrown at pki.go:534

	return
}

func loadCertificate(b []byte) (cert.Certificate, []byte, error) {
	c, b, err := cert.UnmarshalCertificateFromPEM(b)
	if err != nil {
		return nil, b, fmt.Errorf("error while unmarshaling pki.cert: %w", err)
	}

	if c.Expired(time.Now()) {
		return nil, b, fmt.Errorf("nebula certificate for this host is expired")
	}

	if len(c.Networks()) == 0 {
		return nil, b, fmt.Errorf("no networks encoded in certificate")
	}

	if c.IsCA() {
		return nil, b, fmt.Errorf("host certificate is a CA certificate")
	}

	return c, b, nil
}

func loadCAPoolFromConfig(l *slog.Logger, c *config.C) (*cert.CAPool, error) {
	caPathOrPEM := c.GetString("pki.ca", "")
	if caPathOrPEM == "" {
		return nil, errors.New("no pki.ca path or PEM data provided")
	}

	var caReader io.ReadCloser
	var err error

	if strings.Contains(caPathOrPEM, "-----BEGIN") {
		caReader = io.NopCloser(strings.NewReader(caPathOrPEM))
	} else {
		caReader, err = os.Open(caPathOrPEM)

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Point pki.cert at the host certificate issued by the CA, not ca.crt
  2. If no host cert exists, sign one: 'nebula-cert sign -ca-cpath ca.crt -ca-kpath ca.key -name host -ip 10.0.0.5'
  3. Keep pki.ca set to ca.crt and pki.cert set to host.crt in the config

Example fix

// before
pki:
  ca: /etc/nebula/ca.crt
  cert: /etc/nebula/ca.crt   # CA cert reused as host cert
// after
pki:
  ca: /etc/nebula/ca.crt
  cert: /etc/nebula/host.crt # host cert signed by the CA
Defensive patterns

Strategy: validation

Validate before calling

c, _, err := cert.UnmarshalCertificateFromPEM(certBytes)
if err == nil && c.IsCA() {
    return fmt.Errorf("%s is a CA cert; use the host cert for pki.cert", path)
}

Type guard

func isHostCert(c cert.Certificate) bool { return !c.IsCA() }

Try / catch

if err := startNebula(); err != nil && strings.Contains(err.Error(), "is a CA certificate") {
    return fmt.Errorf("pki.cert must be a host cert, not ca.crt: %w", err)
}

Prevention

When it happens

Trigger: newCertStateFromConfig is given a certificate produced by 'nebula-cert ca' (IsCA set) instead of one produced by 'nebula-cert sign'.

Common situations: Config copy-paste mixup where pki.cert and pki.ca both point at ca.crt; packaging scripts that bundle the wrong file as the host cert.

Understand the failure class

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/9b9540e1915d8940. Report an issue: GitHub.