Skip to content

Access policy»

Warning

Access policies are deprecated. Use space access control instead.

By default, non-admin Spacelift users have no access to any stacks or modules and must be granted explicit permissions. There are two levels of non-admin access, reader and writer. For now all we need to care about is that access policies are what we use to give appropriate level of access to individual stacks to non-admin users in your account.

This type of access control is typically done either by building a separate user management system on your end or piggy-backing on one created by your identity provider. Both solutions have their limitations:

  • Separate user management system: Makes it more difficult for organizations to onboard and offboard users, and can be difficult to get right, especially if you require granular and sophisticated access controls.
  • Identity provider: A safer bet used by many DevTools vendors. With this approach, having some access level to a GitHub repo would give you the same access level to all Spacelift stacks and/or modules associated with it, which can be inflexible if you want separate access controls for two stacks linked to one repo (such as staging and production).

Access policies give you a tool to build your own access management system using data obtained either from our identity provider (GitHub), or from your identity provider if using the Single Sign-On integration.

Rules»

Your access policy can define the following boolean rules:

  • write: Grants the current user write access to the stack or module.
  • read: Grants the current user read access to the stack or module.
  • deny: Denies the current user all access to the stack or module, no matter the outcome of other rules.
  • deny_write: Denies the current user write access to the stack or module, no matter the outcome of other rules.

Write access automatically assumes read permissions, so there's no need to define separate read policies for writers.

Access policies are executed quickly. Internally, we expect that running all access policies on all the stacks in one request (stacks in the GraphQL API) will take less than 500 milliseconds, otherwise the request fails. While that is usually enough time for modern computers, fancy regex rules in your access policies could cause issues in some cases.

Access levels»

There are two levels of non-admin access to a Spacelift stack or module: reader and writer. These are pretty intuitive for most developers, but this section will cover them in more detail to avoid any possible confusion.

In every non-trivial organization there will be different roles: people who build and manage shared infrastructure, people who build and manage their team or project-level infrastructure, and people who use this infrastructure to build great things.

Admins»

The people who manage your Spacelift accounts. They need to be able to set up:

Use login policies to manage admin access.

Writers»

The people who manage their team or project-level infrastructure. This group should be able to:

Writers should still operate within the boundaries defined by admins, who do that mainly by attaching contexts and policies to the stacks.

Readers»

The people who build on top of existing infrastructure. They don't need to define the infra, but they need to understand what's available and when things are changing. You'll probably want to allow them to contribute to infra definitions, too, and allow them to see feedback from proposed runs.

Readers can't do anything, but they can see everything. Most modern organizations tend to provide this level of access to as many stakeholders as possible to maintain transparency and facilitate collaboration.

Data input schema»

Each policy request will receive this data input.

Official Schema Reference

For the most up-to-date and complete schema definition, please refer to the official Spacelift policy contract schema under the ACCESS policy type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "request": {
    "remote_ip": "string - IP of the user making a request",
    "timestamp_ns": "number - current Unix timestamp in nanoseconds"
  },
  "session": {
    "admin": "boolean - is the current user a Spacelift admin",
    "creator_ip": "string - IP address of the user who created the session",
    "login": "string - GitHub username of the logged in user",
    "name":  "string - full name of the logged in GitHub user - may be empty",
    "teams": ["string - names of org teams the user is a member of"],
    "machine": "boolean - whether the creator is a machine or a user"
  },
  "stack": { // when access to a stack is being evaluated
    "id": "string - unique ID of the stack",
    "administrative": "boolean - is the stack administrative",
    "autodeploy": "boolean - is the stack currently set to autodeploy",
    "branch": "string - tracked branch of the stack",
    "labels": ["string - list of arbitrary, user-defined selectors"],
    "locked_by": "optional string - if the stack is locked, this is the name of the user who did it",
    "name": "string - name of the stack",
    "namespace": "string - repository namespace, only relevant to GitLab repositories",
    "project_root": "optional string - project root as set on the Stack, if any",
    "repository": "string - name of the source repository",
    "state": "string - current state of the stack",
    "terraform_version": "string or null - last Terraform version used to apply changes"
  },
  "module": { // when access to a module is being evaluated
    "id": "string - unique ID of the module",
    "administrative": "boolean - is the stack administrative",
    "branch": "string - tracked branch of the module",
    "labels": ["string - list of arbitrary, user-defined selectors"],
    "namespace": "string - repository namespace, only relevant to GitLab repositories",
    "repository": "string - name of the source repository",
    "terraform_provider": "string - name of the main Terraform provider used by the module"
  }
}

Examples»

Tip

We maintain a library of example policies that are ready to use or alter to meet your specific needs.

If you cannot find what you are looking for below or in the library, please reach out to our support and we will craft a policy to do exactly what you need.

This section will cover some common examples that can be copied more or less directly, and some contrived ones to serve as inspiration.

Access policies must be attached to a stack or a module to take effect.

Read access to engineers»

In this policy, every member of the engineering team gets read access:

1
2
3
package spacelift

read { input.session.teams[_] == "Engineering" }

Write only in-office during business hours»

In this policy, write access is only provided to users that are in the office (using the office's IP address range) during business hours (9 to 5, weekdays). Write access is restricted in other cases. This policy is best combined with one that gives read access.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package spacelift

now     := input.request.timestamp_ns
clock   := time.clock([now, "America/Los_Angeles"])
weekend := { "Saturday", "Sunday" }
weekday := time.weekday(now)
ip      := input.request.remote_ip

write      { input.session.teams[_] == "Product team" }
deny_write { weekend[weekday] }
deny_write { clock[0] < 9 }
deny_write { clock[0] > 17 }
deny_write { not net.cidr_contains("12.34.56.0/24", ip) }

Protect administrative stacks»

Administrative stacks are powerful. Having write access to one is almost as good as being an admin, as you can define and attach contexts and policies. In this policy, we deny write access to administrative stacks entirely. This works since access policies are not evaluated for admin users.

1
2
3
package spacelift

deny_write { input.stack.administrative }