nixos/authelia: init module
Co-authored-by: Martin Weinelt <hexa@darmstadt.ccc.de>
This commit is contained in:
parent
fb44ef6f0a
commit
6373a3966b
@ -45,6 +45,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
- [opensearch](https://opensearch.org), a search server alternative to Elasticsearch. Available as [services.opensearch](options.html#opt-services.opensearch.enable).
|
||||
|
||||
- [authelia](https://www.authelia.com/), is an open-source authentication and authorization server. Available under [services.authelia](options.html#opt-services.authelia.enable).
|
||||
|
||||
- [goeland](https://github.com/slurdge/goeland), an alternative to rss2email written in golang with many filters. Available as [services.goeland](#opt-services.goeland.enable).
|
||||
|
||||
- [alertmanager-irc-relay](https://github.com/google/alertmanager-irc-relay), a Prometheus Alertmanager IRC Relay. Available as [services.prometheus.alertmanagerIrcRelay](options.html#opt-services.prometheus.alertmanagerIrcRelay.enable).
|
||||
|
@ -1212,6 +1212,7 @@
|
||||
./services/web-apps/zabbix.nix
|
||||
./services/web-servers/agate.nix
|
||||
./services/web-servers/apache-httpd/default.nix
|
||||
./services/web-servers/authelia.nix
|
||||
./services/web-servers/caddy/default.nix
|
||||
./services/web-servers/darkhttpd.nix
|
||||
./services/web-servers/fcgiwrap.nix
|
||||
|
401
nixos/modules/services/web-servers/authelia.nix
Normal file
401
nixos/modules/services/web-servers/authelia.nix
Normal file
@ -0,0 +1,401 @@
|
||||
{ lib
|
||||
, pkgs
|
||||
, config
|
||||
, ...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.authelia;
|
||||
|
||||
format = pkgs.formats.yaml { };
|
||||
configFile = format.generate "config.yml" cfg.settings;
|
||||
|
||||
autheliaOpts = with lib; { name, ... }: {
|
||||
options = {
|
||||
enable = mkEnableOption (mdDoc "Authelia instance");
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = mdDoc ''
|
||||
Name is used as a suffix for the service name, user, and group.
|
||||
By default it takes the value you use for `<instance>` in:
|
||||
{option}`services.authelia.<instance>`
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
default = pkgs.authelia;
|
||||
type = types.package;
|
||||
defaultText = literalExpression "pkgs.authelia";
|
||||
description = mdDoc "Authelia derivation to use.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
default = "authelia-${name}";
|
||||
type = types.str;
|
||||
description = mdDoc "The name of the user for this authelia instance.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
default = "authelia-${name}";
|
||||
type = types.str;
|
||||
description = mdDoc "The name of the group for this authelia instance.";
|
||||
};
|
||||
|
||||
secrets = mkOption {
|
||||
description = mdDoc ''
|
||||
It is recommended you keep your secrets separate from the configuration.
|
||||
It's especially important to keep the raw secrets out of your nix configuration,
|
||||
as the values will be preserved in your nix store.
|
||||
This attribute allows you to configure the location of secret files to be loaded at runtime.
|
||||
|
||||
https://www.authelia.com/configuration/methods/secrets/
|
||||
'';
|
||||
default = { };
|
||||
type = types.submodule {
|
||||
options = {
|
||||
manual = mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
description = mdDoc ''
|
||||
Configuring authelia's secret files via the secrets attribute set
|
||||
is intended to be convenient and help catch cases where values are required
|
||||
to run at all.
|
||||
If a user wants to set these values themselves and bypass the validation they can set this value to true.
|
||||
'';
|
||||
type = types.bool;
|
||||
};
|
||||
|
||||
# required
|
||||
jwtSecretFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Path to your JWT secret used during identity verificaiton.
|
||||
'';
|
||||
};
|
||||
|
||||
oidcIssuerPrivateKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Path to your private key file used to encrypt OIDC JWTs.
|
||||
'';
|
||||
};
|
||||
|
||||
oidcHmacSecretFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Path to your HMAC secret used to sign OIDC JWTs.
|
||||
'';
|
||||
};
|
||||
|
||||
sessionSecretFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Path to your session secret. Only used when redis is used as session storage.
|
||||
'';
|
||||
};
|
||||
|
||||
# required
|
||||
storageEncryptionKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Path to your storage encryption key.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
environmentVariables = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = mdDoc ''
|
||||
Additional environment variables to provide to authelia.
|
||||
If you are providing secrets please consider the options under {option}`services.authelia.<instance>.secrets`
|
||||
or make sure you use the `_FILE` suffix.
|
||||
If you provide the raw secret rather than the location of a secret file that secret will be preserved in the nix store.
|
||||
For more details: https://www.authelia.com/configuration/methods/secrets/
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
description = mdDoc ''
|
||||
Your Authelia config.yml as a Nix attribute set.
|
||||
There are several values that are defined and documented in nix such as `default_2fa_method`,
|
||||
but additional items can also be included.
|
||||
|
||||
https://github.com/authelia/authelia/blob/master/config.template.yml
|
||||
'';
|
||||
default = { };
|
||||
example = ''
|
||||
{
|
||||
theme = "light";
|
||||
default_2fa_method = "totp";
|
||||
log.level = "debug";
|
||||
server.disable_healthcheck = true;
|
||||
}
|
||||
'';
|
||||
type = types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
theme = mkOption {
|
||||
type = types.enum [ "light" "dark" "grey" "auto" ];
|
||||
default = "light";
|
||||
example = "dark";
|
||||
description = mdDoc "The theme to display.";
|
||||
};
|
||||
|
||||
default_2fa_method = mkOption {
|
||||
type = types.enum [ "" "totp" "webauthn" "mobile_push" ];
|
||||
default = "";
|
||||
example = "webauthn";
|
||||
description = mdDoc ''
|
||||
Default 2FA method for new users and fallback for preferred but disabled methods.
|
||||
'';
|
||||
};
|
||||
|
||||
server = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
example = "0.0.0.0";
|
||||
description = mdDoc "The address to listen on.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 9091;
|
||||
description = mdDoc "The port to listen on.";
|
||||
};
|
||||
};
|
||||
|
||||
log = {
|
||||
level = mkOption {
|
||||
type = types.enum [ "info" "debug" "trace" ];
|
||||
default = "debug";
|
||||
example = "info";
|
||||
description = mdDoc "Level of verbosity for logs: info, debug, trace.";
|
||||
};
|
||||
|
||||
format = mkOption {
|
||||
type = types.enum [ "json" "text" ];
|
||||
default = "json";
|
||||
example = "text";
|
||||
description = mdDoc "Format the logs are written as.";
|
||||
};
|
||||
|
||||
file_path = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/var/log/authelia/authelia.log";
|
||||
description = mdDoc "File path where the logs will be written. If not set logs are written to stdout.";
|
||||
};
|
||||
|
||||
keep_stdout = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = mdDoc "Whether to also log to stdout when a `file_path` is defined.";
|
||||
};
|
||||
};
|
||||
|
||||
telemetry = {
|
||||
metrics = {
|
||||
enabled = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = mdDoc "Enable Metrics.";
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "tcp://127.0.0.1:9959";
|
||||
example = "tcp://0.0.0.0:8888";
|
||||
description = mdDoc "The address to listen on for metrics. This should be on a different port to the main `server.port` value.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
settingsFiles = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [ ];
|
||||
example = [ "/etc/authelia/config.yml" "/etc/authelia/access-control.yml" "/etc/authelia/config/" ];
|
||||
description = mdDoc ''
|
||||
Here you can provide authelia with configuration files or directories.
|
||||
It is possible to give authelia multiple files and use the nix generated configuration
|
||||
file set via {option}`services.authelia.<instance>.settings`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.services.authelia.instances = with lib; mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule autheliaOpts);
|
||||
description = mdDoc ''
|
||||
Multi-domain protection currently requires multiple instances of Authelia.
|
||||
If you don't require multiple instances of Authelia you can define just the one.
|
||||
|
||||
https://www.authelia.com/roadmap/active/multi-domain-protection/
|
||||
'';
|
||||
example = ''
|
||||
{
|
||||
main = {
|
||||
enable = true;
|
||||
secrets.storageEncryptionKeyFile = "/etc/authelia/storageEncryptionKeyFile";
|
||||
secrets.jwtSecretFile = "/etc/authelia/jwtSecretFile";
|
||||
settings = {
|
||||
theme = "light";
|
||||
default_2fa_method = "totp";
|
||||
log.level = "debug";
|
||||
server.disable_healthcheck = true;
|
||||
};
|
||||
};
|
||||
preprod = {
|
||||
enable = false;
|
||||
secrets.storageEncryptionKeyFile = "/mnt/pre-prod/authelia/storageEncryptionKeyFile";
|
||||
secrets.jwtSecretFile = "/mnt/pre-prod/jwtSecretFile";
|
||||
settings = {
|
||||
theme = "dark";
|
||||
default_2fa_method = "webauthn";
|
||||
server.host = "0.0.0.0";
|
||||
};
|
||||
};
|
||||
test.enable = true;
|
||||
test.secrets.manual = true;
|
||||
test.settings.theme = "grey";
|
||||
test.settings.server.disable_healthcheck = true;
|
||||
test.settingsFiles = [ "/mnt/test/authelia" "/mnt/test-authelia.conf" ];
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
mkInstanceServiceConfig = instance:
|
||||
let
|
||||
execCommand = "${instance.package}/bin/authelia";
|
||||
configFile = format.generate "config.yml" instance.settings;
|
||||
configArg = "--config ${builtins.concatStringsSep "," (lib.concatLists [[configFile] instance.settingsFiles])}";
|
||||
in
|
||||
{
|
||||
description = "Authelia authentication and authorization server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
environment =
|
||||
(lib.filterAttrs (_: v: v != null) {
|
||||
AUTHELIA_JWT_SECRET_FILE = instance.secrets.jwtSecretFile;
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = instance.secrets.storageEncryptionKeyFile;
|
||||
AUTHELIA_SESSION_SECRET_FILE = instance.secrets.sessionSecretFile;
|
||||
AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE = instance.secrets.oidcIssuerPrivateKeyFile;
|
||||
AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE = instance.secrets.oidcHmacSecretFile;
|
||||
})
|
||||
// instance.environmentVariables;
|
||||
|
||||
preStart = "${execCommand} ${configArg} validate-config";
|
||||
serviceConfig = {
|
||||
User = instance.user;
|
||||
Group = instance.group;
|
||||
ExecStart = "${execCommand} ${configArg}";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
StateDirectory = "authelia-${instance.name}";
|
||||
StateDirectoryMode = "0700";
|
||||
|
||||
# Security options:
|
||||
AmbientCapabilities = "";
|
||||
CapabilityBoundingSet = "";
|
||||
DeviceAllow = "";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
PrivateUsers = true;
|
||||
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = "read-only";
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "noaccess";
|
||||
ProtectSystem = "strict";
|
||||
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@cpu-emulation"
|
||||
"~@debug"
|
||||
"~@keyring"
|
||||
"~@memlock"
|
||||
"~@obsolete"
|
||||
"~@privileged"
|
||||
"~@setuid"
|
||||
];
|
||||
};
|
||||
};
|
||||
mkInstanceUsersConfig = instance: {
|
||||
groups."authelia-${instance.name}" =
|
||||
lib.mkIf (instance.group == "authelia-${instance.name}") {
|
||||
name = "authelia-${instance.name}";
|
||||
};
|
||||
users."authelia-${instance.name}" =
|
||||
lib.mkIf (instance.user == "authelia-${instance.name}") {
|
||||
name = "authelia-${instance.name}";
|
||||
isSystemUser = true;
|
||||
group = instance.group;
|
||||
};
|
||||
};
|
||||
instances = lib.attrValues cfg.instances;
|
||||
in
|
||||
{
|
||||
assertions = lib.flatten (lib.flip lib.mapAttrsToList cfg.instances (name: instance:
|
||||
[
|
||||
{
|
||||
assertion = instance.secrets.manual || (instance.secrets.jwtSecretFile != null && instance.secrets.storageEncryptionKeyFile != null);
|
||||
message = ''
|
||||
Authelia requires a JWT Secret and a Storage Encryption Key to work.
|
||||
Either set them like so:
|
||||
services.authelia.${name}.secrets.jwtSecretFile = /my/path/to/jwtsecret;
|
||||
services.authelia.${name}.secrets.storageEncryptionKeyFile = /my/path/to/encryptionkey;
|
||||
Or set services.authelia.${name}.secrets.manual = true and provide them yourself via
|
||||
environmentVariables or settingsFiles.
|
||||
Do not include raw secrets in nix settings.
|
||||
'';
|
||||
}
|
||||
]
|
||||
));
|
||||
|
||||
systemd.services = lib.mkMerge
|
||||
(map
|
||||
(instance: lib.mkIf instance.enable {
|
||||
"authelia-${instance.name}" = mkInstanceServiceConfig instance;
|
||||
})
|
||||
instances);
|
||||
users = lib.mkMerge
|
||||
(map
|
||||
(instance: lib.mkIf instance.enable (mkInstanceUsersConfig instance))
|
||||
instances);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user