Skip to content
roxy

Policy evaluation

The policy is a list of rules. Each rule has an id, a when condition written in the rule language, and a then saying what to do (actions):

rules:
  - id: github-reads
    when: host under "github.com" and method in [GET, HEAD]
    then: allow
 
  - id: no-admin-paths
    when: path starts_with "/admin"
    then: deny
 
  - id: upload-cap
    when: body.bytes > 10mb
    then: deny

Deny wins

roxy checks every rule against the request. If any matching rule denies, the request is denied. If none denies and at least one allows, it is allowed. If no rule matches, it is denied. The order of the rules makes no difference to the decision.

With the rules above, GET https://github.com/admin matches both github-reads and no-admin-paths, and is denied.

When rules run

roxy decides whether to forward a request as soon as it has the request head, before any of the body has been sent on. Most rules are decided then.

Some rules read things that are only known later, such as how many body bytes have been sent (body.bytes) or the response status. roxy checks these rules again each time the value changes. In the example, upload-cap is checked as the body streams, and stops the upload once it passes 10 MiB.

The request phase of one exchange through roxy, left to right. The client's request head (a solid line) travels first and its body bytes (a dashed line) stream behind it, through layer A (enforce), past layer B (observe, which gets a copy beside the path), through layer C (enforce) and the rules, then on to the upstream. Each layer's when is evaluated on the head as it reaches the layer. At the rules the admission decision is made once, on the head, after the stack and before anything is forwarded (when a rule reads body.text, the body is buffered first): allow, deny or change the request. As body bytes stream past the rules they are re-checked in flight (body.bytes, byte metrics), which can deny or quarantine but never allow or change the request.

The reference calls the first kind admission rules and the second in-flight rules. roxy check and roxy rule test say which each rule is.

A rule checked later can only deny, because by then the request has already gone. It can also log, tag, write state, or change response headers that have not been sent yet. When it denies, roxy stops the exchange: the client gets an error if its response has not started, and otherwise the connection is closed.

A rule that reads the whole body (body.text) is different: roxy holds the body back until it has read it, and then decides (body rules).

Effects

Order matters for what rules change, not for the decision. When a request is allowed, every matching rule's changes apply from top to bottom, so if two rules set the same header, the lower one wins. The flow log names the rule that decided as terminal_rule.

A tag set by a rule can only be read by rules below it. roxy rejects a config where a rule reads a tag that a rule further down sets.

When an input is missing

If a rule needs a value roxy cannot get, such as a metric when its table is full or an address list that failed to load, the request is denied with 503. roxy never guesses that the condition was false. A value that is just absent, like a header the client did not send, is null and compares normally (missing values).