slackhq/nebula · error

unknown protocol %v

Error message

unknown protocol %v

What it means

AddRule on the firewall validates the protocol of a new rule. Only tcp, udp, icmp, and any are supported; anything else falls through to the default case and returns this error. The rule is not installed when this occurs.

Source

Thrown at firewall.go:281

	}

	switch proto {
	case iputil.IPProtocolTCP:
		fp = ft.TCP
	case iputil.IPProtocolUDP:
		fp = ft.UDP
	case iputil.IPProtocolICMP, iputil.IPProtocolICMPv6:
		//ICMP traffic doesn't have ports, so we always coerce to "any", even if a value is provided
		if startPort != firewall.PortAny {
			f.l.Warn("ignoring port specification for ICMP firewall rule", "startPort", startPort)
		}
		startPort = firewall.PortAny
		endPort = firewall.PortAny
		fp = ft.ICMP
	case firewall.ProtoAny:
		fp = ft.AnyProto
	default:
		return fmt.Errorf("unknown protocol %v", proto)
	}

	// We need this rule string because we generate a hash. Removing this will break firewall reload.
	ruleString := fmt.Sprintf(
		"incoming: %v, proto: %v, startPort: %v, endPort: %v, groups: %v, host: %v, ip: %v, localIp: %v, caName: %v, caSha: %s",
		incoming, proto, startPort, endPort, groups, host, cidr, localCidr, caName, caSha,
	)
	f.rules += ruleString + "\n"

	direction := "incoming"
	if !incoming {
		direction = "outgoing"
	}
	f.l.Info("Firewall rule added",
		"firewallRule", m{"direction": direction, "proto": proto, "startPort": startPort, "endPort": endPort, "groups": groups, "host": host, "cidr": cidr, "localCidr": localCidr, "caName": caName, "caSha": caSha},
	)

	return fp.addRule(f, startPort, endPort, groups, host, cidr, localCidr, caName, caSha)

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Use one of the supported protocols: tcp, udp, icmp, or any.
  2. If you need a rule that matches all traffic, use ProtoAny instead of an invented value.
  3. Normalize/validate the proto string from config before constructing the rule (config parsing normally lowercases it via convertRule).

Example fix

// before
fw.AddRule(true, firewall.Proto("http"), 0, 443, []string{"app"}, "", nil, nil, nil, "", "")
// after
fw.AddRule(true, firewall.ProtoTCP, 0, 443, []string{"app"}, "", nil, nil, nil, "", "")
Defensive patterns

Strategy: validation

Validate before calling

var validProtos = map[firewall.Protocol]bool{
    firewall.ProtoTCP: true, firewall.ProtoUDP: true,
    firewall.ProtoICMP: true, firewall.ProtoAny: true,
}
if !validProtos[proto] {
    return fmt.Errorf("unsupported proto %q; use tcp, udp, icmp, or any", proto)
}
err := fw.AddRule(true, proto, 0, 443, groups, "", nil, nil, nil, "", "")

Type guard

func isSupportedProto(p firewall.Protocol) bool {
    switch p {
    case firewall.ProtoTCP, firewall.ProtoUDP, firewall.ProtoICMP, firewall.ProtoAny:
        return true
    }
    return false
}

Try / catch

if err := fw.AddRule(true, proto, start, end, groups, "", nil, nil, nil, "", ""); err != nil {
    if strings.Contains(err.Error(), "unknown protocol") {
        log.Fatalf("proto %v not supported; use tcp/udp/icmp/any", proto)
    }
    return err
}

Prevention

When it happens

Trigger: Calling firewall.AddRule with a FirewallProtocol value other than ProtoTCP, ProtoUDP, ProtoICMP, or ProtoAny (e.g. an unvalidated value read from config or a zero-value custom constant).

Common situations: Typo in a firewall rule's "proto" field in the Nebula config YAML (e.g. "http" instead of "tcp"), programmatic rule construction with a wrong constant, or parsing raw user input into a proto value without validation.

Related errors


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