Skip to main content

Pomerium Policy Language

Pomerium Policy Language (PPL) is a yaml-based notation for creating easy and flexible authorization policies. This document covers the usage of PPL and provides several example policies.

PPL allows administrators to express authorization policy in a high-level, declarative language that promotes safe, performant, fine-grained controls.

note

See the Policy setting page to learn how to apply a PPL policy to a route.

At a Glance

Each PPL policy has at the top level a set of allow or deny actions, with a list of logical operators, criteria, matchers, and values underneath. For example:

allow:
and:
- domain:
is: example.com
- user:
is: user1@example.com
deny:
or:
- user:
is: user2@example.com
- user:
is: user3@example.com

This policy will allow a user with an email address at example.com who is also user. It will deny user2 or user3, regardless of their domain and group membership.

Rules

A PPL document is either an object or an array of objects. The object represents a rule where the action is the key and the value is an object containing the logical operators.

Actions

Only two actions are supported: allow and deny. deny takes precedence over allow. More precisely: a user will have access to a route if at least one allow rule matches and no deny rules match.

Logical Operators

A logical operator combines multiple criteria together for the evaluation of a rule. There are 4 logical operators: and, or, not and nor.

More on Logical Operators

Given the following example with OPERATOR replaced:

allow:
OPERATOR:
- domain:
is: example.com
- claim/groups: admin

If and is used, the user will have access if their email address ends in example.com and they are a member of the admin group. (A ∧ B)

If or is used, the user will have access if their email address ends in example.com or they are a member of the admin group. (A ∨ B)

If not is used, the user will have access if their email address does not end in example.com and they are not a member of the admin group. (¬A ∧ ¬B) = ¬(A ∨ B) (This operation is traditionally called NOR in the Boolean algebra.)

If nor is used, the user will have access if their email address does not end in example.com or they are not a member of the admin group. (¬A ∨ ¬B) = ¬(A ∧ B) (This operation is traditionally called NAND in the Boolean algebra.)

Multiple Operators in a Rule

You can add multiple operators under the same rule, and it would be valid PPL.

For example, this policy would grant access to anyone with a family_name: Smith claim, or users with email addresses ending in domain1 or domain2:

allow:
and:
- claim/family_name: Smith
or:
- domain:
is: domain1.com
- domain:
is: domain2.com

However, you could write an equivalent policy with multiple allow blocks:

- allow:
and:
- claim/family_name: Smith
- allow:
or:
- domain:
is: domain1.com
- domain:
is: domain2.com

Although these policies are equally effective, we recommend using just one operator per rule.

Criteria

Criteria in PPL are represented as an object where the key is the name and optional sub-path of the criterion, and the value changes depending on which criterion is used. A sub-path is indicated with a / in the name:

allow:
and:
- claim/family_name: Smith
deny:
not:
- http_method:
is: GET

Supported PPL Criteria

Below is an exhaustive list of PPL criteria.

Entries marked with * denote criteria that are only available in the Enterprise Console PPL builder. All other entries are available in both Pomerium Core and Pomerium Enterprise.

Criterion NameData FormatDescription
acceptAnything. Typically true.Always returns true, thus always allowing access. Equivalent to the allow_public_unauthenticated_access option.
authenticated_userAnything. Typically true.Always returns true for logged-in users. Equivalent to the allow_any_authenticated_user option.
claimAnything. Typically a string.Returns true if a token claim matches the supplied value exactly. The claim to check is determined via the sub-path.
For example, claim/family_name: Smith matches if the user's family_name claim is Smith.
client_certificateCertificate matcherReturns true if a client presented a TLS certificate matching the provided condition.
cors_preflightAnything. Typically true.Returns true if the incoming request uses the OPTIONS method and has both the Access-Control-Request-Method and Origin headers. Used to allow CORS pre-flight requests.
* dateDate MatcherReturns true if the time of the request matches the constraints.
* day_of_weekDay of Week MatcherReturns true if the day of the request matches the constraints.
deviceDevice matcherReturns true if the incoming request includes a valid device ID or type.
domainString MatcherReturns true if the logged-in user's email address domain (the part after @) matches the given value.
emailString MatcherReturns true if the logged-in user's email address matches the given value.
* groupsString List MatcherReturns true if a user's group ID matches the supplied value exactly. groups data is only available after a successful directory sync. See Identity Providers for vendor-specific directory sync steps.
http_methodString MatcherReturns true if the HTTP method matches the given value.
http_pathString MatcherReturns true if the HTTP path matches the given value.
invalid_client_certificateAnything. Typically true.Returns true if the incoming request does not have a trusted client certificate. By default, a deny rule using this criterion is added to all Pomerium policies when downstream mTLS is configured (but this default can be changed using the Enforcement Mode setting.)
pomerium_routesAnything. Typically true.Returns true if the incoming request is for the special .pomerium routes. A default allow rule using this criterion is added to all Pomerium policies.
* recordvariableAllows policies to be extended using data from external data sources
rejectAnything. Typically true.Always returns false. The opposite of accept.
* time_of_dayTime of Day MatcherReturns true if the time of the request (for the current day) matches the constraints.
userString MatcherReturns true if the logged-in user's id matches the given value.

Entries marked with * denote criteria that are only available in the Enterprise Console PPL builder. All other entries are available in both Pomerium Core and Pomerium Enterprise.

Matchers

Certificate Matcher

caution

The certificate matcher is a beta feature. The syntax and capabilities are subject to change in a future Pomerium release.

A certificate matcher can be used to allow or deny certain TLS certificates. This matcher is represented as an object that may have the following key/value entries:

Key NameValue TypeDescription
fingerprintstring or array of stringsThe certificate's SHA-256 fingerprint must match one of the provided values.
san_dnsString MatcherThe certificate must contain a Subject Alternative Name with a DNS name satisfying the provided condition.
san_emailString MatcherThe certificate must contain a Subject Alternative Name with an email address satisfying the provided condition.
san_uriString MatcherThe certificate must contain a Subject Alternative Name with a URI satisfying the provided condition.
spki_hashstring or array of stringsThe base64-encoded SHA-256 hash of the certificate's Subject Public Key Info must match one of the provided values.
Notes on certificate fingerprint

The certificate fingerprint is a SHA-256 hash of the entire certificate. You can compute a certificate's fingerprint using the openssl command:

$ openssl x509 -in path/to/certificate.pem -noout -fingerprint -sha256
sha256 Fingerprint=17:85:92:73:E8:A9:80:63:1D:36:7B:2D:5A:6A:66:35:41:2B:0F:22:83:5F:69:E4:7B:3F:65:62:45:46:A7:04

This is the "long" form of a certificate fingerprint (32 uppercase hexadecimal bytes separated by colons). A "short" form is also acceptable (32 lowercase hexadecimal bytes, without colons). You can also compute this form using the openssl command:

$ openssl x509 -in path/to/certificate.pem -outform DER | openssl dgst -sha256
SHA2-256(stdin)= 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704
Notes on SPKI hash

The SPKI hash is a base64-encoded SHA-256 hash of the Subject Public Key Info section of the certificate. You can compute a certificate's SPKI hash using a sequence of openssl commands:

$ openssl x509 -in path/to/certificate.pem -noout -pubkey \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -binary \
| openssl enc -base64
FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U=

The advantage of using the SPKI hash rather than the certificate fingerprint is that the SPKI hash may be stable across certificate renewals (if the public/private key pair is the same).

For example, to allow only certificates containing a Subject Alternative Name with an email address ending in @yourdomain.com (while also requiring the user to sign in with the configured identity provider):

allow:
and:
- authenticated_user: true
- client_certificate:
san_email:
ends_with: '@yourdomain.com'

Or, to allow only one specific trusted certificate (again, while still requiring the user to sign in with the configured identity provider):

allow:
and:
- authenticated_user: true
- client_certificate:
fingerprint: '17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704'

Or, to enforce an allowlist of trusted certificate key pairs:

allow:
and:
- authenticated_user: true
- client_certificate:
spki_hash:
- 'FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U='
- 'pbdFxDXEtpabt3MZiik71farokMg6ZIn2azvsdXtZYA='
- 'WTu9ETBS1/v/ll20erWcf+TAj7rzrJix/oCUv5GMPtg='
...

Day of Week Matcher

The day of week matcher is a string. The string can either be *, a comma-separated list of days, or a dash-separated list of days.

  • * matches all days.

  • , matches either day (e.g. mon,wed,fri).

  • - matches a range of days. (e.g. mon-fri). Days can be specified as English full day names, or as 3 character abbreviations. For example:

    allow:
    and:
    - day_of_week: tue-fri

Date Matcher

The date matcher is an object with operators as keys. It supports the following operators: after and before. The values are ISO-8601 date strings. after means that the time of the request must be after the supplied date and before means that the time of the request must be before the supplied date. For example:

allow:
and:
- date:
after: 2020-01-02T16:20:00
before: 2150-01-02T16:20:00

Device Matcher

A device matcher is an object with operators as keys. It supports the following operators:

  • is - an exact match of the device ID.
  • approved - true if the device has been approved. This is an enterprise-only feature.
  • type - Specifies the type of device to match on. The available types are enclave_only and any.

For example, a policy to allow any user with a registered device:

- allow:
or:
- device:
type: any

Compare to a policy that only allows a set of specific devices:

- allow:
or:
- device:
is: "5Vn3...C1RS"
- device:
is: "GAtL...doqu"
tip

Users can find their device IDs at the /.pomerium endpoint from any route.

String Matcher

A string matcher is an object with operators as keys. It supports the following operators: contains, ends_with, is and starts_with.

For example:

allow:
and:
- email:
starts_with: 'admin@'

Or:

allow:
and:
- record:
type: example.com/geoip
field: country
is: 'US'

String List Matcher

A string list matcher is an object that supports a single has operator as a key. The has operator checks that a given string is present in a list of strings.

The groups and record criteria both support the has operator.

For example, using the groups criterion:

allow:
and:
- groups:
has: '00gv40ki4gmtCyl5d4x6'

Using the record criterion:

- record:
type: example.com/hr_user
field: departments
has: 'engineering'

Time of Day Matcher

The time of day matcher is an object with operators as keys. It supports the following operators: timezone, after, and before.

timezone is required and specifies the timezone to use when interpreting the supplied times. It is recommended to use city names (like America/Phoenix) instead of standard timezone abbreviations because standard timezones change throughout the year (i.e. EST becomes EDT and back again).

after means the time of the request must be after the supplied time and before means that the time of the request must be before the supplied time. For example:

allow:
and:
- time_of_day:
timezone: UTC
after: 2:20:00
before: 4:30PM