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 = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
nixos-modules = {
|
||||
url = "github:SuperSandro2000/nixos-modules";
|
||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
sops-nix = {
|
||||
url = "github:Mic92/sops-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
nixpkgs-stable.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
nix-index-database = {
|
||||
@ -15,33 +23,36 @@
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, nix-index-database, sops-nix, ... }: {
|
||||
src = builtins.filterSource (path: type: type == "directory" || lib.hasSuffix ".nix" (baseNameOf path)) ./.;
|
||||
ls = dir: lib.attrNames (builtins.readDir (src + "/${dir}"));
|
||||
fileList = dir: map (file: ./. + "/${dir}/${file}") (ls dir);
|
||||
outputs = { nixpkgs, nixos-modules, nix-index-database, sops-nix, ... }:
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
in {
|
||||
nixosConfigurations = let
|
||||
constructSystem = {
|
||||
hostname,
|
||||
system ? "x86_64-linux",
|
||||
modules ? [],
|
||||
users ? [],
|
||||
}: nixpkgs.lib.nixosSystem {
|
||||
inherit system hostname;
|
||||
}: lib.nixosSystem {
|
||||
inherit system;
|
||||
|
||||
modules = [
|
||||
nixos-modules.nixosModule
|
||||
sops-nix.nixosModules.sops
|
||||
nix-index-database.nixosModules.nix-index
|
||||
./system/programs.nix
|
||||
./system/configuration.nix
|
||||
./system/${hostname}/configuration.nix
|
||||
] ++ fileList "modules" ++ modules ++ map (user: ./users/${user}/default.nix ) users;
|
||||
./systems/programs.nix
|
||||
./systems/configuration.nix
|
||||
./systems/${hostname}/configuration.nix
|
||||
] ++ modules ++ map(user: ./users/${user}) users;
|
||||
|
||||
};
|
||||
in {
|
||||
photon = constructSystem {
|
||||
hostname = "photon"
|
||||
hostname = "photon";
|
||||
};
|
||||
|
||||
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, ... }:
|
||||
let
|
||||
in {
|
||||
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
i18n = {
|
||||
defaultLocale = "en_US.utf8";
|
||||
supportedLocales = [
|
||||
@ -10,7 +8,6 @@ in {
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||
|
||||
services = {
|
||||
@ -26,13 +23,6 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
users.users.brain = {
|
||||
isNormalUser = true;
|
||||
description = "Administrator";
|
||||
extraGroups = [ "networkmanager" "wheel" ];
|
||||
shell = pkgs.zsh;
|
||||
};
|
||||
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
programs = {
|
||||
@ -180,7 +170,7 @@ in {
|
||||
options = "--delete-oder-than 14d";
|
||||
};
|
||||
|
||||
diff-system = true;
|
||||
diffSystem = true;
|
||||
};
|
||||
|
||||
system = {
|
||||
|
@ -1,6 +1,5 @@
|
||||
{ pkgs, lib, config, ... }:
|
||||
let
|
||||
in {
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
time.timeZone = "America/New_York";
|
||||
console.keyMap = "us";
|
||||
|
||||
|
@ -1,27 +1,8 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
}: let
|
||||
pubKeys = import ./keys/default.nix;
|
||||
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})
|
||||
];
|
||||
{ pkgs, lib, config }:
|
||||
import ../default.nix {
|
||||
inherit pkgs lib config;
|
||||
userName = "AmethystAndroid";
|
||||
pubKeys = {
|
||||
palatine-hill = "ed25516-AAAAAAA";
|
||||
};
|
||||
}
|
@ -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