update secret contract (#311)
This makes the secret contract better (IMNSHO): - Improves documentation, explains better the reasoning behind the contract. - Makes it easier to create an option implementing the secret contract.
This commit is contained in:
parent
fb890645bf
commit
5a0ae36c85
11 changed files with 289 additions and 177 deletions
|
@ -15,6 +15,8 @@
|
||||||
- `shb.authelia.oidcClients.secret` -> `shb.authelia.oidcClients.client_secret`
|
- `shb.authelia.oidcClients.secret` -> `shb.authelia.oidcClients.client_secret`
|
||||||
- `shb.authelia.ldapEndpoint` -> `shb.authelia.ldapHostname` and `shb.authelia.ldapPort`
|
- `shb.authelia.ldapEndpoint` -> `shb.authelia.ldapHostname` and `shb.authelia.ldapPort`
|
||||||
- Make Nextcloud automatically disable maintenance mode upon service restart.
|
- Make Nextcloud automatically disable maintenance mode upon service restart.
|
||||||
|
- `shb.ldap.ldapUserPasswordFile` -> `shb.ldap.ldapUserPassword.result.path`
|
||||||
|
- `shb.ldap.jwtSecretFile` -> `shb.ldap.jwtSecret.result.path`
|
||||||
|
|
||||||
## User Facing Backwards Compatible Changes
|
## User Facing Backwards Compatible Changes
|
||||||
|
|
||||||
|
|
|
@ -47,43 +47,21 @@ in
|
||||||
default = 17170;
|
default = 17170;
|
||||||
};
|
};
|
||||||
|
|
||||||
ldapUserPasswordFile = lib.mkOption {
|
ldapUserPassword = contracts.secret.mkOption {
|
||||||
type = lib.types.path;
|
description = "LDAP admin user secret.";
|
||||||
description = "File containing the LDAP admin user password.";
|
|
||||||
};
|
|
||||||
|
|
||||||
jwtSecretFile = lib.mkOption {
|
|
||||||
type = lib.types.path;
|
|
||||||
description = "File containing the JWT secret.";
|
|
||||||
};
|
|
||||||
|
|
||||||
secret = {
|
|
||||||
ldapUserPasswordFile = lib.mkOption {
|
|
||||||
type = contracts.secret;
|
|
||||||
description = ''
|
|
||||||
Secret configuration for the file containing the LDAP admin user password.
|
|
||||||
'';
|
|
||||||
default = {
|
|
||||||
mode = "0440";
|
mode = "0440";
|
||||||
owner = "lldap";
|
owner = "lldap";
|
||||||
group = "lldap";
|
group = "lldap";
|
||||||
restartUnits = [ "lldap.service" ];
|
restartUnits = [ "lldap.service" ];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
jwtSecretFile = lib.mkOption {
|
jwtSecret = contracts.secret.mkOption {
|
||||||
type = contracts.secret;
|
description = "JWT secret.";
|
||||||
description = ''
|
|
||||||
Secret configuration for the file containing the JWT secret.
|
|
||||||
'';
|
|
||||||
default = {
|
|
||||||
mode = "0440";
|
mode = "0440";
|
||||||
owner = "lldap";
|
owner = "lldap";
|
||||||
group = "lldap";
|
group = "lldap";
|
||||||
restartUnits = [ "lldap.service" ];
|
restartUnits = [ "lldap.service" ];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
restrictAccessIPRange = lib.mkOption {
|
restrictAccessIPRange = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
@ -174,8 +152,8 @@ in
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
LLDAP_JWT_SECRET_FILE = toString cfg.jwtSecretFile;
|
LLDAP_JWT_SECRET_FILE = toString cfg.jwtSecret.result.path;
|
||||||
LLDAP_LDAP_USER_PASS_FILE = toString cfg.ldapUserPasswordFile;
|
LLDAP_LDAP_USER_PASS_FILE = toString cfg.ldapUserPassword.result.path;
|
||||||
|
|
||||||
RUST_LOG = lib.mkIf cfg.debug "debug";
|
RUST_LOG = lib.mkIf cfg.debug "debug";
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,44 @@
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
lib.types.submodule {
|
{
|
||||||
|
mkOption =
|
||||||
|
{ description,
|
||||||
|
mode ? "0400",
|
||||||
|
owner ? "root",
|
||||||
|
group ? "root",
|
||||||
|
restartUnits ? [],
|
||||||
|
}: lib.mkOption {
|
||||||
|
inherit description;
|
||||||
|
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
request = lib.mkOption {
|
||||||
|
default = {
|
||||||
|
inherit mode owner group restartUnits;
|
||||||
|
};
|
||||||
|
|
||||||
|
readOnly = true;
|
||||||
|
|
||||||
|
description = ''
|
||||||
|
Options set by the requester module
|
||||||
|
enforcing some properties the secret should have.
|
||||||
|
|
||||||
|
Use the `contracts.secret.mkOption` function to
|
||||||
|
create a secret option for a requester module.
|
||||||
|
See the [requester usage section](contracts-secret.html#secret-contract-usage-requester) for an example.
|
||||||
|
|
||||||
|
Some providers will need more options to be defined and this is allowed.
|
||||||
|
These extra options will be set by the user.
|
||||||
|
For example, the `sops` implementation requires to be given
|
||||||
|
the sops key in which the secret is encrypted.
|
||||||
|
|
||||||
|
`request` options are set read-only
|
||||||
|
because they must be set through option defaults,
|
||||||
|
they shouldn't be changed in the `config` section.
|
||||||
|
This would otherwise lead to infinite recursion
|
||||||
|
during evaluation.
|
||||||
|
This is handled automatically when using the `contracts.secret.mkOption` function.
|
||||||
|
'';
|
||||||
|
type = lib.types.submodule {
|
||||||
freeformType = lib.types.anything;
|
freeformType = lib.types.anything;
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
@ -8,7 +47,7 @@ lib.types.submodule {
|
||||||
Mode of the secret file.
|
Mode of the secret file.
|
||||||
'';
|
'';
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "0400";
|
default = mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
owner = lib.mkOption {
|
owner = lib.mkOption {
|
||||||
|
@ -16,7 +55,7 @@ lib.types.submodule {
|
||||||
Linux user owning the secret file.
|
Linux user owning the secret file.
|
||||||
'';
|
'';
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "root";
|
default = owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
group = lib.mkOption {
|
group = lib.mkOption {
|
||||||
|
@ -24,7 +63,7 @@ lib.types.submodule {
|
||||||
Linux group owning the secret file.
|
Linux group owning the secret file.
|
||||||
'';
|
'';
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "root";
|
default = group;
|
||||||
};
|
};
|
||||||
|
|
||||||
restartUnits = lib.mkOption {
|
restartUnits = lib.mkOption {
|
||||||
|
@ -32,7 +71,31 @@ lib.types.submodule {
|
||||||
Systemd units to restart after the secret is updated.
|
Systemd units to restart after the secret is updated.
|
||||||
'';
|
'';
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [];
|
default = restartUnits;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
result = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Options set by the provider module that indicates where the secret can be found.
|
||||||
|
'';
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
Path to the file containing the secret generated out of band.
|
||||||
|
|
||||||
|
This path will exist after deploying to a target host,
|
||||||
|
it is not available through the nix store.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,100 +1,15 @@
|
||||||
# Secret Contract {#secret-contract}
|
# Secret Contract {#secret-contract}
|
||||||
|
|
||||||
This NixOS contract represents a secret file
|
This NixOS contract represents a secret file
|
||||||
that must be created out of band, from outside the nix store,
|
that must be created out of band - from outside the nix store -
|
||||||
and that must be placed in an expected location with expected permission.
|
and that must be placed in an expected location with expected permission.
|
||||||
|
|
||||||
It is a contract between a service that needs a secret
|
More formally, this contract is made between a requester module - the one needing a secret -
|
||||||
and a service that will provide the secret.
|
and a provider module - the one creating the secret and making it available.
|
||||||
All options in this contract should be set by the former.
|
|
||||||
The latter will then use the values of those options to know where to produce the file.
|
|
||||||
|
|
||||||
## Contract Reference {#secret-contract-options}
|
## Problem Statement {#secret-contract-problem}
|
||||||
|
|
||||||
These are all the options that are expected to exist for this contract to be respected.
|
Let's provide the [ldap SHB module][ldap-module] option `ldapUserPasswordFile`
|
||||||
|
|
||||||
```{=include=} options
|
|
||||||
id-prefix: contracts-secret-options-
|
|
||||||
list-id: selfhostblocks-options
|
|
||||||
source: @OPTIONS_JSON@
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage {#secret-contract-usage}
|
|
||||||
|
|
||||||
A service that needs access to a secret will provide one or more `secret` option.
|
|
||||||
|
|
||||||
Here is an example module defining two `secret` options:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
myservice.secret = lib.mkOption {
|
|
||||||
type = lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
adminPassword = lib.mkOption {
|
|
||||||
type = contracts.secret;
|
|
||||||
readOnly = true;
|
|
||||||
default = {
|
|
||||||
owner = "myservice";
|
|
||||||
group = "myservice";
|
|
||||||
mode = "0440";
|
|
||||||
restartUnits = [ "myservice.service" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
databasePassword = lib.mkOption {
|
|
||||||
type = contracts.secret;
|
|
||||||
readOnly = true;
|
|
||||||
default = {
|
|
||||||
owner = "myservice";
|
|
||||||
restartUnits = [ "myservice.service" "mysql.service" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
As you can see, NixOS modules are a bit abused to make contracts work.
|
|
||||||
Default values are set as well as the `readOnly` attribute to ensure those values stay as defined.
|
|
||||||
|
|
||||||
Now, on the other side we have a service that uses these `secret` options and provides the secrets
|
|
||||||
Let's assume such a module is available under the `secretservice` option
|
|
||||||
and that one can create multiple instances under `secretservice.instances`.
|
|
||||||
Then, to actually provide the secrets defined above, one would write:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
secretservice.instances.adminPassword = myservice.secret.adminPassword // {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
secretFile = ./secret.yaml;
|
|
||||||
|
|
||||||
# ... Other options specific to secretservice.
|
|
||||||
};
|
|
||||||
|
|
||||||
secretservice.instances.databasePassword = myservice.secret.databasePassword // {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
secretFile = ./secret.yaml;
|
|
||||||
|
|
||||||
# ... Other options specific to secretservice.
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Assuming the `secretservice` module accepts default options,
|
|
||||||
the above snippet could be reduced to:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
secretservice.default.secretFile = ./secret.yaml;
|
|
||||||
|
|
||||||
secretservice.instances.adminPassword = myservice.secret.adminPassword;
|
|
||||||
secretservice.instances.databasePassword = myservice.secret.databasePassword;
|
|
||||||
```
|
|
||||||
|
|
||||||
### With sops-nix {#secret-contract-usage-sopsnix}
|
|
||||||
|
|
||||||
For a concrete example, let's provide the [ldap SHB module][ldap-module] option `ldapUserPasswordFile`
|
|
||||||
with a secret managed by [sops-nix][].
|
with a secret managed by [sops-nix][].
|
||||||
|
|
||||||
[ldap-module]: TODO
|
[ldap-module]: TODO
|
||||||
|
@ -114,24 +29,26 @@ sops.secrets."ldap/user_password" = {
|
||||||
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path;
|
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path;
|
||||||
```
|
```
|
||||||
|
|
||||||
We can already see the problem here.
|
The problem this contract intends to fix is how to ensure
|
||||||
How does the end user know what values to give to the
|
the end user knows what values to give to the
|
||||||
`mode`, `owner`, `group` and `restartUnits` options?
|
`mode`, `owner`, `group` and `restartUnits` options?
|
||||||
|
|
||||||
If lucky, the documentation of the option would tell them
|
If lucky, the documentation of the option would tell them
|
||||||
or more likely, they will need to figure it out by looking
|
or more likely, they will need to figure it out by looking
|
||||||
at the module source code. Not a great user experience.
|
at the module source code.
|
||||||
|
Not a great user experience.
|
||||||
|
|
||||||
Now, with this contract, the configuration becomes:
|
Now, with this contract, the configuration becomes:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
sops.secrets."ldap/user_password" = config.shb.ldap.secret.ldapUserPasswordFile // {
|
sops.secrets."ldap/user_password" = config.shb.ldap.secret.ldapUserPassword.request // {
|
||||||
sopsFile = ./secrets.yaml;
|
sopsFile = ./secrets.yaml;
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path;
|
shb.ldap.ldapUserPassword.result.path = config.sops.secrets."ldap/user_password".path;
|
||||||
```
|
```
|
||||||
|
|
||||||
The issue is now gone.
|
The issue is now gone at the expense of some plumbing.
|
||||||
The module maintainer is now in charge of describing
|
The module maintainer is now in charge of describing
|
||||||
how the module expects the secret to be provided.
|
how the module expects the secret to be provided.
|
||||||
|
|
||||||
|
@ -144,7 +61,148 @@ sops.defaultSopsFile = ./secrets.yaml;
|
||||||
Then the snippet above is even more simplified:
|
Then the snippet above is even more simplified:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
sops.secrets."ldap/user_password" = config.shb.ldap.secret.ldapUserPasswordFile;
|
sops.secrets."ldap/user_password" = config.shb.ldap.secret.ldapUserPassword.request;
|
||||||
|
|
||||||
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path;
|
shb.ldap.ldapUserPassword.result.path = config.sops.secrets."ldap/user_password".path;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contract Reference {#secret-contract-options}
|
||||||
|
|
||||||
|
These are all the options that are expected to exist for this contract to be respected.
|
||||||
|
|
||||||
|
```{=include=} options
|
||||||
|
id-prefix: contracts-secret-options-
|
||||||
|
list-id: selfhostblocks-options
|
||||||
|
source: @OPTIONS_JSON@
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage {#secret-contract-usage}
|
||||||
|
|
||||||
|
A contract involves 3 parties:
|
||||||
|
|
||||||
|
- The implementer of a requester module.
|
||||||
|
- The implementer of a provider module.
|
||||||
|
- The end user which sets up the requester module and picks a provider implementation.
|
||||||
|
|
||||||
|
The usage of this contract is similarly separated into 3 sections.
|
||||||
|
|
||||||
|
### Requester Module {#secret-contract-usage-requester}
|
||||||
|
|
||||||
|
Here is an example module requesting two secrets through the `secret` contract.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
myservice = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
adminPassword = contracts.secret.mkOption {
|
||||||
|
owner = "myservice";
|
||||||
|
group = "myservice";
|
||||||
|
mode = "0440";
|
||||||
|
restartUnits = [ "myservice.service" ];
|
||||||
|
};
|
||||||
|
databasePassword = contracts.secret.mkOption {
|
||||||
|
owner = "myservice";
|
||||||
|
# group defaults to "root"
|
||||||
|
# mode defaults to "0400"
|
||||||
|
restartUnits = [ "myservice.service" "mysql.service" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
// Do something with the secrets, available at:
|
||||||
|
// config.myservice.adminPassword.result.path
|
||||||
|
// config.myservice.databasePassword.result.path
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Provider Module {#secret-contract-usage-provider}
|
||||||
|
|
||||||
|
Now, on the other side, we have a module that uses those options and provides a secret.
|
||||||
|
Let's assume such a module is available under the `secretservice` option
|
||||||
|
and that one can create multiple instances.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
secretservice = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
mode = lib.mkOption {
|
||||||
|
description = "Mode of the secret file.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
owner = lib.mkOption {
|
||||||
|
description = "Linux user owning the secret file.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
group = lib.mkOption {
|
||||||
|
description = "Linux group owning the secret file.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
restartUnits = lib.mkOption {
|
||||||
|
description = "Systemd units to restart after the secret is updated.";
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
path = lib.mkOption {
|
||||||
|
description = "Path where the secret file will be located.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The contract allows more options to be defined to accomodate specific implementations.
|
||||||
|
secretFile = lib.mkOption {
|
||||||
|
description = "File containing the encrypted secret.";
|
||||||
|
type = lib.types.path;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### End User {#secret-contract-usage-enduser}
|
||||||
|
|
||||||
|
The end user's responsibility is now to do some plumbing.
|
||||||
|
|
||||||
|
They will setup the provider module - here `secretservice` - with the options set by the requester module,
|
||||||
|
while also setting other necessary options to satisfy the provider service.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
secretservice.adminPassword = myservice.secret.adminPassword.request // {
|
||||||
|
secretFile = ./secret.yaml;
|
||||||
|
};
|
||||||
|
|
||||||
|
secretservice.databasePassword = myservice.secret.databasePassword.request // {
|
||||||
|
secretFile = ./secret.yaml;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming the `secretservice` module accepts default options,
|
||||||
|
the above snippet could be reduced to:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
secretservice.default.secretFile = ./secret.yaml;
|
||||||
|
|
||||||
|
secretservice.adminPassword = myservice.secret.adminPassword.request;
|
||||||
|
secretservice.databasePassword = myservice.secret.databasePassword.request;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then they will setup the requester module - here `myservice` - with the result of the provider module.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
myservice.secret.adminPassword.result.path = secretservice.adminPassword.result.path;
|
||||||
|
|
||||||
|
myservice.secret.databasePassword.result.path = secretservice.adminPassword.result.path;
|
||||||
```
|
```
|
||||||
|
|
|
@ -3,8 +3,19 @@ let
|
||||||
contracts = pkgs.callPackage ../. {};
|
contracts = pkgs.callPackage ../. {};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.shb.contracts.secret = lib.mkOption {
|
options.shb.contracts.secret = contracts.secret.mkOption {
|
||||||
description = "Contract for secrets.";
|
description = ''
|
||||||
type = contracts.secret;
|
Contract for secrets between a requester module
|
||||||
|
and a provider module.
|
||||||
|
|
||||||
|
The requester communicates to the provider
|
||||||
|
some properties the secret should have
|
||||||
|
through the `request` options.
|
||||||
|
|
||||||
|
The provider reads from the `request` options
|
||||||
|
and creates the secret as requested.
|
||||||
|
It then communicates to the requester where the secret can be found
|
||||||
|
through the `result` options.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ in
|
||||||
dcdomain = "dc=example,dc=com";
|
dcdomain = "dc=example,dc=com";
|
||||||
subdomain = "ldap";
|
subdomain = "ldap";
|
||||||
domain = "machine.com";
|
domain = "machine.com";
|
||||||
ldapUserPasswordFile = pkgs.writeText "user_password" ldapAdminPassword;
|
ldapUserPassword.result.path = pkgs.writeText "user_password" ldapAdminPassword;
|
||||||
jwtSecretFile = pkgs.writeText "jwt_secret" "securejwtsecret";
|
jwtSecret.result.path = pkgs.writeText "jwt_secret" "securejwtsecret";
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.authelia = {
|
shb.authelia = {
|
||||||
|
|
|
@ -23,8 +23,8 @@ in
|
||||||
dcdomain = "dc=example,dc=com";
|
dcdomain = "dc=example,dc=com";
|
||||||
subdomain = "ldap";
|
subdomain = "ldap";
|
||||||
domain = "example.com";
|
domain = "example.com";
|
||||||
ldapUserPasswordFile = pkgs.writeText "user_password" "securepw";
|
ldapUserPassword.result.path = pkgs.writeText "user_password" "securepw";
|
||||||
jwtSecretFile = pkgs.writeText "jwt_secret" "securejwtsecret";
|
jwtSecret.result.path = pkgs.writeText "jwt_secret" "securejwtsecret";
|
||||||
debug = true;
|
debug = true;
|
||||||
};
|
};
|
||||||
networking.firewall.allowedTCPPorts = [ 80 ]; # nginx port
|
networking.firewall.allowedTCPPorts = [ 80 ]; # nginx port
|
||||||
|
|
|
@ -154,8 +154,8 @@ in
|
||||||
ldapPort = 3890;
|
ldapPort = 3890;
|
||||||
webUIListenPort = 17170;
|
webUIListenPort = 17170;
|
||||||
dcdomain = "dc=example,dc=com";
|
dcdomain = "dc=example,dc=com";
|
||||||
ldapUserPasswordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
ldapUserPassword.result.path = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
jwtSecretFile = pkgs.writeText "jwtSecret" "jwtSecret";
|
jwtSecret.result.path = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ let
|
||||||
host = "127.0.0.1";
|
host = "127.0.0.1";
|
||||||
port = config.shb.ldap.ldapPort;
|
port = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
adminPasswordFile = config.shb.ldap.ldapUserPasswordFile;
|
adminPasswordFile = config.shb.ldap.ldapUserPassword.result.path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,7 @@ let
|
||||||
host = "127.0.0.1";
|
host = "127.0.0.1";
|
||||||
port = config.shb.ldap.ldapPort;
|
port = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
passwordFile = config.shb.ldap.ldapUserPasswordFile;
|
passwordFile = config.shb.ldap.ldapUserPassword.result.path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -152,7 +152,7 @@ let
|
||||||
port = config.shb.ldap.ldapPort;
|
port = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
adminName = "admin";
|
adminName = "admin";
|
||||||
adminPasswordFile = config.shb.ldap.ldapUserPasswordFile;
|
adminPasswordFile = config.shb.ldap.ldapUserPassword.result.path;
|
||||||
userGroup = "nextcloud_user";
|
userGroup = "nextcloud_user";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue