style: format tree
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
@@ -7,12 +12,12 @@ let
|
||||
auth-passthru = config.selfprivacy.passthru.auth;
|
||||
keys-path = auth-passthru.keys-path;
|
||||
# generate OAuth2 client secret
|
||||
mkKanidmExecStartPreScript = oauthClientID: linuxGroup:
|
||||
mkKanidmExecStartPreScript =
|
||||
oauthClientID: linuxGroup:
|
||||
let
|
||||
secretFP = auth-passthru.mkOAuth2ClientSecretFP linuxGroup;
|
||||
in
|
||||
pkgs.writeShellScript
|
||||
"${oauthClientID}-kanidm-ExecStartPre-script.sh" ''
|
||||
pkgs.writeShellScript "${oauthClientID}-kanidm-ExecStartPre-script.sh" ''
|
||||
set -o pipefail
|
||||
set -o errexit
|
||||
if ! [ -f "${secretFP}" ]
|
||||
@@ -22,17 +27,16 @@ let
|
||||
chmod 640 "${secretFP}"
|
||||
fi
|
||||
'';
|
||||
mkKanidmExecStartPostScript = oauthClientID: linuxGroup: isMailserver:
|
||||
mkKanidmExecStartPostScript =
|
||||
oauthClientID: linuxGroup: isMailserver:
|
||||
let
|
||||
kanidmServiceAccountName = "sp.${oauthClientID}.service-account";
|
||||
kanidmServiceAccountTokenName = "${oauthClientID}-service-account-token";
|
||||
kanidmServiceAccountTokenFP =
|
||||
auth-passthru.mkServiceAccountTokenFP linuxGroup;
|
||||
kanidmServiceAccountTokenFP = auth-passthru.mkServiceAccountTokenFP linuxGroup;
|
||||
isRW = oauthClientID == "selfprivacy-api";
|
||||
in
|
||||
pkgs.writeShellScript
|
||||
"${oauthClientID}-kanidm-ExecStartPost-script.sh"
|
||||
(''
|
||||
pkgs.writeShellScript "${oauthClientID}-kanidm-ExecStartPost-script.sh" (
|
||||
''
|
||||
export HOME=$RUNTIME_DIRECTORY/client_home
|
||||
readonly KANIDM="${pkgs.kanidm}/bin/kanidm"
|
||||
|
||||
@@ -87,8 +91,7 @@ in
|
||||
{
|
||||
options.selfprivacy.auth = {
|
||||
clients = mkOption {
|
||||
description =
|
||||
"Configurations for OAuth2 & LDAP servers clients services. Corresponding Kanidm provisioning configuration and systemd scripts are generated.";
|
||||
description = "Configurations for OAuth2 & LDAP servers clients services. Corresponding Kanidm provisioning configuration and systemd scripts are generated.";
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
@@ -107,69 +110,58 @@ in
|
||||
};
|
||||
enablePkce = mkOption {
|
||||
type = lib.types.bool;
|
||||
description =
|
||||
"Whether PKCE must be used between client and Kanidm.";
|
||||
description = "Whether PKCE must be used between client and Kanidm.";
|
||||
default = false;
|
||||
};
|
||||
adminsGroup = mkOption {
|
||||
type =
|
||||
types.nullOr (lib.types.strMatching "sp\.[A-Za-z0-9]+\.admins");
|
||||
description =
|
||||
"Name of admins group in Kanidm, whose members have admin level access to resources (service) associated with OAuth2 client authorization.";
|
||||
type = types.nullOr (lib.types.strMatching "sp\.[A-Za-z0-9]+\.admins");
|
||||
description = "Name of admins group in Kanidm, whose members have admin level access to resources (service) associated with OAuth2 client authorization.";
|
||||
default = null;
|
||||
};
|
||||
usersGroup = mkOption {
|
||||
type =
|
||||
types.nullOr (lib.types.strMatching "sp\.[A-Za-z0-9]+\.users");
|
||||
description =
|
||||
"Name of users group in Kanidm, whose members have user level access to resources (service) associated with OAuth2 client authorization.";
|
||||
type = types.nullOr (lib.types.strMatching "sp\.[A-Za-z0-9]+\.users");
|
||||
description = "Name of users group in Kanidm, whose members have user level access to resources (service) associated with OAuth2 client authorization.";
|
||||
default = null;
|
||||
};
|
||||
originLanding = mkOption {
|
||||
type = types.nullOr lib.types.str;
|
||||
description =
|
||||
"The origin landing of the service for OAuth2 redirects.";
|
||||
description = "The origin landing of the service for OAuth2 redirects.";
|
||||
};
|
||||
originUrl = mkOption {
|
||||
type = types.nullOr lib.types.str;
|
||||
description =
|
||||
"The origin URL of the service for OAuth2 redirects.";
|
||||
description = "The origin URL of the service for OAuth2 redirects.";
|
||||
};
|
||||
subdomain = lib.mkOption {
|
||||
type =
|
||||
lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain of the service.";
|
||||
};
|
||||
# when true, "name" is passed to a service instead of "name@domain"
|
||||
useShortPreferredUsername = mkOption {
|
||||
description =
|
||||
"Use 'name' instead of 'spn' in the preferred_username claim.";
|
||||
description = "Use 'name' instead of 'spn' in the preferred_username claim.";
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
linuxUserOfClient = mkOption {
|
||||
type = types.nullOr lib.types.str;
|
||||
description =
|
||||
"Name of a Linux OAuth2 client user, under which it should get access through a folder with keys.";
|
||||
description = "Name of a Linux OAuth2 client user, under which it should get access through a folder with keys.";
|
||||
default = null;
|
||||
};
|
||||
linuxGroupOfClient = mkOption {
|
||||
type = types.nullOr lib.types.str;
|
||||
description =
|
||||
"Name of Linux OAuth2 client group, under which it should read an OAuth2 client secret file.";
|
||||
description = "Name of Linux OAuth2 client group, under which it should read an OAuth2 client secret file.";
|
||||
default = null;
|
||||
};
|
||||
isTokenNeeded = mkOption {
|
||||
description =
|
||||
"Whether a read-only needs to be generated for LDAP access.";
|
||||
description = "Whether a read-only needs to be generated for LDAP access.";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
clientSystemdUnits = mkOption {
|
||||
description = "A list of systemd services, which depend on OAuth service";
|
||||
# taken from nixos/lib/systemd-lib.nix: unitNameType
|
||||
type = types.listOf
|
||||
(types.strMatching "[a-zA-Z0-9@%:_.\\-]+[.](service|socket|device|mount|automount|swap|target|path|timer|scope|slice)");
|
||||
type = types.listOf (
|
||||
types.strMatching "[a-zA-Z0-9@%:_.\\-]+[.](service|socket|device|mount|automount|swap|target|path|timer|scope|slice)"
|
||||
);
|
||||
};
|
||||
scopeMaps = mkOption {
|
||||
description = ''
|
||||
@@ -231,111 +223,108 @@ in
|
||||
};
|
||||
config = lib.mkIf config.selfprivacy.sso.enable (
|
||||
let
|
||||
clientsAttrsList = lib.attrsets.mapAttrsToList
|
||||
(name: attrs: attrs // rec {
|
||||
clientID =
|
||||
if attrs.clientID == null
|
||||
then name
|
||||
else attrs.clientID;
|
||||
displayName =
|
||||
if attrs.displayName == null
|
||||
then clientID
|
||||
else attrs.displayName;
|
||||
adminsGroup =
|
||||
if attrs.adminsGroup == null
|
||||
then "sp.${clientID}.admins"
|
||||
else attrs.adminsGroup;
|
||||
usersGroup =
|
||||
if attrs.usersGroup == null
|
||||
then "sp.${clientID}.users"
|
||||
else attrs.usersGroup;
|
||||
basicSecretFile =
|
||||
"${keys-path}/${linuxGroupOfClient}/kanidm-oauth-client-secret";
|
||||
linuxUserOfClient =
|
||||
if attrs.linuxUserOfClient == null
|
||||
then clientID
|
||||
else attrs.linuxUserOfClient;
|
||||
clientsAttrsList = lib.attrsets.mapAttrsToList (
|
||||
name: attrs:
|
||||
attrs
|
||||
// rec {
|
||||
clientID = if attrs.clientID == null then name else attrs.clientID;
|
||||
displayName = if attrs.displayName == null then clientID else attrs.displayName;
|
||||
adminsGroup = if attrs.adminsGroup == null then "sp.${clientID}.admins" else attrs.adminsGroup;
|
||||
usersGroup = if attrs.usersGroup == null then "sp.${clientID}.users" else attrs.usersGroup;
|
||||
basicSecretFile = "${keys-path}/${linuxGroupOfClient}/kanidm-oauth-client-secret";
|
||||
linuxUserOfClient = if attrs.linuxUserOfClient == null then clientID else attrs.linuxUserOfClient;
|
||||
linuxGroupOfClient =
|
||||
if attrs.linuxGroupOfClient == null
|
||||
then clientID
|
||||
else attrs.linuxGroupOfClient;
|
||||
if attrs.linuxGroupOfClient == null then clientID else attrs.linuxGroupOfClient;
|
||||
originLanding =
|
||||
if attrs.originLanding == null
|
||||
then "https://${attrs.subdomain}.${config.selfprivacy.domain}/"
|
||||
else attrs.originLanding;
|
||||
if attrs.originLanding == null then
|
||||
"https://${attrs.subdomain}.${config.selfprivacy.domain}/"
|
||||
else
|
||||
attrs.originLanding;
|
||||
scopeMaps =
|
||||
if attrs.scopeMaps == null
|
||||
then { "${usersGroup}" = [ "email" "openid" "profile" ]; }
|
||||
else attrs.scopeMaps;
|
||||
})
|
||||
config.selfprivacy.auth.clients;
|
||||
if attrs.scopeMaps == null then
|
||||
{
|
||||
"${usersGroup}" = [
|
||||
"email"
|
||||
"openid"
|
||||
"profile"
|
||||
];
|
||||
}
|
||||
else
|
||||
attrs.scopeMaps;
|
||||
}
|
||||
) config.selfprivacy.auth.clients;
|
||||
in
|
||||
{
|
||||
# for each OAuth2 client: member of the `keys` group for directory access
|
||||
users.groups.keys.members = lib.mkMerge (lib.forEach
|
||||
clientsAttrsList
|
||||
({ linuxUserOfClient, ... }: [ linuxUserOfClient ])
|
||||
users.groups.keys.members = lib.mkMerge (
|
||||
lib.forEach clientsAttrsList ({ linuxUserOfClient, ... }: [ linuxUserOfClient ])
|
||||
);
|
||||
|
||||
systemd.tmpfiles.settings."kanidm-secrets" = lib.mkMerge (lib.forEach
|
||||
clientsAttrsList
|
||||
({ linuxGroupOfClient, ... }: {
|
||||
systemd.tmpfiles.settings."kanidm-secrets" = lib.mkMerge (
|
||||
lib.forEach clientsAttrsList (
|
||||
{ linuxGroupOfClient, ... }:
|
||||
{
|
||||
"${keys-path}/${linuxGroupOfClient}".d = {
|
||||
user = "kanidm";
|
||||
group = linuxGroupOfClient;
|
||||
mode = "2750";
|
||||
};
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
# for each OAuth2 client: scripts with Kanidm CLI commands
|
||||
systemd.services.kanidm = {
|
||||
before =
|
||||
lib.lists.concatMap
|
||||
({ clientSystemdUnits, ... }: clientSystemdUnits)
|
||||
clientsAttrsList;
|
||||
serviceConfig =
|
||||
lib.mkMerge (lib.forEach
|
||||
clientsAttrsList
|
||||
({ clientID, isTokenNeeded, linuxGroupOfClient, isMailserver, ... }:
|
||||
before = lib.lists.concatMap ({ clientSystemdUnits, ... }: clientSystemdUnits) clientsAttrsList;
|
||||
serviceConfig = lib.mkMerge (
|
||||
lib.forEach clientsAttrsList (
|
||||
{
|
||||
clientID,
|
||||
isTokenNeeded,
|
||||
linuxGroupOfClient,
|
||||
isMailserver,
|
||||
...
|
||||
}:
|
||||
{
|
||||
ExecStartPre = [
|
||||
# "-" prefix means to ignore exit code of prefixed script
|
||||
("-" + mkKanidmExecStartPreScript clientID linuxGroupOfClient)
|
||||
];
|
||||
ExecStartPost = lib.mkIf isTokenNeeded
|
||||
(lib.mkAfter [
|
||||
("-" +
|
||||
mkKanidmExecStartPostScript
|
||||
clientID
|
||||
linuxGroupOfClient
|
||||
isMailserver)
|
||||
]);
|
||||
}));
|
||||
ExecStartPost = lib.mkIf isTokenNeeded (
|
||||
lib.mkAfter [
|
||||
("-" + mkKanidmExecStartPostScript clientID linuxGroupOfClient isMailserver)
|
||||
]
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
# for each OAuth2 client: Kanidm provisioning options
|
||||
services.kanidm.provision = lib.mkMerge (lib.forEach
|
||||
clientsAttrsList
|
||||
({ adminsGroup
|
||||
, basicSecretFile
|
||||
, claimMaps
|
||||
, clientID
|
||||
, displayName
|
||||
, enablePkce
|
||||
, imageFile
|
||||
, originLanding
|
||||
, originUrl
|
||||
, scopeMaps
|
||||
, useShortPreferredUsername
|
||||
, usersGroup
|
||||
, ...
|
||||
}: {
|
||||
services.kanidm.provision = lib.mkMerge (
|
||||
lib.forEach clientsAttrsList (
|
||||
{
|
||||
adminsGroup,
|
||||
basicSecretFile,
|
||||
claimMaps,
|
||||
clientID,
|
||||
displayName,
|
||||
enablePkce,
|
||||
imageFile,
|
||||
originLanding,
|
||||
originUrl,
|
||||
scopeMaps,
|
||||
useShortPreferredUsername,
|
||||
usersGroup,
|
||||
...
|
||||
}:
|
||||
{
|
||||
groups = lib.mkIf (clientID != "selfprivacy-api") {
|
||||
"${adminsGroup}".members =
|
||||
[ auth-passthru.admins-group ];
|
||||
"${usersGroup}".members =
|
||||
[ adminsGroup auth-passthru.full-users-group ];
|
||||
"${adminsGroup}".members = [ auth-passthru.admins-group ];
|
||||
"${usersGroup}".members = [
|
||||
adminsGroup
|
||||
auth-passthru.full-users-group
|
||||
];
|
||||
};
|
||||
systems.oauth2.${clientID} = {
|
||||
inherit
|
||||
@@ -357,7 +346,9 @@ in
|
||||
# supplementaryScopeMaps."${admins-group}" =
|
||||
# [ "read:admin" "write:admin" ];
|
||||
};
|
||||
}));
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
domain = config.selfprivacy.domain;
|
||||
subdomain = "auth";
|
||||
@@ -38,7 +43,6 @@ lib.mkIf config.selfprivacy.sso.enable {
|
||||
"127.0.0.1" = [ auth-fqdn ];
|
||||
};
|
||||
|
||||
|
||||
# kanidm uses TLS in internal connection with nginx too
|
||||
# FIXME revise this: maybe kanidm must not have access to a public TLS
|
||||
users.groups."acmereceivers".members = [ "kanidm" ];
|
||||
@@ -69,16 +73,13 @@ lib.mkIf config.selfprivacy.sso.enable {
|
||||
origin = "https://" + auth-fqdn;
|
||||
|
||||
# TODO revise this: maybe kanidm must not have access to a public TLS
|
||||
tls_chain =
|
||||
"${config.security.acme.certs.${domain}.directory}/fullchain.pem";
|
||||
tls_key =
|
||||
"${config.security.acme.certs.${domain}.directory}/key.pem";
|
||||
tls_chain = "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
|
||||
tls_key = "${config.security.acme.certs.${domain}.directory}/key.pem";
|
||||
|
||||
# nginx should proxy requests to it
|
||||
bindaddress = kanidm-bind-address;
|
||||
|
||||
ldapbindaddress =
|
||||
"${ldap-host}:${toString ldap-port}";
|
||||
ldapbindaddress = "${ldap-host}:${toString ldap-port}";
|
||||
|
||||
# kanidm is behind a proxy
|
||||
trust_x_forward_for = true;
|
||||
@@ -101,8 +102,7 @@ lib.mkIf config.selfprivacy.sso.enable {
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
additionalModules =
|
||||
lib.mkIf config.selfprivacy.sso.debug [ pkgs.nginxModules.lua ];
|
||||
additionalModules = lib.mkIf config.selfprivacy.sso.debug [ pkgs.nginxModules.lua ];
|
||||
commonHttpConfig = lib.mkIf config.selfprivacy.sso.debug ''
|
||||
log_format kanidm escape=none '$request $status\n'
|
||||
'[Request body]: $request_body\n'
|
||||
@@ -158,8 +158,7 @@ lib.mkIf config.selfprivacy.sso.enable {
|
||||
|
||||
systemd.services.kanidm.serviceConfig.ExecStartPre =
|
||||
# idempotent script to run on each startup only for kanidm v1.5.0
|
||||
lib.mkIf (pkgs.kanidm.version == "1.5.0")
|
||||
(lib.mkBefore [ kanidmMigrateDbScript ]);
|
||||
lib.mkIf (pkgs.kanidm.version == "1.5.0") (lib.mkBefore [ kanidmMigrateDbScript ]);
|
||||
|
||||
selfprivacy.passthru.auth = {
|
||||
inherit
|
||||
@@ -171,25 +170,20 @@ lib.mkIf config.selfprivacy.sso.enable {
|
||||
keys-path
|
||||
;
|
||||
oauth2-introspection-url-prefix = client_id: "https://${client_id}:";
|
||||
oauth2-introspection-url-postfix =
|
||||
"@${auth-fqdn}/oauth2/token/introspect";
|
||||
oauth2-discovery-url = client_id:
|
||||
"https://${auth-fqdn}/oauth2/openid/${client_id}/.well-known/openid-configuration";
|
||||
oauth2-introspection-url-postfix = "@${auth-fqdn}/oauth2/token/introspect";
|
||||
oauth2-discovery-url =
|
||||
client_id: "https://${auth-fqdn}/oauth2/openid/${client_id}/.well-known/openid-configuration";
|
||||
oauth2-provider-name = "Kanidm";
|
||||
oauth2-systemd-service = "kanidm.service";
|
||||
|
||||
# e.g. "dc=mydomain,dc=com"
|
||||
ldap-base-dn =
|
||||
lib.strings.concatMapStringsSep
|
||||
","
|
||||
(x: "dc=" + x)
|
||||
(lib.strings.splitString "." domain);
|
||||
ldap-base-dn = lib.strings.concatMapStringsSep "," (x: "dc=" + x) (
|
||||
lib.strings.splitString "." domain
|
||||
);
|
||||
|
||||
# TODO consider to pass a value or throw exception if token is not generated
|
||||
mkServiceAccountTokenFP = linuxGroup:
|
||||
"${keys-path}/${linuxGroup}/kanidm-service-account-token";
|
||||
mkServiceAccountTokenFP = linuxGroup: "${keys-path}/${linuxGroup}/kanidm-service-account-token";
|
||||
|
||||
mkOAuth2ClientSecretFP = linuxGroup:
|
||||
"${keys-path}/${linuxGroup}/kanidm-oauth-client-secret";
|
||||
mkOAuth2ClientSecretFP = linuxGroup: "${keys-path}/${linuxGroup}/kanidm-oauth-client-secret";
|
||||
};
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{ config
|
||||
, lib
|
||||
, options
|
||||
, pkgs
|
||||
, ...
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
@@ -40,7 +41,9 @@ let
|
||||
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 (a: filterAttrsRecursive (_: v: v != null) (builtins.removeAttrs a [ "provision" ]));
|
||||
filterConfig = converge (
|
||||
a: filterAttrsRecursive (_: v: v != null) (builtins.removeAttrs a [ "provision" ])
|
||||
);
|
||||
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);
|
||||
@@ -54,8 +57,7 @@ let
|
||||
# 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'
|
||||
(
|
||||
mergePaths = foldl' (
|
||||
merged: newPath:
|
||||
let
|
||||
# If the new path is a prefix to some existing path, we need to filter it out
|
||||
@@ -127,10 +129,12 @@ let
|
||||
filterPresent = filterAttrs (_: v: v.present);
|
||||
|
||||
selfprivacy-admin-groups-regex = "^sp\.([[:alnum:]]+\.|)admins$";
|
||||
is-selfprivacy-admin-group = name:
|
||||
! builtins.isNull (builtins.match selfprivacy-admin-groups-regex name);
|
||||
is-selfprivacy-admin-group =
|
||||
name: !builtins.isNull (builtins.match selfprivacy-admin-groups-regex name);
|
||||
|
||||
isGroupNonOverwritable = g: false
|
||||
isGroupNonOverwritable =
|
||||
g:
|
||||
false
|
||||
|| !g ? members
|
||||
|| g ? members && g.members == [ ]
|
||||
|| g ? members && builtins.any is-selfprivacy-admin-group g.members;
|
||||
@@ -138,8 +142,7 @@ let
|
||||
provisionStateJson = pkgs.writeText "provision-state.json" (
|
||||
builtins.toJSON {
|
||||
inherit (cfg.provision) persons systems;
|
||||
groups =
|
||||
lib.attrsets.filterAttrs (_n: v: ! isGroupNonOverwritable v) cfg.provision.groups;
|
||||
groups = lib.attrsets.filterAttrs (_n: v: !isGroupNonOverwritable v) cfg.provision.groups;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -182,8 +185,9 @@ let
|
||||
fi
|
||||
'';
|
||||
|
||||
groupsToCreateAndPopulate =
|
||||
lib.attrsets.filterAttrs (_n: isGroupNonOverwritable) cfg.provision.groups;
|
||||
groupsToCreateAndPopulate = lib.attrsets.filterAttrs (
|
||||
_n: isGroupNonOverwritable
|
||||
) cfg.provision.groups;
|
||||
|
||||
createGroups = ''
|
||||
for group_name in ${lib.strings.concatStringsSep " " (builtins.attrNames groupsToCreateAndPopulate)}
|
||||
@@ -199,9 +203,9 @@ let
|
||||
done
|
||||
'';
|
||||
|
||||
createAndPopulateGroups =
|
||||
lib.concatLines ([ createGroups ]
|
||||
++ (lib.mapAttrsToList populateGroup groupsToCreateAndPopulate));
|
||||
createAndPopulateGroups = lib.concatLines (
|
||||
[ createGroups ] ++ (lib.mapAttrsToList populateGroup groupsToCreateAndPopulate)
|
||||
);
|
||||
|
||||
postStartScript = pkgs.writeShellScript "post-start" ''
|
||||
set -euo pipefail
|
||||
@@ -476,11 +480,9 @@ in
|
||||
config.members = concatLists (
|
||||
flip mapAttrsToList cfg.provision.persons (
|
||||
person: personCfg:
|
||||
optional
|
||||
(
|
||||
optional (
|
||||
personCfg.present && builtins.elem groupSubmod.config._module.args.name personCfg.groups
|
||||
)
|
||||
person
|
||||
) person
|
||||
)
|
||||
);
|
||||
})
|
||||
@@ -683,12 +685,9 @@ in
|
||||
++ entityList "oauth2" cfg.provision.systems.oauth2;
|
||||
|
||||
# Accumulate entities by name. Track corresponding entity types for later duplicate check.
|
||||
entitiesByName = foldl'
|
||||
(
|
||||
entitiesByName = foldl' (
|
||||
acc: { type, name }: acc // { ${name} = (acc.${name} or [ ]) ++ [ type ]; }
|
||||
)
|
||||
{ }
|
||||
entities;
|
||||
) { } entities;
|
||||
|
||||
assertGroupsKnown =
|
||||
opt: groups:
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
redis-sp-api-srv-name = "sp-api";
|
||||
sp-print-api-token = pkgs.writeShellApplication {
|
||||
@@ -76,7 +81,8 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
services.do-agent.enable = if config.selfprivacy.server.provider == "DIGITALOCEAN" then true else false;
|
||||
services.do-agent.enable =
|
||||
if config.selfprivacy.server.provider == "DIGITALOCEAN" then true else false;
|
||||
|
||||
boot.tmp.cleanOnBoot = true;
|
||||
networking = {
|
||||
@@ -84,14 +90,31 @@ in
|
||||
domain = config.selfprivacy.domain;
|
||||
usePredictableInterfaceNames = false;
|
||||
firewall = {
|
||||
allowedTCPPorts = [ 22 25 80 143 443 465 587 993 4443 8443 ];
|
||||
allowedUDPPorts = [ 8443 10000 ];
|
||||
allowedTCPPorts = [
|
||||
22
|
||||
25
|
||||
80
|
||||
143
|
||||
443
|
||||
465
|
||||
587
|
||||
993
|
||||
4443
|
||||
8443
|
||||
];
|
||||
allowedUDPPorts = [
|
||||
8443
|
||||
10000
|
||||
];
|
||||
extraCommands = ''
|
||||
iptables --table nat --append POSTROUTING --out-interface eth0 -j MASQUERADE
|
||||
iptables --append FORWARD --in-interface vpn00 -j ACCEPT
|
||||
'';
|
||||
};
|
||||
nameservers = [ "1.1.1.1" "1.0.0.1" ];
|
||||
nameservers = [
|
||||
"1.1.1.1"
|
||||
"1.0.0.1"
|
||||
];
|
||||
};
|
||||
time.timeZone = config.selfprivacy.timezone;
|
||||
i18n.defaultLocale = "en_GB.UTF-8";
|
||||
@@ -107,8 +130,15 @@ in
|
||||
};
|
||||
services.fail2ban.enable = true;
|
||||
programs.ssh = {
|
||||
pubkeyAcceptedKeyTypes = [ "ssh-ed25519" "ssh-rsa" "ecdsa-sha2-nistp256" ];
|
||||
hostKeyAlgorithms = [ "ssh-ed25519" "ssh-rsa" ];
|
||||
pubkeyAcceptedKeyTypes = [
|
||||
"ssh-ed25519"
|
||||
"ssh-rsa"
|
||||
"ecdsa-sha2-nistp256"
|
||||
];
|
||||
hostKeyAlgorithms = [
|
||||
"ssh-ed25519"
|
||||
"ssh-rsa"
|
||||
];
|
||||
};
|
||||
environment.systemPackages = with pkgs; [
|
||||
git
|
||||
@@ -124,9 +154,9 @@ in
|
||||
"R! /old-root"
|
||||
"d /etc/selfprivacy/dump 0700 0700 selfprivacy-api selfprivacy-api"
|
||||
];
|
||||
system.stateVersion =
|
||||
lib.mkIf (config.selfprivacy.stateVersion != null)
|
||||
config.selfprivacy.stateVersion;
|
||||
system.stateVersion = lib.mkIf (
|
||||
config.selfprivacy.stateVersion != null
|
||||
) config.selfprivacy.stateVersion;
|
||||
system.autoUpgrade = {
|
||||
enable = config.selfprivacy.autoUpgrade.enable;
|
||||
allowReboot = config.selfprivacy.autoUpgrade.allowReboot;
|
||||
@@ -168,7 +198,11 @@ in
|
||||
};
|
||||
nix.settings = {
|
||||
sandbox = true;
|
||||
experimental-features = [ "nix-command" "flakes" "repl-flake" ];
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
"repl-flake"
|
||||
];
|
||||
# auto-optimise-store = true;
|
||||
|
||||
# evaluation restrictions:
|
||||
|
138
flake.nix
138
flake.nix
@@ -2,93 +2,97 @@
|
||||
description = "SelfPrivacy NixOS configuration flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = github:nixos/nixpkgs;
|
||||
nixos-unstable.url = github:nixos/nixpkgs/nixos-unstable;
|
||||
nixpkgs.url = "github:nixos/nixpkgs";
|
||||
nixos-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
selfprivacy-api.url =
|
||||
git+https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git;
|
||||
selfprivacy-api.url = "git+https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git";
|
||||
# make selfprivacy-api use the same shared nixpkgs
|
||||
selfprivacy-api.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, nixos-unstable, selfprivacy-api }: {
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
nixos-unstable,
|
||||
selfprivacy-api,
|
||||
}:
|
||||
{
|
||||
nixosConfigurations-fun =
|
||||
{ hardware-configuration
|
||||
, deployment
|
||||
, userdata
|
||||
, top-level-flake
|
||||
, sp-modules
|
||||
{
|
||||
hardware-configuration,
|
||||
deployment,
|
||||
userdata,
|
||||
top-level-flake,
|
||||
sp-modules,
|
||||
}:
|
||||
{
|
||||
default = nixpkgs.lib.nixosSystem {
|
||||
modules = [
|
||||
modules =
|
||||
[
|
||||
hardware-configuration
|
||||
deployment
|
||||
./configuration.nix
|
||||
./auth/auth.nix
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(
|
||||
_final: prev:
|
||||
{
|
||||
(_final: prev: {
|
||||
inherit (nixos-unstable.legacyPackages.${prev.system})
|
||||
kanidm
|
||||
kanidm-provision
|
||||
;
|
||||
}
|
||||
)
|
||||
})
|
||||
];
|
||||
disabledModules = [ "services/security/kanidm.nix" ];
|
||||
imports = [ ./auth/kanidm.nix ];
|
||||
}
|
||||
selfprivacy-api.nixosModules.default
|
||||
({ pkgs, lib, ... }: {
|
||||
environment.etc = (lib.attrsets.mapAttrs'
|
||||
(name: sp-module: {
|
||||
(
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
environment.etc =
|
||||
(lib.attrsets.mapAttrs' (name: sp-module: {
|
||||
name = "sp-modules/${name}";
|
||||
value.text = import ./lib/meta.nix { inherit pkgs sp-module; };
|
||||
})
|
||||
sp-modules) // {
|
||||
}) sp-modules)
|
||||
// {
|
||||
suggested-sp-modules.text = builtins.toJSON (builtins.attrNames (builtins.readDir ./sp-modules));
|
||||
};
|
||||
})
|
||||
}
|
||||
)
|
||||
(
|
||||
let
|
||||
deepFilter = ref: attrset:
|
||||
builtins.foldl'
|
||||
(acc: key:
|
||||
deepFilter =
|
||||
ref: attrset:
|
||||
builtins.foldl' (
|
||||
acc: key:
|
||||
if builtins.hasAttr key ref then
|
||||
let
|
||||
value = attrset.${key};
|
||||
refValue = ref.${key};
|
||||
in
|
||||
acc // {
|
||||
acc
|
||||
// {
|
||||
${key} =
|
||||
if builtins.isAttrs value && builtins.isAttrs refValue then
|
||||
deepFilter refValue value
|
||||
else
|
||||
value;
|
||||
if builtins.isAttrs value && builtins.isAttrs refValue then deepFilter refValue value else value;
|
||||
}
|
||||
else
|
||||
acc
|
||||
)
|
||||
{ }
|
||||
(builtins.attrNames attrset);
|
||||
) { } (builtins.attrNames attrset);
|
||||
in
|
||||
{ options, ... }: {
|
||||
{ options, ... }:
|
||||
{
|
||||
# pass userdata (parsed from JSON) options to selfprivacy module
|
||||
selfprivacy = deepFilter options.selfprivacy userdata;
|
||||
|
||||
# embed top-level flake source folder into the build
|
||||
environment.etc."selfprivacy/nixos-config-source".source =
|
||||
top-level-flake;
|
||||
environment.etc."selfprivacy/nixos-config-source".source = top-level-flake;
|
||||
|
||||
# for running "nix search nixpkgs", "nix shell nixpkgs#PKG... etc
|
||||
nix.registry.nixpkgs.flake = nixpkgs;
|
||||
|
||||
# embed commit sha1 for `nixos-version --configuration-revision`
|
||||
system.configurationRevision = self.rev
|
||||
or "@${self.lastModifiedDate}"; # for development
|
||||
system.configurationRevision = self.rev or "@${self.lastModifiedDate}"; # for development
|
||||
# TODO assertion to forbid dirty builds caused by top-level-flake
|
||||
|
||||
# reset contents of /etc/nixos to match running NixOS generation
|
||||
@@ -102,46 +106,50 @@
|
||||
++
|
||||
# add SP modules, but constrain available config attributes for each
|
||||
# (TODO revise evaluation performance of the code below)
|
||||
nixpkgs.lib.attrsets.mapAttrsToList
|
||||
(name: sp-module: args@{ config, pkgs, ... }:
|
||||
nixpkgs.lib.attrsets.mapAttrsToList (
|
||||
name: sp-module:
|
||||
args@{ config, pkgs, ... }:
|
||||
let
|
||||
lib = nixpkgs.lib;
|
||||
configPathsNeeded = sp-module.configPathsNeeded or
|
||||
(abort "allowed config paths not set for module \"${name}\"");
|
||||
constrainConfigArgs = args'@{ pkgs, ... }: args' // {
|
||||
configPathsNeeded =
|
||||
sp-module.configPathsNeeded or (abort "allowed config paths not set for module \"${name}\"");
|
||||
constrainConfigArgs =
|
||||
args'@{ pkgs, ... }:
|
||||
args'
|
||||
// {
|
||||
config =
|
||||
# TODO use lib.attrsets.mergeAttrsList from nixpkgs 23.05
|
||||
(builtins.foldl' lib.attrsets.recursiveUpdate { }
|
||||
(map
|
||||
(p: lib.attrsets.setAttrByPath p
|
||||
(lib.attrsets.getAttrFromPath p config))
|
||||
configPathsNeeded
|
||||
(
|
||||
builtins.foldl' lib.attrsets.recursiveUpdate { } (
|
||||
map (p: lib.attrsets.setAttrByPath p (lib.attrsets.getAttrFromPath p config)) configPathsNeeded
|
||||
)
|
||||
);
|
||||
};
|
||||
constrainImportsArgsRecursive = lib.attrsets.mapAttrsRecursive
|
||||
(p: v:
|
||||
constrainImportsArgsRecursive = lib.attrsets.mapAttrsRecursive (
|
||||
p: v:
|
||||
# TODO traverse only imports and imports of imports, etc
|
||||
# without traversing all attributes
|
||||
if lib.lists.last p == "imports"
|
||||
then
|
||||
map
|
||||
(m:
|
||||
(args'@{ pkgs, ... }: constrainImportsArgsRecursive
|
||||
(if builtins.isPath m
|
||||
then import m (constrainConfigArgs args')
|
||||
if lib.lists.last p == "imports" then
|
||||
map (
|
||||
m:
|
||||
(
|
||||
args'@{ pkgs, ... }:
|
||||
constrainImportsArgsRecursive (
|
||||
if builtins.isPath m then
|
||||
import m (constrainConfigArgs args')
|
||||
else if builtins.isFunction m then
|
||||
m (constrainConfigArgs args')
|
||||
else
|
||||
if builtins.isFunction m
|
||||
then m (constrainConfigArgs args')
|
||||
else m))
|
||||
m
|
||||
)
|
||||
)
|
||||
) v
|
||||
else
|
||||
v
|
||||
else v);
|
||||
);
|
||||
in
|
||||
constrainImportsArgsRecursive
|
||||
(sp-module.nixosModules.default (constrainConfigArgs args))
|
||||
)
|
||||
sp-modules;
|
||||
constrainImportsArgsRecursive (sp-module.nixosModules.default (constrainConfigArgs args))
|
||||
) sp-modules;
|
||||
};
|
||||
};
|
||||
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixpkgs-fmt;
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.selfprivacy;
|
||||
dnsCredentialsTemplates = {
|
||||
@@ -27,7 +32,11 @@ in
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
email = "${cfg.username}@${cfg.domain}";
|
||||
server = if cfg.dns.useStagingACME then "https://acme-staging-v02.api.letsencrypt.org/directory" else "https://acme-v02.api.letsencrypt.org/directory";
|
||||
server =
|
||||
if cfg.dns.useStagingACME then
|
||||
"https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
else
|
||||
"https://acme-v02.api.letsencrypt.org/directory";
|
||||
reloadServices = [ "nginx" ];
|
||||
dnsResolver = "8.8.8.8:53";
|
||||
};
|
||||
@@ -38,7 +47,9 @@ in
|
||||
dnsProvider = lib.strings.toLower cfg.dns.provider;
|
||||
credentialsFile = acme-env-filepath;
|
||||
dnsPropagationCheck =
|
||||
! ((lib.elem cfg.dns.provider dnsPropagationCheckExceptions) || cfg.dns.forceDisableDnsPropagationCheck);
|
||||
!(
|
||||
(lib.elem cfg.dns.provider dnsPropagationCheckExceptions) || cfg.dns.forceDisableDnsPropagationCheck
|
||||
);
|
||||
};
|
||||
"root-${cfg.domain}" = {
|
||||
domain = cfg.domain;
|
||||
@@ -51,7 +62,10 @@ in
|
||||
before = [ "acme-${cfg.domain}.service" ];
|
||||
requiredBy = [ "acme-${cfg.domain}.service" ];
|
||||
serviceConfig.Type = "oneshot";
|
||||
path = with pkgs; [ coreutils jq ];
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
jq
|
||||
];
|
||||
script = ''
|
||||
set -o nounset
|
||||
|
||||
|
18
lib/meta.nix
18
lib/meta.nix
@@ -1,18 +1,28 @@
|
||||
{ sp-module, pkgs }:
|
||||
let
|
||||
lib = pkgs.lib;
|
||||
options = (pkgs.lib.evalModules { modules = [{ _module.check = false; } sp-module.nixosModules.default]; }).options;
|
||||
options =
|
||||
(pkgs.lib.evalModules {
|
||||
modules = [
|
||||
{ _module.check = false; }
|
||||
sp-module.nixosModules.default
|
||||
];
|
||||
}).options;
|
||||
# Transform a Nix option to a JSON structure with metadata
|
||||
optionToMeta = (name: option: {
|
||||
optionToMeta = (
|
||||
name: option: {
|
||||
name = name;
|
||||
description = if builtins.hasAttr "description" option then option.description else null;
|
||||
loc = option.loc;
|
||||
meta = if builtins.hasAttr "meta" option then option.meta else null;
|
||||
default = if builtins.hasAttr "default" option then option.default else null;
|
||||
});
|
||||
}
|
||||
);
|
||||
in
|
||||
builtins.toJSON ({
|
||||
meta = if builtins.hasAttr "meta" sp-module then sp-module.meta { inherit lib; } else null;
|
||||
configPathsNeeded = sp-module.configPathsNeeded;
|
||||
options = pkgs.lib.mapAttrs optionToMeta (builtins.head (lib.mapAttrsToList (name: value: value) options.selfprivacy.modules));
|
||||
options = pkgs.lib.mapAttrs optionToMeta (
|
||||
builtins.head (lib.mapAttrsToList (name: value: value) options.selfprivacy.modules)
|
||||
);
|
||||
})
|
||||
|
@@ -1,6 +1,4 @@
|
||||
system:
|
||||
_final: _prev:
|
||||
{
|
||||
system: _final: _prev: {
|
||||
# Here is a template to bring a specific package from a given nixpkgs commit:
|
||||
# PACKAGE_NAME = (builtins.getFlake "github:nixos/nixpkgs/NIXPKGS_COMMIT_SHA1").legacyPackages.${system}.PACKAGE_NAME;
|
||||
# Substitute `PACKAGE_NAME` and `NIXPKGS_COMMIT_SHA1` accordingly.
|
||||
|
@@ -1,22 +1,34 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
sp = config.selfprivacy;
|
||||
pleroma_location =
|
||||
if lib.attrsets.hasAttr "pleroma" sp.modules && lib.attrsets.hasAttr "location" sp.modules.pleroma
|
||||
then sp.modules.pleroma.location
|
||||
else null;
|
||||
if
|
||||
lib.attrsets.hasAttr "pleroma" sp.modules && lib.attrsets.hasAttr "location" sp.modules.pleroma
|
||||
then
|
||||
sp.modules.pleroma.location
|
||||
else
|
||||
null;
|
||||
postgres_location =
|
||||
if lib.attrsets.hasAttr "postgresql" sp && lib.attrsets.hasAttr "location" sp.postgresql
|
||||
then sp.postgresql.location
|
||||
else null;
|
||||
if lib.attrsets.hasAttr "postgresql" sp && lib.attrsets.hasAttr "location" sp.postgresql then
|
||||
sp.postgresql.location
|
||||
else
|
||||
null;
|
||||
# Priority: postgresql > pleroma
|
||||
location = if postgres_location != null then postgres_location else pleroma_location;
|
||||
# Active if there is a location
|
||||
enable = location != null;
|
||||
pleroma_enabled =
|
||||
if lib.attrsets.hasAttr "pleroma" sp.modules && lib.attrsets.hasAttr "enable" sp.modules.pleroma
|
||||
then sp.modules.pleroma.enable
|
||||
else false;
|
||||
if
|
||||
lib.attrsets.hasAttr "pleroma" sp.modules && lib.attrsets.hasAttr "enable" sp.modules.pleroma
|
||||
then
|
||||
sp.modules.pleroma.enable
|
||||
else
|
||||
false;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.postgresqlUpgrade12to16;
|
||||
@@ -62,12 +67,12 @@ in
|
||||
|
||||
ExecStartPre =
|
||||
# Stop Pleroma only if pleromaEnabled is true
|
||||
optional cfg.pleromaEnabled "${pkgs.writeShellScript "postgresql-upgrade12to16-pre.sh" ''
|
||||
optional cfg.pleromaEnabled
|
||||
"${pkgs.writeShellScript "postgresql-upgrade12to16-pre.sh" ''
|
||||
if [ -d "${cfg.dataDir12}" ] && [ ! -d "${cfg.dataDir16}" ]; then
|
||||
${pkgs.systemd}/bin/systemctl stop pleroma.service
|
||||
fi
|
||||
''
|
||||
}";
|
||||
''}";
|
||||
|
||||
ExecStart = "${pkgs.writeShellScript "postgresql-upgrade12to16.sh" ''
|
||||
set -e
|
||||
@@ -119,15 +124,13 @@ in
|
||||
''}";
|
||||
|
||||
# Start Pleroma only if pleromaEnabled is true
|
||||
ExecStartPost =
|
||||
optional cfg.pleromaEnabled "${pkgs.writeShellScript "postgresql-upgrade12to16-post.sh" ''
|
||||
ExecStartPost = optional cfg.pleromaEnabled "${pkgs.writeShellScript "postgresql-upgrade12to16-post.sh" ''
|
||||
if test -e "${cfg.dataDir16}/.sp_migrated"; then
|
||||
${pkgs.systemd}/bin/systemctl start --no-block pleroma.service
|
||||
|
||||
rm -f "${cfg.dataDir16}/.sp_migrated"
|
||||
fi
|
||||
''
|
||||
}";
|
||||
''}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -5,12 +5,7 @@ in
|
||||
# FIXME do we really want to delete passwords on module deactivation!?
|
||||
{
|
||||
config = lib.mkIf (!sp.modules.bitwarden.enable) {
|
||||
system.activationScripts.bitwarden =
|
||||
lib.trivial.warn
|
||||
(
|
||||
"bitwarden service is disabled, ${bitwarden-env} will be removed!"
|
||||
)
|
||||
''
|
||||
system.activationScripts.bitwarden = lib.trivial.warn ("bitwarden service is disabled, ${bitwarden-env} will be removed!") ''
|
||||
rm -f -v ${bitwarden-env}
|
||||
'';
|
||||
};
|
||||
|
@@ -1,5 +1,4 @@
|
||||
config:
|
||||
{
|
||||
config: {
|
||||
sp = config.selfprivacy;
|
||||
bitwarden-env = "/var/lib/bitwarden/.env";
|
||||
}
|
||||
|
@@ -1,12 +1,19 @@
|
||||
{
|
||||
description = "PoC SP module for Bitwarden password management solution";
|
||||
|
||||
outputs = { self }: {
|
||||
nixosModules.default = _:
|
||||
{ imports = [ ./module.nix ./cleanup-module.nix ]; };
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = _: {
|
||||
imports = [
|
||||
./module.nix
|
||||
./cleanup-module.nix
|
||||
];
|
||||
};
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "bitwarden";
|
||||
name = "Bitwarden";
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
secrets-filepath = "/etc/selfprivacy/secrets.json";
|
||||
backup-dir = "/var/lib/bitwarden/backup";
|
||||
@@ -7,28 +12,34 @@ let
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.bitwarden = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Vaultwarden";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Vaultwarden location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "password";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -36,31 +47,37 @@ in
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
signupsAllowed = (lib.mkOption {
|
||||
signupsAllowed =
|
||||
(lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Allow new user signups";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 1;
|
||||
};
|
||||
};
|
||||
sendsAllowed = (lib.mkOption {
|
||||
sendsAllowed =
|
||||
(lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Allow users to use Bitwarden Send";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 2;
|
||||
};
|
||||
};
|
||||
emergencyAccessAllowed = (lib.mkOption {
|
||||
emergencyAccessAllowed =
|
||||
(lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Allow users to enable Emergency Access";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 3;
|
||||
@@ -118,7 +135,10 @@ in
|
||||
before = [ "vaultwarden.service" ];
|
||||
requiredBy = [ "vaultwarden.service" ];
|
||||
serviceConfig.Type = "oneshot";
|
||||
path = with pkgs; [ coreutils jq ];
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
jq
|
||||
];
|
||||
script = ''
|
||||
set -o nounset
|
||||
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "PoC SP module for Gitea forge service";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "gitea";
|
||||
name = "Forgejo";
|
||||
|
@@ -1,10 +1,12 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
sp = config.selfprivacy;
|
||||
stateDir =
|
||||
if sp.useBinds
|
||||
then "/volumes/${cfg.location}/gitea"
|
||||
else "/var/lib/gitea";
|
||||
stateDir = if sp.useBinds then "/volumes/${cfg.location}/gitea" else "/var/lib/gitea";
|
||||
cfg = sp.modules.gitea;
|
||||
themes = [
|
||||
"forgejo-auto"
|
||||
@@ -18,8 +20,7 @@ let
|
||||
oauthClientID = "forgejo";
|
||||
auth-passthru = config.selfprivacy.passthru.auth;
|
||||
oauth2-provider-name = auth-passthru.oauth2-provider-name;
|
||||
redirect-uri =
|
||||
"https://${cfg.subdomain}.${sp.domain}/user/oauth2/${oauth2-provider-name}/callback";
|
||||
redirect-uri = "https://${cfg.subdomain}.${sp.domain}/user/oauth2/${oauth2-provider-name}/callback";
|
||||
oauthDiscoveryURL = auth-passthru.oauth2-discovery-url oauthClientID;
|
||||
|
||||
# SelfPrivacy uses SP Module ID to identify the group!
|
||||
@@ -30,35 +31,39 @@ let
|
||||
linuxGroupOfService = "gitea";
|
||||
forgejoPackage = pkgs.forgejo;
|
||||
|
||||
serviceAccountTokenFP =
|
||||
auth-passthru.mkServiceAccountTokenFP linuxGroupOfService;
|
||||
oauthClientSecretFP =
|
||||
auth-passthru.mkOAuth2ClientSecretFP linuxGroupOfService;
|
||||
serviceAccountTokenFP = auth-passthru.mkServiceAccountTokenFP linuxGroupOfService;
|
||||
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP linuxGroupOfService;
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.gitea = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Forgejo";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Forgejo location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "git";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -66,82 +71,98 @@ in
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
appName = (lib.mkOption {
|
||||
appName =
|
||||
(lib.mkOption {
|
||||
default = "SelfPrivacy git Service";
|
||||
type = lib.types.str;
|
||||
description = "The name displayed in the web interface";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "string";
|
||||
weight = 1;
|
||||
};
|
||||
};
|
||||
enableLfs = (lib.mkOption {
|
||||
enableLfs =
|
||||
(lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Git LFS";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 2;
|
||||
};
|
||||
};
|
||||
forcePrivate = (lib.mkOption {
|
||||
forcePrivate =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Force all new repositories to be private";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 3;
|
||||
};
|
||||
};
|
||||
disableRegistration = (lib.mkOption {
|
||||
disableRegistration =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Disable registration of new users";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 4;
|
||||
};
|
||||
};
|
||||
requireSigninView = (lib.mkOption {
|
||||
requireSigninView =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Force users to log in to view any page";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 5;
|
||||
};
|
||||
};
|
||||
defaultTheme = (lib.mkOption {
|
||||
defaultTheme =
|
||||
(lib.mkOption {
|
||||
default = "forgejo-auto";
|
||||
type = lib.types.enum themes;
|
||||
description = "Default theme";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enum";
|
||||
options = themes;
|
||||
weight = 6;
|
||||
};
|
||||
};
|
||||
enableSso = (lib.mkOption {
|
||||
enableSso =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Single Sign-On";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 7;
|
||||
};
|
||||
};
|
||||
debug = (lib.mkOption {
|
||||
debug =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable debug logging";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 8;
|
||||
@@ -149,13 +170,13 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enableSso -> sp.sso.enable;
|
||||
message =
|
||||
"SSO cannot be enabled for Forgejo when SSO is disabled globally.";
|
||||
message = "SSO cannot be enabled for Forgejo when SSO is disabled globally.";
|
||||
}
|
||||
];
|
||||
fileSystems = lib.mkIf sp.useBinds {
|
||||
@@ -393,8 +414,7 @@ in
|
||||
displayName = "Forgejo";
|
||||
subdomain = cfg.subdomain;
|
||||
isTokenNeeded = true;
|
||||
originLanding =
|
||||
"https://${cfg.subdomain}.${sp.domain}/user/login?redirect_to=%2f";
|
||||
originLanding = "https://${cfg.subdomain}.${sp.domain}/user/login?redirect_to=%2f";
|
||||
originUrl = redirect-uri;
|
||||
clientSystemdUnits = [ "forgejo.service" ];
|
||||
enablePkce = lib.versionAtLeast forgejoPackage.version "8.0";
|
||||
@@ -406,5 +426,6 @@ in
|
||||
};
|
||||
};
|
||||
})
|
||||
]);
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "PoC SP module for Jitsi Meet video conferences server";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "jitsi-meet";
|
||||
name = "JitsiMeet";
|
||||
|
@@ -5,20 +5,24 @@ let
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.jitsi-meet = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable JitsiMeet";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "meet";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -26,11 +30,13 @@ in
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
appName = (lib.mkOption {
|
||||
appName =
|
||||
(lib.mkOption {
|
||||
default = "Jitsi Meet";
|
||||
type = lib.types.str;
|
||||
description = "The name displayed in the web interface";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "string";
|
||||
weight = 1;
|
||||
@@ -43,7 +49,9 @@ in
|
||||
(_: prev: {
|
||||
# We disable E2E for clients below
|
||||
jitsi-meet = prev.jitsi-meet.overrideAttrs (old: {
|
||||
meta = old.meta // { knownVulnerabilities = [ ]; };
|
||||
meta = old.meta // {
|
||||
knownVulnerabilities = [ ];
|
||||
};
|
||||
});
|
||||
})
|
||||
];
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "PoC SP module for Prometheus-based monitoring";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "monitoring";
|
||||
name = "Prometheus";
|
||||
|
@@ -4,19 +4,23 @@ let
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.monitoring = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable monitoring service";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Monitoring data location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
@@ -54,15 +58,19 @@ in
|
||||
scrapeConfigs = [
|
||||
{
|
||||
job_name = "node-exporter";
|
||||
static_configs = [{
|
||||
static_configs = [
|
||||
{
|
||||
targets = [ "127.0.0.1:9002" ];
|
||||
}];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
job_name = "cadvisor";
|
||||
static_configs = [{
|
||||
static_configs = [
|
||||
{
|
||||
targets = [ "127.0.0.1:9003" ];
|
||||
}];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "PoC SP module for Mumble conferences server";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "mumble";
|
||||
name = "Mumble";
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
domain = config.selfprivacy.domain;
|
||||
sp = config.selfprivacy;
|
||||
@@ -6,20 +11,24 @@ let
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.mumble = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Mumble";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "mumble";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -27,29 +36,35 @@ in
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
appName = (lib.mkOption {
|
||||
appName =
|
||||
(lib.mkOption {
|
||||
default = "SelfPrivacy Mumble Service";
|
||||
type = lib.types.str;
|
||||
description = "The name of your Mumble server";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "string";
|
||||
weight = 1;
|
||||
};
|
||||
};
|
||||
welcomeText = (lib.mkOption {
|
||||
welcomeText =
|
||||
(lib.mkOption {
|
||||
default = "Welcome to my Mumble server!";
|
||||
type = lib.types.str;
|
||||
description = "Welcome message";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "string";
|
||||
weight = 2;
|
||||
@@ -79,7 +94,9 @@ in
|
||||
};
|
||||
systemd = {
|
||||
services = {
|
||||
murmur = { serviceConfig.Slice = "mumble.slice"; };
|
||||
murmur = {
|
||||
serviceConfig.Slice = "mumble.slice";
|
||||
};
|
||||
murmur-ensure-folder-ownership = {
|
||||
description = "Ensure murmur folder ownership";
|
||||
before = [ "murmur.service" ];
|
||||
|
@@ -13,8 +13,8 @@ in
|
||||
system.activationScripts.nextcloudSecrets =
|
||||
lib.trivial.warn
|
||||
(
|
||||
"nextcloud service is disabled, " +
|
||||
"${override-config-fp}, ${db-pass-filepath} and ${admin-pass-filepath} will be removed!"
|
||||
"nextcloud service is disabled, "
|
||||
+ "${override-config-fp}, ${db-pass-filepath} and ${admin-pass-filepath} will be removed!"
|
||||
)
|
||||
''
|
||||
rm -f -v ${db-pass-filepath}
|
||||
|
@@ -1,12 +1,19 @@
|
||||
{
|
||||
description = "PoC SP module for nextcloud";
|
||||
|
||||
outputs = { self }: {
|
||||
nixosModules.default = _:
|
||||
{ imports = [ ./module.nix ./cleanup-module.nix ]; };
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = _: {
|
||||
imports = [
|
||||
./module.nix
|
||||
./cleanup-module.nix
|
||||
];
|
||||
};
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "nextcloud";
|
||||
name = "Nextcloud";
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (import ./common.nix config)
|
||||
admin-pass-filepath
|
||||
@@ -27,40 +32,43 @@ let
|
||||
usersGroup = "sp.${oauthClientID}.users";
|
||||
wildcardGroup = "sp.${oauthClientID}.*";
|
||||
|
||||
serviceAccountTokenFP =
|
||||
auth-passthru.mkServiceAccountTokenFP linuxUserOfService;
|
||||
oauthClientSecretFP =
|
||||
auth-passthru.mkOAuth2ClientSecretFP linuxUserOfService;
|
||||
serviceAccountTokenFP = auth-passthru.mkServiceAccountTokenFP linuxUserOfService;
|
||||
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP linuxUserOfService;
|
||||
|
||||
updater-page-substitute =
|
||||
pkgs.runCommandNoCC "nextcloud-updater-page-substitute" { } ''
|
||||
updater-page-substitute = pkgs.runCommandNoCC "nextcloud-updater-page-substitute" { } ''
|
||||
install -m644 ${./updater.html} -DT $out/index.html
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.nextcloud = with lib; {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Nextcloud";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Nextcloud location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "cloud";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -68,51 +76,61 @@ in
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
enableImagemagick = (lib.mkOption {
|
||||
enableImagemagick =
|
||||
(lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable ImageMagick";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 1;
|
||||
};
|
||||
};
|
||||
enableSso = (lib.mkOption {
|
||||
enableSso =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Single Sign-On";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 2;
|
||||
};
|
||||
};
|
||||
enableSambaFeatures = (lib.mkOption {
|
||||
enableSambaFeatures =
|
||||
(lib.mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable support for Samba/CIFS features";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 3;
|
||||
};
|
||||
};
|
||||
debug = (lib.mkOption {
|
||||
debug =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable debug logging";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 4;
|
||||
};
|
||||
};
|
||||
disableMaintenanceModeAtStart = (lib.mkOption {
|
||||
disableMaintenanceModeAtStart =
|
||||
(lib.mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Disable maintenance mode at Nextcloud service startup";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 5;
|
||||
@@ -121,13 +139,13 @@ in
|
||||
};
|
||||
|
||||
# config = lib.mkIf sp.modules.nextcloud.enable
|
||||
config = lib.mkIf sp.modules.nextcloud.enable (lib.mkMerge [
|
||||
config = lib.mkIf sp.modules.nextcloud.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enableSso -> sp.sso.enable;
|
||||
message =
|
||||
"SSO cannot be enabled for Nextcloud when SSO is disabled globally.";
|
||||
message = "SSO cannot be enabled for Nextcloud when SSO is disabled globally.";
|
||||
}
|
||||
];
|
||||
fileSystems = lib.mkIf sp.useBinds {
|
||||
@@ -144,8 +162,7 @@ in
|
||||
};
|
||||
|
||||
# for ExecStartPost script to have access to /run/keys/*
|
||||
users.groups.keys.members =
|
||||
lib.mkIf is-auth-enabled [ linuxUserOfService ];
|
||||
users.groups.keys.members = lib.mkIf is-auth-enabled [ linuxUserOfService ];
|
||||
|
||||
# not needed, due to turnOffCertCheck=1 in used_ldap
|
||||
# users.groups.${config.security.acme.certs.${domain}.group}.members =
|
||||
@@ -165,7 +182,10 @@ in
|
||||
before = [ "nextcloud-setup.service" ];
|
||||
requiredBy = [ "nextcloud-setup.service" ];
|
||||
serviceConfig.Type = "oneshot";
|
||||
path = with pkgs; [ coreutils jq ];
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
jq
|
||||
];
|
||||
script = ''
|
||||
databasePassword=$(jq -re '.modules.nextcloud.databasePassword' ${secrets-filepath})
|
||||
adminPassword=$(jq -re '.modules.nextcloud.adminPassword' ${secrets-filepath})
|
||||
@@ -202,10 +222,12 @@ in
|
||||
|
||||
configureRedis = true;
|
||||
|
||||
settings = {
|
||||
settings =
|
||||
{
|
||||
# further forces Nextcloud to use HTTPS
|
||||
overwriteprotocol = "https";
|
||||
} // lib.attrsets.optionalAttrs is-auth-enabled {
|
||||
}
|
||||
// lib.attrsets.optionalAttrs is-auth-enabled {
|
||||
loglevel = 0;
|
||||
# log_type = "file";
|
||||
social_login_auto_redirect = false;
|
||||
@@ -262,7 +284,10 @@ in
|
||||
# only apply cifs-utils package to this module
|
||||
services.phpfpm.pools.nextcloud.phpEnv.PATH =
|
||||
lib.mkForce "${pkgs.samba}/bin:${pkgs.cifs-utils}/bin:/run/wrappers/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin";
|
||||
systemd.services.nextcloud-cron.path = [ pkgs.samba pkgs.cifs-utils ];
|
||||
systemd.services.nextcloud-cron.path = [
|
||||
pkgs.samba
|
||||
pkgs.cifs-utils
|
||||
];
|
||||
})
|
||||
# the following part is active only when "auth" module is enabled
|
||||
(lib.mkIf is-auth-enabled {
|
||||
@@ -390,15 +415,20 @@ in
|
||||
subdomain = cfg.subdomain;
|
||||
isTokenNeeded = true;
|
||||
originUrl = "https://${cfg.subdomain}.${domain}/apps/user_oidc/code";
|
||||
originLanding =
|
||||
"https://${cfg.subdomain}.${domain}/apps/user_oidc/login/1";
|
||||
originLanding = "https://${cfg.subdomain}.${domain}/apps/user_oidc/login/1";
|
||||
useShortPreferredUsername = true;
|
||||
clientSystemdUnits =
|
||||
[ "nextcloud-setup.service" "phpfpm-nextcloud.service" ];
|
||||
clientSystemdUnits = [
|
||||
"nextcloud-setup.service"
|
||||
"phpfpm-nextcloud.service"
|
||||
];
|
||||
enablePkce = true;
|
||||
linuxUserOfClient = linuxUserOfService;
|
||||
linuxGroupOfClient = linuxGroupOfService;
|
||||
scopeMaps.${usersGroup} = [ "email" "openid" "profile" ];
|
||||
scopeMaps.${usersGroup} = [
|
||||
"email"
|
||||
"openid"
|
||||
"profile"
|
||||
];
|
||||
claimMaps.groups = {
|
||||
joinType = "array";
|
||||
valuesByGroup.${adminsGroup} = [ "admin" ];
|
||||
@@ -413,5 +443,6 @@ in
|
||||
'';
|
||||
};
|
||||
})
|
||||
]);
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "PoC SP module for OpenConnect VPN server (ocserv)";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "ocserv";
|
||||
name = "OpenConnect VPN";
|
||||
|
@@ -7,11 +7,13 @@ let
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.ocserv = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
@@ -66,7 +68,10 @@ in
|
||||
systemd = {
|
||||
services = {
|
||||
ocserv = {
|
||||
unitConfig.ConditionPathExists = [ cert key ];
|
||||
unitConfig.ConditionPathExists = [
|
||||
cert
|
||||
key
|
||||
];
|
||||
serviceConfig.Slice = "ocserv.slice";
|
||||
};
|
||||
};
|
||||
|
@@ -5,12 +5,7 @@ in
|
||||
# FIXME do we really want to delete passwords on module deactivation!?
|
||||
{
|
||||
config = lib.mkIf (!sp.modules.pleroma.enable) {
|
||||
system.activationScripts.pleroma =
|
||||
lib.trivial.warn
|
||||
(
|
||||
"pleroma service is disabled, ${secrets-exs} will be removed!"
|
||||
)
|
||||
''
|
||||
system.activationScripts.pleroma = lib.trivial.warn ("pleroma service is disabled, ${secrets-exs} will be removed!") ''
|
||||
rm -f -v ${secrets-exs}
|
||||
'';
|
||||
};
|
||||
|
@@ -1,5 +1,4 @@
|
||||
config:
|
||||
{
|
||||
config: {
|
||||
sp = config.selfprivacy;
|
||||
secrets-exs = "/var/lib/pleroma/secrets.exs";
|
||||
}
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "PoC SP module for Pleroma lightweight fediverse server";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "pleroma";
|
||||
name = "Pleroma";
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
secrets-filepath = "/etc/selfprivacy/secrets.json";
|
||||
cfg = config.selfprivacy.modules.pleroma;
|
||||
@@ -6,28 +11,34 @@ let
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.pleroma = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "social";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -55,10 +66,9 @@ in
|
||||
user = "pleroma";
|
||||
group = "pleroma";
|
||||
configs = [
|
||||
(builtins.replaceStrings
|
||||
[ "$DOMAIN" "$LUSER" ]
|
||||
[ sp.domain sp.username ]
|
||||
(builtins.readFile ./config.exs.in))
|
||||
(builtins.replaceStrings [ "$DOMAIN" "$LUSER" ] [ sp.domain sp.username ] (
|
||||
builtins.readFile ./config.exs.in
|
||||
))
|
||||
];
|
||||
};
|
||||
postgresql = {
|
||||
@@ -94,7 +104,10 @@ in
|
||||
before = [ "pleroma.service" ];
|
||||
requiredBy = [ "pleroma.service" ];
|
||||
serviceConfig.Type = "oneshot";
|
||||
path = with pkgs; [ coreutils jq ];
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
jq
|
||||
];
|
||||
script = ''
|
||||
set -o nounset
|
||||
|
||||
|
@@ -1,11 +1,14 @@
|
||||
{
|
||||
description = "Roundcube is a web-based email client.";
|
||||
|
||||
outputs = { self }: {
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "roundcube";
|
||||
name = "Roundcube";
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
domain = config.selfprivacy.domain;
|
||||
cfg = config.selfprivacy.modules.roundcube;
|
||||
@@ -16,31 +21,32 @@ let
|
||||
usersGroup = "sp.${sp-module-name}.users";
|
||||
|
||||
oauth-donor = config.selfprivacy.passthru.mailserver;
|
||||
oauthClientSecretFP =
|
||||
auth-passthru.mkOAuth2ClientSecretFP linuxGroupOfService;
|
||||
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP linuxGroupOfService;
|
||||
# copy client secret from mailserver
|
||||
kanidmExecStartPreScriptRoot = pkgs.writeShellScript
|
||||
"${sp-module-name}-kanidm-ExecStartPre-root-script.sh"
|
||||
''
|
||||
kanidmExecStartPreScriptRoot = pkgs.writeShellScript "${sp-module-name}-kanidm-ExecStartPre-root-script.sh" ''
|
||||
install -v -m640 -o kanidm -g ${linuxGroupOfService} ${oauth-donor.oauth-client-secret-fp} ${oauthClientSecretFP}
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.roundcube = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "roundcube";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
@@ -48,11 +54,13 @@ in
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
enableSso = (lib.mkOption {
|
||||
enableSso =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Single Sign-On";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 1;
|
||||
@@ -60,13 +68,13 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enableSso -> config.selfprivacy.sso.enable;
|
||||
message =
|
||||
"SSO cannot be enabled for Roundcube when SSO is disabled globally.";
|
||||
message = "SSO cannot be enabled for Roundcube when SSO is disabled globally.";
|
||||
}
|
||||
];
|
||||
services.roundcube = {
|
||||
@@ -127,7 +135,10 @@ in
|
||||
originUrl = "https://${cfg.subdomain}.${domain}/index.php/login/oauth";
|
||||
originLanding = "https://${cfg.subdomain}.${domain}/";
|
||||
useShortPreferredUsername = false;
|
||||
clientSystemdUnits = [ "dovecot2.service" "phpfpm-roundcube.service" ];
|
||||
clientSystemdUnits = [
|
||||
"dovecot2.service"
|
||||
"phpfpm-roundcube.service"
|
||||
];
|
||||
enablePkce = false;
|
||||
linuxUserOfClient = linuxUserOfService;
|
||||
linuxGroupOfClient = linuxGroupOfService;
|
||||
@@ -140,5 +151,6 @@ in
|
||||
};
|
||||
};
|
||||
})
|
||||
]);
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@@ -1,8 +1,14 @@
|
||||
{ mailserver-service-account-name
|
||||
, mailserver-service-account-token-name
|
||||
, mailserver-service-account-token-fp
|
||||
{
|
||||
mailserver-service-account-name,
|
||||
mailserver-service-account-token-name,
|
||||
mailserver-service-account-token-fp,
|
||||
}:
|
||||
{ config, lib, pkgs, ... }@nixos-args:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}@nixos-args:
|
||||
let
|
||||
inherit (import ./common.nix nixos-args)
|
||||
appendSetting
|
||||
@@ -17,9 +23,7 @@ let
|
||||
keysPath = auth-passthru.keys-path;
|
||||
|
||||
# create service account token, needed for LDAP
|
||||
kanidmExecStartPostScript = pkgs.writeShellScript
|
||||
"mailserver-kanidm-ExecStartPost-script.sh"
|
||||
''
|
||||
kanidmExecStartPostScript = pkgs.writeShellScript "mailserver-kanidm-ExecStartPost-script.sh" ''
|
||||
export HOME=$RUNTIME_DIRECTORY/client_home
|
||||
readonly KANIDM="${pkgs.kanidm}/bin/kanidm"
|
||||
|
||||
@@ -66,10 +70,15 @@ let
|
||||
'';
|
||||
|
||||
ldapConfFile = "/run/${runtime-folder}/dovecot-ldap.conf.ext";
|
||||
mkLdapSearchScope = scope: (
|
||||
if scope == "sub" then "subtree"
|
||||
else if scope == "one" then "onelevel"
|
||||
else scope
|
||||
mkLdapSearchScope =
|
||||
scope:
|
||||
(
|
||||
if scope == "sub" then
|
||||
"subtree"
|
||||
else if scope == "one" then
|
||||
"onelevel"
|
||||
else
|
||||
scope
|
||||
);
|
||||
dovecot-ldap-config = pkgs.writeTextFile {
|
||||
name = "dovecot-ldap.conf.ext.template";
|
||||
@@ -101,10 +110,8 @@ let
|
||||
destination = ldapConfFile;
|
||||
};
|
||||
oauth-client-id = "mailserver";
|
||||
oauth-client-secret-fp =
|
||||
"${keysPath}/${group}/kanidm-oauth-client-secret";
|
||||
oauth-secret-ExecStartPreScript = pkgs.writeShellScript
|
||||
"${oauth-client-id}-kanidm-ExecStartPre-script.sh" ''
|
||||
oauth-client-secret-fp = "${keysPath}/${group}/kanidm-oauth-client-secret";
|
||||
oauth-secret-ExecStartPreScript = pkgs.writeShellScript "${oauth-client-id}-kanidm-ExecStartPre-script.sh" ''
|
||||
set -o xtrace
|
||||
[ -f "${oauth-client-secret-fp}" ] || \
|
||||
"${lib.getExe pkgs.openssl}" rand -base64 32 | tr "\n:@/+=" "012345" > "${oauth-client-secret-fp}"
|
||||
@@ -122,8 +129,8 @@ let
|
||||
openid_configuration_url = ${auth-passthru.oauth2-discovery-url oauth-client-id}
|
||||
debug = "no"
|
||||
'';
|
||||
prefix = ''introspection_url = "'' +
|
||||
(auth-passthru.oauth2-introspection-url-prefix oauth-client-id);
|
||||
prefix =
|
||||
''introspection_url = "'' + (auth-passthru.oauth2-introspection-url-prefix oauth-client-id);
|
||||
suffix = auth-passthru.oauth2-introspection-url-postfix + ''"'';
|
||||
passwordFile = oauth-client-secret-fp;
|
||||
destination = dovecot-oauth2-conf-fp;
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }@nixos-args:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}@nixos-args:
|
||||
let
|
||||
inherit (import ./common.nix nixos-args)
|
||||
appendSetting
|
||||
@@ -9,8 +14,7 @@ let
|
||||
cfg = config.mailserver;
|
||||
|
||||
ldapSenderLoginMapFile = "/run/postfix/ldap-sender-login-map.cf";
|
||||
submissionOptions.smtpd_sender_login_maps =
|
||||
lib.mkForce "hash:/etc/postfix/vaccounts,ldap:${ldapSenderLoginMapFile}";
|
||||
submissionOptions.smtpd_sender_login_maps = lib.mkForce "hash:/etc/postfix/vaccounts,ldap:${ldapSenderLoginMapFile}";
|
||||
commonLdapConfig = ''
|
||||
server_host = ${lib.concatStringsSep " " cfg.ldap.uris}
|
||||
start_tls = ${if cfg.ldap.startTls then "yes" else "no"}
|
||||
@@ -61,8 +65,10 @@ in
|
||||
${appendPwdInVirtualMailboxMap}
|
||||
${appendPwdInSenderLoginMap}
|
||||
'';
|
||||
restartTriggers =
|
||||
[ appendPwdInVirtualMailboxMap appendPwdInSenderLoginMap ];
|
||||
restartTriggers = [
|
||||
appendPwdInVirtualMailboxMap
|
||||
appendPwdInSenderLoginMap
|
||||
];
|
||||
wants = [ auth-passthru.oauth2-systemd-service ];
|
||||
after = [ auth-passthru.oauth2-systemd-service ];
|
||||
};
|
||||
|
@@ -4,11 +4,17 @@ rec {
|
||||
domain = config.selfprivacy.domain;
|
||||
group = "dovecot2";
|
||||
is-auth-enabled =
|
||||
config.selfprivacy.modules.simple-nixos-mailserver.enableSso
|
||||
&& config.selfprivacy.sso.enable;
|
||||
config.selfprivacy.modules.simple-nixos-mailserver.enableSso && config.selfprivacy.sso.enable;
|
||||
|
||||
appendSetting =
|
||||
{ name, file, prefix, suffix ? "", passwordFile, destination }:
|
||||
{
|
||||
name,
|
||||
file,
|
||||
prefix,
|
||||
suffix ? "",
|
||||
passwordFile,
|
||||
destination,
|
||||
}:
|
||||
pkgs.writeScript "append-ldap-bind-pwd-in-${name}" ''
|
||||
#!${pkgs.stdenv.shell}
|
||||
set -euo pipefail
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }@nixos-args:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}@nixos-args:
|
||||
let
|
||||
sp = config.selfprivacy;
|
||||
|
||||
@@ -11,26 +16,22 @@ let
|
||||
mailserver-service-account = {
|
||||
mailserver-service-account-name = "sp.mailserver.service-account";
|
||||
mailserver-service-account-token-name = "mailserver-service-account-token";
|
||||
mailserver-service-account-token-fp =
|
||||
"/run/keys/${group}/kanidm-service-account-token"; # FIXME sync with auth module
|
||||
mailserver-service-account-token-fp = "/run/keys/${group}/kanidm-service-account-token"; # FIXME sync with auth module
|
||||
};
|
||||
in
|
||||
lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
lib.mkIf sp.modules.simple-nixos-mailserver.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
config.selfprivacy.modules.simple-nixos-mailserver.enableSso
|
||||
-> config.selfprivacy.sso.enable;
|
||||
message =
|
||||
"SSO cannot be enabled for Mailserver when SSO is disabled globally.";
|
||||
config.selfprivacy.modules.simple-nixos-mailserver.enableSso -> config.selfprivacy.sso.enable;
|
||||
message = "SSO cannot be enabled for Mailserver when SSO is disabled globally.";
|
||||
}
|
||||
];
|
||||
fileSystems = lib.mkIf sp.useBinds
|
||||
{
|
||||
fileSystems = lib.mkIf sp.useBinds {
|
||||
"/var/vmail" = {
|
||||
device =
|
||||
"/volumes/${sp.modules.simple-nixos-mailserver.location}/vmail";
|
||||
device = "/volumes/${sp.modules.simple-nixos-mailserver.location}/vmail";
|
||||
options = [
|
||||
"bind"
|
||||
"x-systemd.required-by=postfix.service"
|
||||
@@ -38,8 +39,7 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
];
|
||||
};
|
||||
"/var/sieve" = {
|
||||
device =
|
||||
"/volumes/${sp.modules.simple-nixos-mailserver.location}/sieve";
|
||||
device = "/volumes/${sp.modules.simple-nixos-mailserver.location}/sieve";
|
||||
options = [
|
||||
"bind"
|
||||
"x-systemd.required-by=dovecot2.service"
|
||||
@@ -54,7 +54,11 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
};
|
||||
};
|
||||
|
||||
users.groups.acmereceivers.members = [ "dovecot2" "postfix" "virtualMail" ];
|
||||
users.groups.acmereceivers.members = [
|
||||
"dovecot2"
|
||||
"postfix"
|
||||
"virtualMail"
|
||||
];
|
||||
|
||||
mailserver = {
|
||||
enable = true;
|
||||
@@ -64,7 +68,8 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
|
||||
# A list of all login accounts. To create the password hashes, use
|
||||
# mkpasswd -m sha-512 "super secret password"
|
||||
loginAccounts = ({
|
||||
loginAccounts = (
|
||||
{
|
||||
"${sp.username}@${sp.domain}" = {
|
||||
hashedPassword = sp.hashedMasterPassword;
|
||||
sieveScript = ''
|
||||
@@ -76,8 +81,9 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
}
|
||||
'';
|
||||
};
|
||||
} // builtins.listToAttrs (builtins.map
|
||||
(user: {
|
||||
}
|
||||
// builtins.listToAttrs (
|
||||
builtins.map (user: {
|
||||
name = "${user.username}@${sp.domain}";
|
||||
value = {
|
||||
hashedPassword = user.hashedPassword;
|
||||
@@ -90,8 +96,9 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
}
|
||||
'';
|
||||
};
|
||||
})
|
||||
sp.users));
|
||||
}) sp.users
|
||||
)
|
||||
);
|
||||
|
||||
extraVirtualAliases = {
|
||||
"admin@${sp.domain}" = "${sp.username}@${sp.domain}";
|
||||
@@ -144,8 +151,7 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
# bind.dn = "uid=mail,ou=persons," + ldap_base_dn;
|
||||
bind.dn = "dn=token";
|
||||
# TODO change in this file should trigger system restart dovecot
|
||||
bind.passwordFile =
|
||||
mailserver-service-account.mailserver-service-account-token-fp;
|
||||
bind.passwordFile = mailserver-service-account.mailserver-service-account-token-fp;
|
||||
|
||||
# searchBase = "ou=persons," + ldap_base_dn;
|
||||
searchBase = auth-passthru.ldap-base-dn; # TODO refine this
|
||||
@@ -155,7 +161,7 @@ lib.mkIf sp.modules.simple-nixos-mailserver.enable (lib.mkMerge [
|
||||
};
|
||||
};
|
||||
})
|
||||
(lib.mkIf is-auth-enabled
|
||||
(import ./auth-dovecot.nix mailserver-service-account nixos-args))
|
||||
(lib.mkIf is-auth-enabled (import ./auth-dovecot.nix mailserver-service-account nixos-args))
|
||||
(lib.mkIf is-auth-enabled (import ./auth-postfix.nix nixos-args))
|
||||
])
|
||||
]
|
||||
)
|
||||
|
@@ -1,10 +1,11 @@
|
||||
{
|
||||
description = "PoC SP module for the simple-nixos-mailserver";
|
||||
|
||||
inputs.mailserver.url =
|
||||
gitlab:simple-nixos-mailserver/nixos-mailserver;
|
||||
inputs.mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver";
|
||||
|
||||
outputs = { self, mailserver }: {
|
||||
outputs =
|
||||
{ self, mailserver }:
|
||||
{
|
||||
nixosModules.default = _: {
|
||||
imports = [
|
||||
mailserver.nixosModules.default
|
||||
@@ -12,9 +13,10 @@
|
||||
./config.nix
|
||||
];
|
||||
};
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "simple-nixos-mailserver";
|
||||
name = "Mail Server";
|
||||
|
@@ -1,28 +1,34 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
options.selfprivacy.modules.simple-nixos-mailserver = {
|
||||
enable = (lib.mkOption {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable mail server";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Location";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
enableSso = (lib.mkOption {
|
||||
enableSso =
|
||||
(lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Enable SSO for mail server";
|
||||
}) // {
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
|
@@ -5,11 +5,14 @@
|
||||
nixpkgs-24-11.url = "github:NixOS/nixpkgs/nixos-24.11";
|
||||
};
|
||||
|
||||
outputs = {nixpkgs-24-11, ...}: {
|
||||
outputs =
|
||||
{ nixpkgs-24-11, ... }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix nixpkgs-24-11.legacyPackages.x86_64-linux;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = {lib, ...}: {
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "vikunja";
|
||||
name = "Vikunja";
|
||||
|
@@ -1,8 +1,10 @@
|
||||
latestPkgs: {
|
||||
latestPkgs:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
}:
|
||||
let
|
||||
sp = config.selfprivacy;
|
||||
cfg = sp.modules.vikunja;
|
||||
oauthClientID = "vikunja";
|
||||
@@ -13,18 +15,16 @@ latestPkgs: {
|
||||
# SelfPrivacy uses SP Module ID to identify the group!
|
||||
usersGroup = "sp.vikunja.users";
|
||||
|
||||
oauthClientSecretFP =
|
||||
auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
|
||||
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
|
||||
|
||||
vikunjaPackage = latestPkgs.vikunja.overrideAttrs (old: {
|
||||
doCheck = false; # Tests are slow.
|
||||
patches =
|
||||
(old.patches or [])
|
||||
++ [
|
||||
patches = (old.patches or [ ]) ++ [
|
||||
./load-client-secret-from-env.patch
|
||||
];
|
||||
});
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.vikunja = {
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
@@ -63,9 +63,7 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
lib.mkIf cfg.enable
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = sp.sso.enable;
|
||||
@@ -186,7 +184,11 @@ in {
|
||||
|
||||
ProtectSystem = "strict";
|
||||
|
||||
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
@@ -195,7 +197,16 @@ in {
|
||||
|
||||
RemoveIPC = true;
|
||||
|
||||
SystemCallFilter = ["@system-service" "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"];
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@cpu-emulation"
|
||||
"~@debug"
|
||||
"~@keyring"
|
||||
"~@memlock"
|
||||
"~@obsolete"
|
||||
"~@privileged"
|
||||
"~@setuid"
|
||||
];
|
||||
};
|
||||
environment.SP_VIKUNJA_CLIENT_SECRET_PATH = "%d/oauth2-secret";
|
||||
};
|
||||
|
12
users.nix
12
users.nix
@@ -6,21 +6,23 @@ in
|
||||
users = {
|
||||
mutableUsers = false;
|
||||
allowNoPasswordLogin = true;
|
||||
users = {
|
||||
users =
|
||||
{
|
||||
"${cfg.username}" = {
|
||||
isNormalUser = true;
|
||||
hashedPassword = cfg.hashedMasterPassword;
|
||||
openssh.authorizedKeys.keys = cfg.sshKeys;
|
||||
};
|
||||
} // builtins.listToAttrs (builtins.map
|
||||
(user: {
|
||||
}
|
||||
// builtins.listToAttrs (
|
||||
builtins.map (user: {
|
||||
name = "${user.username}";
|
||||
value = {
|
||||
isNormalUser = true;
|
||||
hashedPassword = user.hashedPassword;
|
||||
openssh.authorizedKeys.keys = (if user ? sshKeys then user.sshKeys else [ ]);
|
||||
};
|
||||
})
|
||||
cfg.users);
|
||||
}) cfg.users
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@@ -3,13 +3,13 @@ let
|
||||
cfg = config.selfprivacy;
|
||||
in
|
||||
{
|
||||
fileSystems = builtins.listToAttrs (builtins.map
|
||||
(volume: {
|
||||
fileSystems = builtins.listToAttrs (
|
||||
builtins.map (volume: {
|
||||
name = "${volume.mountPoint}";
|
||||
value = {
|
||||
device = "${volume.device}";
|
||||
fsType = "${volume.fsType}";
|
||||
};
|
||||
})
|
||||
cfg.volumes);
|
||||
}) cfg.volumes
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user