Access Control¶
Authentication¶
In nixbuild.net, users authenticate either using an SSH key, or using an access token. When using the Nix client to interact with nixbuild.net (running builds or using it as a Nix store), you can either use SSH keys or access tokens for authentication. When using the nixbuild.net's HTTP API you must use access tokens.
Both SSH keys and access tokens have their strengths and weaknesses, and you must yourself find out what fits your use case best. As a general advice, sticking with SSH keys is usually easier until you need the additional flexibility that access tokens provide.
Using SSH Keys¶
SSH keys are the default authentication method in nixbuild.net. When you create an account, you must provide a public SSH key which is tied to your account. After account creation, you can add or remove SSH keys as you like, using the administration shell.
If you're a team, you can add each team member's personal SSH key to your nixbuild.net account. That way, all members can use nixbuild.net as a remote builder and, since they all share the same nixbuild.net account, they can reuse build inputs and outputs.
On your Nix clients, you set up your SSH config to use the corresponding keys
when connecting to eu.nixbuild.net. This does not differ from using regular
remote Nix builders, or using SSH in general. You can see step-by-step
instructions in the Getting Started guide.
Using Access Tokens¶
To be able to use an access token, you first need to create one using the
tokens create command in the administration shell, or
from the Access Tokens page in the web UI.
Manually created tokens require a unique, non-empty name, an expiration date,
and a set of permissions. The permissions are described in
more detail below. If you want to create an access token named
cache-upload that should be valid for 30 days and only be allowed to query,
pull and push store paths from nixbuild.net (not start any builds), you invoke
the command like this:
nixbuild.net> tokens create --name cache-upload --ttl-seconds 2592000 -p store:read -p store:write
The complete token is shown only once, when it is created. nixbuild.net stores
its name, revocation ID, creation time, and expiration time so that the token
can be managed later. Run tokens list to list all active named tokens. Token
names can be reused after the previous token expires or is revoked.
Revoking Access Tokens¶
Access tokens should be treated as secrets and stored in a sensible way. You can revoke a root token by its name or revocation ID without having the secret token:
nixbuild.net> tokens revoke cache-upload
The token has been revoked
nixbuild.net> tokens revoke 5f83f043272e4c939eea8219cecc2caec738b70f00635589665ce637903edf3749b6352bd1e8b791877a5a47c0b5500149a52709a4220cbc9a40936192ecb00f
The token has been revoked
The command automatically determines whether its argument is a name, a revocation ID, or a complete token. Passing the complete token is required to revoke an attenuated token, because attenuated tokens are created offline and are not listed by nixbuild.net:
nixbuild.net> tokens revoke EpYCCqsBCgxhY2NvdW50OnJlYWQKDWFjY291bnQ6d3JpdGUKCmJ1aWxkOnJlYWQKC2J1aWxkOndyaXRlCgpzdG9yZTpyZWFkCgtzdG9yZTp3cml0ZRgDIggKBggHEgIQBCImCiQIBBIgOh4KAxiACAoDGIEICgMYgggKAxiDCAoDGIQICgMYhQgyJgokCgIIGxIGCAUSAggFGhYKBAoCCAUKCAoGIJesxKkGCgQaAggAEiQIABIg_vub4yDKe3X_ZYwW1OPNcQhBzm9aCFq87JuDioh3Iz4aQF-D8EMnLkyTnuqCGc7MLK7HOLcPAGNViWZc5jeQPt83SbY1K9Hot5GHelpHwLVQAUmlJwmkIgy8mkCTYZLssA8atQEKSwoKb3BlcmF0aW9ucwoDb3BzGAMyNgo0CgIIGxIICIYIEgMIhwgaJAoFCgMIhwgKCQoHOgUKAxiBCAoEGgIIBQoEEgIIAQoEEgIIABIkCAASIO--7mSwDxqhhL4O6K6DtqYDfKJUH0JvYM2OErB0a2jhGkBWHj5nDRyU7w8oItWMd5A2c5SuJy-3tSqNXmPRJvuz9rfhu4BNF2YIJ9OkuMg6Ob3onzT5OlZLdYh94GiN_eIPIiIKIO6HoXcsjkDS8eE1nPsigbmq_YfTUykRL9JWqWHL9Ftj
The token has been revoked
When revoked, the token will no longer work, but existing builds or operations that have been started using the token will not be cancelled. You can also revoke attenuated tokens, in which case only the attenuated token will be revoked, not the original token. On the other hand, if you revoke a token, any attenuated tokens that originated from the revoked token will also be revoked automatically.
Accessing the HTTP API using an Access Token¶
To access the HTTP API using an access token, you must provide an
Authorization header in your requests. The header value should be set to
Bearer <TOKEN>.
Using the Nix Client with an Access Token¶
To make the Nix client use an access token when performing builds and other store operations in nixbuild.net, you need some specific SSH configuration:
Host eu.nixbuild.net
User authtoken
PreferredAuthentications none
SetEnv NIXBUILDNET_TOKEN=<TOKEN>
ServerAliveInterval 60
The important settings above are User, PreferredAuthentications and
SetEnv. Setting up your SSH client configuration like above bypasses the
normal SSH authentication, and sends the access token to nixbuild.net
in the environment passed along when the SSH connection is initiated. When
nixbuild.net gets such a connection request, it will not look at the public SSH
key at all. Instead it will decode and verify the access token provided in the
NIXBUILDNET_TOKEN SSH environment variable. If the token is valid,
nixbuild.net will use the account embedded inside the token to perform
authentication.
Authorization¶
Once authentication is done, and nixbuild.net has figured out which account a request belongs to, an authorization phase starts. Authorization in nixbuild.net is centered around a few simple permissions, but there is also support for implementing more complex access policies, if access tokens are used. We will demonstrate this below.
Authorization with SSH Keys¶
When an SSH key is used for authenticating to nixbuild.net, the permissions that take effect are decided by the value of the default-permissions setting. This means you can lock down specific SSH keys to a specific set of permissions, but it is not possible to implement more complex access policies. In most cases, assigning permissions to SSH keys is perfectly adequate, though.
Authorization with Access Tokens¶
Each access token has a set of permissions built-in. Those permissions are assigned during token creation, as shown above.
In addition to building-in permissions, you can further weaken the permissions of tokens through an operation called attenuation. The key advantage of this operation is that it does not need involvement of nixbuild.net at all. You would first create one or more root tokens with wide capabilities, through the nixbuild.net administration shell. Then you could create much less capable sub-tokens by attenuation, completely offline from nixbuild.net. This functionality is possible since nixbuild.net access tokens are Biscuit authorization tokens.
Token Attenuation¶
To perform attenuation, you need to use the
biscuit-cli command line tool
which provides the biscuit executable. This tool is available in
nixpkgs. Lets walk
through the complete process of creating and attenuating an access token.
We create a new token, with all permissions enabled and an expiration time of 30 days:
nixbuild.net> tokens create --name full-access --ttl-seconds 2592000 --all-permissions
-------------------------------------------------------------
EpYCCqsBCgxhY2NvdW50OnJlYWQKDWFjY291bnQ6d3JpdGUKCmJ1aWxkOnJlYWQKC2J1aWxkOndyaXRlCgpzdG9yZTpyZWFkCgtzdG9yZTp3cml0ZRgDIggKBggHEgIQBCImCiQIBBIgOh4KAxiACAoDGIEICgMYgggKAxiDCAoDGIQICgMYhQgyJgokCgIIGxIGCAUSAggFGhYKBAoCCAUKCAoGIJesxKkGCgQaAggAEiQIABIg_vub4yDKe3X_ZYwW1OPNcQhBzm9aCFq87JuDioh3Iz4aQF-D8EMnLkyTnuqCGc7MLK7HOLcPAGNViWZc5jeQPt83SbY1K9Hot5GHelpHwLVQAUmlJwmkIgy8mkCTYZLssA8iIgogTCydeLS2B7m8L6-e5tWH0PfdDimN6JXBvDN-WP0l20Q=
-------------------------------------------------------------
The token will expire at:
2023-10-19 11:42:14Z
The token has the following permissions:
account:read
account:write
build:read
build:write
store:read
store:write
The token has the following name:
full-access
We store the token in a file to make it easier to handle with the biscuit
program:
$ echo >token EpYCCqsBCgxhY2NvdW50OnJlYWQKDWFjY291bnQ6d3JpdGUKCmJ1aWxkOnJlYWQKC2J1aWxkOndyaXRlCgpzdG9yZTpyZWFkCgtzdG9yZTp3cml0ZRgDIggKBggHEgIQBCImCiQIBBIgOh4KAxiACAoDGIEICgMYgggKAxiDCAoDGIQICgMYhQgyJgokCgIIGxIGCAUSAggFGhYKBAoCCAUKCAoGIJesxKkGCgQaAggAEiQIABIg_vub4yDKe3X_ZYwW1OPNcQhBzm9aCFq87JuDioh3Iz4aQF-D8EMnLkyTnuqCGc7MLK7HOLcPAGNViWZc5jeQPt83SbY1K9Hot5GHelpHwLVQAUmlJwmkIgy8mkCTYZLssA8iIgogTCydeLS2B7m8L6-e5tWH0PfdDimN6JXBvDN-WP0l20Q=
We can use biscuit inspect to take a look at what's inside the token:
$ biscuit inspect ./token
Authority block:
== Datalog ==
owner(4);
right(["account:read", "account:write", "build:read", "build:write", "store:read", "store:write"]);
check if time($time), $time < 2023-10-19T11:42:15Z;
== Revocation id ==
5f83f043272e4c939eea8219cecc2caec738b70f00635589665ce637903edf3749b6352bd1e8b791877a5a47c0b5500149a52709a4220cbc9a40936192ecb00f
==========
🙈 Public key check skipped 🔑
🙈 Datalog check skipped 🛡️
We can see that the token contains an owner account identifier, a set of
permissions and an expiration check. The Authority block is using the
Biscuit policy
language,
and that is also what you'll use to attenuate the token.
Lets attenuate the token now, and create a new token that can't be used to perform any changes to the account configuration using the administration shell:
$ biscuit attenuate ./token --block 'check if operations($ops), !($ops.contains(["account:write"]))'
EpYCCqsBCgxhY2NvdW50OnJlYWQKDWFjY291bnQ6d3JpdGUKCmJ1aWxkOnJlYWQKC2J1aWxkOndyaXRlCgpzdG9yZTpyZWFkCgtzdG9yZTp3cml0ZRgDIggKBggHEgIQBCImCiQIBBIgOh4KAxiACAoDGIEICgMYgggKAxiDCAoDGIQICgMYhQgyJgokCgIIGxIGCAUSAggFGhYKBAoCCAUKCAoGIJesxKkGCgQaAggAEiQIABIg_vub4yDKe3X_ZYwW1OPNcQhBzm9aCFq87JuDioh3Iz4aQF-D8EMnLkyTnuqCGc7MLK7HOLcPAGNViWZc5jeQPt83SbY1K9Hot5GHelpHwLVQAUmlJwmkIgy8mkCTYZLssA8atQEKSwoKb3BlcmF0aW9ucwoDb3BzGAMyNgo0CgIIGxIICIYIEgMIhwgaJAoFCgMIhwgKCQoHOgUKAxiBCAoEGgIIBQoEEgIIAQoEEgIIABIkCAASIO--7mSwDxqhhL4O6K6DtqYDfKJUH0JvYM2OErB0a2jhGkBWHj5nDRyU7w8oItWMd5A2c5SuJy-3tSqNXmPRJvuz9rfhu4BNF2YIJ9OkuMg6Ob3onzT5OlZLdYh94GiN_eIPIiIKIO6HoXcsjkDS8eE1nPsigbmq_YfTUykRL9JWqWHL9Ftj
If we use the resulting token when accessing the administration shell, then we can not run any operations that require write access:
nixbuild.net> tokens create --name example --all-permissions
Authorization failed. You might lack one or more of these permissions:
account:write
An attenuated token can be further attenuated into new tokens, if one wishes to do so.
You may wonder where the operations() and time() constructs, that were used in the Biscuit policies above, came from. These are Biscuit facts and they are added by nixbuild.net during the authorization phase. You can make use of these facts to define your own access policies. Below follows the complete list of facts that you can use when authoring access policies.
It is strongly recommended to read through the Biscuit documentation to gain a deeper understanding of how Biscuit tokens work and how they can be used.
Available Permissions¶
The permissions available in nixbuild.net are the following:
account:read¶
Grants read-only access to the administration shell.
account:write¶
Grants read and write access to the administration shell. A user holding this permission can make any changes to the account configuration. This includes adding new public SSH keys and generating new access tokens.
build:read¶
Grants read access to build information, including build logs.
build:write¶
Grants the ability to run builds on nixbuild.net. In practice,
to be able to run Nix builds, you also need the build:read, store:read and
store:write permissions since Nix performs multiple different store
operations when running a build.
store:read¶
Grants read-only access to the Nix store. A user holding this permission can download build outputs or any other store paths from nixbuild.net (as long as the paths belong to the account).
store:write¶
Grants the ability to upload paths to nixbuild.net.
Available Biscuit Facts¶
These are the facts available to your Biscuit policies. It is expected that this list of available facts will grow in the future, to make it possible to define a wide range of access policies.
build¶
Type: (integer)
This fact will be set for any request that concerns a specific build. It can be used to restrict a token to only access information about a specific build id.
jwt_claim¶
Type: (*, string)
This fact contains the iss and sub claims of an OIDC JWT that has been
passed to nixbuild.net during auth, if nixbuild.net successfully verified the
JWT signature. This allows you to restrict token usage based on and OIDC ID
Token.
Example attenuation block:
check if
jwt_claim("iss", "https://token.actions.githubusercontent.com");
check if
jwt_claim("sub", $sub),
$sub.contains("repo:nixbuild/nixbuild-oidc-auth-example");
nixbuild.net uses OIDC Discovery to find out which endpoint to ask for public keys to perform JWT verification.
To pass an OIDC JWT to nixbuild.net, assign it to the SSH environment variable
or HTTP header NIXBUILDNET_OIDC_ID_TOKEN.
operations¶
Type: ([string])
The set of the permissions necessary to allow the request to be performed. This fact can be used to restrict the permissions of a token to only a subset of its initially assigned permissions.
Example attenuation block:
check if operations($ops), !($ops.contains(["account:write"]));
rolling_30_day_usage¶
Type: (*, string)
This fact records your account's rolling 30 days consumption of different resources.
The resource types available in this fact are listed below.
CPU Hours¶
Type: integer
This fact can be used to create attenuated tokens that can't incur charges beyond a certain limit.
Example attenuation block, that limits usage to 500 CPU hours per month:
check if
operations($ops),
rolling_30_day_usage($x, "CPU Hours"),
!($ops.contains("build:write")) || $x <= 500;
Note that the max-cpu-hours-per-month setting also adds an attenuation like the one above.
setting¶
Type: (string, *)
This fact represents a subset of the settings that was in effect during the request.
The settings available in this fact are listed below.
trusted-public-keys¶
Type: [string]
You can use this fact to restrict a token to only be allowed to trust a specific set of public signing keys. If such a token is used, there is no way for a user to use any store paths signed by other keys as build inputs, or fetch such store paths . Here is an example attenuation block that achieves this:
check if
setting("trusted-public-keys", $tpks),
(
[ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
, "nixbuild.net/AOEWUB-1:/vW9PcQO5qLaIxeWMD2WPlOfJ9itCCU9n1JcAdwee0k="
]
).contains($tpks);
Note that your build signing key and your
upload signing key are implicitly included
in the trusted-public-keys setting, and will
also be part of the setting("trusted-public-keys", $tpks) fact. You may
therefore need to include them in your policy.
time¶
Type: (date)
The current date and time, in RFC 3339 format. This fact can be used to create tokens with a shorter expiration time.
Example attenuation block:
check if time($time), $time <= 2022-03-30T20:00:00Z;