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

Add support for kanidm #71

Merged
merged 2 commits into from
Feb 8, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ roots/
.direnv/
result
*.txt
.nixos-test-history
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Added `bindPath` and `socketUser` options to the NixOS module.
- Added support for Kanidm
- Added a `additional_scopes` option to the NixOS module.
- Added a `roles_path` option to the NixOS module.

### Removed

Expand Down
65 changes: 54 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OIDC Pages is designed to work seamlessly with documentation tools such as Sphin

## Features

- Integrates with Keycloak
- Works with keycloak or kanidm
- Respects system dark / light settings
- NixOS module provided
- Supports dynamically uploaded documents
Expand All @@ -23,15 +23,13 @@ OIDC Pages is designed to work seamlessly with documentation tools such as Sphin

### Adapting to other OIDC providers

There are two assumptions that make this keycloak-specific.

1. The OIDC specification doesn't define a type for the access token,
keycloak uses a JSON web token which is the de-facto standard.
2. The OIDC specification doesn't provide a standard way to read roles.
Roles are assumed to be under `resource_access` -> `<client_id>` -> `roles`.

Majority of OIDC providers use a JWT for the access token,
the only modifications necessary should be how to obtain roles.
1. **Access Token Type**
- The OIDC specification doesn't define a type for the access token.
- Your OIDC provider must use a JSON web token which is the de-facto standard.
2. **Roles Path Configuration**
- The OIDC specification doesn't provide a standard way to read roles.
- To configure the path to the roles in the token, use the `roles_path` setting in your configuration file.
- Determining the appropriate value for `roles_path` typically involves inspecting the return data from your OIDC provider. This can be done by examining the responses from a working application.

### Planned features

Expand Down Expand Up @@ -88,6 +86,40 @@ You need to bring a reverse proxy for TLS, I suggest [nginx].
- Add to lightweight access token: `Off`
- Add to token introspection: `On`

### Kanidm configuration

Create the OAuth2 client:

```bash
kanidm system oauth2 create pages "pages.domain.name" https://pages.domain.name
kanidm system oauth2 update-scope-map pages oidc_pages_users email openid profile groups
kanidm system oauth2 get pages
kanidm system oauth2 show-basic-secret pages
<SECRET>
```

Create permission groups:

```bash
kanidm group create 'oidc_pages_users'
kanidm group create 'oidc_pages_pagename'
```

Setup the claim map:

```bash
kanidm system oauth2 update-claim-map-join 'pages' 'pages_roles' array
kanidm system oauth2 update-claim-map 'pages' 'pages_roles' 'oidc_pages_pagename' 'pagename'
```

Add users to the groups:

```bash
kanidm person update myusername --legalname "Personal Name" --mail "[email protected]"
kanidm group add-members 'oidc_pages_users' 'myusername'
kanidm group add-members 'oidc_pages_pagename' 'myusername'
```

### NixOS configuration

Reference `nixos/module.nix` for a complete list of options,
Expand Down Expand Up @@ -119,10 +151,21 @@ in {
socketUser = config.services.nginx.user;
settings = {
public_url = "https://${pagesDomain}";
issuer_url = "https://sso.company.com/realms/company";
client_id = "pages";
pages_path = "/var/www/pages";
log_level = "info";
# provider specific:
# - keycloak: "https://sso.company.com/realms/company"
# - kanidm: "https://sso.company.com/oauth2/openid/${client_id}"
issuer_url = "";
# provider specific:
# - keycloak: ["roles"]
# - kanidm: []
additional_scopes = [];
# provider specific:
# - keycloak: ["resource_access" client_id "roles"]
# - kanidm: ["pages_roles"]
roles_path = [];
};
};

Expand Down
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
});

keycloak = pkgs.callPackage ./nixos/tests/keycloak.nix {inherit self;};

kanidm = pkgs.callPackage ./nixos/tests/kanidm.nix {inherit self;};
};

overlays.default = final: prev: {
Expand Down
22 changes: 22 additions & 0 deletions nixos/module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ in {
type = lib.types.path;
default = "${pkgs.oidc_pages}/share/oidc_pages/assets";
};

additional_scopes = lib.mkOption {
description = ''
Additional scopes to request from the OIDC server.

These are in addition to "openid", "email", and "profile".
'';
type = lib.types.listOf lib.types.str;
default = [];
example = ["roles"];
};

roles_path = lib.mkOption {
description = ''
Path within the access token or user info claims to a list
of strings containing roles.

Roles grant permission to access pages of the same name.
'';
type = lib.types.listOf lib.types.str;
example = ["resource_access" "client_id" "roles"];
};
};
};
};
Expand Down
Loading