slackhq/nebula · error

%s rule #%v; at least one of host, group, cidr, local_cidr,

Error message

%s rule #%v; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided

What it means

Every firewall rule must select some target: a host, at least one group, a cidr, a local_cidr, a ca_name, or a ca_sha. AddFirewallRulesFromConfig rejects rules that specify none of these, because such a rule could never match a meaningful connection set.

Source

Thrown at firewall.go:349

	}

	rs, ok := r.([]any)
	if !ok {
		return fmt.Errorf("%s failed to parse, should be an array of rules", table)
	}

	for i, t := range rs {
		r, err := convertRule(l, t, table, i)
		if err != nil {
			return fmt.Errorf("%s rule #%v; %s", table, i, err)
		}

		if r.Code != "" && r.Port != "" {
			return fmt.Errorf("%s rule #%v; only one of port or code should be provided", table, i)
		}

		if r.Host == "" && len(r.Groups) == 0 && r.Cidr == "" && r.LocalCidr == "" && r.CAName == "" && r.CASha == "" {
			return fmt.Errorf("%s rule #%v; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided", table, i)
		}

		var sPort, errPort string
		if r.Code != "" {
			errPort = "code"
			sPort = r.Code
		} else {
			errPort = "port"
			sPort = r.Port
		}

		var proto uint8
		var startPort, endPort int32
		switch r.Proto {
		case "any":
			proto = firewall.ProtoAny
			startPort, endPort, err = parsePort(sPort)
		case "tcp":

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Add a selector to the rule — typically "group: <name>" or "cidr: <prefix>".
  2. For host-specific rules use "host", for CA-based trust use "ca_name" or "ca_sha".
  3. Use "cidr: 0.0.0.0/0" (and/or "local_cidr: any") only if you truly intend an allow-all rule.

Example fix

// before (config)
- port: 443
  proto: tcp
// after
- port: 443
  proto: tcp
  group: web-servers
Defensive patterns

Strategy: validation

Validate before calling

func ruleHasSelector(rule map[string]any) bool {
    for _, k := range []string{"host", "group", "groups", "cidr", "local_cidr", "ca_name", "ca_sha"} {
        if v, ok := rule[k]; ok && v != nil && v != "" {
            return true
        }
    }
    return false
}
// before AddFirewallRulesFromConfig: ensure ruleHasSelector(r) for every rule

Try / catch

if err := fw.AddFirewallRulesFromConfig(l, "outbound", rules); err != nil {
    if strings.Contains(err.Error(), "at least one of host, group") {
        log.Fatalf("every rule needs host/group/cidr/local_cidr/ca_name/ca_sha: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: A rule with only port/proto (e.g. {port: "443", proto: "tcp"}) and no host, group, cidr, local_cidr, ca_name, or ca_sha field.

Common situations: Oversimplified example configs, deleting the group/cidr line while editing, or templating that leaves selector fields empty.

Related errors


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