slackhq/nebula · error

certificate contained a group not present on the signing ca:

Error message

certificate contained a group not present on the signing ca: %s

What it means

If the signing CA has a non-empty Groups list, any certificate it signs may only contain groups that are a subset of the CA's groups. This error names the offending group found on the certificate but absent from the CA.

Source

Thrown at cert/ca_pool.go:303

// checkCAConstraints is a very generic function allowing both Certificates and TBSCertificates to be tested.
func checkCAConstraints(signer Certificate, notBefore, notAfter time.Time, groups []string, networks, unsafeNetworks []netip.Prefix) error {
	// Make sure this cert isn't valid after the root
	if notAfter.After(signer.NotAfter()) {
		return fmt.Errorf("certificate expires after signing certificate")
	}

	// Make sure this cert wasn't valid before the root
	if notBefore.Before(signer.NotBefore()) {
		return fmt.Errorf("certificate is valid before the signing certificate")
	}

	// If the signer has a limited set of groups make sure the cert only contains a subset
	signerGroups := signer.Groups()
	if len(signerGroups) > 0 {
		for _, g := range groups {
			if !slices.Contains(signerGroups, g) {
				return fmt.Errorf("certificate contained a group not present on the signing ca: %s", g)
			}
		}
	}

	// If the signer has a limited set of ip ranges to issue from make sure the cert only contains a subset
	signingNetworks := signer.Networks()
	if len(signingNetworks) > 0 {
		for _, certNetwork := range networks {
			found := false
			for _, signingNetwork := range signingNetworks {
				if signingNetwork.Contains(certNetwork.Addr()) && signingNetwork.Bits() <= certNetwork.Bits() {
					found = true
					break
				}
			}

			if !found {
				return fmt.Errorf("certificate contained a network assignment outside the limitations of the signing ca: %s", certNetwork.String())

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Remove or correct the offending group so the cert's groups are a subset of the CA's
  2. Add the group to the signing CA's Groups list and re-issue the CA (or use a CA that already includes it)
  3. Sign with a different CA whose group set covers the requested groups

Example fix

// before
opts.Groups = []string{"laptop", "servers"} // CA only allows "laptop"
nc, err := ca.SignWith(pubKey, curve, opts)
// after
opts.Groups = intersect(opts.Groups, ca.Groups()) // keep only CA-sanctioned groups
nc, err := ca.SignWith(pubKey, curve, opts)
Defensive patterns

Strategy: validation

Validate before calling

signerGroups := signer.Groups()
for _, g := range certGroups {
    if !slices.Contains(signerGroups, g) {
        return fmt.Errorf("group %q not allowed by CA", g)
    }
}
err := pool.CheckCAConstraints(signer, sub)

Prevention

When it happens

Trigger: CheckCAConstraints(signer, sub) where a group in sub.Groups() is not in signer.Groups(); SignWith whose options request groups outside the CA's allowed set; verify() invoking this after a successful signature check.

Common situations: Typo in a group name in the host config vs the CA definition; CA later narrowed its group list while old certs still request removed groups; operator assigns a new group to a host without updating the CA's group allowlist.

Understand the failure class

Related errors


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