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.

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.

Appendix: 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)