---
title: "RBAC"
url: "/docs/security/rbac/"
kind: "feature"
summary: "Declarative allow/deny policies on connections (network RBAC) and requests (HTTP RBAC), with per-route overrides."
---

> Arion site index: [/llms.txt](/llms.txt)

# RBAC

Declarative allow/deny policies on connections (network RBAC) and requests (HTTP RBAC), with per-route overrides.

RBAC filters allow or deny traffic based on declarative policies — no external calls, no policy language. Network RBAC runs when a connection is accepted; HTTP RBAC runs per request inside the HTTP connection manager. Use them for IP allowlists, port and SNI restrictions, and header- or path-based rules.

## How policies decide

Both filters share one shape: an `action` (`ALLOW` or `DENY`) and named `policies`. A policy matches when **any** of its `permissions` and **any** of its `principals` match. Policies are checked in the lexicographical order of their names; with `ALLOW`, traffic passes only if some policy matches, with `DENY` it passes only if none does. A denied request gets `403` with body `RBAC: access denied`; a denied connection is closed.

## Network RBAC

List `envoy.filters.network.rbac` in a filter chain's `filters`, before the HTTP connection manager or TCP proxy. Several RBAC filters may be stacked; each must permit the connection.

```yaml,name=arion.yaml,arion=fragment
filters:
  - name: envoy.filters.network.rbac
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC
      rules:
        action: ALLOW
        policies:
          internal-only:
            permissions:
              - any: true
            principals:
              - direct_remote_ip: { address_prefix: 10.0.0.0, prefix_len: 8 }
  - name: https_gateway
    # http_connection_manager ...
```

Supported permission kinds: `any`, `destination_ip`, `destination_port`, `destination_port_range`, `requested_server_name` (a string matcher against the SNI — add the TLS inspector listener filter so it is available). Supported principal kind besides `any`: `direct_remote_ip` (the peer address of the accepted connection).

## HTTP RBAC

Add `envoy.filters.http.rbac` to `http_filters`, before the router.

```yaml,name=arion.yaml,arion=fragment
http_filters:
  - name: envoy.filters.http.rbac
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
      rules:
        action: DENY
        policies:
          no-admin:
            permissions:
              - header:
                  name: ":path"
                  string_match: { prefix: /admin }
            principals:
              - any: true
  - name: envoy.filters.http.router
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
```

At the HTTP level both permissions and principals support exactly two kinds: `any` and `header`. That is less restrictive than it sounds — header matchers also target the `:method`, `:path` and `:scheme` pseudo-headers, and support `exact`, `prefix`, `suffix`, `contains` and `safe_regex` patterns (plus `present_match`, `invert_match`, `ignore_case` and `treat_missing_header_as_empty`). A "principal" is therefore anything a header can express, such as a `claim_to_headers` value stamped by [JWT authentication](@/docs/security/jwt.md).

## Per-route override

A route can replace the HTTP RBAC policy or switch the filter off. The key under `typed_per_filter_config` must equal the filter's `name` in `http_filters`:

```yaml,arion=fragment
routes:
  - match: { prefix: /public }
    route: { cluster: api }
    typed_per_filter_config:
      envoy.filters.http.rbac:
        "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
        # empty: no RBAC on this route; add `rbac: { rules: ... }` to replace the policy
```

## Verify

```bash
curl -i http://127.0.0.1:8080/admin/users
```

Expected: `HTTP/1.1 403 Forbidden` with body `RBAC: access denied`. With debug logging enabled, the log names the policy that enforced the decision.

## Limits

- Shadow rules (`shadow_rules`, `shadow_matcher`) and the newer matcher API are not supported; neither are CEL conditions on policies.
- The `LOG` action is not supported — only `ALLOW` and `DENY`.
- Principal kinds beyond the ones listed (for example `authenticated` mTLS identities, `remote_ip` behind a proxy, `url_path`, metadata) are refused at startup.
- Network RBAC ignores `stat_prefix` (with a warning) and does not support `enforcement_type` or `delay_deny`.
- For identity-aware rules richer than header matching, use [Cedar policies](@/docs/security/cedar-policies.md).
