-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add secret contract and use it in ldap block
- Loading branch information
Showing
7 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ lib, ... }: | ||
lib.types.submodule { | ||
freeformType = lib.types.anything; | ||
|
||
options = { | ||
mode = lib.mkOption { | ||
description = '' | ||
Mode of the secret file. | ||
''; | ||
type = lib.types.str; | ||
default = "0400"; | ||
}; | ||
|
||
owner = lib.mkOption { | ||
description = '' | ||
Linux user owning the secret file. | ||
''; | ||
type = lib.types.str; | ||
default = "root"; | ||
}; | ||
|
||
group = lib.mkOption { | ||
description = '' | ||
Linux group owning the secret file. | ||
''; | ||
type = lib.types.str; | ||
default = "root"; | ||
}; | ||
|
||
restartUnits = lib.mkOption { | ||
description = '' | ||
Systemd units to restart after the secret is updated. | ||
''; | ||
type = lib.types.listOf lib.types.str; | ||
default = []; | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
# Secret Contract {#secret-contract} | ||
|
||
This NixOS contract represents a secret file | ||
that must be created out of band, from outside the nix store, | ||
and that must be placed in an expected location with expected permission. | ||
|
||
It is a contract between a service that needs a secret | ||
and a service that will provide the secret. | ||
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} | ||
|
||
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 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][]. | ||
|
||
[ldap-module]: TODO | ||
[sops-nix]: TODO | ||
|
||
Without the secret contract, configuring the option would look like so: | ||
|
||
```nix | ||
sops.secrets."ldap/user_password" = { | ||
sopsFile = ./secrets.yaml; | ||
mode = "0440"; | ||
owner = "lldap"; | ||
group = "lldap"; | ||
restartUnits = [ "lldap.service" ]; | ||
}; | ||
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path; | ||
``` | ||
|
||
We can already see the problem here. | ||
How does the end user know what values to give to the | ||
`mode`, `owner`, `group` and `restartUnits` options? | ||
If lucky, the documentation of the option would tell them | ||
or more likely, they will need to figure it out by looking | ||
at the module source code. Not a great user experience. | ||
|
||
Now, with this contract, the configuration becomes: | ||
|
||
```nix | ||
sops.secrets."ldap/user_password" = config.shb.ldap.secret.ldapUserPasswordFile // { | ||
sopsFile = ./secrets.yaml; | ||
}; | ||
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path; | ||
``` | ||
|
||
The issue is now gone. | ||
The module maintainer is now in charge of describing | ||
how the module expects the secret to be provided. | ||
|
||
If taking advantage of the `sops.defaultSopsFile` option like so: | ||
|
||
```nix | ||
sops.defaultSopsFile = ./secrets.yaml; | ||
``` | ||
|
||
Then the snippet above is even more simplified: | ||
|
||
```nix | ||
sops.secrets."ldap/user_password" = config.shb.ldap.secret.ldapUserPasswordFile; | ||
shb.ldap.ldapUserPasswordFile = config.sops.secrets."ldap/user_password".path; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ pkgs, lib, ... }: | ||
let | ||
contracts = pkgs.callPackage ../. {}; | ||
in | ||
{ | ||
options.shb.contracts.secret = lib.mkOption { | ||
description = "Contract for secrets."; | ||
type = contracts.secret; | ||
}; | ||
} |