switched modules to flakes
This commit is contained in:
parent
aca834a717
commit
469038e980
93
flake.lock
generated
Normal file
93
flake.lock
generated
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nix-index-database": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703387252,
|
||||||
|
"narHash": "sha256-XKJqGj0BaEn/zyctEnkgVIh6Ba1rgTRc+UBi9EU8Y54=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "nix-index-database",
|
||||||
|
"rev": "f4340c1a42c38d79293ba69bfd839fbd6268a538",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "nix-index-database",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixos-modules": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703426109,
|
||||||
|
"narHash": "sha256-/ujCgLJUq+iMgrvMVj58uvXan/nKvG0SeNVVrsvzJHk=",
|
||||||
|
"owner": "SuperSandro2000",
|
||||||
|
"repo": "nixos-modules",
|
||||||
|
"rev": "caa008d22e663c6190ffe12286566b2e87f357e4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "SuperSandro2000",
|
||||||
|
"repo": "nixos-modules",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703255338,
|
||||||
|
"narHash": "sha256-Z6wfYJQKmDN9xciTwU3cOiOk+NElxdZwy/FiHctCzjU=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6df37dc6a77654682fe9f071c62b4242b5342e04",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nix-index-database": "nix-index-database",
|
||||||
|
"nixos-modules": "nixos-modules",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"sops-nix": "sops-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703387502,
|
||||||
|
"narHash": "sha256-JnWuQmyanPtF8c5yAEFXVWzaIlMxA3EAZCh8XNvnVqE=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "e523e89763ff45f0a6cf15bcb1092636b1da9ed3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
37
flake.nix
37
flake.nix
@ -4,9 +4,17 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
nixos-modules = {
|
||||||
|
url = "github:SuperSandro2000/nixos-modules";
|
||||||
|
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
sops-nix = {
|
sops-nix = {
|
||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs = {
|
||||||
|
nixpkgs.follows = "nixpkgs";
|
||||||
|
nixpkgs-stable.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
nix-index-database = {
|
nix-index-database = {
|
||||||
@ -15,33 +23,36 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { nixpkgs, nix-index-database, sops-nix, ... }: {
|
outputs = { nixpkgs, nixos-modules, nix-index-database, sops-nix, ... }:
|
||||||
src = builtins.filterSource (path: type: type == "directory" || lib.hasSuffix ".nix" (baseNameOf path)) ./.;
|
let
|
||||||
ls = dir: lib.attrNames (builtins.readDir (src + "/${dir}"));
|
inherit (nixpkgs) lib;
|
||||||
fileList = dir: map (file: ./. + "/${dir}/${file}") (ls dir);
|
in {
|
||||||
nixosConfigurations = let
|
nixosConfigurations = let
|
||||||
constructSystem = {
|
constructSystem = {
|
||||||
hostname,
|
hostname,
|
||||||
system ? "x86_64-linux",
|
system ? "x86_64-linux",
|
||||||
modules ? [],
|
modules ? [],
|
||||||
users ? [],
|
users ? [],
|
||||||
}: nixpkgs.lib.nixosSystem {
|
}: lib.nixosSystem {
|
||||||
inherit system hostname;
|
inherit system;
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
|
nixos-modules.nixosModule
|
||||||
sops-nix.nixosModules.sops
|
sops-nix.nixosModules.sops
|
||||||
nix-index-database.nixosModules.nix-index
|
nix-index-database.nixosModules.nix-index
|
||||||
./system/programs.nix
|
./systems/programs.nix
|
||||||
./system/configuration.nix
|
./systems/configuration.nix
|
||||||
./system/${hostname}/configuration.nix
|
./systems/${hostname}/configuration.nix
|
||||||
] ++ fileList "modules" ++ modules ++ map (user: ./users/${user}/default.nix ) users;
|
] ++ modules ++ map(user: ./users/${user}) users;
|
||||||
|
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
photon = constructSystem {
|
photon = constructSystem {
|
||||||
hostname = "photon"
|
hostname = "photon";
|
||||||
};
|
};
|
||||||
|
|
||||||
palatine-hill = constructSystem {
|
palatine-hill = constructSystem {
|
||||||
hostname = "palatine-hill"
|
hostname = "palatine-hill";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{ lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
mkUserGroupOption = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
description = lib.mdDoc "Restrict logins to users in this group";
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
mkOpinionatedOption = text: lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = config.opinionatedDefaults;
|
|
||||||
description = lib.mdDoc "Whether to ${text}.";
|
|
||||||
};
|
|
||||||
|
|
||||||
mkRecursiveDefault = lib.mapAttrsRecursive (_: lib.mkDefault);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
{ lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# taken from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/misc/nix-daemon.nix#L828-L832
|
|
||||||
# a builder can run code for `gcc.arch` and inferior architectures
|
|
||||||
gcc-system-features = arch: [ "gccarch-${arch}" ]
|
|
||||||
++ map (x: "gccarch-${x}") lib.systems.architectures.inferiors.${arch};
|
|
||||||
}
|
|
10
lib/ssh.nix
10
lib/ssh.nix
@ -1,10 +0,0 @@
|
|||||||
_:
|
|
||||||
|
|
||||||
{
|
|
||||||
mkPubKey = name: type: publicKey: {
|
|
||||||
"${name}-${type}" = {
|
|
||||||
extraHostNames = [ name ];
|
|
||||||
publicKey = "${type} ${publicKey}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.security.acme;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.security.acme.staging = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
If set to true, use Let's Encrypt's staging environment instead of the production one.
|
|
||||||
The staging environment has much higher rate limits but does not generate fully signed certificates.
|
|
||||||
This is great for testing when the normla rate limit is hit fast and impacts other people on the same IP.
|
|
||||||
See <literal>https://letsencrypt.org/docs/staging-environment</literal> for more detail.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.staging {
|
|
||||||
security.acme.server = "https://acme-staging-v02.api.letsencrypt.org/directory";
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.boot;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
boot = {
|
|
||||||
default = lib.mkOpinionatedOption "enable the boot builder";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
cfg = lib.mkIf cfg.default {
|
|
||||||
supportedFilesystems = [ "zfs" ];
|
|
||||||
tmp.useTmpfs = true;
|
|
||||||
kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
|
|
||||||
kernelParams = [ "kvm-amd" "nordrand" ];
|
|
||||||
zfs = {
|
|
||||||
enableUnstable = true;
|
|
||||||
devNodes = "/dev/disk/by-id/";
|
|
||||||
forceImportRoot = true;
|
|
||||||
};
|
|
||||||
loader = {
|
|
||||||
efi = {
|
|
||||||
canTouchEfiVariables = false;
|
|
||||||
efiSysMountPoint = "/boot/efis/nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0W178262L-part1";
|
|
||||||
};
|
|
||||||
generationsDir.copyKernels = true;
|
|
||||||
grub = {
|
|
||||||
enable = true;
|
|
||||||
copyKernels = true;
|
|
||||||
zfsSupport = true;
|
|
||||||
efiSupport = true;
|
|
||||||
efiInstallAsRemovable = true;
|
|
||||||
fsIdentifier = "uuid";
|
|
||||||
device = "nodev";
|
|
||||||
extraInstallCommands = "[ ! -e /boot/efis/nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0W178262L-part1/EFI ] || cp -r /boot/efis/nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0W178262L-part1/EFI/* /boot/efis/nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0W178262L-part1";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.virtualisation;
|
|
||||||
cfgd = cfg.docker;
|
|
||||||
cfgp = cfg.podman;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.virtualisation = {
|
|
||||||
docker = {
|
|
||||||
aggresiveAutoPrune = libS.mkOpinionatedOption "configure aggresive auto prune which removes everything unreferenced by running containers. This includes named volumes and mounts should be used instead";
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended and maintenance reducing default settings";
|
|
||||||
};
|
|
||||||
|
|
||||||
podman.recommendedDefaults = libS.mkOpinionatedOption "set recommended and maintenance reducing default settings";
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
virtualisation = {
|
|
||||||
containers.registries.search = lib.mkIf cfgp.recommendedDefaults [
|
|
||||||
"docker.io"
|
|
||||||
"quay.io"
|
|
||||||
"ghcr.io"
|
|
||||||
"gcr.io"
|
|
||||||
];
|
|
||||||
|
|
||||||
docker = {
|
|
||||||
daemon.settings = let
|
|
||||||
useIPTables = !config.networking.nftables.enable;
|
|
||||||
in lib.mkIf cfgd.recommendedDefaults {
|
|
||||||
fixed-cidr-v6 = "fd00::/80"; # TODO: is this a good idea for all networks?
|
|
||||||
iptables = useIPTables;
|
|
||||||
ip6tables = useIPTables;
|
|
||||||
ipv6 = true;
|
|
||||||
# userland proxy is slow, does not give back ports and if iptables/nftables is avaible just worsefgd.aggresiveAutoPrune
|
|
||||||
userland-proxy = false;
|
|
||||||
};
|
|
||||||
autoPrune = lib.mkIf cfgd.aggresiveAutoPrune {
|
|
||||||
enable = true;
|
|
||||||
flags = [
|
|
||||||
"--all"
|
|
||||||
"--external"
|
|
||||||
"--force"
|
|
||||||
"--volumes"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
podman = {
|
|
||||||
autoPrune.enable = lib.mkIf cfgp.recommendedDefaults true;
|
|
||||||
defaultNetwork.settings.dns_enabled = lib.mkIf cfgp.recommendedDefaults true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.gitea;
|
|
||||||
cfgl = cfg.ldap;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.gitea = {
|
|
||||||
# based on https://github.com/majewsky/nixos-modules/blob/master/gitea.nix
|
|
||||||
ldap = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc "login via ldap");
|
|
||||||
|
|
||||||
adminGroup = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
example = "gitea-admins";
|
|
||||||
description = lib.mdDoc "Name of the ldap group that grants admin access in gitea.";
|
|
||||||
};
|
|
||||||
|
|
||||||
bindPasswordFile = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
example = "/var/lib/secrets/bind-password";
|
|
||||||
description = lib.mdDoc "Path to a file containing the bind password.";
|
|
||||||
};
|
|
||||||
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
|
|
||||||
options =
|
|
||||||
let
|
|
||||||
mkOptStr = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
id = lib.mkOption {
|
|
||||||
type = lib.types.ints.unsigned;
|
|
||||||
default = 1;
|
|
||||||
};
|
|
||||||
name = mkOptStr;
|
|
||||||
security-protocol = mkOptStr;
|
|
||||||
host = mkOptStr;
|
|
||||||
port = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr port;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
bind-dn = mkOptStr;
|
|
||||||
bind-password = mkOptStr;
|
|
||||||
user-search-base = mkOptStr;
|
|
||||||
user-filter = mkOptStr;
|
|
||||||
admin-filter = mkOptStr;
|
|
||||||
username-attribute = mkOptStr;
|
|
||||||
firstname-attribute = mkOptStr;
|
|
||||||
surname-attribute = mkOptStr;
|
|
||||||
email-attribute = mkOptStr;
|
|
||||||
public-ssh-key-attribute = mkOptStr;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended, secure default settings";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.gitea = lib.mkIf (cfg.enable && cfgl.enable) {
|
|
||||||
ldap.options = {
|
|
||||||
name = "ldap";
|
|
||||||
security-protocol = "LDAPS";
|
|
||||||
host = ldap.domainName;
|
|
||||||
inherit (ldap) port;
|
|
||||||
bind-dn = ldap.bindDN;
|
|
||||||
bind-password = "$(cat ${cfgl.bindPasswordFile})";
|
|
||||||
user-search-base = ldap.userBaseDN;
|
|
||||||
user-filter = ldap.searchFilterWithGroupFilter cfgl.userGroup (ldap.userFilter "%[1]s");
|
|
||||||
admin-filter = ldap.groupFilter cfgl.adminGroup;
|
|
||||||
username-attribute = ldap.userField;
|
|
||||||
firstname-attribute = ldap.givenNameField;
|
|
||||||
surname-attribute = ldap.surnameField;
|
|
||||||
email-attribute = ldap.mailField;
|
|
||||||
public-ssh-key-attribute = ldap.sshPublicKeyField;
|
|
||||||
};
|
|
||||||
settings = lib.mkIf cfg.recommendedDefaults (libS.modules.mkRecursiveDefault {
|
|
||||||
cors = {
|
|
||||||
ALLOW_DOMAIN = cfg.settings.server.DOMAIN;
|
|
||||||
ENABLED = true;
|
|
||||||
SCHEME = "https";
|
|
||||||
};
|
|
||||||
cron.ENABLED = true;
|
|
||||||
"cron.resync_all_sshkeys".ENABLED = true;
|
|
||||||
"cron.resync_all_hooks".ENABLED = true;
|
|
||||||
other.SHOW_FOOTER_VERSION = false;
|
|
||||||
repository.ACCESS_CONTROL_ALLOW_ORIGIN = cfg.settings.server.DOMAIN;
|
|
||||||
"repository.signing".DEFAULT_TRUST_MODEL = "committer";
|
|
||||||
security.DISABLE_GIT_HOOKS = true;
|
|
||||||
server = {
|
|
||||||
ENABLE_GZIP = true;
|
|
||||||
ROOT_URL = "https://${cfg.settings.server.DOMAIN}/";
|
|
||||||
SSH_SERVER_CIPHERS = "chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com";
|
|
||||||
SSH_SERVER_KEY_EXCHANGES = "curve25519-sha256@libssh.org, ecdh-sha2-nistp521, ecdh-sha2-nistp384, ecdh-sha2-nistp256, diffie-hellman-group14-sha1";
|
|
||||||
SSH_SERVER_MACS = "hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1";
|
|
||||||
};
|
|
||||||
session = {
|
|
||||||
COOKIE_SECURE = true;
|
|
||||||
PROVIDER = "db";
|
|
||||||
SAME_SITE = "strict";
|
|
||||||
SESSION_LIFE_TIME = 28 * 86400; # 28 days
|
|
||||||
};
|
|
||||||
"ssh.minimum_key_sizes" = {
|
|
||||||
ECDSA = -1;
|
|
||||||
RSA = 4095;
|
|
||||||
};
|
|
||||||
time.DEFAULT_UI_LOCATION = config.time.timeZone;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
config.systemd.services = lib.mkIf (cfg.enable && cfgl.enable) {
|
|
||||||
gitea.preStart =
|
|
||||||
let
|
|
||||||
exe = lib.getExe cfg.package;
|
|
||||||
# allow executing shell after the --bind-password argument to e.g. cat a password file
|
|
||||||
formatOption = key: value: "--${key} ${if key == "bind-password" then value else lib.escapeShellArg value}";
|
|
||||||
ldapOptionsStr = opt: lib.concatStringsSep " " (lib.mapAttrsToList formatOption opt);
|
|
||||||
commonArgs = "--attributes-in-bind --synchronize-users";
|
|
||||||
in
|
|
||||||
lib.mkAfter ''
|
|
||||||
if ${exe} admin auth list | grep -q ${cfgl.options.name}; then
|
|
||||||
${exe} admin auth update-ldap ${commonArgs} ${ldapOptionsStr cfgl.options}
|
|
||||||
else
|
|
||||||
${exe} admin auth add-ldap ${commonArgs} ${ldapOptionsStr (lib.filterAttrs (name: _: name != "id") cfgl.options)}
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.portunus.seedSettings.groups = [
|
|
||||||
(lib.mkIf (cfgl.adminGroup != null) {
|
|
||||||
long_name = "Gitea Administrators";
|
|
||||||
name = cfgl.adminGroup;
|
|
||||||
permissions = { };
|
|
||||||
})
|
|
||||||
(lib.mkIf (cfgl.userGroup != null) {
|
|
||||||
long_name = "Gitea Users";
|
|
||||||
name = cfgl.userGroup;
|
|
||||||
permissions = { };
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.grafana;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.grafana = {
|
|
||||||
configureNginx = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "Wether to configure Nginx.";
|
|
||||||
};
|
|
||||||
|
|
||||||
oauth = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc ''login only via OAuth2'');
|
|
||||||
enableViewerRole = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "Wether to enable the fallback Viewer role when users do not have the user- or adminGroup.";
|
|
||||||
};
|
|
||||||
adminGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
};
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended and secure default settings";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
# the default values are hardcoded instead of using options. because I couldn't figure out how to extract them from the freeform type
|
|
||||||
assertions = lib.mkIf cfg.enable [
|
|
||||||
{
|
|
||||||
assertion = cfg.oauth.enable -> cfg.settings."auth.generic_oauth".client_secret != null;
|
|
||||||
message = ''
|
|
||||||
Setting services.grafana.oauth.enable to true requires to set services.grafana.settings."auth.generic_oauth".client_secret.
|
|
||||||
Use this `$__file{/path/to/some/secret}` syntax to reference secrets securely.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = cfg.settings.security.secret_key != "SW2YcwTIb9zpOOhoPsMm";
|
|
||||||
message = "services.grafana.settings.security.secret_key must be changed from it's insecure, default value!";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = cfg.settings.security.admin_password != "admin";
|
|
||||||
message = "services.grafana.settings.security.admin_password must be changed from it's insecure, default value!";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
services.grafana.settings = lib.mkMerge [
|
|
||||||
(lib.mkIf (cfg.enable && cfg.recommendedDefaults) (libS.modules.mkRecursiveDefault {
|
|
||||||
# no analytics, sorry, not sorry
|
|
||||||
analytics = {
|
|
||||||
# TODO: drop after https://github.com/NixOS/nixpkgs/pull/240323 is merged
|
|
||||||
check_for_updates = false;
|
|
||||||
feedback_links_enabled = false;
|
|
||||||
reporting_enabled = false;
|
|
||||||
};
|
|
||||||
log.level = "warn";
|
|
||||||
security = {
|
|
||||||
cookie_secure = true;
|
|
||||||
content_security_policy = true;
|
|
||||||
strict_transport_security = true;
|
|
||||||
};
|
|
||||||
server = {
|
|
||||||
enable_gzip = true;
|
|
||||||
root_url = "https://${cfg.settings.server.domain}";
|
|
||||||
};
|
|
||||||
}))
|
|
||||||
|
|
||||||
(lib.mkIf (cfg.enable && cfg.oauth.enable) {
|
|
||||||
"auth.generic_oauth" = let
|
|
||||||
inherit (config.services.dex.settings) issuer;
|
|
||||||
in {
|
|
||||||
enabled = true;
|
|
||||||
allow_assign_grafana_admin = true; # required for grafana-admins
|
|
||||||
allow_sign_up = true; # otherwise no new users can be created
|
|
||||||
api_url = "${issuer}/userinfo";
|
|
||||||
auth_url = "${issuer}/auth";
|
|
||||||
client_id = "grafana";
|
|
||||||
disable_login_form = true; # only allow OAuth
|
|
||||||
icon = "signin";
|
|
||||||
name = config.services.portunus.domain;
|
|
||||||
oauth_allow_insecure_email_lookup = true; # otherwise updating the mail in ldap will break login
|
|
||||||
oauth_auto_login = true; # redirect automatically to the only oauth provider
|
|
||||||
use_refresh_token = true;
|
|
||||||
role_attribute_path = "contains(groups[*], 'grafana-admins') && 'Admin' || contains(info.roles[*], 'grafana-user') && 'Editor'"
|
|
||||||
+ lib.optionalString cfg.oauth.enableViewerRole "|| 'Viewer'";
|
|
||||||
role_attribute_strict = true;
|
|
||||||
# https://dexidp.io/docs/custom-scopes-claims-clients/
|
|
||||||
scopes = "openid email groups profile offline_access";
|
|
||||||
token_url = "${issuer}/token";
|
|
||||||
};
|
|
||||||
server.protocol = "socket";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.nginx = lib.mkIf (cfg.enable && cfg.configureNginx) {
|
|
||||||
upstreams.grafana.servers."unix:${cfg.settings.server.socket}" = {};
|
|
||||||
virtualHosts = {
|
|
||||||
"${cfg.settings.server.domain}".locations = {
|
|
||||||
"/".proxyPass = "http://grafana";
|
|
||||||
"/api/live/ws" = {
|
|
||||||
proxyPass = "http://grafana";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.portunus = {
|
|
||||||
dex = lib.mkIf cfg.oauth.enable {
|
|
||||||
enable = true;
|
|
||||||
oidcClients = [{
|
|
||||||
callbackURL = "https://${cfg.settings.server.domain}/login/generic_oauth";
|
|
||||||
id = "grafana";
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
seedSettings.groups = lib.optional (cfg.oauth.adminGroup != null) {
|
|
||||||
long_name = "Grafana Administrators";
|
|
||||||
name = cfg.oauth.adminGroup;
|
|
||||||
permissions = { };
|
|
||||||
} ++ lib.optional (cfg.oauth.userGroup != null) {
|
|
||||||
long_name = "Grafana Users";
|
|
||||||
name = cfg.oauth.userGroup;
|
|
||||||
permissions = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.hedgedoc.ldap;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.hedgedoc.ldap = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc ''
|
|
||||||
login only via LDAP.
|
|
||||||
Use `service.hedgedoc.environmentFile` in format `bindCredentials=password` to set the credentials used by the search user
|
|
||||||
'');
|
|
||||||
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.hedgedoc.settings.ldap = lib.mkIf cfg.enable {
|
|
||||||
url = "ldaps://${ldap.domainName}:${toString ldap.port}";
|
|
||||||
bindDn = ldap.bindDN;
|
|
||||||
bindCredentials = "$bindCredentials";
|
|
||||||
searchBase = ldap.userBaseDN;
|
|
||||||
searchFilter = ldap.searchFilterWithGroupFilter cfg.userGroup (ldap.userFilter "{{username}}");
|
|
||||||
tlsca = "/etc/ssl/certs/ca-certificates.crt";
|
|
||||||
useridField = ldap.userField;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.portunus.seedSettings.groups = lib.optional (cfg.userGroup != null) {
|
|
||||||
long_name = "Hedgedoc Users";
|
|
||||||
name = cfg.userGroup;
|
|
||||||
permissions = {};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.home-assistant;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.home-assistant = {
|
|
||||||
ldap = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc ''login only via LDAP
|
|
||||||
|
|
||||||
::: {.note}
|
|
||||||
Only enable this after completing the onboarding!
|
|
||||||
:::
|
|
||||||
'');
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
};
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended default settings";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.home-assistant = lib.mkMerge [
|
|
||||||
(lib.mkIf (cfg.enable && cfg.recommendedDefaults) {
|
|
||||||
config = {
|
|
||||||
automation = "!include automations.yaml";
|
|
||||||
default_config = { }; # yes, this is required...
|
|
||||||
homeassistant = {
|
|
||||||
auth_providers = lib.mkIf (!cfg.ldap.enable) [
|
|
||||||
{ type = "homeassistant"; }
|
|
||||||
];
|
|
||||||
temperature_unit = "C";
|
|
||||||
time_zone = config.time.timeZone;
|
|
||||||
unit_system = "metric";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
(lib.mkIf (cfg.enable && cfg.ldap.enable) {
|
|
||||||
config.homeassistant.auth_providers = [{
|
|
||||||
type = "command_line";
|
|
||||||
# the script is not inheriting PATH from home-assistant
|
|
||||||
command = pkgs.resholve.mkDerivation {
|
|
||||||
pname = "ldap-auth-sh";
|
|
||||||
version = "unstable-2019-02-23";
|
|
||||||
|
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "bob1de";
|
|
||||||
repo = "ldap-auth-sh";
|
|
||||||
rev = "819f9233116e68b5af5a5f45167bcbb4ed412ed4";
|
|
||||||
hash = "sha256-+QjRP5SKUojaCv3lZX2Kv3wkaNvpWFd97phwsRlhroY=";
|
|
||||||
};
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
install -Dm755 ldap-auth.sh -t $out/bin
|
|
||||||
'';
|
|
||||||
|
|
||||||
solutions.default = {
|
|
||||||
fake.external = [ "on_auth_failure" "on_auth_success" ];
|
|
||||||
inputs = with pkgs; [ coreutils curl gnugrep gnused openldap ];
|
|
||||||
interpreter = "${pkgs.bash}/bin/bash";
|
|
||||||
keep."source:$CONFIG_FILE" = true;
|
|
||||||
scripts = [ "bin/ldap-auth.sh" ];
|
|
||||||
};
|
|
||||||
}+ "/bin/ldap-auth.sh";
|
|
||||||
args = [
|
|
||||||
# https://github.com/bob1de/ldap-auth-sh/blob/master/examples/home-assistant.cfg
|
|
||||||
(pkgs.writeText "config.cfg" /* shell */ ''
|
|
||||||
ATTRS="${ldap.userField}"
|
|
||||||
CLIENT="ldapsearch"
|
|
||||||
DEBUG=0
|
|
||||||
FILTER="${ldap.groupFilter "home-assistant-users"}"
|
|
||||||
NAME_ATTR="${ldap.userField}"
|
|
||||||
SCOPE="base"
|
|
||||||
SERVER="ldaps://${ldap.domainName}"
|
|
||||||
USERDN="uid=$(ldap_dn_escape "$username"),${ldap.userBaseDN}"
|
|
||||||
BASEDN="$USERDN"
|
|
||||||
|
|
||||||
on_auth_success() {
|
|
||||||
# print the meta entries for use in HA
|
|
||||||
if [ ! -z "$NAME_ATTR" ]; then
|
|
||||||
name=$(echo "$output" | ${lib.getExe pkgs.gnused} -nr "s/^\s*$NAME_ATTR:\s*(.+)\s*\$/\1/Ip")
|
|
||||||
[ -z "$name" ] || echo "name=$name"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
'')
|
|
||||||
];
|
|
||||||
meta = true;
|
|
||||||
}];
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
config.services.portunus.seedSettings.groups = lib.optional (cfg.ldap.userGroup != null) {
|
|
||||||
long_name = "Home-Assistant Users";
|
|
||||||
name = cfg.ldap.userGroup;
|
|
||||||
permissions = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
config.systemd.tmpfiles.rules = lib.mkIf (cfg.enable && cfg.recommendedDefaults) [
|
|
||||||
"f ${cfg.configDir}/automations.yaml 0444 hass hass"
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.hydra.ldap;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.hydra.ldap = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc ''
|
|
||||||
login only via LDAP.
|
|
||||||
The bind user password must be placed at `/var/lib/hydra/ldap-password.conf` in the format `bindpw = "PASSWORD"
|
|
||||||
It is recommended to use a password without special characters because the perl config parser has weird escaping rule like that comment characters `#` must be escape with backslash
|
|
||||||
'');
|
|
||||||
|
|
||||||
roleMappings = lib.mkOption {
|
|
||||||
type = with lib.types; listOf (attrsOf str);
|
|
||||||
example = [{ hydra-admins = "admins"; }];
|
|
||||||
default = [ ];
|
|
||||||
description = lib.mdDoc "Map LDAP groups to hydra permissions. See upstream doc, especially role_mapping.";
|
|
||||||
};
|
|
||||||
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.hydra.extraConfig = lib.mkIf cfg.enable /* xml */ ''
|
|
||||||
# https://hydra.nixos.org/build/196107287/download/1/hydra/configuration.html#using-ldap-as-authentication-backend-optional
|
|
||||||
<ldap>
|
|
||||||
<config>
|
|
||||||
<credential>
|
|
||||||
class = Password
|
|
||||||
password_field = password
|
|
||||||
password_type = self_check
|
|
||||||
</credential>
|
|
||||||
<store>
|
|
||||||
class = LDAP
|
|
||||||
ldap_server = "${ldap.domainName}"
|
|
||||||
<ldap_server_options>
|
|
||||||
scheme = ldaps
|
|
||||||
timeout = 10
|
|
||||||
</ldap_server_options>
|
|
||||||
binddn = "${ldap.bindDN}"
|
|
||||||
include ldap-password.conf
|
|
||||||
start_tls = 0
|
|
||||||
<start_tls_options>
|
|
||||||
ciphers = TLS_AES_256_GCM_SHA384
|
|
||||||
sslversion = tlsv1_3
|
|
||||||
</start_tls_options>
|
|
||||||
user_basedn = "${ldap.userBaseDN}"
|
|
||||||
user_filter = "${ldap.searchFilterWithGroupFilter cfg.userGroup (ldap.userFilter "%s")}"
|
|
||||||
user_scope = one
|
|
||||||
user_field = ${ldap.userField}
|
|
||||||
<user_search_options>
|
|
||||||
deref = always
|
|
||||||
</user_search_options>
|
|
||||||
# Important for role mappings to work:
|
|
||||||
use_roles = 1
|
|
||||||
role_basedn = "${ldap.roleBaseDN}"
|
|
||||||
role_filter = "${ldap.roleFilter}"
|
|
||||||
role_scope = one
|
|
||||||
role_field = ${ldap.roleField}
|
|
||||||
role_value = ${ldap.roleValue}
|
|
||||||
<role_search_options>
|
|
||||||
deref = always
|
|
||||||
</role_search_options>
|
|
||||||
</store>
|
|
||||||
</config>
|
|
||||||
<role_mapping>
|
|
||||||
# Make all users in the hydra-admin group Hydra admins
|
|
||||||
# hydra-admins = admin
|
|
||||||
# Allow all users in the dev group to restart jobs and cancel builds
|
|
||||||
# dev = restart-jobs
|
|
||||||
# dev = cancel-build
|
|
||||||
${lib.concatStringsSep "\n" (lib.concatMap (lib.mapAttrsToList (name: value: "${name} = ${value}")) cfg.roleMappings)}
|
|
||||||
</role_mapping>
|
|
||||||
</ldap>
|
|
||||||
'';
|
|
||||||
|
|
||||||
config.services.portunus.seedSettings.groups = [
|
|
||||||
(lib.mkIf (cfg.userGroup != null) {
|
|
||||||
long_name = "Hydra Users";
|
|
||||||
name = cfg.userGroup;
|
|
||||||
permissions = { };
|
|
||||||
})
|
|
||||||
] ++ lib.flatten (map lib.attrValues (map (lib.mapAttrs (ldapGroup: _: {
|
|
||||||
long_name = "Hydra Role ${ldapGroup}";
|
|
||||||
name = ldapGroup;
|
|
||||||
permissions = { };
|
|
||||||
})) cfg.roleMappings));
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
options.hardware = {
|
|
||||||
intelGPU = lib.mkEnableOption "" // { description = "Whether to add drivers for intel hardware acceleration."; };
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
hardware.opengl = {
|
|
||||||
extraPackages = with pkgs; lib.mkIf config.hardware.intelGPU [
|
|
||||||
intel-compute-runtime # OpenCL library for iGPU
|
|
||||||
# video encoding/decoding hardware acceleration
|
|
||||||
intel-media-driver # broadwell or newer
|
|
||||||
intel-vaapi-driver # older hardware like haswell
|
|
||||||
];
|
|
||||||
extraPackages32 = with pkgs.pkgsi686Linux; lib.mkIf config.hardware.intelGPU [
|
|
||||||
# video encoding/decoding hardware acceleration
|
|
||||||
intel-media-driver # broadwell or newer
|
|
||||||
intel-vaapi-driver # older hardware like haswell
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
146
modules/ldap.nix
146
modules/ldap.nix
@ -1,146 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.security.ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.security.ldap = lib.mkOption {
|
|
||||||
type = lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
bindDN = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "uid=search";
|
|
||||||
default = "uid=${cfg.searchUID}";
|
|
||||||
apply = s: s + "," + cfg.userBaseDN;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The DN of the service user used by services.
|
|
||||||
The user base dn will be automatically appended.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
domainComponent = lib.mkOption {
|
|
||||||
type = with lib.types; listOf str;
|
|
||||||
example = [ "example" "com" ];
|
|
||||||
apply = dc: lib.removeSuffix "," (lib.concatMapStrings (x: "dc=${x},") dc);
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Domain component(s) (dc) represented as a list of strings.
|
|
||||||
|
|
||||||
Each entry will be prefixed with `dc=` and all are concatinated with `,`, except the last one.
|
|
||||||
The example would be concatinated to `dc=example,dc=com`
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
domainName = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "auth.example.com";
|
|
||||||
description = lib.mdDoc "The domain name to connect to the ldap server.";
|
|
||||||
};
|
|
||||||
|
|
||||||
givenNameField = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "givenName";
|
|
||||||
description = lib.mdDoc "The attribute of the user object where to find its given name.";
|
|
||||||
};
|
|
||||||
|
|
||||||
groupFilter = lib.mkOption {
|
|
||||||
type = with lib.types; functionTo str;
|
|
||||||
example = lib.literalExpression ''group: "(&(objectclass=person)(isMemberOf=cn=''${group},''${config.security.ldap.roleBaseDN}"'';
|
|
||||||
description = lib.mdDoc "A function that returns a group filter that matches the first argument against the names of the groups the user is part of.";
|
|
||||||
};
|
|
||||||
|
|
||||||
mailField = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "mail";
|
|
||||||
description = lib.mdDoc "The attribute of the user object where to find its email.";
|
|
||||||
};
|
|
||||||
|
|
||||||
port = lib.mkOption {
|
|
||||||
type = lib.types.port;
|
|
||||||
example = "636";
|
|
||||||
description = lib.mdDoc "The port the ldap server listens on. Usually this is 389 for ldap and 636 for ldaps.";
|
|
||||||
};
|
|
||||||
|
|
||||||
roleBaseDN = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "ou=groups";
|
|
||||||
apply = s: s + "," + cfg.domainComponent;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The directory path where applications should search for users.
|
|
||||||
Domain component will be automatically appended.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
roleField = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "cn";
|
|
||||||
description = lib.mdDoc "The attribute where the user account is listed in a group.";
|
|
||||||
};
|
|
||||||
|
|
||||||
roleFilter = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "(&(objectclass=groupOfNames)(member=%s))";
|
|
||||||
description = lib.mdDoc "Filter to get the groups of an user object.";
|
|
||||||
};
|
|
||||||
|
|
||||||
roleValue = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "dn";
|
|
||||||
description = lib.mdDoc "The attribute of the user object where to find its distinguished name.";
|
|
||||||
};
|
|
||||||
|
|
||||||
searchUID = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "search";
|
|
||||||
description = lib.mdDoc "The uid of the service user used by services, often referred as search user.";
|
|
||||||
};
|
|
||||||
|
|
||||||
searchFilterWithGroupFilter = lib.mkOption {
|
|
||||||
type = with lib.types; functionTo (functionTo str);
|
|
||||||
example = lib.literalExpression ''userFilterGroup: userFilter: if (userFilterGroup != null) then "(&''${config.security.ldap.groupFilter userFilterGroup})" else userFilter'';
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
A function that returns a search filter that may include a group filter.
|
|
||||||
The first argument may be the group that is filtered upon or null.
|
|
||||||
If set to null no additional filtering is done. If set the supplied filter is combined with the user filter.
|
|
||||||
The second argument must be the user filter including the applications placeholders or ideally the userFilter option.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
sshPublicKeyField = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "sshPublicKey";
|
|
||||||
description = lib.mdDoc "The attribute of the user object where to find its ssh public key.";
|
|
||||||
};
|
|
||||||
|
|
||||||
surnameField = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "sn";
|
|
||||||
description = lib.mdDoc "The attribute of the user object where to find its surname.";
|
|
||||||
};
|
|
||||||
|
|
||||||
userBaseDN = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "ou=users";
|
|
||||||
apply = s: s + "," + cfg.domainComponent;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The directory path where applications should search for users.
|
|
||||||
Domain component will be automatically appended.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
userField = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "uid";
|
|
||||||
description = lib.mdDoc "The attribute of the user object where to find its username.";
|
|
||||||
};
|
|
||||||
|
|
||||||
userFilter = lib.mkOption {
|
|
||||||
type = with lib.types; functionTo str;
|
|
||||||
example = ''param: "(&(objectclass=person)(|(uid=''${param})(mail=''${param})))"'';
|
|
||||||
description = lib.mdDoc "A function that returns a user search filter that uses the first argument as the placeholder.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
default = { };
|
|
||||||
description = "LDAP options used in other services.";
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.mastodon;
|
|
||||||
cfgl = cfg.ldap;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.mastodon = {
|
|
||||||
ldap = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc "login only via LDAP");
|
|
||||||
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
};
|
|
||||||
|
|
||||||
enableBirdUITheme = lib.mkEnableOption (lib.mdDoc "Bird UI Theme");
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.mastodon = {
|
|
||||||
package = lib.mkIf cfg.enableBirdUITheme (pkgs.mastodon.overrideAttrs (_: with pkgs; let
|
|
||||||
src = pkgs.applyPatches {
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "mstdn";
|
|
||||||
repo = "Bird-UI-Theme-Admins";
|
|
||||||
rev = "2f9921db746593f393c13f9b79e5b4c2e19b03bd";
|
|
||||||
hash = "sha256-+7FUm5GNXRWyS9Oiow6kwX+pWh11wO3stm5iOTY3sYY=";
|
|
||||||
};
|
|
||||||
|
|
||||||
patches = [
|
|
||||||
# fix compose box background
|
|
||||||
(fetchpatch {
|
|
||||||
url = "https://github.com/mstdn/Bird-UI-Theme-Admins/commit/d5a07d653680fba0ad8dd941405e2d0272ff9cd1.patch";
|
|
||||||
hash = "sha256-1gnQNCSSuTE/pkPCf49lJQbmeLAbaiPD9u/q8KiFvlU=";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
mastodonModules = mastodon.mastodonModules.overrideAttrs (oldAttrs: {
|
|
||||||
pname = "mastodon-birdui-theme";
|
|
||||||
|
|
||||||
nativeBuildInputs = oldAttrs.nativeBuildInputs ++ [
|
|
||||||
rsync
|
|
||||||
xorg.lndir
|
|
||||||
];
|
|
||||||
|
|
||||||
postPatch = ''
|
|
||||||
rsync -r ${src}/mastodon/ .
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
postBuild = ''
|
|
||||||
cp ${src}/mastodon/config/themes.yml config/themes.yml
|
|
||||||
'';
|
|
||||||
}));
|
|
||||||
|
|
||||||
extraConfig = lib.mkIf cfgl.enable {
|
|
||||||
LDAP_ENABLED = "true";
|
|
||||||
LDAP_BASE = ldap.userBaseDN;
|
|
||||||
LDAP_BIND_DN = ldap.bindDN;
|
|
||||||
LDAP_HOST = ldap.domainName;
|
|
||||||
LDAP_METHOD = "simple_tls";
|
|
||||||
LDAP_PORT = toString ldap.port;
|
|
||||||
LDAP_UID = ldap.userField;
|
|
||||||
# convert .,- (space) in LDAP usernames to underscore, otherwise those users cannot log in
|
|
||||||
LDAP_UID_CONVERSION_ENABLED = "true";
|
|
||||||
LDAP_SEARCH_FILTER = ldap.searchFilterWithGroupFilter cfgl.userGroup "(|(%{uid}=%{email})(%{mail}=%{email}))";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.portunus.seedSettings.groups = lib.optional (cfgl.userGroup != null) {
|
|
||||||
long_name = "Mastodon Users";
|
|
||||||
name = cfgl.userGroup;
|
|
||||||
permissions = { };
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,158 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.matrix-synapse;
|
|
||||||
cfge = cfg.element-web;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.matrix-synapse = {
|
|
||||||
addAdditionalOembedProvider = libS.mkOpinionatedOption "add additional oembed providers from oembed.com";
|
|
||||||
|
|
||||||
element-web = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc "the element-web client");
|
|
||||||
|
|
||||||
domain = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "element.example.org";
|
|
||||||
description = lib.mdDoc "The domain that element-web will use.";
|
|
||||||
};
|
|
||||||
|
|
||||||
package = lib.mkPackageOptionMD pkgs "Element-Web" {
|
|
||||||
default = [ "element-web" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
enableConfigFeatures = libS.mkOpinionatedOption "enable most features available via config.json";
|
|
||||||
};
|
|
||||||
|
|
||||||
ldap = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc "login via ldap");
|
|
||||||
|
|
||||||
userGroup = libS.ldap.mkUserGroupOption;
|
|
||||||
|
|
||||||
bindPasswordFile = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "/var/lib/secrets/bind-password";
|
|
||||||
description = lib.mdDoc "Path to a file containing the bind password.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended and secure default settings";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.environment.etc = lib.mkIf cfg.enable {
|
|
||||||
"matrix-synapse/config.yaml".source = cfg.configFile;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.nginx = lib.mkIf cfge.enable {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts."${cfge.domain}" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = lib.mkDefault true;
|
|
||||||
root = (cfge.package.override {
|
|
||||||
conf = with config.services.matrix-synapse.settings; {
|
|
||||||
default_server_config."m.homeserver" = {
|
|
||||||
"base_url" = public_baseurl;
|
|
||||||
"server_name" = server_name;
|
|
||||||
};
|
|
||||||
default_theme = "dark";
|
|
||||||
room_directory.servers = [ server_name ];
|
|
||||||
} // lib.optionalAttrs cfge.enableConfigFeatures {
|
|
||||||
features = {
|
|
||||||
# https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/settings/Settings.tsx
|
|
||||||
# https://github.com/vector-im/element-web/blob/develop/docs/labs.md
|
|
||||||
feature_ask_to_join = true;
|
|
||||||
feature_bridge_state = true;
|
|
||||||
feature_exploring_public_spaces = true;
|
|
||||||
feature_jump_to_date = true;
|
|
||||||
feature_mjolnir = true;
|
|
||||||
feature_pinning = true;
|
|
||||||
feature_presence_in_room_list = true;
|
|
||||||
feature_report_to_moderators = true;
|
|
||||||
feature_qr_signin_reciprocate_show = true;
|
|
||||||
};
|
|
||||||
show_labs_settings = true;
|
|
||||||
};
|
|
||||||
}).overrideAttrs ({ postInstall ? "", ... }: {
|
|
||||||
# prevent 404 spam in nginx log
|
|
||||||
postInstall = postInstall + ''
|
|
||||||
ln -rs $out/config.json $out/config.${cfge.domain}.json
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.matrix-synapse = lib.mkMerge [
|
|
||||||
{
|
|
||||||
settings = lib.mkIf cfge.enable rec {
|
|
||||||
email.client_base_url = web_client_location;
|
|
||||||
web_client_location = "https://${cfge.domain}";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
(lib.mkIf cfg.ldap.enable {
|
|
||||||
plugins = with config.services.matrix-synapse.package.plugins; [
|
|
||||||
matrix-synapse-ldap3
|
|
||||||
];
|
|
||||||
|
|
||||||
settings.modules = [{
|
|
||||||
module = "ldap_auth_provider.LdapAuthProviderModule";
|
|
||||||
config = {
|
|
||||||
enabled = true;
|
|
||||||
mode = "search";
|
|
||||||
uri = "ldaps://${ldap.domainName}:${toString ldap.port}";
|
|
||||||
base = ldap.userBaseDN;
|
|
||||||
attributes = {
|
|
||||||
uid = ldap.userField;
|
|
||||||
mail = ldap.mailField;
|
|
||||||
name = ldap.givenNameField;
|
|
||||||
};
|
|
||||||
bind_dn = ldap.bindDN;
|
|
||||||
bind_password_file = cfg.ldap.bindPasswordFile;
|
|
||||||
tls_options.validate = true;
|
|
||||||
} // lib.optionalAttrs (cfg.ldap.userGroup != null) {
|
|
||||||
filter = ldap.groupFilter cfg.ldap.userGroup;
|
|
||||||
};
|
|
||||||
}];
|
|
||||||
})
|
|
||||||
|
|
||||||
{
|
|
||||||
settings.oembed.additional_providers = lib.mkIf cfg.addAdditionalOembedProvider [
|
|
||||||
(
|
|
||||||
let
|
|
||||||
providers = pkgs.fetchurl {
|
|
||||||
url = "https://oembed.com/providers.json?2023-03-23";
|
|
||||||
sha256 = "sha256-OdgBgkLbtNMn84ixKuC1gGzpyr+X+ORiLl6TAK3lYuQ=";
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.runCommand "providers.json"
|
|
||||||
{
|
|
||||||
nativeBuildInputs = with pkgs; [ jq ];
|
|
||||||
} ''
|
|
||||||
# filter out entries that do not contain a schemes entry
|
|
||||||
# Error in configuration at 'oembed.additional_providers.<item 0>.<item 22>.endpoints.<item 0>': 'schemes' is a required property
|
|
||||||
# and have none http protocols: Unsupported oEmbed scheme (spotify) for pattern: spotify:*
|
|
||||||
jq '[ ..|objects| select(.endpoints[0]|has("schemes")) | .endpoints[0].schemes=([ .endpoints[0].schemes[]|select(.|contains("http")) ]) ]' ${providers} > $out
|
|
||||||
''
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
(lib.mkIf cfg.recommendedDefaults {
|
|
||||||
settings = {
|
|
||||||
federation_client_minimum_tls_version = "1.2";
|
|
||||||
suppress_key_server_warning = true;
|
|
||||||
user_directory.prefer_local_users = true;
|
|
||||||
};
|
|
||||||
withJemalloc = true;
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
config.services.portunus.seedSettings.groups = lib.optional (cfg.ldap.userGroup != null) {
|
|
||||||
long_name = "Matrix Users";
|
|
||||||
name = cfg.ldap.userGroup;
|
|
||||||
permissions = { };
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,218 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.nextcloud;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.nextcloud = {
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended default settings";
|
|
||||||
|
|
||||||
configureImaginary = libS.mkOpinionatedOption "configure and use Imaginary for preview generation";
|
|
||||||
|
|
||||||
configureMemories = libS.mkOpinionatedOption "configure dependencies for Memories App";
|
|
||||||
|
|
||||||
configureMemoriesVaapi = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = config.hardware.intelGPU;
|
|
||||||
defaultText = "config.hardware.intelGPU";
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Wether to configure Memories App to use an Intel iGPU for hardware acceleration.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
configurePreviewSettings = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = cfg.configureImaginary;
|
|
||||||
defaultText = "config.services.nextcloud.configureImaginary";
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Wether to configure the preview settings to be more optimised for real world usage.
|
|
||||||
By default this is enabled, when Imaginary is configured.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
configureRecognize = libS.mkOpinionatedOption "configure dependencies for Recognize App";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
services = {
|
|
||||||
imaginary = lib.mkIf cfg.configureImaginary {
|
|
||||||
enable = true;
|
|
||||||
address = "127.0.0.1";
|
|
||||||
settings.return-size = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
nextcloud = {
|
|
||||||
# otherwise the Logging App does not function
|
|
||||||
logType = lib.mkIf cfg.recommendedDefaults "file";
|
|
||||||
|
|
||||||
phpOptions = lib.mkIf cfg.recommendedDefaults {
|
|
||||||
# https://docs.nextcloud.com/server/latest/admin_manual/installation/server_tuning.html#:~:text=opcache.jit%20%3D%201255%20opcache.jit_buffer_size%20%3D%20128m
|
|
||||||
"opcache.jit" = 1255;
|
|
||||||
"opcache.jit_buffer_size" = "128M";
|
|
||||||
};
|
|
||||||
|
|
||||||
extraOptions = lib.mkMerge [
|
|
||||||
(lib.mkIf cfg.configureImaginary {
|
|
||||||
enabledPreviewProviders = [
|
|
||||||
# default from https://github.com/nextcloud/server/blob/master/config/config.sample.php#L1295-L1304
|
|
||||||
''OC\Preview\BMP''
|
|
||||||
''OC\Preview\GIF''
|
|
||||||
''OC\Preview\JPEG''
|
|
||||||
''OC\Preview\Krita''
|
|
||||||
''OC\Preview\MarkDown''
|
|
||||||
''OC\Preview\MP3''
|
|
||||||
''OC\Preview\OpenDocument''
|
|
||||||
''OC\Preview\PNG''
|
|
||||||
''OC\Preview\TXT''
|
|
||||||
''OC\Preview\XBitmap''
|
|
||||||
# https://docs.nextcloud.com/server/24/admin_manual/installation/server_tuning.html#previews
|
|
||||||
''OC\Preview\Imaginary''
|
|
||||||
];
|
|
||||||
})
|
|
||||||
|
|
||||||
(lib.mkIf cfg.configureMemories {
|
|
||||||
enabledPreviewProviders = [
|
|
||||||
# https://github.com/pulsejet/memories/wiki/File-Type-Support
|
|
||||||
# TODO: not sure if this should be under configurePreviewSettings instead or both
|
|
||||||
''OC\Preview\Image'' # alias for png,jpeg,gif,bmp
|
|
||||||
''OC\Preview\HEIC''
|
|
||||||
''OC\Preview\TIFF''
|
|
||||||
''OC\Preview\Movie''
|
|
||||||
];
|
|
||||||
|
|
||||||
"memories.exiftool" = "${pkgs.exiftool}/bin/exiftool";
|
|
||||||
"memories.vod.vaapi" = lib.mkIf cfg.configureMemoriesVaapi true;
|
|
||||||
"memories.vod.ffmpeg" = "${pkgs.ffmpeg-headless}/bin/ffmpeg";
|
|
||||||
"memories.vod.ffprobe" = "${pkgs.ffmpeg-headless}/bin/ffprobe";
|
|
||||||
})
|
|
||||||
|
|
||||||
(lib.mkIf cfg.configurePreviewSettings {
|
|
||||||
enabledPreviewProviders = [
|
|
||||||
# https://github.com/nextcloud/server/tree/master/lib/private/Preview
|
|
||||||
''OC\Preview\Font''
|
|
||||||
''OC\Preview\PDF''
|
|
||||||
''OC\Preview\SVG''
|
|
||||||
''OC\Preview\WebP''
|
|
||||||
];
|
|
||||||
|
|
||||||
jpeg_quality = 60;
|
|
||||||
preview_max_filesize_image = 128; # MB
|
|
||||||
preview_max_memory = 512; # MB
|
|
||||||
preview_max_x = 2048; # px
|
|
||||||
preview_max_y = 2048; # px
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
phpfpm.pools = lib.mkIf cfg.configurePreviewSettings {
|
|
||||||
# add user packages to phpfpm process PATHs, required to find ffmpeg for preview generator
|
|
||||||
# beginning taken from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/nextcloud.nix#L985
|
|
||||||
nextcloud.phpEnv.PATH = lib.mkForce "/run/wrappers/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin:/etc/profiles/per-user/nextcloud/bin";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd = {
|
|
||||||
services = {
|
|
||||||
nextcloud-cron = lib.mkIf cfg.configureMemories {
|
|
||||||
# required for memories
|
|
||||||
# see https://github.com/pulsejet/memories/blob/master/docs/troubleshooting.md#issues-with-nixos
|
|
||||||
path = with pkgs; [ perl ];
|
|
||||||
# fix memories app being unpacked without the x-bit on binaries
|
|
||||||
# could be done in nextcloud-update-plugins but then manually updates would be broken until the next auto update
|
|
||||||
preStart = "${pkgs.coreutils}/bin/chmod +x /var/lib/nextcloud/store-apps/memories/bin-ext/*";
|
|
||||||
};
|
|
||||||
|
|
||||||
nextcloud-cron-preview-generator = lib.mkIf cfg.configurePreviewSettings {
|
|
||||||
environment.NEXTCLOUD_CONFIG_DIR = "${config.services.nextcloud.datadir}/config";
|
|
||||||
serviceConfig = {
|
|
||||||
ExecStart = "/run/current-system/sw/bin/nextcloud-occ preview:pre-generate";
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "nextcloud";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nextcloud-preview-generator-setup = lib.mkIf cfg.configurePreviewSettings {
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
requires = [ "phpfpm-nextcloud.service" ];
|
|
||||||
after = [ "phpfpm-nextcloud.service" ];
|
|
||||||
environment.NEXTCLOUD_CONFIG_DIR = "${config.services.nextcloud.datadir}/config";
|
|
||||||
script =
|
|
||||||
let
|
|
||||||
occ = "/run/current-system/sw/bin/nextcloud-occ";
|
|
||||||
in
|
|
||||||
/* bash */ ''
|
|
||||||
# check with:
|
|
||||||
# for size in squareSizes widthSizes heightSizes; do echo -n "$size: "; nextcloud-occ config:app:get previewgenerator $size; done
|
|
||||||
|
|
||||||
# extra commands run for preview generator:
|
|
||||||
# 32 icon file list
|
|
||||||
# 64 icon file list android app, photos app
|
|
||||||
# 96 nextcloud client VFS windows file preview
|
|
||||||
# 256 file app grid view, many requests
|
|
||||||
# 512 photos app tags
|
|
||||||
${occ} config:app:set --value="32 64 96 256 512" previewgenerator squareSizes
|
|
||||||
|
|
||||||
# 341 hover in maps app
|
|
||||||
# 1920 files/photos app when viewing picture
|
|
||||||
${occ} config:app:set --value="341 1920" previewgenerator widthSizes
|
|
||||||
|
|
||||||
# 256 hover in maps app
|
|
||||||
# 1080 files/photos app when viewing picture
|
|
||||||
${occ} config:app:set --value="256 1080" previewgenerator heightSizes
|
|
||||||
'';
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "nextcloud";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nextcloud-setup = lib.mkIf cfg.configureRecognize {
|
|
||||||
script = /* bash */ ''
|
|
||||||
export PATH=$PATH:/etc/profiles/per-user/nextcloud/bin:/run/current-system/sw/bin
|
|
||||||
|
|
||||||
if [[ ! -e /var/lib/nextcloud/store-apps/recognize/node_modules/@tensorflow/tfjs-node/lib/napi-v8/tfjs_binding.node ]]; then
|
|
||||||
if [[ -d /var/lib/nextcloud/store-apps/recognize/node_modules/ ]]; then
|
|
||||||
cd /var/lib/nextcloud/store-apps/recognize/node_modules/
|
|
||||||
npm rebuild @tensorflow/tfjs-node --build-addon-from-source
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
phpfpm-nextcloud.serviceConfig = lib.mkIf cfg.configureMemoriesVaapi {
|
|
||||||
DeviceAllow = [ "/dev/dri/renderD128 rwm" ];
|
|
||||||
PrivateDevices = lib.mkForce false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
timers.nextcloud-cron-preview-generator = lib.mkIf cfg.configurePreviewSettings {
|
|
||||||
timerConfig = {
|
|
||||||
OnUnitActiveSec = "5m";
|
|
||||||
Unit = "nextcloud-cron-preview-generator.service";
|
|
||||||
};
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.nextcloud = {
|
|
||||||
extraGroups = lib.mkIf cfg.configureMemoriesVaapi [
|
|
||||||
"render" # access /dev/dri/renderD128
|
|
||||||
];
|
|
||||||
packages = with pkgs;
|
|
||||||
# generate video thumbnails with preview generator
|
|
||||||
lib.optional cfg.configurePreviewSettings ffmpeg-headless
|
|
||||||
# required for memories, duplicated with nextcloud-cron to better debug
|
|
||||||
++ lib.optional cfg.configureMemories perl
|
|
||||||
# required for recognize app
|
|
||||||
++ lib.optionals cfg.configureRecognize [
|
|
||||||
gnumake # installation requirement
|
|
||||||
nodejs_16 # runtime and installation requirement
|
|
||||||
nodejs_16.pkgs.node-pre-gyp # installation requirement
|
|
||||||
python3 # requirement for node-pre-gyp otherwise fails with exit code 236
|
|
||||||
util-linux # runtime requirement for taskset
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.nginx;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.nginx = {
|
|
||||||
allCompression = libS.mkOpinionatedOption "set all recommended compression options";
|
|
||||||
|
|
||||||
default404Server = {
|
|
||||||
enable = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Wether to add a default server which always responds with 404.
|
|
||||||
This is useful when using a wildcard cname with a wildcard certitificate to not return the first server entry in the config on unknown subdomains
|
|
||||||
or to do the same for an old and not fully removed domain.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
acmeHost = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The acme host to use for the default 404 server.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
generateDhparams = libS.mkOpinionatedOption "generate more secure, 2048 bits dhparams replacing the default 1024 bits";
|
|
||||||
|
|
||||||
openFirewall = libS.mkOpinionatedOption "open the firewall port for the http (80) and https (443) default ports";
|
|
||||||
|
|
||||||
quic = {
|
|
||||||
enable = lib.mkEnableOption (lib.mdDoc "quic support in nginx");
|
|
||||||
|
|
||||||
bpf = libS.mkOpinionatedOption "configure nginx' bpf support which routes quic packets from the same source to the same worker";
|
|
||||||
};
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended performance options not grouped into other settings";
|
|
||||||
|
|
||||||
resolverAddrFromNameserver = libS.mkOpinionatedOption "set resolver address to environment.nameservers";
|
|
||||||
|
|
||||||
rotateLogsFaster = libS.mkOpinionatedOption "keep logs only for 7 days and rotate them daily";
|
|
||||||
|
|
||||||
setHSTSHeader = libS.mkOpinionatedOption "add the HSTS header to all virtual hosts";
|
|
||||||
|
|
||||||
tcpFastOpen = libS.mkOpinionatedOption "enable tcp fast open";
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = cfg.quic.enable && cfg.quic.bpf -> !lib.versionOlder cfg.package.version "1.25.0";
|
|
||||||
message = "Setting services.nginx.quic.bpf to true requires nginx version 1.25.0 or newer, but currently \"${cfg.package.version}\" is used!";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
boot.kernel.sysctl = lib.mkIf cfg.tcpFastOpen {
|
|
||||||
# enable tcp fastopen for outgoing and incoming connections
|
|
||||||
"net.ipv4.tcp_fastopen" = 3;
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ 80 443 ];
|
|
||||||
|
|
||||||
nixpkgs.overlays = lib.mkIf cfg.tcpFastOpen [
|
|
||||||
(final: prev:
|
|
||||||
let
|
|
||||||
configureFlags = [ "-DTCP_FASTOPEN=23" ];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
nginx = prev.nginx.override { inherit configureFlags; };
|
|
||||||
nginxQuic = prev.nginxQuic.override { inherit configureFlags; };
|
|
||||||
nginxStable = prev.nginxStable.override { inherit configureFlags; };
|
|
||||||
nginxMainline = prev.nginxMainline.override { inherit configureFlags; };
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
services = {
|
|
||||||
logrotate.settings.nginx = lib.mkIf cfg.rotateLogsFaster {
|
|
||||||
frequency = "daily";
|
|
||||||
rotate = 7;
|
|
||||||
};
|
|
||||||
|
|
||||||
# NOTE: do not use mkMerge here to prevent infinite recursions
|
|
||||||
nginx = {
|
|
||||||
appendConfig = lib.optionalString (cfg.quic.enable && cfg.quic.bpf) /* nginx */ ''
|
|
||||||
quic_bpf on;
|
|
||||||
'' + lib.optionalString cfg.recommendedDefaults /* nginx */ ''
|
|
||||||
worker_processes auto;
|
|
||||||
worker_cpu_affinity auto;
|
|
||||||
'';
|
|
||||||
|
|
||||||
commonHttpConfig = lib.optionalString cfg.recommendedDefaults /* nginx */ ''
|
|
||||||
error_log syslog:server=unix:/dev/log;
|
|
||||||
'' + lib.optionalString cfg.quic.enable /* nginx */''
|
|
||||||
quic_retry on;
|
|
||||||
'' + lib.optionalString cfg.recommendedZstdSettings /* nginx */ ''
|
|
||||||
# TODO: upstream this?
|
|
||||||
zstd_types application/x-nix-archive;
|
|
||||||
'';
|
|
||||||
|
|
||||||
commonServerConfig = lib.mkIf cfg.setHSTSHeader /* nginx */ ''
|
|
||||||
more_set_headers "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload";
|
|
||||||
'';
|
|
||||||
|
|
||||||
package = lib.mkIf cfg.quic.enable pkgs.nginxQuic; # based on pkgs.nginxMainline
|
|
||||||
|
|
||||||
recommendedBrotliSettings = lib.mkIf cfg.allCompression true;
|
|
||||||
recommendedGzipSettings = lib.mkIf cfg.allCompression true;
|
|
||||||
recommendedOptimisation = lib.mkIf cfg.allCompression true;
|
|
||||||
recommendedProxySettings = lib.mkIf cfg.allCompression true;
|
|
||||||
recommendedTlsSettings = lib.mkIf cfg.allCompression true;
|
|
||||||
recommendedZstdSettings = lib.mkIf cfg.allCompression true;
|
|
||||||
|
|
||||||
resolver.addresses =
|
|
||||||
let
|
|
||||||
isIPv6 = addr: builtins.match ".*:.*:.*" addr != null;
|
|
||||||
escapeIPv6 = addr:
|
|
||||||
if isIPv6 addr then
|
|
||||||
"[${addr}]"
|
|
||||||
else
|
|
||||||
addr;
|
|
||||||
in
|
|
||||||
lib.optionals (cfg.resolverAddrFromNameserver && config.networking.nameservers != [ ]) (map escapeIPv6 config.networking.nameservers);
|
|
||||||
sslDhparam = lib.mkIf cfg.generateDhparams config.security.dhparams.params.nginx.path;
|
|
||||||
|
|
||||||
# NOTE: do not use mkMerge here to prevent infinite recursions
|
|
||||||
virtualHosts =
|
|
||||||
let
|
|
||||||
extraParameters = [
|
|
||||||
# net.core.somaxconn is set to 4096
|
|
||||||
# see https://www.nginx.com/blog/tuning-nginx/#:~:text=to%20a%20value-,greater%20than%20512,-%2C%20change%20the%20backlog
|
|
||||||
"backlog=1024"
|
|
||||||
|
|
||||||
"deferred"
|
|
||||||
"fastopen=256" # requires nginx to be compiled with -DTCP_FASTOPEN=23
|
|
||||||
];
|
|
||||||
in
|
|
||||||
lib.mkIf (cfg.recommendedDefaults || cfg.default404Server.enable || cfg.quic.enable) {
|
|
||||||
"_" = {
|
|
||||||
kTLS = lib.mkIf cfg.recommendedDefaults true;
|
|
||||||
reuseport = lib.mkIf (cfg.recommendedDefaults || cfg.quic.enable) true;
|
|
||||||
|
|
||||||
default = lib.mkIf cfg.default404Server.enable true;
|
|
||||||
forceSSL = lib.mkIf cfg.default404Server.enable true;
|
|
||||||
useACMEHost = lib.mkIf cfg.default404Server.enable cfg.default404Server.acmeHost;
|
|
||||||
extraConfig = lib.mkIf cfg.default404Server.enable /* nginx */ ''
|
|
||||||
return 404;
|
|
||||||
'';
|
|
||||||
|
|
||||||
listen = lib.mkIf cfg.tcpFastOpen (lib.mkDefault [
|
|
||||||
{ addr = "0.0.0.0"; port = 80; inherit extraParameters; }
|
|
||||||
{ addr = "0.0.0.0"; port = 443; ssl = true; inherit extraParameters; }
|
|
||||||
{ addr = "[::]"; port = 80; inherit extraParameters; }
|
|
||||||
{ addr = "[::]"; port = 443; ssl = true; inherit extraParameters; }
|
|
||||||
]);
|
|
||||||
|
|
||||||
quic = lib.mkIf cfg.quic.enable true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
security.dhparams = lib.mkIf cfg.generateDhparams {
|
|
||||||
enable = cfg.generateDhparams;
|
|
||||||
params.nginx = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.nginx.serviceConfig = lib.mkIf (cfg.quic.enable && cfg.quic.bpf) {
|
|
||||||
# NOTE: CAP_BPF is included in CAP_SYS_ADMIN but it is not enough alone
|
|
||||||
AmbientCapabilities = [ "CAP_BPF" "CAP_NET_ADMIN" "CAP_SYS_ADMIN" ];
|
|
||||||
CapabilityBoundingSet = [ "CAP_BPF" "CAP_NET_ADMIN" "CAP_SYS_ADMIN" ];
|
|
||||||
SystemCallFilter = [ "bpf" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.nix;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.nix = {
|
|
||||||
deleteChannels = lib.mkEnableOption "" // { description = "Whether to delete all channels on a system switch."; };
|
|
||||||
|
|
||||||
deleteUserProfiles = lib.mkEnableOption "" // { description = "Whether to delete all user profiles on a system switch."; };
|
|
||||||
|
|
||||||
diffSystem = libS.mkOpinionatedOption "system closure diffing on updates";
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended default settings";
|
|
||||||
|
|
||||||
remoteBuilder = {
|
|
||||||
enable = lib.mkEnableOption "restricted nix remote builder";
|
|
||||||
|
|
||||||
sshPublicKeys = lib.mkOption {
|
|
||||||
description = "SSH public keys accepted by the remote build user.";
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
};
|
|
||||||
|
|
||||||
name = lib.mkOption {
|
|
||||||
description = "Name of the user used for remote building.";
|
|
||||||
type = lib.types.str;
|
|
||||||
readOnly = true;
|
|
||||||
default = "nix-remote-builder";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
# based on https://github.com/numtide/srvos/blob/main/nixos/roles/nix-remote-builder.nix
|
|
||||||
# and https://discourse.nixos.org/t/wrapper-to-restrict-builder-access-through-ssh-worth-upstreaming/25834
|
|
||||||
nix.settings = {
|
|
||||||
builders-use-substitutes = lib.mkIf cfg.recommendedDefaults true;
|
|
||||||
connect-timeout = lib.mkIf cfg.recommendedDefaults 20;
|
|
||||||
experimental-features = lib.mkIf cfg.recommendedDefaults [ "nix-command" "flakes" ];
|
|
||||||
trusted-users = lib.mkIf cfg.remoteBuilder.enable [ cfg.remoteBuilder.name ];
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.${cfg.remoteBuilder.name} = lib.mkIf cfg.remoteBuilder.enable {
|
|
||||||
group = "nogroup";
|
|
||||||
isNormalUser = true;
|
|
||||||
openssh.authorizedKeys.keys = map
|
|
||||||
(key:
|
|
||||||
let
|
|
||||||
wrapper-dispatch-ssh-nix = pkgs.writeShellScriptBin "wrapper-dispatch-ssh-nix" /* bash */ ''
|
|
||||||
case $SSH_ORIGINAL_COMMAND in
|
|
||||||
"nix-daemon --stdio")
|
|
||||||
exec ${config.nix.package}/bin/nix-daemon --stdio
|
|
||||||
;;
|
|
||||||
"nix-store --serve --write")
|
|
||||||
exec ${config.nix.package}/bin/nix-store --serve --write
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Access is only allowed for the nix remote builder" 1>&2
|
|
||||||
exit 1
|
|
||||||
esac
|
|
||||||
'';
|
|
||||||
|
|
||||||
in
|
|
||||||
"restrict,pty,command=\"${wrapper-dispatch-ssh-nix}/bin/wrapper-dispatch-ssh-nix\" ${key}"
|
|
||||||
)
|
|
||||||
config.nix.remoteBuilder.sshPublicKeys;
|
|
||||||
};
|
|
||||||
|
|
||||||
system.activationScripts = {
|
|
||||||
deleteChannels = lib.mkIf cfg.deleteChannels ''
|
|
||||||
echo "Deleting all channels..."
|
|
||||||
rm -rf /root/.nix-channels /home/*/.nix-channels /nix/var/nix/profiles/per-user/*/channels* || true
|
|
||||||
'';
|
|
||||||
|
|
||||||
deleteUserProfiles = lib.mkIf cfg.deleteUserProfiles ''
|
|
||||||
echo "Deleting all user profiles..."
|
|
||||||
rm -rf /root/.nix-profile /home/*/.nix-profile /nix/var/nix/profiles/per-user/*/profile* || true
|
|
||||||
'';
|
|
||||||
|
|
||||||
diff-system = lib.mkIf cfg.diffSystem {
|
|
||||||
supportsDryActivation = true;
|
|
||||||
text = ''
|
|
||||||
if [[ -e /run/current-system && -e $systemConfig ]]; then
|
|
||||||
echo System package diff:
|
|
||||||
${lib.getExe config.nix.package} --extra-experimental-features nix-command store diff-closures /run/current-system $systemConfig || true
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.openvpn;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
# TODO: OpenVPN
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
diff --git a/internal/frontend/core.go b/internal/frontend/core.go
|
|
||||||
index 5976377..7c67991 100644
|
|
||||||
--- a/internal/frontend/core.go
|
|
||||||
+++ b/internal/frontend/core.go
|
|
||||||
@@ -43,8 +43,6 @@ func HTTPHandler(nexus core.Nexus, isBehindTLSProxy bool) http.Handler {
|
|
||||||
r.Methods("POST").Path(`/users/{uid}/delete`).Handler(postUserDeleteHandler(nexus))
|
|
||||||
|
|
||||||
r.Methods("GET").Path(`/groups`).Handler(getGroupsHandler(nexus))
|
|
||||||
- r.Methods("GET").Path(`/groups/new`).Handler(getGroupsNewHandler(nexus))
|
|
||||||
- r.Methods("POST").Path(`/groups/new`).Handler(postGroupsNewHandler(nexus))
|
|
||||||
r.Methods("GET").Path(`/groups/{name}/edit`).Handler(getGroupEditHandler(nexus))
|
|
||||||
r.Methods("POST").Path(`/groups/{name}/edit`).Handler(postGroupEditHandler(nexus))
|
|
||||||
r.Methods("GET").Path(`/groups/{name}/delete`).Handler(getGroupDeleteHandler(nexus))
|
|
||||||
diff --git a/internal/frontend/groups.go b/internal/frontend/groups.go
|
|
||||||
index 5ac6a75..ac59f4f 100644
|
|
||||||
--- a/internal/frontend/groups.go
|
|
||||||
+++ b/internal/frontend/groups.go
|
|
||||||
@@ -38,7 +38,6 @@ func getGroupsHandler(n core.Nexus) http.Handler {
|
|
||||||
<th>Members</th>
|
|
||||||
<th>Permissions granted</th>
|
|
||||||
<th class="actions">
|
|
||||||
- <a href="/groups/new" class="button button-primary">New group</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
@ -1,183 +0,0 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.portunus;
|
|
||||||
inherit (config.security) ldap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.portunus = {
|
|
||||||
# TODO: how to automatically set this?
|
|
||||||
# maybe based on $service.ldap.enable && services.portunus.enable?
|
|
||||||
addToHosts = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "Whether to add a hosts entry for the portunus domain pointing to externalIp";
|
|
||||||
};
|
|
||||||
|
|
||||||
configureOAuth2Proxy = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Wether to configure OAuth2 Proxy with Portunus' Dex.
|
|
||||||
|
|
||||||
Use `services.oauth2_proxy.nginx.virtualHosts` to configure the nginx virtual hosts that should require authentication.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
internalIp4 = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
description = lib.mdDoc "Internal IPv4 of portunus instance. This is used in the addToHosts option.";
|
|
||||||
};
|
|
||||||
|
|
||||||
internalIp6 = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
description = lib.mdDoc "Internal IPv6 of portunus instance. This is used in the addToHosts option.";
|
|
||||||
};
|
|
||||||
|
|
||||||
ldapPreset = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "Whether to set config.security.ldap to portunus specific settings.";
|
|
||||||
};
|
|
||||||
|
|
||||||
removeAddGroup = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "When enabled, remove the function to add new Groups via the web ui, to enforce seeding usage.";
|
|
||||||
};
|
|
||||||
|
|
||||||
seedGroups = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "Wether to seed groups configured in services as not member managed groups.";
|
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: upstream to nixos
|
|
||||||
seedSettings = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr (attrsOf (listOf (attrsOf anything)));
|
|
||||||
default = null;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Seed settings for users and grousp.
|
|
||||||
See upstream for format <https://github.com/majewsky/portunus#seeding-users-and-groups-from-static-configuration>
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = cfg.configureOAuth2Proxy -> config.services.oauth2_proxy.keyFile != null;
|
|
||||||
message = ''
|
|
||||||
Setting services.portunus.configureOAuth2Proxy to true requires to set service.oauth2_proxy.keyFile
|
|
||||||
to a file that contains `OAUTH2_PROXY_CLIENT_SECRET` and `OAUTH2_PROXY_COOKIE_SECRET`.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
networking.hosts = lib.mkIf cfg.addToHosts {
|
|
||||||
${cfg.internalIp4} = [ cfg.domain ];
|
|
||||||
${cfg.internalIp6} = [ cfg.domain ];
|
|
||||||
};
|
|
||||||
|
|
||||||
nixpkgs.overlays = lib.mkIf cfg.enable [
|
|
||||||
(final: prev: with final; {
|
|
||||||
dex-oidc = prev.dex-oidc.override {
|
|
||||||
buildGoModule = args: buildGoModule (args // {
|
|
||||||
patches = args.patches or [ ] ++ [
|
|
||||||
# remember session
|
|
||||||
(fetchpatch {
|
|
||||||
url = "https://github.com/SuperSandro2000/dex/commit/d2fb6cdf8188e6973721ddac657a7c5d3daf6955.patch";
|
|
||||||
hash = "sha256-PKC7jsNyFN28qFZ7SLYgnd0s09G2cb+vBeFvRzyyLGQ=";
|
|
||||||
})
|
|
||||||
# Complain if the env set in SecretEnv cannot be found
|
|
||||||
(fetchpatch {
|
|
||||||
url = "https://github.com/dexidp/dex/commit/f25f72053c9282cfe22521cd508698a07dc5190f.patch";
|
|
||||||
hash = "sha256-dyo+UPpceHxL3gcBQaGaDAHJqmysDJw051gMG1aeh5o=";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
vendorHash = "sha256-YIi67pPIcVndIjWk94ckv6X4WLELUe/J/03e+XWIdHE=";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
portunus = (prev.portunus.override { buildGoModule = buildGo121Module; }).overrideAttrs ({ patches ? [ ], buildInputs ? [ ], ... }: let
|
|
||||||
version = "2.0.0-beta.2";
|
|
||||||
in {
|
|
||||||
inherit version;
|
|
||||||
|
|
||||||
# TODO: upstream
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "majewsky";
|
|
||||||
repo = "portunus";
|
|
||||||
rev = "v${version}";
|
|
||||||
hash = "sha256-1OU3bepvqriGCW1qDszPnUDJ6eqBzNTiBZ2J4KF4ynw=";
|
|
||||||
};
|
|
||||||
|
|
||||||
patches = patches
|
|
||||||
++ lib.optional cfg.removeAddGroup ./portunus-remove-add-group.diff;
|
|
||||||
|
|
||||||
# TODO: upstream
|
|
||||||
buildInputs = buildInputs ++ [
|
|
||||||
libxcrypt-legacy
|
|
||||||
];
|
|
||||||
});
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
services = let
|
|
||||||
callbackURL = "https://${cfg.domain}/oauth2/callback";
|
|
||||||
clientID = "oauth2_proxy"; # - is not allowed in environment variables
|
|
||||||
in {
|
|
||||||
dex = {
|
|
||||||
enable = lib.mkIf cfg.configureOAuth2Proxy true;
|
|
||||||
# the user has no other option to accept this and all clients are internal anyway
|
|
||||||
settings.oauth2.skipApprovalScreen = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
oauth2_proxy = lib.mkIf cfg.configureOAuth2Proxy {
|
|
||||||
enable = true;
|
|
||||||
inherit clientID;
|
|
||||||
nginx = {
|
|
||||||
inherit (config.services.portunus) domain;
|
|
||||||
};
|
|
||||||
provider = "oidc";
|
|
||||||
redirectURL = callbackURL;
|
|
||||||
reverseProxy = true;
|
|
||||||
upstream = "http://127.0.0.1:4181";
|
|
||||||
extraConfig = {
|
|
||||||
oidc-issuer-url = config.services.dex.settings.issuer;
|
|
||||||
provider-display-name = "Portunus";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
portunus = {
|
|
||||||
dex.oidcClients = lib.mkIf cfg.configureOAuth2Proxy [{
|
|
||||||
inherit callbackURL;
|
|
||||||
id = clientID;
|
|
||||||
}];
|
|
||||||
seedPath = pkgs.writeText "seed.json" (builtins.toJSON cfg.seedSettings);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
security.ldap = lib.mkIf cfg.ldapPreset {
|
|
||||||
domainName = cfg.domain;
|
|
||||||
givenNameField = "givenName";
|
|
||||||
groupFilter = group: "(&(objectclass=person)(isMemberOf=cn=${group},${ldap.roleBaseDN}))";
|
|
||||||
mailField = "mail";
|
|
||||||
port = 636;
|
|
||||||
roleBaseDN = "ou=groups";
|
|
||||||
roleField = "cn";
|
|
||||||
roleFilter = "(&(objectclass=groupOfNames)(member=%s))";
|
|
||||||
roleValue = "dn";
|
|
||||||
searchFilterWithGroupFilter = userFilterGroup: userFilter: if (userFilterGroup != null) then "(&${ldap.groupFilter userFilterGroup}${userFilter})" else userFilter;
|
|
||||||
sshPublicKeyField = "sshPublicKey";
|
|
||||||
searchUID = "search";
|
|
||||||
surnameField = "sn";
|
|
||||||
userField = "uid";
|
|
||||||
userFilter = replaceStr: "(&(objectclass=person)(|(uid=${replaceStr})(mail=${replaceStr})))";
|
|
||||||
userBaseDN = "ou=users";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
{ config, lib, libS, pkgs, ... }:
|
|
||||||
|
|
||||||
# NOTE: requires https://github.com/NixOS/nixpkgs/pull/257503 because of new usage of extraPlugins
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.postgresql;
|
|
||||||
cfgu = config.services.postgresql.upgrade;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.postgresql = {
|
|
||||||
upgrade = {
|
|
||||||
enable = libS.mkOpinionatedOption "install the upgrade-pg-cluster script to update postgres.";
|
|
||||||
|
|
||||||
extraArgs = lib.mkOption {
|
|
||||||
type = with lib.types; listOf str;
|
|
||||||
default = [ "--link" "--jobs=$(nproc)" ];
|
|
||||||
description = lib.mdDoc "Extra arguments to pass to pg_upgrade. See https://www.postgresql.org/docs/current/pgupgrade.html for doc.";
|
|
||||||
};
|
|
||||||
|
|
||||||
newPackage = (lib.mkPackageOptionMD pkgs "postgresql" {
|
|
||||||
default = [ "postgresql_16" ];
|
|
||||||
}) // {
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The postgres package to which should be updated.
|
|
||||||
After running upgrade-pg-cluster this must be set to services.postgresql.package to complete the update.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
stopServices = lib.mkOption {
|
|
||||||
type = with lib.types; listOf str;
|
|
||||||
default = [ ];
|
|
||||||
example = [ "hedgedoc" "hydra" "nginx" ];
|
|
||||||
description = lib.mdDoc "Systemd services to stop when upgrade is started.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended default settings";
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
environment.systemPackages = lib.optional cfgu.enable (
|
|
||||||
let
|
|
||||||
# conditions copied from nixos/modules/services/databases/postgresql.nix
|
|
||||||
newPackage = if cfg.enableJIT && !cfgu.newPackage.jitSupport then cfgu.newPackage.withJIT else cfg.newPackage;
|
|
||||||
newData = "/var/lib/postgresql/${cfgu.newPackage.psqlSchema}";
|
|
||||||
newBin = "${if cfg.extraPlugins == [] then cfgu.newPackage else cfgu.newPackage.withPackages cfg.extraPlugins}/bin";
|
|
||||||
|
|
||||||
oldPackage = if cfg.enableJIT && !cfg.package.jitSupport then cfg.package.withJIT else cfg.package;
|
|
||||||
oldData = config.services.postgresql.dataDir;
|
|
||||||
oldBin = "${if cfg.extraPlugins == [] then oldPackage else oldPackage.withPackages cfg.extraPlugins}/bin";
|
|
||||||
in
|
|
||||||
pkgs.writeScriptBin "upgrade-pg-cluster" /* bash */ ''
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
echo "Current version: ${cfg.package.version}"
|
|
||||||
echo "Update version: ${cfgu.newPackage.version}"
|
|
||||||
|
|
||||||
if [[ ${cfgu.newPackage.version} == ${cfg.package.version} ]]; then
|
|
||||||
echo "There is no major postgres update available."
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
systemctl stop postgresql ${lib.concatStringsSep " " cfgu.stopServices}
|
|
||||||
|
|
||||||
install -d -m 0700 -o postgres -g postgres "${newData}"
|
|
||||||
cd "${newData}"
|
|
||||||
sudo -u postgres "${newBin}/initdb" -D "${newData}"
|
|
||||||
|
|
||||||
sudo -u postgres "${newBin}/pg_upgrade" \
|
|
||||||
--old-datadir "${oldData}" --new-datadir "${newData}" \
|
|
||||||
--old-bindir ${oldBin} --new-bindir ${newBin} \
|
|
||||||
${lib.concatStringsSep " " cfgu.extraArgs} \
|
|
||||||
"$@"
|
|
||||||
|
|
||||||
echo "
|
|
||||||
|
|
||||||
|
|
||||||
Run the following commands after setting:
|
|
||||||
services.postgresql.package = pkgs.postgresql_${lib.versions.major cfgu.newPackage.version}
|
|
||||||
sudo -u postgres vacuumdb --all --analyze-in-stages
|
|
||||||
${newData}/delete_old_cluster.sh
|
|
||||||
"
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
services = {
|
|
||||||
postgresql.enableJIT = lib.mkIf cfg.recommendedDefaults true;
|
|
||||||
|
|
||||||
postgresqlBackup = lib.mkIf cfg.recommendedDefaults {
|
|
||||||
compression = "zstd";
|
|
||||||
compressionLevel = 9;
|
|
||||||
pgdumpOptions = "--create --clean";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.simd;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.simd = {
|
|
||||||
enable = lib.mkEnableOption "optimized builds with simd instructions";
|
|
||||||
arch = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Microarchitecture string for nixpkgs.hostPlatform.gcc.march and to generate system-features.
|
|
||||||
Can be determined with: ``nix shell nixpkgs#gcc -c gcc -march=native -Q --help=target | grep march``
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
nix.settings.system-features = lib.mkIf (cfg.arch != null) (libS.nix.gcc-system-features config.simd.arch);
|
|
||||||
|
|
||||||
nixpkgs.hostPlatform = lib.mkIf cfg.enable {
|
|
||||||
gcc.arch = config.simd.arch;
|
|
||||||
inherit (config.nixpkgs) system;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.slim;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.slim = {
|
|
||||||
enable = libS.mkOpinionatedOption "disable some usual rarely used things to slim down the system";
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
documentation = {
|
|
||||||
# html docs and info are not required, man pages are enough
|
|
||||||
doc.enable = false;
|
|
||||||
info.enable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.defaultPackages = lib.mkForce [ ];
|
|
||||||
|
|
||||||
# durring testing only 550K-650K of the tmpfs where used
|
|
||||||
security.wrapperDirSize = "10M";
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfgP = config.programs.ssh;
|
|
||||||
cfgS = config.services.openssh;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
programs.ssh = {
|
|
||||||
addPopularKnownHosts = libS.mkOpinionatedOption "add ssh public keys of popular websites to known_hosts";
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommend and secure default settings";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.openssh = {
|
|
||||||
fixPermissions = libS.mkOpinionatedOption "fix host key permissions to prevent lock outs";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfgP.addPopularKnownHosts {
|
|
||||||
programs.ssh = {
|
|
||||||
extraConfig = lib.mkIf cfgP.recommendedDefaults ''
|
|
||||||
# hard complain about wrong knownHosts
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
# make automated host key rotation possible
|
|
||||||
UpdateHostKeys yes
|
|
||||||
# fetch host keys via DNS and trust them
|
|
||||||
VerifyHostKeyDNS yes
|
|
||||||
'';
|
|
||||||
knownHosts = lib.mkMerge [
|
|
||||||
(libS.mkPubKey "github.com" "ssh-rsa" "AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=")
|
|
||||||
(libS.mkPubKey "github.com" "ecdsa-sha2-nistp256" "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=")
|
|
||||||
(libS.mkPubKey "github.com" "ssh-ed25519" "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl")
|
|
||||||
(libS.mkPubKey "gitlab.com" "ssh-rsa" "AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9")
|
|
||||||
(libS.mkPubKey "gitlab.com" "ecdsa-sha2-nistp256" "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=")
|
|
||||||
(libS.mkPubKey "gitlab.com" "ssh-ed25519" "AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf")
|
|
||||||
(libS.mkPubKey "git.openwrt.org" "ssh-rsa" "AAAAB3NzaC1yc2EAAAABIwAAAQEAtnM1w/A1uRZqZuYHhw4ASOe9mr3J2qKAa9K9zR8jG+B+NQVtYlIBSkmCFyP6OuydCmoRZ5Gs1I9pl/hEyi7ieEi6g9yww/JbV322cw04Tli46enIYDG1bnSxF6Qt4aXqvPhcObI3z/1Z3XR6weS1fiLDzLvzq+w1gNM77xExD4Mh27LTPkdwOWjkGa5joNx3EQUC3rzwxUqE4fhOT2Ii93h8FSAUXY9C32jkJj9x7vfaJEsCacs6YTiUKKxyzEB+TvFZdUtGtoRThX7UVICUCD2th/r3UeSp8ItWPg/KqzSg2pRfWeYszlVoD59JZ6YCupSjjRqZddghQc94Hev7oQ==")
|
|
||||||
(libS.mkPubKey "git.openwrt.org" "ecdsa-sha2-nistp256" "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBASOHg+tghASiZF0ClxYb/HEhUcqnD43I86YatRZSUsXNWLEd8yOzjOJExDHHaKtmZtQ/jfEMmoYbCjdEDOYm5g=")
|
|
||||||
(libS.mkPubKey "git.openwrt.org" "ssh-ed25519" "AAAAC3NzaC1lZDI1NTE5AAAAIJZFpKQMaLM8bG9lAPfEpTBExrzuiTKMni7PgktmDbJe")
|
|
||||||
(libS.mkPubKey "git.sr.ht" "ssh-rsa" "AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ+l/lvYmaeOAPeijHL8d4794Am0MOvmXPyvHTtrqvgmvCJB8pen/qkQX2S1fgl9VkMGSNxbp7NF7HmKgs5ajTGV9mB5A5zq+161lcp5+f1qmn3Dp1MWKp/AzejWXKW+dwPBd3kkudDBA1fa3uK6g1gK5nLw3qcuv/V4emX9zv3P2ZNlq9XRvBxGY2KzaCyCXVkL48RVTTJJnYbVdRuq8/jQkDRA8lHvGvKI+jqnljmZi2aIrK9OGT2gkCtfyTw2GvNDV6aZ0bEza7nDLU/I+xmByAOO79R1Uk4EYCvSc1WXDZqhiuO2sZRmVxa0pQSBDn1DB3rpvqPYW+UvKB3SOz")
|
|
||||||
(libS.mkPubKey "git.sr.ht" "ecdsa-sha2-nistp256" "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCj6y+cJlqK3BHZRLZuM+KP2zGPrh4H66DacfliU1E2DHAd1GGwF4g1jwu3L8gOZUTIvUptqWTkmglpYhFp4Iy4=")
|
|
||||||
(libS.mkPubKey "git.sr.ht" "ssh-ed25519" "AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60")
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = lib.mkIf cfgS.fixPermissions [
|
|
||||||
"d /etc 0755 root root -"
|
|
||||||
"d /etc/ssh 0755 root root -"
|
|
||||||
"f /etc/ssh/ssh_host_ed25519_key 0700 root root -"
|
|
||||||
"f /etc/ssh/ssh_host_ed25519_key.pub 0744 root root -"
|
|
||||||
"f /etc/ssh/ssh_host_rsa_key 0700 root root -"
|
|
||||||
"f /etc/ssh/ssh_host_rsa_key.pub 0744 root root -"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.openssh.banner = ''
|
|
||||||
Welcome to another
|
|
||||||
░██╗░░░░░░░██╗░█████╗░██╗░░░██╗███████╗██╗░░░░░███████╗███╗░░██╗░██████╗
|
|
||||||
░██║░░██╗░░██║██╔══██╗██║░░░██║██╔════╝██║░░░░░██╔════╝████╗░██║██╔════╝
|
|
||||||
░╚██╗████╗██╔╝███████║╚██╗░██╔╝█████╗░░██║░░░░░█████╗░░██╔██╗██║╚█████╗░
|
|
||||||
░░████╔═████║░██╔══██║░╚████╔╝░██╔══╝░░██║░░░░░██╔══╝░░██║╚████║░╚═══██╗
|
|
||||||
░░╚██╔╝░╚██╔╝░██║░░██║░░╚██╔╝░░███████╗███████╗███████╗██║░╚███║██████╔╝
|
|
||||||
░░░╚═╝░░░╚═╝░░╚═╝░░╚═╝░░░╚═╝░░░╚══════╝╚══════╝╚══════╝╚═╝░░╚══╝╚═════╝░
|
|
||||||
Server :o
|
|
||||||
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.programs.tmux;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
programs.tmux.recommendedDefaults = libS.mkOpinionatedOption "set recommended default settings";
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.recommendedDefaults {
|
|
||||||
programs.tmux = {
|
|
||||||
keyMode = "vi";
|
|
||||||
shortcut = "Space";
|
|
||||||
aggressiveResize = true;
|
|
||||||
baseIndex = 1;
|
|
||||||
clock24 = true;
|
|
||||||
escapeTime = 100;
|
|
||||||
terminal = "xterm-256color";
|
|
||||||
extraConfig = ''
|
|
||||||
# focus events enabled for terminals that support them
|
|
||||||
set -g focus-events on
|
|
||||||
|
|
||||||
# open new tab in PWD
|
|
||||||
bind '"' split-window -c "#{pane_current_path}"
|
|
||||||
bind % split-window -h -c "#{pane_current_path}"
|
|
||||||
bind c new-window -c "#{pane_current_path}"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
{ config, lib, libS, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.vaultwarden;
|
|
||||||
usingPostgres = cfg.dbBackend == "postgresql";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
services.vaultwarden = {
|
|
||||||
configureNginx = libS.mkOpinionatedOption "configure nginx for the configured domain";
|
|
||||||
|
|
||||||
domain = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = null;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The domain under which vaultwarden will be reachable.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "set recommended default settings";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
assertions = [ {
|
|
||||||
assertion = cfg.configureNginx -> cfg.domain != null;
|
|
||||||
message = ''
|
|
||||||
Setting services.vaultwarden.configureNginx to true requires configuring services.vaultwarden.domain!
|
|
||||||
'';
|
|
||||||
} ];
|
|
||||||
|
|
||||||
nixpkgs.overlays = lib.mkIf cfg.recommendedDefaults [
|
|
||||||
(final: prev: {
|
|
||||||
vaultwarden = prev.vaultwarden.overrideAttrs ({ patches ? [], ... }: {
|
|
||||||
patches = patches ++ [
|
|
||||||
# add eu region push support
|
|
||||||
(final.fetchpatch {
|
|
||||||
url = "https://github.com/dani-garcia/vaultwarden/pull/3752.diff";
|
|
||||||
hash = "sha256-QWbuUotNss1TkIIW6c54Y7U7u2yLg2xHopEngtNawcc=";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
});
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
services = {
|
|
||||||
nginx = lib.mkIf cfg.configureNginx {
|
|
||||||
upstreams.vaultwarden.servers."127.0.0.1:${toString config.services.vaultwarden.config.ROCKET_PORT}" = { };
|
|
||||||
virtualHosts.${cfg.domain}.locations = {
|
|
||||||
"/".proxyPass = "http://vaultwarden";
|
|
||||||
"/notifications/hub" = {
|
|
||||||
proxyPass = "http://vaultwarden";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
postgresql = lib.mkIf usingPostgres {
|
|
||||||
enable = true;
|
|
||||||
ensureDatabases = [ "vaultwarden" ];
|
|
||||||
ensureUsers = [{
|
|
||||||
name = "vaultwarden";
|
|
||||||
ensureDBOwnership = true;
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
|
|
||||||
vaultwarden.config = lib.mkMerge [
|
|
||||||
{
|
|
||||||
DATABASE_URL = lib.mkIf usingPostgres "postgresql:///vaultwarden?host=/run/postgresql";
|
|
||||||
DOMAIN = lib.mkIf (cfg.domain != null) "https://${cfg.domain}";
|
|
||||||
}
|
|
||||||
(lib.mkIf cfg.recommendedDefaults {
|
|
||||||
DATA_FOLDER = "/var/lib/vaultwarden"; # changes data directory
|
|
||||||
# TODO: change with 1.31.0 update
|
|
||||||
# ENABLE_WEBSOCKET = true;
|
|
||||||
LOG_LEVEL = "warn";
|
|
||||||
PASSWORD_ITERATIONS = 600000;
|
|
||||||
ROCKET_ADDRESS = "127.0.0.1";
|
|
||||||
ROCKET_PORT = lib.mkDefault 8222;
|
|
||||||
SIGNUPS_VERIFY = true;
|
|
||||||
TRASH_AUTO_DELETE_DAYS = 30;
|
|
||||||
WEBSOCKET_ADDRESS = "127.0.0.1";
|
|
||||||
WEBSOCKET_ENABLED = true;
|
|
||||||
WEBSOCKET_PORT = lib.mkDefault 8223;
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.vaultwarden = {
|
|
||||||
after = lib.mkIf usingPostgres [ "postgresql.service" ];
|
|
||||||
requires = lib.mkIf usingPostgres [ "postgresql.service" ];
|
|
||||||
serviceConfig = lib.mkIf cfg.recommendedDefaults {
|
|
||||||
StateDirectory = lib.mkForce "vaultwarden"; # modules defaults to bitwarden_rs
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
{ config, lib, libS, options, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.boot.zfs;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
boot.zfs = {
|
|
||||||
recommendedDefaults = libS.mkOpinionatedOption "enable recommended ZFS settings";
|
|
||||||
latestCompatibleKernel = libS.mkOpinionatedOption "use the latest ZFS compatible kernel";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enabled {
|
|
||||||
boot.kernelPackages =
|
|
||||||
let
|
|
||||||
ver = config.boot.zfs.package.latestCompatibleLinuxPackages.kernel.version;
|
|
||||||
in
|
|
||||||
# 6.0 has a bug in the bind syscall and does not error correct when the port is already in use
|
|
||||||
# https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/7VPNMC77YC3SI5LFYKUA4B5MTFPLTLVB/
|
|
||||||
# https://lore.kernel.org/stable/CAFsF8vL4CGFzWMb38_XviiEgxoKX0GYup=JiUFXUOmagdk9CRg@mail.gmail.com/
|
|
||||||
lib.mkIf (cfg.latestCompatibleKernel && lib.versions.majorMinor ver != "6.0") (lib.mkDefault config.boot.zfs.package.latestCompatibleLinuxPackages);
|
|
||||||
|
|
||||||
services.zfs = lib.mkIf cfg.recommendedDefaults {
|
|
||||||
autoScrub.enable = true;
|
|
||||||
trim.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.containers.storage.settings = lib.mkIf cfg.recommendedDefaults (lib.recursiveUpdate options.virtualisation.containers.storage.settings.default {
|
|
||||||
# fixes: Error: 'overlay' is not supported over zfs, a mount_program is required: backing file system is unsupported for this graph driver
|
|
||||||
storage.options.mount_program = "${pkgs.fuse-overlayfs}/bin/fuse-overlayfs";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
{ pkgs, lib, config, ... }:
|
{ pkgs, ... }:
|
||||||
let
|
{
|
||||||
in {
|
|
||||||
|
|
||||||
i18n = {
|
i18n = {
|
||||||
defaultLocale = "en_US.utf8";
|
defaultLocale = "en_US.utf8";
|
||||||
supportedLocales = [
|
supportedLocales = [
|
||||||
@ -10,7 +8,6 @@ in {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
@ -26,13 +23,6 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.brain = {
|
|
||||||
isNormalUser = true;
|
|
||||||
description = "Administrator";
|
|
||||||
extraGroups = [ "networkmanager" "wheel" ];
|
|
||||||
shell = pkgs.zsh;
|
|
||||||
};
|
|
||||||
|
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
@ -180,7 +170,7 @@ in {
|
|||||||
options = "--delete-oder-than 14d";
|
options = "--delete-oder-than 14d";
|
||||||
};
|
};
|
||||||
|
|
||||||
diff-system = true;
|
diffSystem = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
system = {
|
system = {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
{ pkgs, lib, config, ... }:
|
{ pkgs, ... }:
|
||||||
let
|
{
|
||||||
in {
|
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
console.keyMap = "us";
|
console.keyMap = "us";
|
||||||
|
|
||||||
|
@ -1,27 +1,8 @@
|
|||||||
{
|
{ pkgs, lib, config }:
|
||||||
pkgs,
|
import ../default.nix {
|
||||||
lib,
|
inherit pkgs lib config;
|
||||||
config,
|
userName = "AmethystAndroid";
|
||||||
}: let
|
pubKeys = {
|
||||||
pubKeys = import ./keys/default.nix;
|
palatine-hill = "ed25516-AAAAAAA";
|
||||||
in {
|
};
|
||||||
isNormalUser = true;
|
|
||||||
description = "AmethystAndroid";
|
|
||||||
uid = 1000;
|
|
||||||
extraGroups = [
|
|
||||||
"wheel"
|
|
||||||
"media"
|
|
||||||
(lib.mkIf config.networking.networkmanager.enable "networkmanager")
|
|
||||||
(lib.mkIf config.programs.adb.enable "adbusers")
|
|
||||||
(lib.mkIf config.programs.wireshark.enable "wireshark")
|
|
||||||
(lib.mkIf config.programs.virtualisation.docker.enable "docker")
|
|
||||||
"libvirtd"
|
|
||||||
"dialout"
|
|
||||||
"plugdev"
|
|
||||||
"uaccess"
|
|
||||||
];
|
|
||||||
shell = pkgs.fish;
|
|
||||||
openssh.authorizedKeys.keys = [
|
|
||||||
(lib.mkIf (pubKeys ? ${config.networking.hostName}) pubKeys.${config.networking.hostName})
|
|
||||||
];
|
|
||||||
}
|
}
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
palatine-hill = "ed25516-AAAAAAA";
|
|
||||||
}
|
|
21
users/default.nix
Normal file
21
users/default.nix
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{ lib, config, pkgs, userName, pubKeys }:
|
||||||
|
{
|
||||||
|
isNormalUser = true;
|
||||||
|
uid = 1000;
|
||||||
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
"media"
|
||||||
|
(lib.mkIf config.networking.networkmanager.enable "networkmanager")
|
||||||
|
(lib.mkIf config.programs.adb.enable "adbusers")
|
||||||
|
(lib.mkIf config.programs.wireshark.enable "wireshark")
|
||||||
|
(lib.mkIf config.programs.virtualisation.docker.enable "docker")
|
||||||
|
"libvirtd"
|
||||||
|
"dialout"
|
||||||
|
"plugdev"
|
||||||
|
"uaccess"
|
||||||
|
];
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
(lib.mkIf (pubKeys ? ${config.networking.hostName}) pubKeys.${config.networking.hostName})
|
||||||
|
];
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user