231 lines
6.0 KiB
Nix
231 lines
6.0 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
sp = config.selfprivacy;
|
|
cfg = sp.modules.vikunja;
|
|
oauthClientID = "vikunja";
|
|
auth-passthru = config.selfprivacy.passthru.auth;
|
|
oauth2-provider-name = auth-passthru.oauth2-provider-name;
|
|
oauthDiscoveryURL = auth-passthru.oauth2-discovery-url oauthClientID;
|
|
|
|
# SelfPrivacy uses SP Module ID to identify the group!
|
|
usersGroup = "sp.vikunja.users";
|
|
|
|
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
|
|
|
|
vikunjaPackage = pkgs.vikunja.overrideAttrs (old: {
|
|
doCheck = false; # Tests are slow.
|
|
patches = (old.patches or [ ]) ++ [
|
|
./load-client-secret-from-env.patch
|
|
];
|
|
});
|
|
in
|
|
{
|
|
options.selfprivacy.modules.vikunja = {
|
|
enable =
|
|
(lib.mkOption {
|
|
default = false;
|
|
type = lib.types.bool;
|
|
description = "Enable Vikunja";
|
|
})
|
|
// {
|
|
meta = {
|
|
type = "enable";
|
|
};
|
|
};
|
|
location =
|
|
(lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Vikunja location";
|
|
})
|
|
// {
|
|
meta = {
|
|
type = "location";
|
|
};
|
|
};
|
|
subdomain =
|
|
(lib.mkOption {
|
|
default = "vikunja";
|
|
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";
|
|
regex = "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
|
weight = 0;
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = sp.sso.enable;
|
|
message = "Vikunja cannot be enabled when SSO is disabled.";
|
|
}
|
|
];
|
|
|
|
fileSystems = lib.mkIf sp.useBinds {
|
|
"/var/lib/vikunja" = {
|
|
device = "/volumes/${cfg.location}/vikunja";
|
|
options = [ "bind" ];
|
|
};
|
|
};
|
|
|
|
users = {
|
|
users.vikunja = {
|
|
isSystemUser = true;
|
|
group = "vikunja";
|
|
};
|
|
groups.vikunja = { };
|
|
};
|
|
|
|
services.postgresql = {
|
|
ensureDatabases = [ "vikunja" ];
|
|
ensureUsers = [
|
|
{
|
|
name = "vikunja";
|
|
ensureDBOwnership = true;
|
|
}
|
|
];
|
|
};
|
|
|
|
services.vikunja = {
|
|
enable = true;
|
|
package = vikunjaPackage;
|
|
frontendScheme = "https";
|
|
frontendHostname = "${cfg.subdomain}.${sp.domain}";
|
|
port = 4835;
|
|
|
|
database = {
|
|
type = "postgres";
|
|
host = "/run/postgresql";
|
|
};
|
|
|
|
settings = {
|
|
service = {
|
|
enableregistration = false;
|
|
enabletotp = false;
|
|
enableuserdeletion = true;
|
|
};
|
|
|
|
auth = {
|
|
local.enabled = false;
|
|
openid = {
|
|
enabled = true;
|
|
providers = [
|
|
{
|
|
name = oauth2-provider-name;
|
|
authurl = lib.strings.removeSuffix "/.well-known/openid-configuration" oauthDiscoveryURL;
|
|
clientid = oauthClientID;
|
|
clientsecret = ""; # There's patch for our Vikunja to make it load client secret from environment variable.
|
|
scope = "openid profile email";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
|
useACMEHost = sp.domain;
|
|
forceSSL = true;
|
|
extraConfig = ''
|
|
add_header Strict-Transport-Security $hsts_header;
|
|
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
|
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
|
add_header X-Frame-Options DENY;
|
|
add_header X-Content-Type-Options nosniff;
|
|
add_header X-XSS-Protection "1; mode=block";
|
|
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
|
'';
|
|
locations = {
|
|
"/" = {
|
|
proxyPass = "http://127.0.0.1:4835";
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd = {
|
|
services.vikunja = {
|
|
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/vikunja";
|
|
serviceConfig = {
|
|
Slice = "vikunja.slice";
|
|
LoadCredential = "oauth2-secret:${oauthClientSecretFP}";
|
|
DynamicUser = lib.mkForce false;
|
|
User = "vikunja";
|
|
Group = "vikunja";
|
|
|
|
AmbientCapabilities = [ "" ];
|
|
|
|
LockPersonality = true;
|
|
MemoryDenyWriteExecute = true;
|
|
NoNewPrivileges = true;
|
|
PrivateDevices = true;
|
|
PrivateTmp = true;
|
|
|
|
PrivateUsers = true;
|
|
ProcSubset = "pid";
|
|
ProtectClock = true;
|
|
ProtectControlGroups = true;
|
|
ProtectHome = true;
|
|
ProtectHostname = true;
|
|
ProtectKernelLogs = true;
|
|
ProtectKernelModules = true;
|
|
ProtectKernelTunables = true;
|
|
|
|
ProtectProc = "invisible";
|
|
|
|
ProtectSystem = "strict";
|
|
|
|
RestrictAddressFamilies = [
|
|
"AF_UNIX"
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
];
|
|
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
RestrictSUIDSGID = true;
|
|
SystemCallArchitectures = "native";
|
|
|
|
RemoveIPC = true;
|
|
|
|
SystemCallFilter = [
|
|
"@system-service"
|
|
"~@cpu-emulation"
|
|
"~@debug"
|
|
"~@keyring"
|
|
"~@memlock"
|
|
"~@obsolete"
|
|
"~@privileged"
|
|
"~@setuid"
|
|
];
|
|
};
|
|
environment.SP_VIKUNJA_CLIENT_SECRET_PATH = "%d/oauth2-secret";
|
|
};
|
|
slices.vikunja = {
|
|
description = "Vikunja service slice";
|
|
};
|
|
};
|
|
|
|
selfprivacy.auth.clients.${oauthClientID} = {
|
|
inherit usersGroup;
|
|
subdomain = cfg.subdomain;
|
|
isTokenNeeded = false;
|
|
originLanding = "https://${cfg.subdomain}.${sp.domain}/";
|
|
originUrl = "https://${cfg.subdomain}.${sp.domain}/auth/openid/${lib.strings.toLower oauth2-provider-name}";
|
|
clientSystemdUnits = [ "vikunja.service" ];
|
|
enablePkce = false;
|
|
linuxUserOfClient = "vikunja";
|
|
linuxGroupOfClient = "vikunja";
|
|
};
|
|
};
|
|
}
|