Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: authz docs #220

Merged
merged 17 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,7 @@ on:
- pull_request

jobs:
format:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn

- name: Install Dependencies
run: yarn --frozen-lockfile

- name: Format
run: yarn format

- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "chore: format code"

lint:
needs: format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
3 changes: 3 additions & 0 deletions .vale/styles/Flipt/spelling-exceptions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ oci
oidc
okta
onboarding
OPA's
otlp
passwordless
performant
Expand All @@ -64,6 +65,7 @@ protoc
Quicksort
redis
regexes
rego
reverst
rollout
rollouts
Expand All @@ -74,6 +76,7 @@ sdks
semver
Splunk
sqlite
toolset
tracecontext
Turso
unary
Expand Down
17 changes: 6 additions & 11 deletions authentication/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ title: Overview
description: This document describes how to enable and use authentication with Flipt.
---

Flipt supports the ability to secure its core API routes.
Flipt supports the ability to secure its core API routes with authentication.

<Info>
Flipt authentication is **disabled** (not required) by default.

Head to the [Configuration: Authentication](/configuration/authentication) section to enable it.
Head to the [Configuration: Authentication](/configuration/authentication) section to learn how to enable it.

</Info>

Once enabled, all routes beneath the following API prefixes will require a [client token](#client-tokens) to authenticate requests:
Once enabled, all routes beneath the following API prefixes will require a [client token](#client-tokens) or [JWT](#json-web-tokens) to authenticate requests:

- `/api/v1/`
- `/auth/v1/`
Expand All @@ -27,14 +27,16 @@ The following URLs aren't protected by authentication:
- `/health`

They're currently unprotected to support backward compatibility.

We're exploring ways to support protecting these endpoints going forward.
For now, we recommend excluding these API prefixes from your load-balancer.

</Warning>

Apart from `/auth/v1/` itself, the rest of the top-level API prefixes can be optionally excluded from authentication.
Allowing for sections, such as the evaluations API, to be publicly accessible while still protecting the management and metadata APIs.
Checkout the [Configuration: Authentication Exclusions](/configuration/authentication#exclusions) documentation for details.

See the [Configuration: Authentication Exclusions](/configuration/authentication#exclusions) documentation for details.

## Client Tokens

Expand All @@ -57,10 +59,3 @@ Flipt can also authenticate requests using externally created and signed [JSON W
To enable JWT authentication, you will need to configure Flipt with the public key used to verify the JWT signature.

See the [Configuration: JWT Authentication](/configuration/authentication#json-web-token) documentation for details.

## Authorization

Currently, Flipt only supports authentication without any extended authorization capabilities.
Authorization is something we're actively exploring and we will update this section as we settle on a design.

We would appreciate your input into designing authorization. Head over to [our Discord](https://flipt.io/discord) and let us know what you need from Flipt.
294 changes: 294 additions & 0 deletions authorization/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
---
title: Overview
description: This document describes how to enable and use authorization with Flipt.
---

Flipt supports the ability to secure its core API routes with authorization.

<Info>
Flipt authorization is **experimental** and **disabled** (not required) by default.

To enable authorization, you must set the `experimental.authorization.enabled` configuration option to `true` in your Flipt [configuration file](/configuration/overview#configuration-file).

See the [experimental](/configuration/experimental) section for more information on how to enable experimental features.

</Info>

Once enabled, all routes beneath the Management API prefix will require a [policy](#policies) to be evaluated before the request is allowed to proceed. The policy must evaluate to `allowed == true` for the request to be allowed.

- `/api/v1/`
markphelps marked this conversation as resolved.
Show resolved Hide resolved

## Open Policy Agent (OPA)

[Open Policy Agent (OPA)](https://www.openpolicyagent.org/) is a general-purpose policy engine that can be used to configure and enforce authorization policies. OPA provides a unified toolset and framework for policy across the cloud native stack. Open Policy Agent is a [CNCF](https://www.cncf.io/) project and is used by many organizations to enforce policies across their cloud-native environments.

Flipt embeds OPA to evaluate policies that determine whether a request should be allowed or denied. This means that no additional infrastructure or services are required to use OPA with Flipt.

<Tip>
Check out our [Role-Based Access Control with Keycloak
guide](/guides/operation/authorization/rbac-with-keycloak) for an example on
how to configure and use role-based access control (RBAC) with Flipt and
Keycloak using OPA.
</Tip>

## Policies

Flipt uses OPA to enforce authorization policies for the Management API. The policies are defined in a `policy.rego` file located in the Flipt configuration directory by default. The path to this file can be customized as described in the [Configuration: Authorization](/configuration/authorization) section.

Part of the power of OPA is that it's extremely flexible as it allows you to define fine-grained policies tailored to your exact needs.

Here's an example of a simple policy that checks whether custom claims provided at authentication time include a key `roles` containing a value `admin`:

```rego policy.rego
package flipt.authz.v1

import rego.v1

default allow = false

allow if {
claims := json.unmarshal(input.authentication.metadata["io.flipt.auth.claims"])
"admin" in claims.roles
}
```

You can find more information on how to write Rego policies in the [OPA documentation](https://www.openpolicyagent.org/docs/latest/policy-language/).

<Tip>
OPA has a rich set of built-in functions that can be used to write complex
policies. They also provide a [Rego
Playground](https://play.openpolicyagent.org/) where you can test your
policies before deploying them.
</Tip>

It's up to you to define the policies that make sense for your organization. During policy evaluation, Flipt will pass the incoming request context to OPA, which will then evaluate the policy against that context.

The context provided to OPA includes the following fields:

- `input.authentication`: The authentication information for the request. These are specific to each authentication provider/method and can include things like the user's roles, email, etc.
- `input.request`: The incoming request details, such as the `namespace`, `resource`, and `verb`.

### Authentication Information

Flipt provides the raw authentication information to OPA for evaluation. This information is specific to the authentication method used to authenticate the request.

For example, if you're using the [OIDC authentication method](/authentication/methods#openid-connect), the `input.authentication.metadata` field may contain the user's name and email as well as custom claims assigned to the user.

Here is an example of the `input.authentication.metadata` field for a request authenticated using an example OIDC provider:

```json
{
"io.flipt.auth.email": "[email protected]",
"io.flipt.auth.name": "John Doe"
"io.flipt.auth.claims": {
"roles": ["admin", "viewer"]
}
}
```

<Note>
The `io.flipt.auth.claims` field is a JSON object that contains custom claims
provided by the authentication provider. Each authentication provider may
provide different claims, so it's up to you to map these claims as needed in
your policies.
</Note>

The following fields are available in the `input.authentication` field:

- `metadata`: A map of authentication metadata provided by the authentication method. This can include the user's email, name, roles, etc.
- `io.flipt.auth.email`: The user's email address.
- `io.flipt.auth.name`: The user's name.
- `io.flipt.auth.claims`: A map of **all** claims provided by the authentication method. This can include the user's roles, groups, etc. These claims are marshaled into a JSON string before being passed to OPA for evaluation.

#### Mapping Identity

Each authentication method configurable within Flipt will provide different information depending on the identity. It's up to you to combine identity information (`authentication`) with the requested resource (`request`) to make an authorization decision whether or not the request should be allowed (`allow`).

Some authentication methods provide user details such as roles directly, while others may provide a user ID or email that you can use to look up roles in your own system. Many authentication providers support adding custom claims to the JWT token, which can be used to provide additional information about the user.

For example, [Okta](https://www.okta.com/) allows you to add custom claims using their groups feature. An example JWT token with custom claims generated by Okta might look like this:

```json
{
"sub": "00uixa271s6x7qt8I0h7",
"ver": 1,
"iss": "https://{yourOktaDomain}",
"aud": "0oaoiuhhch8VRtBnC0h7",
"iat": 1574201516,
"exp": 1574205116,
"jti": "ID.ewMNfSvcpuqyS93OgVeCN3F2LseqROkyYjz7DNb9yhs",
"amr": ["pwd", "mfa", "kba"],
"idp": "00oixa26ycdNcX0VT0h7",
"nonce": "UBGW",
"auth_time": 1574201433,
"groups": ["Everyone", "IT"]
}
```

In this example, the `groups` claim is used to provide the user's organizational groups. You can then write a policy that checks for the presence of specific groups to determine whether the user should be allowed to access a particular resource.

```rego policy.rego
package flipt.authz.v1

import rego.v1

default allow = false

allow if {
claims := json.unmarshal(input.authentication.metadata["io.flipt.auth.claims"])
markphelps marked this conversation as resolved.
Show resolved Hide resolved
"IT" in claims.groups
}
```

<Note>
The Rego builtin [`json.unmarshal`](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonunmarshal) function is used to convert the `groups` claim from a string to a JSON object that can be queried in the policy.

Flipt encodes the raw authentication claims as a JSON string to pass them to OPA for evaluation.

</Note>
markphelps marked this conversation as resolved.
Show resolved Hide resolved

Further documentation on how to configure custom claims and groups in Okta can be found in the [Okta Developer documentation](https://developer.okta.com/docs/guides/customize-tokens-returned-from-okta/main/).

Roles or groups are not a requirement for writing policies. You can write policies that check for any information provided by the authentication method, such as the user's email, id, name, etc.

<Info>
Flipt has no notion of users or roles internally, it simply passes the raw
authentication information along with other request metadata to OPA for
evaluation.
</Info>

### Request Information

The `input.request` field contains information about the incoming request. This includes the `namespace`, `resource`, and `verb` of the request.

- `namespace`: The [namespace](/concepts#namespaces) in Flipt of the resource being accessed. If no namespace is provided, the default namespace is used, or it is not applicable as the resource is not namespace scoped (e.g. authentication)

- `resource`: The resource being accessed. This can be one of:

- `namespace`: Access to [namespace](/concepts#namespaces) resources (e.g., listing or creating namespaces).
- `flag`: Access to [flag](/concepts#flags) resources and sub-resources (e.g., listing or creating flags, variants, rules or rollouts).
- `segment`: Access to [segment](/concepts#segments) resources and sub-resources (e.g., listing or creating segments, constraints or distributions).
- `authentication`: Access to authentication resources (e.g., listing or creating client tokens).

- `subject`: The (optional) nested subject of the request. This can be one of:

- `namespace`: Access to [namespace](/concepts#namespaces) resources.
- `flag`: Access to [flag](/concepts#flags) resources.
- `variant`: Access to flag [variant](/concepts#variant-flags) resources.
- `rule`: Access to flag [rule](/concepts#rules) resources.
- `rollout`: Access to flag [rollout](/concepts#rollouts) resources.
- `segment`: Access to [segment](/concepts#segments) resources.
- `constraint`: Access to segment [constraint](/concepts#constraints) resources.
- `distribution`: Access to segment [distribution](/concepts#distributions) resources.
- `token`: Access to client token resources.

- `verb`: The action being performed on the resource. This can be one of:
- `create`: Access to create resources.
- `read`: Access to list or read resources.
- `update`: Access to update resources.
- `delete`: Access to delete resources.

Here's an example of the `input.request` field for a request to list flags in the default namespace:

```json
{
"namespace": "default",
"resource": "flag",
"subject": "flag",
"verb": "read"
}
```

Here is an example policy that allows a user to list flags in the default namespace:

```rego policy.rego
package flipt.authz.v1

import rego.v1

default allow = false

allow if {
input.request.namespace == "default"
input.request.resource == "flag"
input.request.verb == "read"
}
```

Combining the above policy with the user information policy from the previous example, you can create a policy that allows users with the `IT` group to delete flags in the default namespace:

```rego policy.rego
package flipt.authz.v1

import rego.v1

default allow = false

allow if {
claims := json.unmarshal(input.authentication.metadata["io.flipt.auth.claims"])
"IT" in claims.groups
input.request.namespace == "default"
input.request.resource == "flag"
input.request.verb == "delete"
}
```

## External Data

OPA policies can also use external data sources to make decisions. This can be useful when you need to make decisions based on data that is not available in the request context.

For example, if your authentication method does not provide user roles, you could use an external data source to map user IDs to roles.

Here is an example policy that uses an external data source to check if the user has the `admin` role:

```rego policy.rego
package flipt.authz.v1

import rego.v1

default allow = false

allow if {
role := data.roles[input.authentication.metadata["io.flipt.auth.name"]]
role == "admin"
}
```

And here is an example of the external data source that maps user IDs to roles:

```json data.json
{
"roles": {
"user1": "admin",
"user2": "viewer"
}
}
```

Flipt allows you to define external data sources in the configuration file. You can find more information on how to configure external data sources in the [Configuration: Authorization](/configuration/authorization) section.

The combination of OPA's flexible policy language and the ability to use external data sources makes it possible to define complex authorization policies that can adapt to your organization's needs.

## Authoring Policies

While the examples provided in this document are simple, you can write policies that are as complex as you need. OPA provides a rich set of built-in functions that can be used to write complex policies.

<Tip>
Check out our [Role-Based Access Control with Keycloak
guide](/guides/operation/authorization/rbac-with-keycloak) for an example on
how to configure and use role-based access control (RBAC) with Flipt and
Keycloak using OPA.
</Tip>

Learning how to write policies in Rego can be challenging at first, but OPA provides extensive documentation on the [Rego Language](https://www.openpolicyagent.org/docs/latest/policy-language/) as well as a [Rego Playground](https://play.openpolicyagent.org/) where you can test your policies before deploying them.

OPA also provides a testing framework that you can use to write unit tests for your policies. This can help ensure that your policies are working as expected before deploying them to production.

Here are some resources to help you get started with writing and testing policies:

- [Rego Language Reference](https://www.openpolicyagent.org/docs/latest/policy-language/)
- [Rego Playground](https://play.openpolicyagent.org/)
- [Policy Testing](https://www.openpolicyagent.org/docs/latest/policy-testing/)
- [Policy Performance](https://www.openpolicyagent.org/docs/latest/policy-performance/)

If you have any questions or need help writing policies for Flipt, feel free to reach out to us in our [Discord](https://flipt.io/discord) community.
Loading
Loading