Compare commits
3 commits
91005c544a
...
aab0cc5e2b
Author | SHA1 | Date | |
---|---|---|---|
Patrick | aab0cc5e2b | ||
Patrick | 37f5175f5e | ||
Patrick | 3129e49d30 |
|
@ -47,5 +47,7 @@
|
|||
family = uidGid 2003;
|
||||
printer = uidGid 2005;
|
||||
pr-tracker = uidGid 2006;
|
||||
blog = uidGid 2007;
|
||||
signald = uidGid 2008;
|
||||
};
|
||||
}
|
||||
|
|
83
config/services/blog.nix
Normal file
83
config/services/blog.nix
Normal file
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
prestart = pkgs.writeShellScript "pr-tracker-pre" ''
|
||||
if [ ! -f ./ssh_key ]; then
|
||||
ssh-keygen -t ed25519 -N "" -f ssh_key
|
||||
fi
|
||||
${lib.getExe pkgs.git} config core.sshCommand 'ssh -i ~/ssh_key'
|
||||
if [ ! -d ./blog ]; then
|
||||
${lib.getExe pkgs.git} clone ssh://git@forge.lel.lol:9922/patrick/blog.git |\
|
||||
echo "failed to clone the repository did you forget to add the ssh key?"
|
||||
fi
|
||||
'';
|
||||
in
|
||||
{
|
||||
wireguard.elisabeth = {
|
||||
client.via = "elisabeth";
|
||||
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
|
||||
};
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
user = "blog";
|
||||
virtualHosts."blog.lel.lol" = {
|
||||
root = "/var/lib/blog/blog/public";
|
||||
};
|
||||
};
|
||||
environment.persistence."/persist".directories = [
|
||||
{
|
||||
directory = "/var/lib/blog";
|
||||
user = "blog";
|
||||
group = "blog";
|
||||
mode = "0700";
|
||||
}
|
||||
{
|
||||
directory = "/var/lib/signald";
|
||||
user = "signald";
|
||||
group = "signald";
|
||||
mode = "0700";
|
||||
}
|
||||
];
|
||||
systemd.timers.blog-update = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnBootSec = "1m";
|
||||
OnUnitActiveSec = "1m";
|
||||
};
|
||||
};
|
||||
users.groups.blog = { };
|
||||
users.users.blog = {
|
||||
isSystemUser = true;
|
||||
group = "blog";
|
||||
home = "/var/lib/blog";
|
||||
};
|
||||
|
||||
systemd.services.blog-update = {
|
||||
script = ''
|
||||
${lib.getExe pkgs.git} -C blog pull
|
||||
${lib.getExe pkgs.zola} -r blog/public build
|
||||
'';
|
||||
path = [ pkgs.openssh ];
|
||||
serviceConfig = {
|
||||
Requires = "blog";
|
||||
Type = "oneshot";
|
||||
User = "blog";
|
||||
Group = "blog";
|
||||
StateDirectory = "blog";
|
||||
WorkingDirectory = "/var/lib/blog";
|
||||
LimitNOFILE = "1048576";
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
StateDirectoryMode = "0700";
|
||||
ExecStartPre = prestart;
|
||||
};
|
||||
};
|
||||
|
||||
services.signald = {
|
||||
enable = true;
|
||||
group = "blog";
|
||||
};
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
{ config, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
kanidmdomain = "auth.${config.secrets.secrets.global.domains.web}";
|
||||
in
|
||||
{
|
||||
imports = [ ../../modules/kanidm.nix ];
|
||||
wireguard.elisabeth = {
|
||||
client.via = "elisabeth";
|
||||
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
|
||||
};
|
||||
disabledModules = [ "services/security/kanidm.nix" ];
|
||||
environment.persistence."/persist".directories = [
|
||||
{
|
||||
directory = "/var/lib/kanidm";
|
||||
|
@ -55,6 +53,7 @@ in
|
|||
};
|
||||
};
|
||||
services.kanidm = {
|
||||
package = pkgs.kanidm.withSecretProvisioning;
|
||||
enableServer = true;
|
||||
serverSettings = {
|
||||
domain = kanidmdomain;
|
||||
|
@ -85,6 +84,7 @@ in
|
|||
systems.oauth2.paperless = {
|
||||
displayName = "paperless";
|
||||
originUrl = "https://ppl.${config.secrets.secrets.global.domains.web}/";
|
||||
originLanding = "https://ppl.${config.secrets.secrets.global.domains.web}/";
|
||||
basicSecretFile = config.age.secrets.oauth2-paperless.path;
|
||||
scopeMaps."paperless.access" = [
|
||||
"openid"
|
||||
|
@ -104,6 +104,7 @@ in
|
|||
systems.oauth2.nextcloud = {
|
||||
displayName = "nextcloud";
|
||||
originUrl = "https://nc.${config.secrets.secrets.global.domains.web}/";
|
||||
originLanding = "https://nc.${config.secrets.secrets.global.domains.web}/";
|
||||
basicSecretFile = config.age.secrets.oauth2-nextcloud.path;
|
||||
allowInsecureClientDisablePkce = true;
|
||||
scopeMaps."nextcloud.access" = [
|
||||
|
@ -124,6 +125,7 @@ in
|
|||
systems.oauth2.immich = {
|
||||
displayName = "Immich";
|
||||
originUrl = "https://immich.${config.secrets.secrets.global.domains.web}/";
|
||||
originLanding = "https://immich.${config.secrets.secrets.global.domains.web}/";
|
||||
basicSecretFile = config.age.secrets.oauth2-immich.path;
|
||||
allowInsecureClientDisablePkce = true;
|
||||
enableLegacyCrypto = true;
|
||||
|
@ -145,6 +147,7 @@ in
|
|||
systems.oauth2.oauth2-proxy = {
|
||||
displayName = "Oauth2-Proxy";
|
||||
originUrl = "https://oauth2.${config.secrets.secrets.global.domains.web}/";
|
||||
originLanding = "https://oauth2.${config.secrets.secrets.global.domains.web}/";
|
||||
basicSecretFile = config.age.secrets.oauth2-proxy.path;
|
||||
scopeMaps."adguardhome.access" = [
|
||||
"openid"
|
||||
|
@ -197,6 +200,7 @@ in
|
|||
systems.oauth2.forgejo = {
|
||||
displayName = "Forgejo";
|
||||
originUrl = "https://forge.${config.secrets.secrets.global.domains.web}/";
|
||||
originLanding = "https://forge.${config.secrets.secrets.global.domains.web}/";
|
||||
basicSecretFile = config.age.secrets.oauth2-forgejo.path;
|
||||
scopeMaps."forgejo.access" = [
|
||||
"openid"
|
||||
|
@ -216,6 +220,7 @@ in
|
|||
public = true;
|
||||
displayName = "Netbird";
|
||||
originUrl = "https://netbird.${config.secrets.secrets.global.domains.web}/";
|
||||
originLanding = "https://netbird.${config.secrets.secrets.global.domains.web}/";
|
||||
preferShortUsername = true;
|
||||
enableLocalhostRedirects = true;
|
||||
enableLegacyCrypto = true;
|
||||
|
|
|
@ -122,7 +122,6 @@ in
|
|||
|
||||
systemd.services.pr-tracker-update = {
|
||||
script = ''
|
||||
set -eu
|
||||
${lib.getExe pkgs.git} -C nixpkgs fetch
|
||||
${lib.getExe pkgs.curl} http://localhost:3000/update
|
||||
'';
|
||||
|
|
|
@ -88,7 +88,7 @@ in
|
|||
nix.gc.automatic = lib.mkForce false;
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
|
||||
nixpkgs.config.cudaSupport = true;
|
||||
#nixpkgs.config.cudaSupport = true;
|
||||
|
||||
environment.systemPackages = [ streamcontroller ];
|
||||
services.udev.packages = [ streamcontroller ];
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
services.nginx.virtualHosts."blog.lel.lol" = {
|
||||
root = "/persist/blog";
|
||||
forceSSL = true;
|
||||
useACMEHost = "web";
|
||||
};
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
../../config/hardware/physical.nix
|
||||
|
||||
./blog.nix
|
||||
./net.nix
|
||||
./fs.nix
|
||||
] ++ lib.lists.optionals (!minimal) [ ./guests.nix ];
|
||||
|
|
|
@ -31,6 +31,7 @@ let
|
|||
octoprint = "print";
|
||||
pr-tracker = "tracker";
|
||||
invidious = "yt";
|
||||
blog = "blog";
|
||||
};
|
||||
in
|
||||
"${domains.${hostName}}.${config.secrets.secrets.global.domains.web}";
|
||||
|
@ -183,6 +184,7 @@ in
|
|||
(proxyProtect "ttrss" { port = 80; } true)
|
||||
(proxyProtect "invidious" { } true)
|
||||
(blockOf "yourspotify" { port = 80; })
|
||||
(blockOf "blog" { port = 80; })
|
||||
#(blockOf "homebox" {})
|
||||
(blockOf "pr-tracker" { })
|
||||
{
|
||||
|
@ -317,6 +319,7 @@ in
|
|||
// mkContainer "firefly" { }
|
||||
// mkContainer "yourspotify" { }
|
||||
// mkContainer "netbird" { }
|
||||
// mkContainer "blog" { }
|
||||
// mkContainer "kanidm" { }
|
||||
// mkContainer "nextcloud" { enablePanzer = true; }
|
||||
// mkContainer "paperless" { enableSharedPaperless = true; }
|
||||
|
|
1
hosts/elisabeth/secrets/blog/host.pub
Normal file
1
hosts/elisabeth/secrets/blog/host.pub
Normal file
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBBnyvnrDWPq468Zijt46VTUUGWz3QDgj7h6wJ42IAV6
|
|
@ -1,980 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
any
|
||||
attrNames
|
||||
attrValues
|
||||
concatLines
|
||||
concatLists
|
||||
converge
|
||||
filter
|
||||
filterAttrs
|
||||
filterAttrsRecursive
|
||||
flip
|
||||
foldl'
|
||||
getExe
|
||||
hasInfix
|
||||
hasPrefix
|
||||
isStorePath
|
||||
last
|
||||
mapAttrsToList
|
||||
mdDoc
|
||||
mkEnableOption
|
||||
mkForce
|
||||
mkIf
|
||||
mkMerge
|
||||
mkOption
|
||||
mkPackageOption
|
||||
optional
|
||||
optionalString
|
||||
splitString
|
||||
subtractLists
|
||||
types
|
||||
unique
|
||||
;
|
||||
|
||||
cfg = config.services.kanidm;
|
||||
settingsFormat = pkgs.formats.toml { };
|
||||
# Remove null values, so we can document optional values that don't end up in the generated TOML file.
|
||||
filterConfig = converge (filterAttrsRecursive (_: v: v != null));
|
||||
serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings);
|
||||
clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings);
|
||||
unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings);
|
||||
certPaths = builtins.map builtins.dirOf [
|
||||
cfg.serverSettings.tls_chain
|
||||
cfg.serverSettings.tls_key
|
||||
];
|
||||
|
||||
# Merge bind mount paths and remove paths where a prefix is already mounted.
|
||||
# This makes sure that if e.g. the tls_chain is in the nix store and /nix/store is already in the mount
|
||||
# paths, no new bind mount is added. Adding subpaths caused problems on ofborg.
|
||||
hasPrefixInList =
|
||||
list: newPath: any (path: hasPrefix (builtins.toString path) (builtins.toString newPath)) list;
|
||||
mergePaths = foldl' (
|
||||
merged: newPath:
|
||||
let
|
||||
# If the new path is a prefix to some existing path, we need to filter it out
|
||||
filteredPaths = filter (p: !hasPrefix (builtins.toString newPath) (builtins.toString p)) merged;
|
||||
# If a prefix of the new path is already in the list, do not add it
|
||||
filteredNew = optional (!hasPrefixInList filteredPaths newPath) newPath;
|
||||
in
|
||||
filteredPaths ++ filteredNew
|
||||
) [ ];
|
||||
|
||||
defaultServiceConfig = {
|
||||
BindReadOnlyPaths = [
|
||||
"/nix/store"
|
||||
"-/etc/resolv.conf"
|
||||
"-/etc/nsswitch.conf"
|
||||
"-/etc/hosts"
|
||||
"-/etc/localtime"
|
||||
];
|
||||
CapabilityBoundingSet = [ ];
|
||||
# ProtectClock= adds DeviceAllow=char-rtc r
|
||||
DeviceAllow = "";
|
||||
# Implies ProtectSystem=strict, which re-mounts all paths
|
||||
# DynamicUser = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
PrivateNetwork = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
# Would re-mount paths ignored by temporary root
|
||||
#ProtectSystem = "strict";
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
RestrictAddressFamilies = [ ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged @resources @setuid @keyring"
|
||||
];
|
||||
# Does not work well with the temporary root
|
||||
#UMask = "0066";
|
||||
};
|
||||
|
||||
mkPresentOption =
|
||||
what:
|
||||
mkOption {
|
||||
description = mdDoc "Whether to ensure that this ${what} is present or absent.";
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
filterPresent = filterAttrs (_: v: v.present);
|
||||
|
||||
provisionStateJson = pkgs.writeText "provision-state.json" (
|
||||
builtins.toJSON { inherit (cfg.provision) groups persons systems; }
|
||||
);
|
||||
|
||||
serverPort =
|
||||
# ipv6:
|
||||
if hasInfix "]:" cfg.serverSettings.bindaddress then
|
||||
last (splitString "]:" cfg.serverSettings.bindaddress)
|
||||
else
|
||||
# ipv4:
|
||||
if hasInfix "." cfg.serverSettings.bindaddress then
|
||||
last (splitString ":" cfg.serverSettings.bindaddress)
|
||||
# default is 8443
|
||||
else
|
||||
"8443";
|
||||
|
||||
# Only recover the admin account if a password should explicitly be provisioned
|
||||
# for the account. Otherwise it is not needed for provisioning.
|
||||
maybeRecoverAdmin = optionalString (cfg.provision.adminPasswordFile != null) ''
|
||||
KANIDM_ADMIN_PASSWORD=$(< ${cfg.provision.adminPasswordFile})
|
||||
# We always reset the admin account password if a desired password was specified.
|
||||
if ! KANIDM_RECOVER_ACCOUNT_PASSWORD=$KANIDM_ADMIN_PASSWORD ${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} admin --from-environment >/dev/null; then
|
||||
echo "Failed to recover admin account" >&2
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
|
||||
# Recover the idm_admin account. If a password should explicitly be provisioned
|
||||
# for the account we set it, otherwise we generate a new one because it is required
|
||||
# for provisioning.
|
||||
recoverIdmAdmin =
|
||||
if cfg.provision.idmAdminPasswordFile != null then
|
||||
''
|
||||
KANIDM_IDM_ADMIN_PASSWORD=$(< ${cfg.provision.idmAdminPasswordFile})
|
||||
# We always reset the idm_admin account password if a desired password was specified.
|
||||
if ! KANIDM_RECOVER_ACCOUNT_PASSWORD=$KANIDM_IDM_ADMIN_PASSWORD ${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} idm_admin --from-environment >/dev/null; then
|
||||
echo "Failed to recover idm_admin account" >&2
|
||||
exit 1
|
||||
fi
|
||||
''
|
||||
else
|
||||
''
|
||||
# Recover idm_admin account
|
||||
if ! recover_out=$(${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} idm_admin -o json); then
|
||||
echo "$recover_out" >&2
|
||||
echo "kanidm provision: Failed to recover admin account" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! KANIDM_IDM_ADMIN_PASSWORD=$(grep '{"password' <<< "$recover_out" | ${getExe pkgs.jq} -r .password); then
|
||||
echo "$recover_out" >&2
|
||||
echo "kanidm provision: Failed to parse password for idm_admin account" >&2
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
|
||||
postStartScript = pkgs.writeShellScript "post-start" ''
|
||||
set -euo pipefail
|
||||
|
||||
# Wait for the kanidm server to come online
|
||||
count=0
|
||||
main_pid_existed=false
|
||||
while ! test -e /run/kanidmd/sock; do
|
||||
sleep 0.1
|
||||
if [[ "$count" -eq 600 ]]; then
|
||||
echo "Tried for 60 seconds, giving up..."
|
||||
exit 1
|
||||
fi
|
||||
if [[ -d "/proc/$MAINPID" ]]; then
|
||||
main_pid_existed=true
|
||||
elif [[ "$main_pid_existed" == true ]]; then
|
||||
echo "Main server died, giving up..."
|
||||
exit 1
|
||||
fi
|
||||
count=$((count++))
|
||||
done
|
||||
|
||||
${recoverIdmAdmin}
|
||||
${maybeRecoverAdmin}
|
||||
|
||||
KANIDM_PROVISION_IDM_ADMIN_TOKEN=$KANIDM_IDM_ADMIN_PASSWORD \
|
||||
${getExe pkgs.kanidm-provision} --url "${cfg.provision.instanceUrl}" --state ${provisionStateJson} ${optionalString cfg.provision.acceptInvalidCerts "--accept-invalid-certs"}
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.services.kanidm = {
|
||||
enableClient = mkEnableOption (mdDoc "the Kanidm client");
|
||||
enableServer = mkEnableOption (mdDoc "the Kanidm server");
|
||||
enablePam = mkEnableOption (mdDoc "the Kanidm PAM and NSS integration");
|
||||
|
||||
package = mkPackageOption pkgs "kanidm" { };
|
||||
|
||||
serverSettings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
|
||||
options = {
|
||||
bindaddress = mkOption {
|
||||
description = mdDoc "Address/port combination the webserver binds to.";
|
||||
example = "[::1]:8443";
|
||||
type = types.str;
|
||||
};
|
||||
# Should be optional but toml does not accept null
|
||||
ldapbindaddress = mkOption {
|
||||
description = mdDoc ''
|
||||
Address and port the LDAP server is bound to. Setting this to `null` disables the LDAP interface.
|
||||
'';
|
||||
example = "[::1]:636";
|
||||
default = null;
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
origin = mkOption {
|
||||
description = mdDoc "The origin of your Kanidm instance. Must have https as protocol.";
|
||||
example = "https://idm.example.org";
|
||||
type = types.strMatching "^https://.*";
|
||||
};
|
||||
domain = mkOption {
|
||||
description = mdDoc ''
|
||||
The `domain` that Kanidm manages. Must be below or equal to the domain
|
||||
specified in `serverSettings.origin`.
|
||||
This can be left at `null`, only if your instance has the role `ReadOnlyReplica`.
|
||||
While it is possible to change the domain later on, it requires extra steps!
|
||||
Please consider the warnings and execute the steps described
|
||||
[in the documentation](https://kanidm.github.io/kanidm/stable/administrivia.html#rename-the-domain).
|
||||
'';
|
||||
example = "example.org";
|
||||
default = null;
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
db_path = mkOption {
|
||||
description = mdDoc "Path to Kanidm database.";
|
||||
default = "/var/lib/kanidm/kanidm.db";
|
||||
readOnly = true;
|
||||
type = types.path;
|
||||
};
|
||||
tls_chain = mkOption {
|
||||
description = mdDoc "TLS chain in pem format.";
|
||||
type = types.path;
|
||||
};
|
||||
tls_key = mkOption {
|
||||
description = mdDoc "TLS key in pem format.";
|
||||
type = types.path;
|
||||
};
|
||||
log_level = mkOption {
|
||||
description = mdDoc "Log level of the server.";
|
||||
default = "info";
|
||||
type = types.enum [
|
||||
"info"
|
||||
"debug"
|
||||
"trace"
|
||||
];
|
||||
};
|
||||
role = mkOption {
|
||||
description = mdDoc "The role of this server. This affects the replication relationship and thereby available features.";
|
||||
default = "WriteReplica";
|
||||
type = types.enum [
|
||||
"WriteReplica"
|
||||
"WriteReplicaNoUI"
|
||||
"ReadOnlyReplica"
|
||||
];
|
||||
};
|
||||
online_backup = {
|
||||
path = mkOption {
|
||||
description = mdDoc "Path to the output directory for backups.";
|
||||
type = types.path;
|
||||
default = "/var/lib/kanidm/backups";
|
||||
};
|
||||
schedule = mkOption {
|
||||
description = mdDoc "The schedule for backups in cron format.";
|
||||
type = types.str;
|
||||
default = "00 22 * * *";
|
||||
};
|
||||
versions = mkOption {
|
||||
description = mdDoc ''
|
||||
Number of backups to keep.
|
||||
|
||||
The default is set to `0`, in order to disable backups by default.
|
||||
'';
|
||||
type = types.ints.unsigned;
|
||||
default = 0;
|
||||
example = 7;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = mdDoc ''
|
||||
Settings for Kanidm, see
|
||||
[the documentation](https://kanidm.github.io/kanidm/stable/server_configuration.html)
|
||||
and [example configuration](https://github.com/kanidm/kanidm/blob/master/examples/server.toml)
|
||||
for possible values.
|
||||
'';
|
||||
};
|
||||
|
||||
clientSettings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
|
||||
options.uri = mkOption {
|
||||
description = mdDoc "Address of the Kanidm server.";
|
||||
example = "http://127.0.0.1:8080";
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
description = mdDoc ''
|
||||
Configure Kanidm clients, needed for the PAM daemon. See
|
||||
[the documentation](https://kanidm.github.io/kanidm/stable/client_tools.html#kanidm-configuration)
|
||||
and [example configuration](https://github.com/kanidm/kanidm/blob/master/examples/config)
|
||||
for possible values.
|
||||
'';
|
||||
};
|
||||
|
||||
unixSettings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
|
||||
options = {
|
||||
pam_allowed_login_groups = mkOption {
|
||||
description = mdDoc "Kanidm groups that are allowed to login using PAM.";
|
||||
example = "my_pam_group";
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
hsm_pin_path = mkOption {
|
||||
description = mdDoc "Path to a HSM pin.";
|
||||
default = "/var/cache/kanidm-unixd/hsm-pin";
|
||||
type = types.path;
|
||||
};
|
||||
};
|
||||
};
|
||||
description = mdDoc ''
|
||||
Configure Kanidm unix daemon.
|
||||
See [the documentation](https://kanidm.github.io/kanidm/stable/integrations/pam_and_nsswitch.html#the-unix-daemon)
|
||||
and [example configuration](https://github.com/kanidm/kanidm/blob/master/examples/unixd)
|
||||
for possible values.
|
||||
'';
|
||||
};
|
||||
|
||||
provision = {
|
||||
enable = mkEnableOption "provisioning of groups, users and oauth2 resource servers";
|
||||
|
||||
instanceUrl = mkOption {
|
||||
description = "The instance url to which the provisioning tool should connect.";
|
||||
default = "https://localhost:${serverPort}";
|
||||
defaultText = ''"https://localhost:<port from serverSettings.bindaddress>"'';
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
acceptInvalidCerts = mkOption {
|
||||
description = ''
|
||||
Whether to allow invalid certificates when provisioning the target instance.
|
||||
By default this is only allowed when the instanceUrl is localhost. This is
|
||||
dangerous when used with an external URL.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = hasPrefix "https://localhost:" cfg.provision.instanceUrl;
|
||||
defaultText = ''hasPrefix "https://localhost:" cfg.provision.instanceUrl'';
|
||||
};
|
||||
|
||||
adminPasswordFile = mkOption {
|
||||
description = "Path to a file containing the admin password for kanidm. Do NOT use a file from the nix store here!";
|
||||
example = "/run/secrets/kanidm-admin-password";
|
||||
default = null;
|
||||
type = types.nullOr types.path;
|
||||
};
|
||||
|
||||
idmAdminPasswordFile = mkOption {
|
||||
description = ''
|
||||
Path to a file containing the idm admin password for kanidm. Do NOT use a file from the nix store here!
|
||||
If this is not given but provisioning is enabled, the idm_admin password will be reset on each restart.
|
||||
'';
|
||||
example = "/run/secrets/kanidm-idm-admin-password";
|
||||
default = null;
|
||||
type = types.nullOr types.path;
|
||||
};
|
||||
|
||||
autoRemove = mkOption {
|
||||
description = ''
|
||||
Determines whether deleting an entity in this provisioning config should automatically
|
||||
cause them to be removed from kanidm, too. This works because the provisioning tool tracks
|
||||
all entities it has ever created. If this is set to false, you need to explicitly specify
|
||||
`present = false` to delete an entity.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
groups = mkOption {
|
||||
description = "Provisioning of kanidm groups";
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule (groupSubmod: {
|
||||
options = {
|
||||
present = mkPresentOption "group";
|
||||
|
||||
members = mkOption {
|
||||
description = "List of kanidm entities (persons, groups, ...) which are part of this group.";
|
||||
type = types.listOf types.str;
|
||||
apply = unique;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
config.members = concatLists (
|
||||
flip mapAttrsToList cfg.provision.persons (
|
||||
person: personCfg:
|
||||
optional (
|
||||
personCfg.present && builtins.elem groupSubmod.config._module.args.name personCfg.groups
|
||||
) person
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
persons = mkOption {
|
||||
description = "Provisioning of kanidm persons";
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
present = mkPresentOption "person";
|
||||
|
||||
displayName = mkOption {
|
||||
description = "Display name";
|
||||
type = types.str;
|
||||
example = "My User";
|
||||
};
|
||||
|
||||
legalName = mkOption {
|
||||
description = "Full legal name";
|
||||
type = types.nullOr types.str;
|
||||
example = "Jane Doe";
|
||||
default = null;
|
||||
};
|
||||
|
||||
mailAddresses = mkOption {
|
||||
description = "Mail addresses. First given address is considered the primary address.";
|
||||
type = types.listOf types.str;
|
||||
example = [ "jane.doe@example.com" ];
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
groups = mkOption {
|
||||
description = "List of groups this person should belong to.";
|
||||
type = types.listOf types.str;
|
||||
apply = unique;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
systems.oauth2 = mkOption {
|
||||
description = "Provisioning of oauth2 resource servers";
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
present = mkPresentOption "oauth2 resource server";
|
||||
|
||||
public = mkOption {
|
||||
description = "Whether this is a public client (enforces PKCE, doesn't use a basic secret)";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
displayName = mkOption {
|
||||
description = "Display name";
|
||||
type = types.str;
|
||||
example = "Some Service";
|
||||
};
|
||||
|
||||
originUrl = mkOption {
|
||||
description = "The origin URL of the service. OAuth2 redirects will only be allowed to sites under this origin. Must end with a slash.";
|
||||
type = types.strMatching ".*://.*/$";
|
||||
example = "https://someservice.example.com/";
|
||||
};
|
||||
|
||||
originLanding = mkOption {
|
||||
description = "When redirecting from the Kanidm Apps Listing page, some linked applications may need to land on a specific page to trigger oauth2/oidc interactions.";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "https://someservice.example.com/home";
|
||||
};
|
||||
|
||||
basicSecretFile = mkOption {
|
||||
description = ''
|
||||
The basic secret to use for this service. If null, the random secret generated
|
||||
by kanidm will not be touched. Do NOT use a path from the nix store here!
|
||||
'';
|
||||
type = types.nullOr types.path;
|
||||
example = "/run/secrets/some-oauth2-basic-secret";
|
||||
default = null;
|
||||
};
|
||||
|
||||
enableLocalhostRedirects = mkOption {
|
||||
description = "Allow localhost redirects. Only for public clients.";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
enableLegacyCrypto = mkOption {
|
||||
description = "Enable legacy crypto on this client. Allows JWT signing algorthms like RS256.";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
allowInsecureClientDisablePkce = mkOption {
|
||||
description = ''
|
||||
Disable PKCE on this oauth2 resource server to work around insecure clients
|
||||
that may not support it. You should request the client to enable PKCE!
|
||||
Only for non-public clients.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
preferShortUsername = mkOption {
|
||||
description = "Use 'name' instead of 'spn' in the preferred_username claim";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
scopeMaps = mkOption {
|
||||
description = ''
|
||||
Maps kanidm groups to returned oauth scopes.
|
||||
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
|
||||
'';
|
||||
type = types.attrsOf (types.listOf types.str);
|
||||
default = { };
|
||||
};
|
||||
|
||||
supplementaryScopeMaps = mkOption {
|
||||
description = ''
|
||||
Maps kanidm groups to additionally returned oauth scopes.
|
||||
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
|
||||
'';
|
||||
type = types.attrsOf (types.listOf types.str);
|
||||
default = { };
|
||||
};
|
||||
|
||||
removeOrphanedClaimMaps = mkOption {
|
||||
description = "Whether claim maps not specified here but present in kanidm should be removed from kanidm.";
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
claimMaps = mkOption {
|
||||
description = ''
|
||||
Adds additional claims (and values) based on which kanidm groups an authenticating party belongs to.
|
||||
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
|
||||
'';
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
joinType = mkOption {
|
||||
description = ''
|
||||
Determines how multiple values are joined to create the claim value.
|
||||
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
|
||||
'';
|
||||
type = types.enum [
|
||||
"array"
|
||||
"csv"
|
||||
"ssv"
|
||||
];
|
||||
default = "array";
|
||||
};
|
||||
|
||||
valuesByGroup = mkOption {
|
||||
description = "Maps kanidm groups to values for the claim.";
|
||||
default = { };
|
||||
type = types.attrsOf (types.listOf types.str);
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) {
|
||||
assertions =
|
||||
let
|
||||
entityList =
|
||||
type: attrs: flip mapAttrsToList (filterPresent attrs) (name: _: { inherit type name; });
|
||||
entities =
|
||||
entityList "group" cfg.provision.groups
|
||||
++ entityList "person" cfg.provision.persons
|
||||
++ entityList "oauth2" cfg.provision.systems.oauth2;
|
||||
|
||||
# Accumulate entities by name. Track corresponding entity types for later duplicate check.
|
||||
entitiesByName = foldl' (
|
||||
acc: { type, name }: acc // { ${name} = (acc.${name} or [ ]) ++ [ type ]; }
|
||||
) { } entities;
|
||||
|
||||
assertGroupsKnown =
|
||||
opt: groups:
|
||||
let
|
||||
knownGroups = attrNames (filterPresent cfg.provision.groups);
|
||||
unknownGroups = subtractLists knownGroups groups;
|
||||
in
|
||||
{
|
||||
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownGroups == [ ];
|
||||
message = "${opt} refers to unknown groups: ${toString unknownGroups}";
|
||||
};
|
||||
|
||||
assertEntitiesKnown =
|
||||
opt: entities:
|
||||
let
|
||||
unknownEntities = subtractLists (attrNames entitiesByName) entities;
|
||||
in
|
||||
{
|
||||
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownEntities == [ ];
|
||||
message = "${opt} refers to unknown entities: ${toString unknownEntities}";
|
||||
};
|
||||
in
|
||||
[
|
||||
{
|
||||
assertion =
|
||||
!cfg.enableServer
|
||||
|| ((cfg.serverSettings.tls_chain or null) == null)
|
||||
|| (!isStorePath cfg.serverSettings.tls_chain);
|
||||
message = ''
|
||||
<option>services.kanidm.serverSettings.tls_chain</option> points to
|
||||
a file in the Nix store. You should use a quoted absolute path to
|
||||
prevent this.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
!cfg.enableServer
|
||||
|| ((cfg.serverSettings.tls_key or null) == null)
|
||||
|| (!isStorePath cfg.serverSettings.tls_key);
|
||||
message = ''
|
||||
<option>services.kanidm.serverSettings.tls_key</option> points to
|
||||
a file in the Nix store. You should use a quoted absolute path to
|
||||
prevent this.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = !cfg.enableClient || options.services.kanidm.clientSettings.isDefined;
|
||||
message = ''
|
||||
<option>services.kanidm.clientSettings</option> needs to be configured
|
||||
if the client is enabled.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = !cfg.enablePam || options.services.kanidm.clientSettings.isDefined;
|
||||
message = ''
|
||||
<option>services.kanidm.clientSettings</option> needs to be configured
|
||||
for the PAM daemon to connect to the Kanidm server.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
!cfg.enableServer
|
||||
|| (
|
||||
cfg.serverSettings.domain == null
|
||||
-> cfg.serverSettings.role == "WriteReplica" || cfg.serverSettings.role == "WriteReplicaNoUI"
|
||||
);
|
||||
message = ''
|
||||
<option>services.kanidm.serverSettings.domain</option> can only be set if this instance
|
||||
is not a ReadOnlyReplica. Otherwise the db would inherit it from
|
||||
the instance it follows.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = cfg.provision.enable -> cfg.enableServer;
|
||||
message = "<option>services.kanidm.provision</option> requires <option>services.kanidm.enableServer</option> to be true";
|
||||
}
|
||||
# If any secret is provisioned, the kanidm package must have some required patches applied to it
|
||||
{
|
||||
assertion =
|
||||
(
|
||||
cfg.provision.enable
|
||||
&& (
|
||||
cfg.provision.adminPasswordFile != null
|
||||
|| cfg.provision.idmAdminPasswordFile != null
|
||||
|| any (x: x.basicSecretFile != null) (attrValues (filterPresent cfg.provision.systems.oauth2))
|
||||
)
|
||||
)
|
||||
-> cfg.package.enableSecretProvisioning;
|
||||
message = ''
|
||||
Specifying an admin account password or oauth2 basicSecretFile requires kanidm to be built with the secret provisioning patches.
|
||||
You may want to set `services.kanidm.package = pkgs.kanidm.withSecretProvisioning;`.
|
||||
'';
|
||||
}
|
||||
# Entity names must be globally unique:
|
||||
(
|
||||
let
|
||||
# Filter all names that occurred in more than one entity type.
|
||||
duplicateNames = filterAttrs (_: v: builtins.length v > 1) entitiesByName;
|
||||
in
|
||||
{
|
||||
assertion = cfg.provision.enable -> duplicateNames == { };
|
||||
message = ''
|
||||
services.kanidm.provision requires all entity names (group, person, oauth2, ...) to be unique!
|
||||
${concatLines (
|
||||
mapAttrsToList (name: xs: " - '${name}' used as: ${toString xs}") duplicateNames
|
||||
)}'';
|
||||
}
|
||||
)
|
||||
]
|
||||
++ flip mapAttrsToList (filterPresent cfg.provision.persons) (
|
||||
person: personCfg:
|
||||
assertGroupsKnown "services.kanidm.provision.persons.${person}.groups" personCfg.groups
|
||||
)
|
||||
++ flip mapAttrsToList (filterPresent cfg.provision.groups) (
|
||||
group: groupCfg:
|
||||
assertEntitiesKnown "services.kanidm.provision.groups.${group}.members" groupCfg.members
|
||||
)
|
||||
++ concatLists (
|
||||
flip mapAttrsToList (filterPresent cfg.provision.systems.oauth2) (
|
||||
oauth2: oauth2Cfg:
|
||||
[
|
||||
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.scopeMaps" (
|
||||
attrNames oauth2Cfg.scopeMaps
|
||||
))
|
||||
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.supplementaryScopeMaps" (
|
||||
attrNames oauth2Cfg.supplementaryScopeMaps
|
||||
))
|
||||
]
|
||||
++ concatLists (
|
||||
flip mapAttrsToList oauth2Cfg.claimMaps (
|
||||
claim: claimCfg: [
|
||||
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim}.valuesByGroup" (
|
||||
attrNames claimCfg.valuesByGroup
|
||||
))
|
||||
# At least one group must map to a value in each claim map
|
||||
{
|
||||
assertion =
|
||||
(cfg.provision.enable && cfg.enableServer)
|
||||
-> any (xs: xs != [ ]) (attrValues claimCfg.valuesByGroup);
|
||||
message = "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim} does not specify any values for any group";
|
||||
}
|
||||
# Public clients cannot define a basic secret
|
||||
{
|
||||
assertion =
|
||||
(cfg.provision.enable && cfg.enableServer && oauth2Cfg.public) -> oauth2Cfg.basicSecretFile == null;
|
||||
message = "services.kanidm.provision.systems.oauth2.${oauth2} is a public client and thus cannot specify a basic secret";
|
||||
}
|
||||
# Public clients cannot disable PKCE
|
||||
{
|
||||
assertion =
|
||||
(cfg.provision.enable && cfg.enableServer && oauth2Cfg.public)
|
||||
-> !oauth2Cfg.allowInsecureClientDisablePkce;
|
||||
message = "services.kanidm.provision.systems.oauth2.${oauth2} is a public client and thus cannot disable PKCE";
|
||||
}
|
||||
# Non-public clients cannot enable localhost redirects
|
||||
{
|
||||
assertion =
|
||||
(cfg.provision.enable && cfg.enableServer && !oauth2Cfg.public)
|
||||
-> !oauth2Cfg.enableLocalhostRedirects;
|
||||
message = "services.kanidm.provision.systems.oauth2.${oauth2} is a non-public client and thus cannot enable localhost redirects";
|
||||
}
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
environment.systemPackages = mkIf cfg.enableClient [ cfg.package ];
|
||||
|
||||
systemd.tmpfiles.settings."10-kanidm" = {
|
||||
${cfg.serverSettings.online_backup.path}.d = {
|
||||
mode = "0700";
|
||||
user = "kanidm";
|
||||
group = "kanidm";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.kanidm = mkIf cfg.enableServer {
|
||||
description = "kanidm identity management daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = mkMerge [
|
||||
# Merge paths and ignore existing prefixes needs to sidestep mkMerge
|
||||
(
|
||||
defaultServiceConfig
|
||||
// {
|
||||
BindReadOnlyPaths = mergePaths (defaultServiceConfig.BindReadOnlyPaths ++ certPaths);
|
||||
}
|
||||
)
|
||||
{
|
||||
StateDirectory = "kanidm";
|
||||
StateDirectoryMode = "0700";
|
||||
RuntimeDirectory = "kanidmd";
|
||||
ExecStart = "${cfg.package}/bin/kanidmd server -c ${serverConfigFile}";
|
||||
ExecStartPost = mkIf cfg.provision.enable postStartScript;
|
||||
User = "kanidm";
|
||||
Group = "kanidm";
|
||||
|
||||
BindPaths = [
|
||||
# To create the socket
|
||||
"/run/kanidmd:/run/kanidmd"
|
||||
# To store backups
|
||||
cfg.serverSettings.online_backup.path
|
||||
];
|
||||
|
||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
|
||||
# This would otherwise override the CAP_NET_BIND_SERVICE capability.
|
||||
PrivateUsers = mkForce false;
|
||||
# Port needs to be exposed to the host network
|
||||
PrivateNetwork = mkForce false;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
TemporaryFileSystem = "/:ro";
|
||||
}
|
||||
];
|
||||
environment.RUST_LOG = "info";
|
||||
};
|
||||
|
||||
systemd.services.kanidm-unixd = mkIf cfg.enablePam {
|
||||
description = "Kanidm PAM daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
restartTriggers = [
|
||||
unixConfigFile
|
||||
clientConfigFile
|
||||
];
|
||||
serviceConfig = mkMerge [
|
||||
defaultServiceConfig
|
||||
{
|
||||
CacheDirectory = "kanidm-unixd";
|
||||
CacheDirectoryMode = "0700";
|
||||
RuntimeDirectory = "kanidm-unixd";
|
||||
ExecStart = "${cfg.package}/bin/kanidm_unixd";
|
||||
User = "kanidm-unixd";
|
||||
Group = "kanidm-unixd";
|
||||
|
||||
BindReadOnlyPaths = [
|
||||
"-/etc/kanidm"
|
||||
"-/etc/static/kanidm"
|
||||
"-/etc/ssl"
|
||||
"-/etc/static/ssl"
|
||||
"-/etc/passwd"
|
||||
"-/etc/group"
|
||||
];
|
||||
BindPaths = [
|
||||
# To create the socket
|
||||
"/run/kanidm-unixd:/var/run/kanidm-unixd"
|
||||
];
|
||||
# Needs to connect to kanidmd
|
||||
PrivateNetwork = mkForce false;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
TemporaryFileSystem = "/:ro";
|
||||
}
|
||||
];
|
||||
environment.RUST_LOG = "info";
|
||||
};
|
||||
|
||||
systemd.services.kanidm-unixd-tasks = mkIf cfg.enablePam {
|
||||
description = "Kanidm PAM home management daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"network.target"
|
||||
"kanidm-unixd.service"
|
||||
];
|
||||
partOf = [ "kanidm-unixd.service" ];
|
||||
restartTriggers = [
|
||||
unixConfigFile
|
||||
clientConfigFile
|
||||
];
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/kanidm_unixd_tasks";
|
||||
|
||||
BindReadOnlyPaths = [
|
||||
"/nix/store"
|
||||
"-/etc/resolv.conf"
|
||||
"-/etc/nsswitch.conf"
|
||||
"-/etc/hosts"
|
||||
"-/etc/localtime"
|
||||
"-/etc/kanidm"
|
||||
"-/etc/static/kanidm"
|
||||
];
|
||||
BindPaths = [
|
||||
# To manage home directories
|
||||
"/home"
|
||||
# To connect to kanidm-unixd
|
||||
"/run/kanidm-unixd:/var/run/kanidm-unixd"
|
||||
];
|
||||
# CAP_DAC_OVERRIDE is needed to ignore ownership of unixd socket
|
||||
CapabilityBoundingSet = [
|
||||
"CAP_CHOWN"
|
||||
"CAP_FOWNER"
|
||||
"CAP_DAC_OVERRIDE"
|
||||
"CAP_DAC_READ_SEARCH"
|
||||
];
|
||||
IPAddressDeny = "any";
|
||||
# Need access to users
|
||||
PrivateUsers = false;
|
||||
# Need access to home directories
|
||||
ProtectHome = false;
|
||||
RestrictAddressFamilies = [ "AF_UNIX" ];
|
||||
TemporaryFileSystem = "/:ro";
|
||||
Restart = "on-failure";
|
||||
};
|
||||
environment.RUST_LOG = "info";
|
||||
};
|
||||
|
||||
# These paths are hardcoded
|
||||
environment.etc = mkMerge [
|
||||
(mkIf cfg.enableServer { "kanidm/server.toml".source = serverConfigFile; })
|
||||
(mkIf options.services.kanidm.clientSettings.isDefined {
|
||||
"kanidm/config".source = clientConfigFile;
|
||||
})
|
||||
(mkIf cfg.enablePam { "kanidm/unixd".source = unixConfigFile; })
|
||||
];
|
||||
|
||||
system.nssModules = mkIf cfg.enablePam [ cfg.package ];
|
||||
|
||||
system.nssDatabases.group = optional cfg.enablePam "kanidm";
|
||||
system.nssDatabases.passwd = optional cfg.enablePam "kanidm";
|
||||
|
||||
users.groups = mkMerge [
|
||||
(mkIf cfg.enableServer { kanidm = { }; })
|
||||
(mkIf cfg.enablePam { kanidm-unixd = { }; })
|
||||
];
|
||||
users.users = mkMerge [
|
||||
(mkIf cfg.enableServer {
|
||||
kanidm = {
|
||||
description = "Kanidm server";
|
||||
isSystemUser = true;
|
||||
group = "kanidm";
|
||||
packages = [ cfg.package ];
|
||||
};
|
||||
})
|
||||
(mkIf cfg.enablePam {
|
||||
kanidm-unixd = {
|
||||
description = "Kanidm PAM daemon";
|
||||
isSystemUser = true;
|
||||
group = "kanidm-unixd";
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
erictapen
|
||||
Flakebi
|
||||
oddlama
|
||||
];
|
||||
meta.buildDocsInSandbox = false;
|
||||
}
|
|
@ -28,26 +28,6 @@ _inputs: [
|
|||
--set QT_QPA_PLATFORM xcb
|
||||
'';
|
||||
});
|
||||
kanidm = prev.kanidm.overrideAttrs (
|
||||
old:
|
||||
let
|
||||
provisionSrc = prev.fetchFromGitHub {
|
||||
owner = "oddlama";
|
||||
repo = "kanidm-provision";
|
||||
rev = "v1.1.0";
|
||||
hash = "sha256-pFOFFKh3la/sZGXj+pAM8x4SMeffvvbOvTjPeHS1XPU=";
|
||||
};
|
||||
in
|
||||
{
|
||||
patches = old.patches ++ [
|
||||
"${provisionSrc}/patches/1.2.0-oauth2-basic-secret-modify.patch"
|
||||
"${provisionSrc}/patches/1.2.0-recover-account.patch"
|
||||
];
|
||||
passthru.enableSecretProvisioning = true;
|
||||
doCheck = false;
|
||||
}
|
||||
);
|
||||
kanidm-provision = prev.callPackage ./kanidm-provision.nix { };
|
||||
#pythonPackagesExtension = prev.pythonPackagesExtension ++ [
|
||||
# (_pythonFinal: pythonPrev: {
|
||||
# usb-monitor =
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
lib,
|
||||
rustPlatform,
|
||||
fetchFromGitHub,
|
||||
}:
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "kanidm-provision";
|
||||
version = "1.1.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "oddlama";
|
||||
repo = "kanidm-provision";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-pFOFFKh3la/sZGXj+pAM8x4SMeffvvbOvTjPeHS1XPU=";
|
||||
};
|
||||
|
||||
cargoHash = "sha256-oiKlKIL23xH67tCDbny9Gj97JQQm4mYt0IHXB5hzJ/A=";
|
||||
|
||||
meta = with lib; {
|
||||
description = "A small utility to help with kanidm provisioning";
|
||||
homepage = "https://github.com/oddlama/kanidm-provision";
|
||||
license = with licenses; [
|
||||
asl20
|
||||
mit
|
||||
];
|
||||
maintainers = with maintainers; [ oddlama ];
|
||||
mainProgram = "kanidm-provision";
|
||||
};
|
||||
}
|
Binary file not shown.
17
secrets/wireguard/elisabeth/keys/elisabeth-blog.age
Normal file
17
secrets/wireguard/elisabeth/keys/elisabeth-blog.age
Normal file
|
@ -0,0 +1,17 @@
|
|||
age-encryption.org/v1
|
||||
-> X25519 eVtVzXtvsEgbNOdIy4VDn1FbpMAoSZ89cHEoFF+zDls
|
||||
0naCdMLWG6MRREA/+OC+xbjxDnPXXfcwdvhGY9bmF3I
|
||||
-> piv-p256 ZFgiIw A0b4W+z9JJLdoeLsceIWTgfq9AGhGCYzghM8A/xxi73q
|
||||
9z6A/Xk39YcMlY6vflm/HEvMjjrfC8hcp9SVIZ601Xs
|
||||
-> piv-p256 XTQkUA As6ZR0tijPVbIGJJQE7ebHDJVuMdvEF7uSecCAFZBr8q
|
||||
f6KhqssOYi6Lm7xpNaQEtHKZ6qyd3/lRLDI7Id0+1I8
|
||||
-> piv-p256 ZFgiIw AuJ8buC0fCg9gT9DpLSAfVFpYue6nKwq1Q4RLZU0eIfy
|
||||
+UP/GGc/qW8wznHYVsW7xFuK4/pLgvesoODaafsZDhs
|
||||
-> piv-p256 5vmPtQ AyrqpWUElWE9Ai+DeV1lUq+nHAqaZFZkMTPPIu0DiesF
|
||||
S5T0MFAArqnNXtwrYGzAi5rK+BkWn/Gs8U6vtqijIwc
|
||||
-> 0Skk=zN=-grease E: ]pN}4
|
||||
zV/hyZHiaSckbVGuS+oNFItTxcLKTyL8G4G+Btzcym1Afm0CCrXL5fc/ss7tQ7Mx
|
||||
ac7JEfvv9cCnAezvog
|
||||
--- rvFVqJgSmwEk/Qy4x/LoIlAuJ6JtWxFvInGyO7lv96k
|
||||
4ËñNÕࣅ
|
||||
/ýLÒ©—C³I*³Ë¨¢2ã•‘¸.!Ûøšï.Ñ1þ »¡•Øèöa³@ý‚3‹Tô9ˆ‰)]t•mIý-&5t
|
1
secrets/wireguard/elisabeth/keys/elisabeth-blog.pub
Normal file
1
secrets/wireguard/elisabeth/keys/elisabeth-blog.pub
Normal file
|
@ -0,0 +1 @@
|
|||
DVpnYaoXKKk37IbTyG08bTWogBAD9N/s2PVodeHFaXo=
|
|
@ -0,0 +1,15 @@
|
|||
age-encryption.org/v1
|
||||
-> X25519 8xUsd8+0vzcdMZ+9/Q7c4uHrINfL/YnGb2oi5TPPUwk
|
||||
GrQDEqwQpunmU/Fwa0o2YV1VEMwb7F3uuUqPC2b9kNg
|
||||
-> piv-p256 ZFgiIw AtzJHfEspGUDVtaXot1EE/u3Z5cTVL+PeBN2f5ZWbL6M
|
||||
fOV0Hp6+cZB3NbypVXQtPULDonweA/62/G5gnunWVG4
|
||||
-> piv-p256 XTQkUA AmPo/XlWsLPW+JYoTGCLTxWccJuh4EcKafN+D+URuGoF
|
||||
3rHV1yeANXzWRpWb/0EA1IjCOitoTsLGN4dU1raTr0k
|
||||
-> piv-p256 ZFgiIw AuduWmro6APJsPTCZrtRpkwECkOfsDL109rvrE9UxkkV
|
||||
cnJb8UKLM1Oy9nZr+HQp3p6OhT/+9Htc3GoAqADa2nI
|
||||
-> piv-p256 5vmPtQ A3ge8G2tligkbgdXvrngnObz6/kk3R5HN1Gl31Diz5hc
|
||||
1d0ebykK0ccq7R4UegjAL+dl0EX6dves6Qsg4n7I0sA
|
||||
-> :/-grease 4$+ P= _VV%:"P|
|
||||
4Ny9m7mh1lEg
|
||||
--- CsASon+mZ54A0BLZmBl9NaSa9n6M9mYbpY6igzdGF+U
|
||||
“]lS"qùv¢½Öö˜P2Õb¥“ç]•áÍÞ
nóƒ7ÆÏ¥C@dD-y+å=ÊÇ<C38A>äY§†Dë`v ÆpñÙ~‹ìãþž
|
|
@ -26,7 +26,7 @@ let
|
|||
'';
|
||||
in
|
||||
{
|
||||
#home.packages = [ pkgs.minion ];
|
||||
home.packages = [ pkgs.minion ];
|
||||
# yet another program that uses the config file as a live state file
|
||||
# Why?
|
||||
home.activation.installMinionConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
|
|
Loading…
Reference in a new issue