add ldapgroup contract
This commit is contained in:
parent
b34a1e57a7
commit
3213f119d2
10 changed files with 469 additions and 56 deletions
|
|
@ -1,6 +1,9 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (lib) mkOption;
|
||||||
|
inherit (lib.types) attrsOf str submodule;
|
||||||
|
|
||||||
cfg = config.shb.ldap;
|
cfg = config.shb.ldap;
|
||||||
|
|
||||||
contracts = pkgs.callPackage ../contracts {};
|
contracts = pkgs.callPackage ../contracts {};
|
||||||
|
|
@ -149,16 +152,42 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
groups = lib.mkOption {
|
groups = lib.mkOption {
|
||||||
description = "LDAP Groups to manage declaratively.";
|
description = "LDAP Groups to manage declaratively following the [ldap group contract](./contracts-ldap-group.html).";
|
||||||
default = {};
|
default = {};
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
{
|
{
|
||||||
family = {};
|
family = {};
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
type = lib.types.attrsOf (lib.types.submodule {
|
type = attrsOf (submodule ({ name, config, ... }: {
|
||||||
options = {};
|
options = contracts.ldapgroup.mkProvider {
|
||||||
});
|
settings = mkOption {
|
||||||
|
description = ''
|
||||||
|
Settings specific to the LLDAP provider.
|
||||||
|
|
||||||
|
By default it is the same as the field name.
|
||||||
|
'';
|
||||||
|
default = {
|
||||||
|
inherit name;
|
||||||
|
};
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "Name of the LDAP group";
|
||||||
|
type = str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
resultCfg = {
|
||||||
|
name = config.settings.name;
|
||||||
|
nameText = name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteUnmanagedUsers = lib.mkOption {
|
deleteUnmanagedUsers = lib.mkOption {
|
||||||
|
|
@ -372,8 +401,6 @@ in
|
||||||
|
|
||||||
systemd.services.lldap.postStart =
|
systemd.services.lldap.postStart =
|
||||||
let
|
let
|
||||||
configFile = (pkgs.formats.toml {}).generate "lldap_config.toml" config.services.lldap.settings;
|
|
||||||
|
|
||||||
login = [''
|
login = [''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
@ -388,7 +415,7 @@ in
|
||||||
|
|
||||||
deleteGroups = [''
|
deleteGroups = [''
|
||||||
allUids=(${lib.concatStringsSep " " (
|
allUids=(${lib.concatStringsSep " " (
|
||||||
(lib.mapAttrsToList (id: g: id) cfg.groups)
|
(lib.mapAttrsToList (id: g: g.settings.name) cfg.groups)
|
||||||
++ [ "lldap_admin" "lldap_password_manager" "lldap_strict_readonly" ])
|
++ [ "lldap_admin" "lldap_password_manager" "lldap_strict_readonly" ])
|
||||||
})
|
})
|
||||||
echo All managed groups are: ''${allUids[*]}
|
echo All managed groups are: ''${allUids[*]}
|
||||||
|
|
@ -405,7 +432,7 @@ in
|
||||||
createGroups = [''
|
createGroups = [''
|
||||||
existingUids=$(${lldap-cli}/bin/lldap-cli group list | ${pkgs.jq}/bin/jq -r 'map(.displayName)[]')
|
existingUids=$(${lldap-cli}/bin/lldap-cli group list | ${pkgs.jq}/bin/jq -r 'map(.displayName)[]')
|
||||||
managedUids=(${lib.concatStringsSep " " (
|
managedUids=(${lib.concatStringsSep " " (
|
||||||
(lib.mapAttrsToList (id: g: id) cfg.groups)
|
(lib.mapAttrsToList (id: g: g.settings.name) cfg.groups)
|
||||||
++ [ "lldap_admin" "lldap_password_manager" "lldap_strict_readonly" ])
|
++ [ "lldap_admin" "lldap_password_manager" "lldap_strict_readonly" ])
|
||||||
})
|
})
|
||||||
echo All managed groups are: ''${managedUids[*]}
|
echo All managed groups are: ''${managedUids[*]}
|
||||||
|
|
|
||||||
133
modules/blocks/ldap/docs/default.md
Normal file
133
modules/blocks/ldap/docs/default.md
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
# LLDAP Block {#blocks-lldap}
|
||||||
|
|
||||||
|
Defined in [`/modules/blocks/lldap.nix`](@REPO@/modules/blocks/lldap.nix).
|
||||||
|
|
||||||
|
This block sets up a LDAP server using [LLDAP][].
|
||||||
|
|
||||||
|
[LLDAP]: https://github.com/lldap/lldap
|
||||||
|
|
||||||
|
## Tests {#blocks-lldap-tests}
|
||||||
|
|
||||||
|
Specific integration tests are defined in [`/test/blocks/lldap.nix`](@REPO@/test/blocks/lldap.nix).
|
||||||
|
The tests use a neat trick using specialization and switching configuration
|
||||||
|
to make sure changing the declarative configuration has the expected result.
|
||||||
|
|
||||||
|
## Provider Contracts {#blocks-lldap-contract-provider}
|
||||||
|
|
||||||
|
This block provides the following contract:
|
||||||
|
|
||||||
|
- [groups ldap contract](contracts-groups-ldap.html) under the [`shb.ldap.groups`][groups] option.
|
||||||
|
It is tested with [contract tests][groups ldap contract tests].
|
||||||
|
|
||||||
|
[groups]: #blocks-lldap-options-shb.ldap.groups
|
||||||
|
[groups ldap contract tests]: @REPO@/test/contracts/groups-ldap.nix
|
||||||
|
|
||||||
|
## Usage {#blocks-lldap-usage}
|
||||||
|
|
||||||
|
### Manage groups {#blocks-lldap-usage-groups}
|
||||||
|
|
||||||
|
The following snippet will create group named "family" if it does not exist yet.
|
||||||
|
Also, all other groups will be deleted and only the "family" group will remain.
|
||||||
|
|
||||||
|
Note that the "admin" group, which is internal to LLDAP, will never be deleted.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
shb.ldap.groups = {
|
||||||
|
family = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Changing the configuration to the following will add a new group "friends":
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
shb.ldap.groups = {
|
||||||
|
family = {};
|
||||||
|
friends = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Switching back the configuration to the previous one will delete the group "friends":
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
shb.ldap.groups = {
|
||||||
|
family = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently, only the empty attrset is supported as the value for a group.
|
||||||
|
This will change in the future when LLDAP supports custom group attributes.
|
||||||
|
|
||||||
|
### Manage users {#blocks-lldap-usage-users}
|
||||||
|
|
||||||
|
The following snippet creates a user and makes it a member of the "family" group.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
shb.ldap.users = {
|
||||||
|
dad = {
|
||||||
|
email = "dad@example.com";
|
||||||
|
displayName = "Dad";
|
||||||
|
firstName = "First Name";
|
||||||
|
lastName = "Last Name";
|
||||||
|
groups = [ "family" ];
|
||||||
|
password.result = config.shb.sops.secret."dad".result;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.sops.secret."dad".request =
|
||||||
|
shb.ldap.users.dad.password.request;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The password field assumes usage of the [sops block][] to provide secrets
|
||||||
|
although any blocks providing the [secrets contract][] works too.
|
||||||
|
|
||||||
|
[sops block]: ./blocks-sops.html
|
||||||
|
[secrets contract]: ./contracts-secrets.html
|
||||||
|
|
||||||
|
The user is still editable through the UI.
|
||||||
|
That being said, any change will be overwritten next time the configuration is applied.
|
||||||
|
If instead you just want to set initial values,
|
||||||
|
there are fields for that:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
shb.ldap.users = {
|
||||||
|
dad = {
|
||||||
|
initialEmail = "dad@example.com";
|
||||||
|
initialDisplayName = "Dad";
|
||||||
|
initialFirstName = "First Name";
|
||||||
|
initialLastName = "Last Name";
|
||||||
|
initialGroups = [ "family" ];
|
||||||
|
initialPassword.result = config.shb.sops.secret."dad".result;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.sops.secret."dad".request =
|
||||||
|
shb.ldap.users.dad.password.request;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Also, all fields apart from the email are optional, even the password.
|
||||||
|
|
||||||
|
Adding or removing groups to the `shb.ldap.users.<name>.groups` will make the user member of the groups listed in the option.
|
||||||
|
|
||||||
|
## Troubleshooting {#blocks-lldap-troubleshooting}
|
||||||
|
|
||||||
|
To see the logs, run `journalctl -u lldap.service`.
|
||||||
|
|
||||||
|
To see the trace of the GraphQL queries, set `shb.ldap.debug = true;`.
|
||||||
|
|
||||||
|
## Options Reference {#blocks-lldap-options}
|
||||||
|
|
||||||
|
```{=include=} options
|
||||||
|
id-prefix: blocks-lldap-options-
|
||||||
|
list-id: selfhostblocks-block-lldap-options
|
||||||
|
source: @OPTIONS_JSON@
|
||||||
|
```
|
||||||
|
|
@ -19,29 +19,39 @@ source: @OPTIONS_JSON@
|
||||||
|
|
||||||
## Usage {#contract-databasebackup-usage}
|
## Usage {#contract-databasebackup-usage}
|
||||||
|
|
||||||
A database that can be backed up will provide a `databasebackup` option.
|
What this contract defines is, from the user perspective - that is _you_ - an implementation detail
|
||||||
Such a service is a `requester` providing a `request` for a module `provider` of this contract.
|
|
||||||
|
|
||||||
What this option defines is, from the user perspective - that is _you_ - an implementation detail
|
|
||||||
but it will at least define how to create a database dump,
|
but it will at least define how to create a database dump,
|
||||||
the user to backup with
|
the user to backup with
|
||||||
and how to restore from a database dump.
|
and how to restore from a database dump.
|
||||||
|
|
||||||
|
A NixOS module that can be backed up using this contract will provide a `databasebackup` option.
|
||||||
|
Such a service is a `requester` which has a `request`.
|
||||||
|
|
||||||
Here is an example module defining such a `databasebackup` option:
|
Here is an example module defining such a `databasebackup` option:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
myservice.databasebackup = mkOption {
|
databasebackupservices.instances = mkOption {
|
||||||
type = contracts.databasebackup.request;
|
description = ''
|
||||||
default = {
|
Backup configuration.
|
||||||
user = "myservice";
|
'';
|
||||||
backupCmd = ''
|
|
||||||
${pkgs.postgresql}/bin/pg_dumpall | ${pkgs.gzip}/bin/gzip --rsyncable
|
default = {};
|
||||||
'';
|
type = submodule {
|
||||||
restoreCmd = ''
|
options = contracts.databasebackup.mkRequester {
|
||||||
${pkgs.gzip}/bin/gunzip | ${pkgs.postgresql}/bin/psql postgres
|
user = "postgres";
|
||||||
'';
|
|
||||||
|
backupName = "postgres.sql";
|
||||||
|
|
||||||
|
backupCmd = ''
|
||||||
|
${pkgs.postgresql}/bin/pg_dumpall | ${pkgs.gzip}/bin/gzip --rsyncable
|
||||||
|
'';
|
||||||
|
|
||||||
|
restoreCmd = ''
|
||||||
|
${pkgs.gzip}/bin/gunzip | ${pkgs.postgresql}/bin/psql postgres
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -51,28 +61,60 @@ Here is an example module defining such a `databasebackup` option:
|
||||||
Now, on the other side we have a service that uses this `backup` option and actually backs up files.
|
Now, on the other side we have a service that uses this `backup` option and actually backs up files.
|
||||||
This service is a `provider` of this contract and will provide a `result` option.
|
This service is a `provider` of this contract and will provide a `result` option.
|
||||||
|
|
||||||
Let's assume such a module is available under the `databasebackupservice` option
|
```nix
|
||||||
and that one can create multiple backup instances under `databasebackupservice.instances`.
|
{
|
||||||
Then, to actually backup the `myservice` service, one would write:
|
options = {
|
||||||
|
instances = mkOption {
|
||||||
|
description = "Files to backup.";
|
||||||
|
default = {};
|
||||||
|
type = attrsOf (submodule ({ name, config, ... }: {
|
||||||
|
options = contracts.backup.mkProvider {
|
||||||
|
settings = mkOption {
|
||||||
|
description = ''
|
||||||
|
Settings specific to the this provider.
|
||||||
|
'';
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "this backup intance.";
|
||||||
|
# ... Other options specific to this provider.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
resultCfg = let
|
||||||
|
fullName = name: repository: "backups-${name}_${repository.path}";
|
||||||
|
in {
|
||||||
|
restoreScript = fullName name config.settings.repository;
|
||||||
|
restoreScriptText = "${fullName "<name>" { path = "path/to/repository"; }}";
|
||||||
|
|
||||||
|
backupService = "${fullName name config.settings.repository}.service";
|
||||||
|
backupServiceText = "${fullName "<name>" { path = "path/to/repository"; }}.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, to actually backup the `myservice` service,
|
||||||
|
one would need to link the requester to the provider with:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
databasebackupservice.instances.myservice = {
|
databasebackupservice.instances.myservice = {
|
||||||
request = myservice.databasebackup;
|
request = config.myservice.databasebackup.request;
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
repository = {
|
# ... Other options specific to this provider.
|
||||||
path = "/srv/backup/myservice";
|
|
||||||
};
|
|
||||||
|
|
||||||
# ... Other options specific to backupservice like scheduling.
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
It is advised to backup files to different location, to improve redundancy.
|
It is advised to backup files to different location, to improve redundancy.
|
||||||
Thanks to using contracts, this can be made easily either with the same `databasebackupservice`:
|
Thanks to using contracts, this can be done easily either with the same `databasebackupservice`:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
databasebackupservice.instances.myservice_2 = {
|
databasebackupservice.instances.myservice_2 = {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ in
|
||||||
{
|
{
|
||||||
databasebackup = importContract ./databasebackup.nix;
|
databasebackup = importContract ./databasebackup.nix;
|
||||||
backup = importContract ./backup.nix;
|
backup = importContract ./backup.nix;
|
||||||
|
ldapgroup = importContract ./ldapgroup.nix;
|
||||||
mount = import ./mount.nix { inherit lib; };
|
mount = import ./mount.nix { inherit lib; };
|
||||||
secret = importContract ./secret.nix;
|
secret = importContract ./secret.nix;
|
||||||
ssl = import ./ssl.nix { inherit lib; };
|
ssl = import ./ssl.nix { inherit lib; };
|
||||||
|
|
|
||||||
83
modules/contracts/ldapgroup.nix
Normal file
83
modules/contracts/ldapgroup.nix
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) literalMD mkOption optionalAttrs optionalString;
|
||||||
|
inherit (lib.types) str submodule;
|
||||||
|
|
||||||
|
shblib = pkgs.callPackage ../../lib {};
|
||||||
|
inherit (shblib) anyNotNull;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
mkRequest = {
|
||||||
|
name ? "",
|
||||||
|
nameText ? null,
|
||||||
|
}: mkOption {
|
||||||
|
description = ''
|
||||||
|
Request part of the ldap group contract.
|
||||||
|
|
||||||
|
Options set by the requester module
|
||||||
|
enforcing what properties the group should have.
|
||||||
|
|
||||||
|
This is intentionally empty as the only required property
|
||||||
|
is the existence of this option.
|
||||||
|
'';
|
||||||
|
default = {
|
||||||
|
inherit name;
|
||||||
|
};
|
||||||
|
defaultText = optionalString (anyNotNull [
|
||||||
|
nameText
|
||||||
|
]) (literalMD ''
|
||||||
|
{
|
||||||
|
name = ${if nameText != null then nameText else name};
|
||||||
|
}
|
||||||
|
'');
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = ''
|
||||||
|
Name of the LDAP group.
|
||||||
|
'';
|
||||||
|
type = str;
|
||||||
|
default = name;
|
||||||
|
} // optionalAttrs (nameText != null) {
|
||||||
|
defaultText = literalMD nameText;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkResult = {
|
||||||
|
name ? "",
|
||||||
|
nameText ? null,
|
||||||
|
}: mkOption {
|
||||||
|
description = ''
|
||||||
|
Result part of the ldap group contract.
|
||||||
|
|
||||||
|
Options set by the provider module that indicates the name of the group and other properties.
|
||||||
|
'';
|
||||||
|
default = {
|
||||||
|
inherit name;
|
||||||
|
};
|
||||||
|
defaultText = optionalString (anyNotNull [
|
||||||
|
nameText
|
||||||
|
]) (literalMD ''
|
||||||
|
{
|
||||||
|
name = ${if nameText != null then nameText else name};
|
||||||
|
}
|
||||||
|
'');
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = ''
|
||||||
|
Name of the LDAP group.
|
||||||
|
'';
|
||||||
|
type = str;
|
||||||
|
default = name;
|
||||||
|
} // optionalAttrs (nameText != null) {
|
||||||
|
defaultText = literalMD nameText;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
124
modules/contracts/ldapgroup/docs/default.md
Normal file
124
modules/contracts/ldapgroup/docs/default.md
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
# LDAP Group Contract {#contract-ldapgroup}
|
||||||
|
|
||||||
|
This NixOS contract represents an LDAP group
|
||||||
|
that must be created.
|
||||||
|
|
||||||
|
It is a contract between a service that needs an LDAP group
|
||||||
|
and a service that can provide such a group.
|
||||||
|
|
||||||
|
## Contract Reference {#contract-ldapgroup-options}
|
||||||
|
|
||||||
|
These are all the options that are expected to exist for this contract to be respected.
|
||||||
|
|
||||||
|
```{=include=} options
|
||||||
|
id-prefix: contracts-ldapgroup-options-
|
||||||
|
list-id: selfhostblocks-options
|
||||||
|
source: @OPTIONS_JSON@
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage {#contract-ldapgroup-usage}
|
||||||
|
|
||||||
|
What this contract defines is, from the user perspective - that is _you_ - an implementation detail
|
||||||
|
but you can know it simply defines the LDAP group name.
|
||||||
|
|
||||||
|
A NixOS module that needs a LDAP group using this contract will provide a `ldapgroup` option or similarly named.
|
||||||
|
Such a service is a `requester` providing a `request` for a module `provider` of this contract.
|
||||||
|
|
||||||
|
Here is an example module defining such a `ldapgroup` option:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
myservice.ldapgroup = mkOption {
|
||||||
|
type = contracts.ldapgroup.request;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, on the other side we have a service that uses this `ldapgroup` option and actually creates the LDAP group.
|
||||||
|
This service is a `provider` of this contract and will provide a `result` option containing the name of the group.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
ldap.groups = lib.mkOption {
|
||||||
|
description = "LDAP Groups to manage declaratively.";
|
||||||
|
default = {};
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
family = {};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
type = attrsOf (submodule ({ name, config, ... }: {
|
||||||
|
options = contracts.ldapgroup.mkProvider {
|
||||||
|
settings = mkOption {
|
||||||
|
description = ''
|
||||||
|
Settings specific to the LLDAP provider.
|
||||||
|
|
||||||
|
By default it is the same as the field name.
|
||||||
|
'';
|
||||||
|
default = {
|
||||||
|
inherit name;
|
||||||
|
};
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "Name of the LDAP group";
|
||||||
|
type = str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
resultCfg = {
|
||||||
|
name = config.settings.name;
|
||||||
|
nameText = name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, to actually backup the `myservice` service,
|
||||||
|
one would need to link the requester to the provider with:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# requester -> provider
|
||||||
|
ldapgroupservice.groups.myservice = {
|
||||||
|
request = config.myservice.ldapgroup.request;
|
||||||
|
};
|
||||||
|
# provider -> requester
|
||||||
|
myservice.ldapgroup.result = config.ldapgroupservice.groups.myservice.result;
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the name of the LDAP group will be the same as the field name under the `groups` option. Here, `"myservice"`.
|
||||||
|
|
||||||
|
Usually, a service will require two LDAP groups to work properly,
|
||||||
|
one for users and another one for admin users.
|
||||||
|
In this case, the linking them together will look like so:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# requester -> provider
|
||||||
|
ldapgroupservice.groups = {
|
||||||
|
myservice_user.request = config.myservice.ldap.userGroup.request;
|
||||||
|
myservice_admin.request = config.myservice.ldap.adminGroup.request;
|
||||||
|
};
|
||||||
|
# provider -> requester
|
||||||
|
myservice.ldap = {
|
||||||
|
userGroup.result = config.ldapgroupservice.groups.myservice_user.result;
|
||||||
|
adminGroup.result = config.ldapgroupservice.groups.myservice_admin.result;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Providers of the Database Backup Contract {#contract-ldapgroup-providers}
|
||||||
|
|
||||||
|
- [LDAP block](blocks-ldap.html).
|
||||||
|
|
||||||
|
## Requester Blocks and Services {#contract-ldapgroup-requesters}
|
||||||
|
|
||||||
|
- [Nextcloud service](services-nextcloud.html#services-nextcloud-contract-ldapgroup).
|
||||||
|
|
@ -98,15 +98,13 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
userGroup = mkOption {
|
userGroup = mkOption {
|
||||||
type = str;
|
type = contracts.ldapgroup.request;
|
||||||
description = "Group users must belong to be able to login.";
|
description = "Group users must belong to be able to login.";
|
||||||
default = "forgejo_user";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
adminGroup = mkOption {
|
adminGroup = mkOption {
|
||||||
type = str;
|
type = contracts.ldapgroup.request;
|
||||||
description = "Group users must belong to be admins.";
|
description = "Group users must belong to be admins.";
|
||||||
default = "forgejo_admin";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -394,8 +392,8 @@ in
|
||||||
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPassword.result.path}) \
|
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPassword.result.path}) \
|
||||||
--security-protocol Unencrypted \
|
--security-protocol Unencrypted \
|
||||||
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
||||||
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup.result.name},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
||||||
--admin-filter '(memberof=cn=${cfg.ldap.adminGroup},ou=groups,${cfg.ldap.dcdomain})' \
|
--admin-filter '(memberof=cn=${cfg.ldap.adminGroup.result.name},ou=groups,${cfg.ldap.dcdomain})' \
|
||||||
--username-attribute uid \
|
--username-attribute uid \
|
||||||
--firstname-attribute givenName \
|
--firstname-attribute givenName \
|
||||||
--surname-attribute sn \
|
--surname-attribute sn \
|
||||||
|
|
@ -413,8 +411,8 @@ in
|
||||||
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPassword.result.path}) \
|
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPassword.result.path}) \
|
||||||
--security-protocol Unencrypted \
|
--security-protocol Unencrypted \
|
||||||
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
||||||
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup.result.name},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
||||||
--admin-filter '(memberof=cn=${cfg.ldap.adminGroup},ou=groups,${cfg.ldap.dcdomain})' \
|
--admin-filter '(memberof=cn=${cfg.ldap.adminGroup.result.name},ou=groups,${cfg.ldap.dcdomain})' \
|
||||||
--username-attribute uid \
|
--username-attribute uid \
|
||||||
--firstname-attribute givenName \
|
--firstname-attribute givenName \
|
||||||
--surname-attribute sn \
|
--surname-attribute sn \
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,8 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
userGroup = lib.mkOption {
|
userGroup = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = contracts.ldapgroup.request;
|
||||||
description = "Group users must belong to to be able to login to Nextcloud.";
|
description = "Group users must belong to to be able to login to Nextcloud.";
|
||||||
default = "homeassistant_user";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
keepDefaultAuth = lib.mkOption {
|
keepDefaultAuth = lib.mkOption {
|
||||||
|
|
@ -241,7 +240,7 @@ in
|
||||||
{
|
{
|
||||||
type = "command_line";
|
type = "command_line";
|
||||||
command = ldap_auth_script + "/bin/ldap_auth.sh";
|
command = ldap_auth_script + "/bin/ldap_auth.sh";
|
||||||
args = [ "http://${cfg.ldap.host}:${toString cfg.ldap.port}" cfg.ldap.userGroup ];
|
args = [ "http://${cfg.ldap.host}:${toString cfg.ldap.port}" cfg.ldap.userGroup.result.name ];
|
||||||
meta = true;
|
meta = true;
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -56,15 +56,23 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
userGroup = lib.mkOption {
|
userGroup = lib.mkOption {
|
||||||
type = lib.types.str;
|
|
||||||
description = "LDAP user group";
|
description = "LDAP user group";
|
||||||
default = "jellyfin_user";
|
default = {};
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = contracts.ldapgroup.mkRequester {
|
||||||
|
name = "jellyfin_user";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
adminGroup = lib.mkOption {
|
adminGroup = lib.mkOption {
|
||||||
type = lib.types.str;
|
|
||||||
description = "LDAP admin group";
|
description = "LDAP admin group";
|
||||||
default = "jellyfin_admin";
|
default = {};
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = contracts.ldapgroup.mkRequester {
|
||||||
|
name = "jellyfin_admin";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
adminPassword = lib.mkOption {
|
adminPassword = lib.mkOption {
|
||||||
|
|
@ -313,9 +321,9 @@ in
|
||||||
<LdapBindUser>uid=admin,ou=people,${cfg.ldap.dcdomain}</LdapBindUser>
|
<LdapBindUser>uid=admin,ou=people,${cfg.ldap.dcdomain}</LdapBindUser>
|
||||||
<LdapBindPassword>%LDAP_PASSWORD%</LdapBindPassword>
|
<LdapBindPassword>%LDAP_PASSWORD%</LdapBindPassword>
|
||||||
<LdapBaseDn>ou=people,${cfg.ldap.dcdomain}</LdapBaseDn>
|
<LdapBaseDn>ou=people,${cfg.ldap.dcdomain}</LdapBaseDn>
|
||||||
<LdapSearchFilter>(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})</LdapSearchFilter>
|
<LdapSearchFilter>(memberof=cn=${cfg.ldap.userGroup.result.name},ou=groups,${cfg.ldap.dcdomain})</LdapSearchFilter>
|
||||||
<LdapAdminBaseDn>ou=people,${cfg.ldap.dcdomain}</LdapAdminBaseDn>
|
<LdapAdminBaseDn>ou=people,${cfg.ldap.dcdomain}</LdapAdminBaseDn>
|
||||||
<LdapAdminFilter>(memberof=cn=${cfg.ldap.adminGroup},ou=groups,${cfg.ldap.dcdomain})</LdapAdminFilter>
|
<LdapAdminFilter>(memberof=cn=${cfg.ldap.adminGroup.result.name},ou=groups,${cfg.ldap.dcdomain})</LdapAdminFilter>
|
||||||
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
||||||
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
||||||
<LdapClientCertPath />
|
<LdapClientCertPath />
|
||||||
|
|
|
||||||
|
|
@ -414,11 +414,9 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
userGroup = lib.mkOption {
|
userGroup = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = contracts.ldapgroup.request;
|
||||||
description = "Group users must belong to to be able to login to Nextcloud.";
|
description = "Group users must belong to to be able to login to Nextcloud.";
|
||||||
default = "nextcloud_user";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
configID = lib.mkOption {
|
configID = lib.mkOption {
|
||||||
|
|
@ -1010,21 +1008,21 @@ in
|
||||||
${occ} ldap:set-config "${cID}" 'ldapEmailAttribute' \
|
${occ} ldap:set-config "${cID}" 'ldapEmailAttribute' \
|
||||||
'mail'
|
'mail'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapGroupFilter' \
|
${occ} ldap:set-config "${cID}" 'ldapGroupFilter' \
|
||||||
'(&(|(objectclass=groupOfUniqueNames))(|(cn=${cfg'.userGroup})))'
|
'(&(|(objectclass=groupOfUniqueNames))(|(cn=${cfg'.userGroup.result.name})))'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapGroupFilterGroups' \
|
${occ} ldap:set-config "${cID}" 'ldapGroupFilterGroups' \
|
||||||
'${cfg'.userGroup}'
|
'${cfg'.userGroup.result.name}'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapGroupFilterObjectclass' \
|
${occ} ldap:set-config "${cID}" 'ldapGroupFilterObjectclass' \
|
||||||
'groupOfUniqueNames'
|
'groupOfUniqueNames'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapGroupMemberAssocAttr' \
|
${occ} ldap:set-config "${cID}" 'ldapGroupMemberAssocAttr' \
|
||||||
'uniqueMember'
|
'uniqueMember'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapLoginFilter' \
|
${occ} ldap:set-config "${cID}" 'ldapLoginFilter' \
|
||||||
'(&(&(objectclass=person)(memberOf=cn=${cfg'.userGroup},ou=groups,${cfg'.dcdomain}))(|(uid=%uid)(|(mail=%uid)(objectclass=%uid))))'
|
'(&(&(objectclass=person)(memberOf=cn=${cfg'.userGroup.result.name},ou=groups,${cfg'.dcdomain}))(|(uid=%uid)(|(mail=%uid)(objectclass=%uid))))'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapLoginFilterAttributes' \
|
${occ} ldap:set-config "${cID}" 'ldapLoginFilterAttributes' \
|
||||||
'mail;objectclass'
|
'mail;objectclass'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapUserDisplayName' \
|
${occ} ldap:set-config "${cID}" 'ldapUserDisplayName' \
|
||||||
'displayname'
|
'displayname'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapUserFilter' \
|
${occ} ldap:set-config "${cID}" 'ldapUserFilter' \
|
||||||
'(&(objectclass=person)(memberOf=cn=${cfg'.userGroup},ou=groups,${cfg'.dcdomain}))'
|
'(&(objectclass=person)(memberOf=cn=${cfg'.userGroup.result.name},ou=groups,${cfg'.dcdomain}))'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapUserFilterMode' \
|
${occ} ldap:set-config "${cID}" 'ldapUserFilterMode' \
|
||||||
'1'
|
'1'
|
||||||
${occ} ldap:set-config "${cID}" 'ldapUserFilterObjectclass' \
|
${occ} ldap:set-config "${cID}" 'ldapUserFilterObjectclass' \
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue