grpc/grpc-go · error

rbac: Policy.condition is present

Error message

rbac: Policy.condition is present

What it means

Returned by the RBAC HTTP filter's parseConfig (rbac.go:63-64) when any policy in the RBAC rules has its condition field set. Per gRFC A41, gRPC's RBAC engine does not implement CEL condition expressions on policies, so their presence is a hard validation failure that NACKs the xDS config. The loop iterates every policy and rejects on the first non-nil Condition.

Source

Thrown at internal/xds/httpfilter/rbac/rbac.go:64

	httpfilter.FilterConfig
	chainEngine *rbac.ChainEngine
}

func (builder) TypeURLs() []string {
	return []string{
		"type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
		"type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute",
	}
}

// Parsing is the same for the base config and the override config.
func parseConfig(rbacCfg *rpb.RBAC) (httpfilter.FilterConfig, error) {
	// All the validation logic described in A41.
	for _, policy := range rbacCfg.GetRules().GetPolicies() {
		// "Policy.condition and Policy.checked_condition must cause a
		// validation failure if present." - A41
		if policy.Condition != nil {
			return nil, errors.New("rbac: Policy.condition is present")
		}
		if policy.CheckedCondition != nil {
			return nil, errors.New("rbac: policy.CheckedCondition is present")
		}

		// "It is also a validation failure if Permission or Principal has a
		// header matcher for a grpc- prefixed header name or :scheme." - A41
		for _, principal := range policy.Principals {
			name := principal.GetHeader().GetName()
			if name == ":scheme" || strings.HasPrefix(name, "grpc-") {
				return nil, fmt.Errorf("rbac: principal header matcher for %v is :scheme or starts with grpc", name)
			}
		}
		for _, permission := range policy.Permissions {
			name := permission.GetHeader().GetName()
			if name == ":scheme" || strings.HasPrefix(name, "grpc-") {
				return nil, fmt.Errorf("rbac: permission header matcher for %v is :scheme or starts with grpc", name)
			}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Remove the condition field from all RBAC policies in the xDS config; express the same logic via permissions/principals (header, source IP, path matchers) instead.
  2. If CEL logic is mandatory, enforce it outside gRPC RBAC (e.g. an Envoy sidecar or an application-level interceptor).
  3. Validate the RBAC proto before publishing: any policy with condition != nil should be rejected/folded into matchers.

Example fix

// before
//   policies: { p1: { condition: { ...CEL... }, permissions: [...] } }
// after
//   policies: { p1: { permissions: [...], principals: [...] } }  // no condition
Defensive patterns

Strategy: validation

Validate before calling

func rbacPoliciesHaveNoCondition(cfg *rpb.RBAC) error {
    for _, p := range cfg.GetRules().GetPolicies() {
        if p.GetCondition() != nil {
            return fmt.Errorf("policy %q uses unsupported condition", p)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: An xDS RBAC HTTP filter config (type URL envoy.extensions.filters.http.rbac.v3.RBAC) whose rules.policies[*].condition is populated. parseConfig walks each policy and returns this error immediately.

Common situations: Porting an Envoy RBAC config that uses CEL conditions into an xDS-served gRPC deployment; a control plane that copies full Envoy RBAC features without filtering gRPC-incompatible ones.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/51eb67bae2ad6a48. Report an issue: GitHub.