For the complete documentation index, see llms.txt. This page is also available as Markdown.

Configuring access policies

Reference for P0 Security access policies: the full policy format, worked scenarios for AWS and on-call approvals, and the order in which P0 evaluates a request against your rules.

Access policies give you fine-grained control over your organization's just-in-time access workflow: who can request access, which resources they can request, and how those requests are approved. This page is the reference for the policy format, worked scenarios, and evaluation order.

To build a policy from scratch, start with the Configure your first access policy tutorial. It walks you through Policy Studio and gets a working policy in place in about ten minutes. Come back here for the full field reference.

Prerequisites

Before you begin, confirm the following:

  • Owner or Security Reviewer role in your P0 organization.

  • At least one resource integration installed (AWS, Google Cloud, Azure, Kubernetes, or another supported resource).

  • A directory integration configured (Google Workspace, Okta, or Microsoft Entra ID) if you plan to use group-based policies.

Where to edit policies

Policy Studio is at Policy Studio in the p0.app sidebar, or at https://p0.app/o/<your-organization>/policies. The View options (⋯) menu in the page header switches between Table view and YAML view.

The YAML editor takes the format on this page directly. You can also manage policies through the Access policies API and the Terraform provider.

Guided editor

Add New Policy opens the visual form. Name the policy at the top, for example "Engineering - AWS read-only", then fill in its three sections. Each one sets a part of the rule this page documents below:

Section
Sets
Reference

Identity

Who can request

Resource(s)

What they can request

Actions

How requests are approved

Two buttons add entries within a section. Add New Resource Filter adds a filter under Resource(s), where you choose a filter type, an effect, and a pattern. Add New Access Rule adds another approver under Actions. When a policy has several approvers, approval from any one of them is enough.

Create Policy saves a new policy, and reads Save Policy when you edit an existing one. Confirm the policy then appears in the list with its Enabled toggle on.

Policies take effect as soon as you save them, and apply to every later access request.

Check that a policy works

Have someone who matches the policy make a request through Slack (/p0 request), the P0 CLI, or the web request modal. The Activity page at https://p0.app/o/<your-organization>/access-management/activity shows the request and which policy decided it.

If the requestor sees "This resource doesn't exist, or your organization doesn't allow this principal to access this resource", no policy matched. Check the requestor and resource parts of your rule against the request.

Access policy format

Structure of a policy configuration

A policy configuration is a collection of access policies. To change how P0 routes your requests (such as sending engineers' requests to their managers or restricting access to a particular prod resource to a small group), submit a new policy configuration.

You only have one policy configuration active at any given time, but that one configuration can have as many individual access policies as you want. You don't need to separate out different rule types or anything like that.

Here's an example policy configuration that you can copy into your app as a starting point:

Structure of a "rule"

The core of policy configurations are the rules, so let's break them down.

Requestor

The first part of a rule is a "requestor", which matches who makes the request. There are four types of requestors you can choose from:

"Any"

This matches all requestors. Useful for global rules like fall-back restrictions of sensitive resources.

"Group"

This rule matches any requestor that is a member of a group in your IdP. Currently, this supports Google Workspace, Microsoft Entra ID, and Okta.

All three of type, groups, and effect are required, and no other keys are accepted. Note that the group's own fields sit under groups, not directly on the rule.

groups: A list of one or more groups. Each entry needs all three of id, label, and directory.

effect: keep matches requestors who are a member of at least one of the listed groups. remove matches requestors who are a member of none of them.

id: For Google Workspace, this is the group email address (that is, engineering@yourco.com). For Entra, this is the Entra ID group's UUID. For Okta, this is the group ID found in the URL of the group's page in the admin console. See Okta docs.

label: This is any friendly human-readable name you like, although P0 suggests using the same name as displayed in your directory.

directory: For Google Workspace this is "workspace", for Okta this is "okta", and for Entra ID, this is "entra-id". Use "azure-ad" only if you are on P0's legacy Entra ID integration, which the app labels "Entra ID (Legacy)".

"User"

This rule matches only a specific user, as identified by their email address.

"Agentic"

This rule matches requests made by AI agents through the P0 AI Gateway, based on the combination of the agent that is acting and whoever it is acting for. See Agentic access policies for the actor and subject rule types and examples.

Resource

The second part of an access policy is the resource this rule should apply to.

"Any"

This matches all resources. This is useful, for instance, when you want to route access for a particular user, regardless of what resource or service they are looking to access.

"Integration"

This rule matches a specific type of access request. For instance, maybe you want to create a rule that routes all AWS requests to your DevOps team. Or you want to restrict access to GCP to only a select group.

service: For GCloud, "gcloud", for AWS, "aws", for Microsoft Azure "azure", for Snowflake, "snowflake", and for SSH, "ssh".

accessType (Optional): The access type within the service that the rule applies to, or "any" meaning the rule matches requests of any access type. Defaults to "any" if omitted.

filters (Optional): Filters allow you to apply the rule only to the service components that match the filtering condition.

Each filter has a filter-name that refers to the type of the requested object. For instance, when "service" is "aws", the filter with the name "policy" only allows requesting policies whose ARNs match the pattern.

Filters are independent when the access type is omitted or set to "any." For example, if you specify a filter-name=permission filter for service=gcloud requests, but omit a filter-name=role filter, then roles are unaffected by the filter, and all roles remain requestable.

Each filter has three potential properties:

  • effect - Describes how this filter is applied

    • keep - This rule retains objects that match the pattern as requestable

    • remove - This rule only retains objects that do not match the pattern

    • removeAll - This rule disables the object type entirely.

    • To allow all objects of a type as requestable, omit the filter altogether

  • key - Describes which property of the object the filter must match; only applies if "effect" is one of "keep" or "remove". For instance, if service=aws and access-type=permission-set then the key can be "name" or "arn". If "name", the regex pattern matches the name of permission sets. If "arn", the regex pattern matches the entire arn.

  • pattern - A regex pattern used to match against the specified property; only applies if "effect" is one of "keep" or "remove"

Patterns are unanchored. Use line-start (^) and line-end ($) markers to anchor patterns.

P0 uses the JavaScript regex dialect.

Filter behavior

The behavior of filters depends on the requested access type.

For example, the "role" filter in the Gcloud integration has a slightly different meaning depending on which access type is used in the request:

  • When making a GCP request with "role" as the access type (for example, p0 request gcloud role), the filter limits the roles the user can request access to

  • When making a GCP request with "resource" as the access type (for example, p0 request gcloud resource), the filter limits the roles that the user can request on the resource they are requesting

By setting the rule's access type to either "role" or "resource", you can explicitly specify which of these requests is allowable.

The following table summarizes valid filter filter-name / key combinations. For more detailed information on the filters, see the documentation for AWS, Microsoft Azure, Google Cloud, and SSH.

service
access-type
key
Notes

aws

tag

<tag key>

Only filters policies and permission sets. AWS managed policies can't be tagged.

group

name

Filters IAM group names

permission-set

arn | name

Filters Identity Center permission sets

policy

arn

Filters policies with matching ARNs

resource

arn | name | service

Filters resource requests based on the resource ARN, the resource name, or the service the resource belongs to.

azure

subscription

id

Filters requests at a subscription level

resource

id | name

Filters requests based on what resource is requested

role

id | name

Filters requests based on the role requested

azure-ad|okta

group

id | label

Filters directory groups

gcloud

permission

id

Filters IAM permissions

role

id

Filters IAM roles based on full ID (for example, the ID for "Owner" is roles/owner)

resource

name | type | full-resource-name

Filters resource requests based on the resource name, resource type, or the full resource name.

snowflake

role

name

Filters roles

k8s

resource

kind | name | namespace

Filters resources

role

name

Filters roles and cluster roles

cluster

name

Filters clusters based on the cluster name specified when installed in P0

ssh

provider

id

Filters by cloud provider (aws, gcloud, azure, or self-hosted)

destination

arn | name | full-resource-name

Filters by instance identifier

group

name

Filters by SSH group name

parent

id

Filters by parent resource ID (AWS account ID, GCP project ID, or Azure subscription ID)

region

id

Filters by AWS region, GCP zone, or Azure region

sudo

(boolean)

Filters by whether sudo privileges are requested

Filters are not currently available for AWS or GCP resource-level grants, nor for Snowflake SQL grants.

Example that covers each possible access type in the AWS service:

  1. Exclude all permission sets and policies containing "FullAccess"

  2. Only allow AWS policies and permission sets where the tag "P0Grantable" is equal to the string "true"

  3. Do not allow requesting any AWS groups

Approval

The final part of an access policy is the "approval". Unlike the requestor and resource parts, the approval part is an array of multiple rules, referring to the people/groups/services that can approve (or deny) access requests.

"p0"

This approval type is analogous to the default approval behavior. This rule routes approvals to the people designated as "Security Reviewers" on the Settings page of your app. The "options" key is optional. You can use it to:

  • allow the requestor to approve their own requests for this flow with the allowOneParty: true setting. Defaults to false.

  • require the requestor to specify a reason when submitting requests with the requireReason: true setting. Defaults to false.

  • designate this approver as a break glass approver for SSH "all" access requests with the breakGlassApprover: true setting. Defaults to false. See SSH Filtering for details.

"Group"

This rule matches any requestor who is a member of a group in your Identity Provider. Currently, P0 supports Google Workspace, Microsoft Entra ID, and Okta.

The shape matches a group requestor rule: type, groups, and effect are all required, and the group's own fields sit under groups. Note that options stays on the rule, beside groups, rather than moving in with the group.

groups: A list of one or more groups. Each entry needs all three of id, label, and directory.

effect: keep routes approval to members of the listed groups. remove routes it to everyone who is a member of none of them.

id: For Google Workspace, this is the group email address (that is, engineering@yourco.com). For Entra ID, this is the Entra ID group's UUID. For Okta, this is the group ID found in the URL of the group's page in the admin console. See Okta docs.

label: This is any friendly human-readable name you like, although P0 suggests using the same name as displayed in your directory. This label will be printed in approval notifications.

directory: For Google Workspace this is "workspace", for Okta this is "okta", and for Entra ID this is "entra-id". Use "azure-ad" only if you are on P0's legacy Entra ID integration, which the app labels "Entra ID (Legacy)".

options: See "P0" section

"Auto"

This rule automatically approves matching access requests for one hour.

integration: "pagerduty" or "incidentio". P0 automatically approves the request if the requestor is on-call. For PagerDuty, P0 checks the escalation policies configured on the integration page. For Incident.io, P0 checks the schedules selected on the Incident.io integration settings page.

options: The "options" key is optional. You can use it to require the requestor to specify a reason when submitting requests with the requireReason: true setting. Defaults to false.

"Escalation"

This rule routes approval to users who are currently on-call for the specified services or schedules. When combined with other approval rules, it enables on-call users to approve requests that have been escalated.

integration: "pagerduty" or "incidentio".

  • For PagerDuty, on-call status is determined by the escalation policies of the configured services. An incident is created against the specified services when the request is escalated.

  • For Incident.io, on-call status is determined by the specified schedule IDs. Users who are on-call for any of the listed schedules can approve the request.

options: See "P0" section.

services: List of PagerDuty service IDs or Incident.io schedule IDs, depending on the configured integration.

"Always allowed"

Access is always granted automatically.

options: The "options" key is optional. You can use it to require the requestor to specify a reason when submitting requests with the requireReason: true setting. Defaults to false.

"Deny"

This rule denies all requests that match. This supersedes other matching rules: if at least one "deny" rule matches, the request will be denied.

Common scenarios

These worked scenarios cover the most common access-policy setups. Adapt the group identifiers, account IDs, and service names to your own organization.

Route requests by team

Route your engineering team's requests to the SRE team for approval, and your data team's requests to the Data Ops team.

  1. In Policy Studio, enter the following access policies:

  1. Replace the group email addresses with your organization's actual directory groups.

  2. Click Submit to activate the access policies.

What this does:

  • Any engineer who requests access to any resource gets routed to the SRE team for approval.

  • Any data team member who requests Snowflake access gets routed to Data Ops for approval.

  • allowOneParty: false prevents requestors from approving their own requests.

Add a catch-all policy

To ensure unmatched requests still reach your security reviewers, add this policy at the end of your workflow:

The type: p0 approval routes requests to the Security Reviewers configured under P0 Management → Access control.

Restrict access to production resources

Block requests to production AWS accounts while allowing development and staging access with different approval requirements.

  1. In Policy Studio, add the following policies. Replace the AWS account IDs and Okta group IDs with your own values:

  1. Click Submit.

What this does:

  • Development (account 111111111111): Engineers can self-approve their own requests.

  • Staging (account 222222222222): Engineers need a peer to approve, and must provide a reason.

  • Production (account 333333333333): P0 denies all requests. No one can request access through P0.

Deny policies always take precedence. If any matching policy specifies type: deny, the request is denied immediately, regardless of other matching policies.

Exclude dangerous permissions

To further restrict which AWS permissions engineers can request, add filters that remove overly broad policies:

This removes any policy containing "FullAccess" in its ARN, and only keeps policies tagged with P0Grantable: true.

Auto-approve on-call engineers

Automatically approve requests from engineers who are currently on-call in PagerDuty or Incident.io, while directing all other requests to the SRE team.

  1. Confirm that your PagerDuty or Incident.io integration is installed.

  2. In Policy Studio, add the following policies:

  1. Click Submit.

What this does:

  • If the requestor is currently on-call (as determined by PagerDuty escalation policies or Incident.io schedules), P0 automatically approves their request for one hour.

  • If the requestor is not on-call, P0 routes the request to the SRE team for manual approval.

  • requireReason: true ensures all requests include a justification in the audit log, even auto-approved ones.

Add escalation for urgent requests

You can also configure on-call users as escalation approvers. When a request is pending, the requestor can escalate it to the on-call engineer:

Replace PSJXXXG with your PagerDuty service ID. When the requestor escalates, PagerDuty creates an incident against the specified service, and the on-call engineer receives both a PagerDuty alert and a P0 approval notification.

Additional examples

Access policies support a wide range of setups. These examples show patterns beyond the common scenarios described earlier.

Different rules by AWS account

Development AWS account (123456789) allows one-party approvals. Staging AWS account (234567891) allows peer approvals but no one-party approvals. Production AWS account (345678912) requires a manager approver represented by an Okta group. Assumes an AWS Identity Center setup where resource filters allow requesting customer-managed policies in specific AWS accounts, enforced by an ARN match that includes the AWS account ID. Permission sets are implicitly allowed because the permission-set access type filter is not defined.

Approve by manager or service owner

Allow either the requestor's manager or the service owner to approve requests:

Require a reason for a specific resource type

Make reason mandatory for AWS requests

Exclude cluster-admin and restrict to the default namespace (Kubernetes)

Exclude the cluster-admin role and only allow requests in the default namespace:

Allow Google Cloud roles but not permissions

Let users request Google Cloud roles while denying direct permission requests:

Deny specific roles while allowing others

Deny requests for the Owner role while allowing all other Google Cloud roles:

Evaluation of access policies

  1. Match Rules to the Request

    Identify rules that match the request. A rule matches if both the requestor and the resource criteria in the rule align with the request details.

  2. Handle Missing Matches

    If no rules match, the access request is not created, and further evaluation stops. The requestor will see the following message:

    This resource doesn't exist, or your organization doesn't allow this principal to access this resource

  3. Evaluate Access Control of Matching Rules

    For matching rules, review the Access Control field in the following order:

    • Deny all access If any matching rule specifies "Deny all access", the request is denied immediately, and further evaluation stops.

    • Always allowed If any rule specifies "Always allowed," the request is automatically approved and provisioned, and further evaluation stops.

    • Third-party auto-approval If a rule specifies a third-party auto-approval, such as a PagerDuty or Incident.io on-call rule, the request is evaluated for automatic approval. If it cannot be auto-approved, proceed to the next step.

    • Manual approval For all other Access Control values, the request requires manual approval by a designated person or group. When multiple rules with different manual approvers match, approval from any one approver is sufficient to provision the request. All approvers are notified.

Key points to remember

  • Deny all access: Always takes precedence and stops further evaluation immediately.

  • Always allowed: Overrides other Access Control values and is prioritized in the evaluation process.

  • Multiple matching rules: When multiple rules match the requestor and resource, approvers are combined using an ANY relationship, meaning approval from any one approver is sufficient.

Examples

  1. Always allowed rule takes precedence over other allow rules

Consider a scenario where two rules apply to the same resource, node1 in the devstack group:

  • An "Always allow" rule grants access to node1.

  • Another "Allow" rule requires approval before granting access.

In this case, the "Always allow" rule takes precedence. As a result:

  • The user is immediately granted access to node1 without requiring additional approval.

  1. Deny Rules are evaluated first, before ANY allow rules

Using the same example as before, if there is an additional rule that denies access to any instance containing node in its name, the system evaluates the "Deny" rule first.

In this case:

  • The "Deny" rule matches because node1 contains the string node in its name.

  • The user's request is immediately denied, and is not evaluated further.

This highlights the priority of "Deny" rules: the system enforces them before considering any other rules.

Troubleshooting

Symptom
Cause
Solution

Request rejected with "This resource doesn't exist..."

No access policy matches the request.

Add a catch-all policy or verify that your requestor and resource filters match the request.

Wrong approver notified

A broader policy matches before the specific one.

Reorder policies so more specific policies come first. Deny policies always take precedence regardless of order.

Auto-approval not working

The requestor is not on-call, or the PagerDuty/Incident.io integration is not installed.

Verify the requestor's on-call status in your incident management tool, and confirm the integration is active in Integrations.

Filter not matching

Regex pattern mismatch.

Patterns are unanchored by default. Use ^ and $ to anchor. P0 uses the JavaScript regex dialect.

Next steps

Last updated