lets actually commit for once...
Some checks failed
Test action / kaas (push) Failing after 1s

This commit is contained in:
Chris Kruining 2025-11-20 00:05:34 +01:00
parent 169b62e6f3
commit 2d3da197ee
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
13 changed files with 711 additions and 74 deletions

View file

@ -1,19 +1,23 @@
set unstable
base_path := invocation_directory() / "systems/x86_64-linux" base_path := invocation_directory() / "systems/x86_64-linux"
sops := "nix shell nixpkgs#sops --command sops" # sops := "nix shell nixpkgs#sops --command sops"
yq := "nix shell nixpkgs#yq --command yq" # yq := "nix shell nixpkgs#yq --command yq"
sops := "sops"
yq := "yq"
@_default: @_default:
just --list just --list
[doc('list all vars of the target machine')] [doc('list all vars of the target machine')]
list machine: list machine:
{{ sops }} decrypt {{ base_path }}/{{ machine }}/secrets.yml sops decrypt {{ base_path }}/{{ machine }}/secrets.yml
@edit machine: @edit machine:
{{ sops }} edit {{ base_path }}/{{ machine }}/secrets.yml sops edit {{ base_path }}/{{ machine }}/secrets.yml
@set machine key value: @set machine key value:
{{ sops }} set {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')" "\"$(echo '{{ value }}' | sed 's/\"/\\"/g')\"" sops set {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')" "\"$(echo '{{ value }}' | sed 's/\"/\\\"/g')\""
git add {{ base_path }}/{{ machine }}/secrets.yml git add {{ base_path }}/{{ machine }}/secrets.yml
git commit -m 'chore(secrets): set secret "{{ key }}" for machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null git commit -m 'chore(secrets): set secret "{{ key }}" for machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
@ -21,10 +25,10 @@ list machine:
echo "Done" echo "Done"
@get machine key: @get machine key:
{{ sops }} decrypt {{ base_path }}/{{ machine }}/secrets.yml | {{ yq }} ".$(echo "{{ key }}" | sed -E 's/\//./g')" sops decrypt {{ base_path }}/{{ machine }}/secrets.yml | yq ".$(echo "{{ key }}" | sed -E 's/\//./g')"
@remove machine key: @remove machine key:
{{ sops }} unset {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')" sops unset {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')"
git add {{ base_path }}/{{ machine }}/secrets.yml git add {{ base_path }}/{{ machine }}/secrets.yml
git commit -m 'chore(secrets): removed secret "{{ key }}" from machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null git commit -m 'chore(secrets): removed secret "{{ key }}" from machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null

38
lib/options/default.nix Normal file
View file

@ -0,0 +1,38 @@
{ lib, ...}:
let
inherit (builtins) isString typeOf;
inherit (lib) mkOption types throwIfNot concatStringsSep splitStringBy toLower map;
in
{
options = {
mkUrlOptions =
defaults:
{
host = mkOption {
type = types.str;
example = "host.tld";
description = ''
Hostname
'';
} // (defaults.host or {});
port = mkOption {
type = types.port;
default = 1234;
example = "1234";
description = ''
Port
'';
} // (defaults.port or {});
protocol = mkOption {
type = types.str;
default = "https";
example = "https";
description = ''
Which protocol to use when creating a url string
'';
} // (defaults.protocol or {});
};
};
}

View file

@ -1,10 +1,15 @@
{ lib, ...}: { lib, ...}:
let let
inherit (builtins) isString typeOf; inherit (builtins) isString typeOf match toString head;
inherit (lib) throwIfNot concatStringsSep splitStringBy toLower map; inherit (lib) throwIfNot concatStringsSep splitStringBy toLower map concatMapAttrsStringSep;
in in
{ {
strings = { strings = {
#========================================================================================
# Converts a string to snake case
#
# simply replaces any uppercase letter to its lowercase variant preceeded by an underscore
#========================================================================================
toSnakeCase = toSnakeCase =
str: str:
throwIfNot (isString str) "toSnakeCase only accepts string values, but got ${typeOf str}" ( throwIfNot (isString str) "toSnakeCase only accepts string values, but got ${typeOf str}" (
@ -13,5 +18,22 @@ in
|> map (p: toLower p) |> map (p: toLower p)
|> concatStringsSep "_" |> concatStringsSep "_"
); );
#========================================================================================
# Converts a set of url parts to a string
#========================================================================================
toUrl =
{ protocol ? null, host, port ? null, path ? null, query ? null, hash ? null }:
let
trim_slashes = str: str |> match "^\/*(.+?)\/*$" |> head;
encode_to_str = set: concatMapAttrsStringSep "&" (n: v: "${n}=${v}") set;
_protocol = if protocol != null then "${protocol}://" else "";
_port = if port != null then ":${toString port}" else "";
_path = if path != null then "/${path |> trim_slashes}" else "";
_query = if query != null then "?${query |> encode_to_str}" else "";
_hash = if hash != null then "#${hash |> encode_to_str}" else "";
in
"${_protocol}${host}${_port}${_path}${_query}${_hash}";
}; };
} }

View file

@ -52,7 +52,7 @@ in {
}; };
emoji = { emoji = {
package = pkgs.noto-fonts-emoji; package = pkgs.noto-fonts-color-emoji;
name = "Noto Color Emoji"; name = "Noto Color Emoji";
}; };
}; };

View file

@ -1,6 +1,6 @@
{ config, lib, pkgs, namespace, system, inputs, ... }: { config, lib, pkgs, namespace, system, inputs, ... }:
let let
inherit (lib) mkIf mkEnableOption mkOption types toUpper toSentenceCase nameValuePair mapAttrs mapAttrs' concatMapAttrs filterAttrsRecursive listToAttrs imap0 head drop length literalExpression attrNames; inherit (lib) mkIf mkEnableOption mkOption types toUpper toSentenceCase nameValuePair mapAttrs mapAttrs' concatMapAttrs concatMapStringsSep filterAttrsRecursive listToAttrs imap0 head drop length literalExpression attrNames;
inherit (lib.${namespace}.strings) toSnakeCase; inherit (lib.${namespace}.strings) toSnakeCase;
cfg = config.${namespace}.services.authentication.zitadel; cfg = config.${namespace}.services.authentication.zitadel;
@ -334,6 +334,16 @@ in
concatMapAttrs (k: v: select (drop 1 keys) (callback k) (v.${key} or {})) set concatMapAttrs (k: v: select (drop 1 keys) (callback k) (v.${key} or {})) set
; ;
append = attrList: set: set // (listToAttrs attrList);
forEach = src: key: set:
let
_key = concatMapStringsSep "_" (k: "\${item.${k}}") key;
in
{
forEach = "{ for item in ${src} : \"${_key}\" => item }";
}
// set;
config' = config; config' = config;
# this is a nix package, the generated json file to be exact # this is a nix package, the generated json file to be exact
@ -418,7 +428,7 @@ in
# Users # Users
zitadel_human_user = zitadel_human_user =
(cfg.organization cfg.organization
|> select [ "user" ] (org: name: { email, userName, firstName, lastName, ... }: |> select [ "user" ] (org: name: { email, userName, firstName, lastName, ... }:
{ {
inherit email userName firstName lastName; inherit email userName firstName lastName;
@ -427,24 +437,20 @@ in
} }
|> withRef "org" org |> withRef "org" org
|> toResource "${org}_${name}" |> toResource "${org}_${name}"
)) )
|> append
// { [
"extra_users" = { (forEach "local.extra_users" [ "org" "name" ] {
for_each = lib.tfRef ''{ orgId = lib.tfRef "local.orgs[each.value.org]";
for user in local.extra_users : userName = lib.tfRef "each.value.name";
"''${user.org}_''${user.name}" => user
}'';
org_id = lib.tfRef "local.orgs[each.value.org]";
user_name = lib.tfRef "each.value.name";
email = lib.tfRef "each.value.email"; email = lib.tfRef "each.value.email";
first_name = lib.tfRef "each.value.firstName"; firstName = lib.tfRef "each.value.firstName";
last_name = lib.tfRef "each.value.lastName"; lastName = lib.tfRef "each.value.lastName";
is_email_verified = true; isEmailVerified = true;
};
} }
|> toResource "extraUsers")
]
; ;
# Global user roles # Global user roles
@ -708,6 +714,12 @@ in
restartUnits = [ "zitadelApplyTerraform.service" ]; restartUnits = [ "zitadelApplyTerraform.service" ];
}; };
}; };
templates = {
"users.yml" = {
};
};
}; };
}; };
} }

View file

@ -10,13 +10,22 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
programs.ssh.extraConfig = ''
Host beheer.hazelhof.nl
Port 222
User chris
AddressFamily inet
IdentityFile /home/chris/.ssh/id_ed25519
'';
services = { services = {
borgbackup.jobs = { borgbackup.jobs = {
media = { media = {
paths = "/var/media/test"; paths = "/var/media/test";
encryption.mode = "none"; encryption.mode = "none";
environment.BORG_SSH = "ssh -i /home/chris/.ssh/id_ed25519 -4"; # environment.BORG_SSH = "ssh -4 -i /home/chris/.ssh/id_ed25519";
repo = "ssh://chris@beheer.hazelhof.nl:222/media"; environment.BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
repo = "ssh://beheer.hazelhof.nl//media";
compression = "auto,zstd"; compression = "auto,zstd";
startAt = "daily"; startAt = "daily";
}; };

View file

@ -1,9 +1,11 @@
{ pkgs, lib, namespace, config, ... }: { pkgs, lib, namespace, config, inputs, system, ... }:
let let
inherit (lib) mkIf mkEnableOption mkOption; inherit (lib) mkIf mkEnableOption mkOption;
inherit (lib.types) str; inherit (lib.types) str;
cfg = config.${namespace}.services.media; cfg = config.${namespace}.services.media;
arr = ["radarr" ];
in in
{ {
options.${namespace}.services.media = { options.${namespace}.services.media = {
@ -60,47 +62,48 @@ in
"d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -" "d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -" "d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -" "d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -"
"d /var/lib/radarrApplyTerraform 0755 ${cfg.user} ${cfg.group} -"
]; ];
#========================================================================= #=========================================================================
# Services # Services
#========================================================================= #=========================================================================
services = let services = let
arrService = { arr-services =
arr
|> lib.imap (i: service: {
name = service;
value = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
environmentFiles = [
config.sops.templates."${service}/config.env".path
];
settings = { settings = {
auth.AuthenticationMethod = "External"; auth.authenticationMethod = "External";
};
server = {
bindaddress = "0.0.0.0";
port = 2000 + i;
}; };
withPort = port: service: service // { settings.server.Port = builtins.toString port; }; postgres = {
host = "localhost";
withUserAndGroup = service: service // { port = "5432";
user = cfg.user; user = service;
group = cfg.group; maindb = service;
logdb = service;
}; };
in { };
radarr = }
arrService // (if service != "prowlarr" then { user = cfg.user; group = cfg.group; } else {});
|> withPort 2001 })
|> withUserAndGroup; |> lib.listToAttrs
;
sonarr = in
arrService arr-services // {
|> withPort 2002
|> withUserAndGroup;
lidarr =
arrService
|> withPort 2003
|> withUserAndGroup;
prowlarr =
arrService
|> withPort 2004;
bazarr = { bazarr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
@ -146,6 +149,19 @@ in
group = cfg.group; group = cfg.group;
}; };
postgresql =
let
databases = arr |> lib.concatMap (s: [ s "${s}-log" ]);
in
{
enable = true;
ensureDatabases = arr;
ensureUsers = arr |> lib.map (service: {
name = service;
ensureDBOwnership = true;
});
};
caddy = { caddy = {
enable = true; enable = true;
virtualHosts = { virtualHosts = {
@ -156,6 +172,136 @@ in
}; };
}; };
systemd.services.radarrApplyTerraform =
let
# this is a nix package, the generated json file to be exact
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
modules = [
({ config, lib, ... }: {
config = {
variable = {
api_key = {
type = "string";
description = "Radarr api key";
};
};
terraform.required_providers.radarr = {
source = "devopsarr/radarr";
version = "2.2.0";
};
provider.radarr = {
url = "http://127.0.0.1:2001";
api_key = lib.tfRef "var.api_key";
};
resource = {
radarr_root_folder.local = {
path = "/var/media/movies";
};
};
};
})
];
};
in
{
description = "Radarr terraform apply";
wantedBy = [ "multi-user.target" ];
wants = [ "radarr.service" ];
script = ''
#!/usr/bin/env bash
if [ "$(systemctl is-active radarr)" != "active" ]; then
echo "Radarr is not running"
exit 1
fi
# Sleep for a bit to give radarr the chance to start up
sleep 5s
# Print the path to the source for easier debugging
echo "config location: ${terraformConfiguration}"
# Copy infra code into workspace
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${lib.getExe pkgs.opentofu} init
# Run the infrastructure code
# ${lib.getExe pkgs.opentofu} plan -var-file='${config.sops.templates."radarr/config.tfvars".path}'
${lib.getExe pkgs.opentofu} apply -auto-approve -var-file='${config.sops.templates."radarr/config.tfvars".path}'
'';
serviceConfig = {
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = "/var/lib/radarrApplyTerraform";
EnvironmentFile = [
config.sops.templates."radarr/config.env".path
];
};
};
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL"; systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
sops = {
secrets =
arr
|> lib.map (service: {
name = "${service}/apikey";
value = {
owner = cfg.user;
group = cfg.group;
restartUnits = [ "${service}.service" ];
};
})
|> lib.listToAttrs
;
templates =
let
apikeys =
arr
|> lib.map (service: {
name = "${service}/config.env";
value = {
owner = cfg.user;
group = cfg.group;
restartUnits = [ "${service}.service" ];
content = ''
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
'';
};
})
|> lib.listToAttrs;
tfvars =
arr
|> lib.map(service: {
name = "${service}/config.tfvars";
value = {
owner = cfg.user;
group = cfg.group;
restartUnits = [ "${service}ApplyTerraform.service" ];
content = ''
api_key = "${config.sops.placeholder."${service}/apikey"}"
'';
};
})
|> lib.listToAttrs;
in
apikeys // tfvars
;
};
}; };
} }

View file

@ -103,7 +103,7 @@ in
type = "Radarr"; type = "Radarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/radarr.svg"; logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/radarr.svg";
tag = "app"; tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.radarr.settings.server.port}"; url = "http://${config.networking.hostName}:2001";
target = "_blank"; target = "_blank";
} }
@ -112,7 +112,7 @@ in
type = "Sonarr"; type = "Sonarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sonarr.svg"; logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sonarr.svg";
tag = "app"; tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.sonarr.settings.server.port}"; url = "http://${config.networking.hostName}:2002";
target = "_blank"; target = "_blank";
} }
@ -121,7 +121,7 @@ in
type = "Lidarr"; type = "Lidarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/lidarr.svg"; logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/lidarr.svg";
tag = "app"; tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.lidarr.settings.server.port}"; url = "http://${config.networking.hostName}:2003";
target = "_blank"; target = "_blank";
} }
@ -130,7 +130,7 @@ in
type = "Prowlarr"; type = "Prowlarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prowlarr.svg"; logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prowlarr.svg";
tag = "app"; tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.prowlarr.settings.server.port}"; url = "http://${config.networking.hostName}:2004";
target = "_blank"; target = "_blank";
} }

View file

@ -0,0 +1,214 @@
{ pkgs, config, lib, namespace, inputs, system, ... }:
let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption mkOption types;
cfg = config.${namespace}.services.media.servarr;
in
{
options.${namespace}.services.media = {
servarr = mkOption {
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
enable = mkEnableOption "Enable ${name}";
debug = mkEnableOption "Use tofu plan instead of tofu apply for ${name} ";
port = mkOption {
type = types.port;
};
rootFolders = mkOption {
type = types.listOf types.str;
default = [];
};
};
}));
default = {};
};
};
config = {
services =
cfg
|> lib.mapAttrsToList (service: { enable, port, ... }: (mkIf enable {
"${service}" = {
enable = true;
openFirewall = true;
environmentFiles = [
config.sops.templates."${service}/config.env".path
];
settings = {
auth.authenticationMethod = "External";
server = {
bindaddress = "0.0.0.0";
port = port;
};
postgres = {
host = "localhost";
port = "5432";
user = service;
maindb = service;
logdb = service;
};
};
};
}))
|> lib.mergeAttrsList
|> (set: set // {
postgres = {
ensureDatabases = cfg |> lib.attrNames;
ensureUsers = cfg |> lib.attrNames |> lib.map (service: {
name = service;
ensureDBOwnership = true;
});
};
})
;
systemd =
cfg
|> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable {
tmpfiles.rules = [
"d /var/lib/${service}ApplyTerraform 0755 ${service} ${service} -"
];
services."${service}ApplyTerraform" =
let
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
modules = [
({ config, lib, ... }: {
config = {
variable = {
api_key = {
type = "string";
description = "${service} api key";
};
};
terraform.required_providers.${service} = {
source = "devopsarr/${service}";
version = "2.2.0";
};
provider.${service} = {
url = "http://127.0.0.1:${toString port}";
api_key = lib.tfRef "var.api_key";
};
resource = {
"${service}_root_folder" =
rootFolders
|> lib.imap (i: f: lib.nameValuePair "local${toString i}" { path = f; })
|> lib.listToAttrs
;
};
};
})
];
};
in
{
description = "${service} terraform apply";
wantedBy = [ "multi-user.target" ];
wants = [ "${service}.service" ];
script = ''
#!/usr/bin/env bash
# Sleep for a bit to give the service a chance to start up
sleep 5s
if [ "$(systemctl is-active ${service})" != "active" ]; then
echo "${service} is not running"
exit 1
fi
# Print the path to the source for easier debugging
echo "config location: ${terraformConfiguration}"
# Copy infra code into workspace
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${lib.getExe pkgs.opentofu} init
# Run the infrastructure code
${lib.getExe pkgs.opentofu} \
${if debug then "plan" else "apply -auto-approve"} \
-var-file='${config.sops.templates."${service}/config.tfvars".path}'
'';
serviceConfig = {
Type = "oneshot";
User = service;
Group = service;
WorkingDirectory = "/var/lib/${service}ApplyTerraform";
EnvironmentFile = [
config.sops.templates."${service}/config.env".path
];
};
};
}))
|> lib.mergeAttrsList
;
users.users =
cfg
|> lib.mapAttrsToList (service: { enable, ... }: (mkIf enable {
"${service}".extraGroups = [ "media" ];
}))
|> lib.mergeAttrsList
;
sops =
cfg
|> lib.mapAttrsToList (service: { enable, ... }: (mkIf enable {
secrets."${service}/apikey" = {
owner = service;
group = service;
restartUnits = [ "${service}.service" ];
};
templates = {
"${service}/config.env" = {
owner = service;
group = service;
restartUnits = [ "${service}.service" ];
content = ''
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
'';
};
"${service}/config.tfvars" = {
owner = service;
group = service;
restartUnits = [ "${service}.service" ];
content = ''
api_key = "${config.sops.placeholder."${service}/apikey"}"
'';
};
};
}))
|> lib.mergeAttrsList
;
};
# cfg
# |> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable {
# # sops = {
# # };
# }))
# |> lib.mergeAttrsList
# ;
}

View file

@ -0,0 +1,25 @@
{ pkgs, config, lib, namespace, ... }:
let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.observability.uptime-kuma;
in
{
options.${namespace}.services.observability.uptime-kuma = {
enable = mkEnableOption "enable uptime kuma";
};
config = mkIf cfg.enable {
services.uptime-kuma = {
enable = true;
settings = {
PORT = toString 9006;
HOST = "0.0.0.0";
};
};
networking.firewall.allowedTCPPorts = [ 9006 ];
};
}

View file

@ -1,13 +1,87 @@
{ pkgs, config, lib, namespace, ... }: { pkgs, config, lib, namespace, ... }:
let let
inherit (builtins) toString; inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption; inherit (lib) mkIf mkEnableOption mkOption types getAttrs toUpper concatMapAttrsStringSep;
cfg = config.${namespace}.services.security.vaultwarden; cfg = config.${namespace}.services.security.vaultwarden;
databaseProviderSqlite = types.submodule ({ ... }: {
options = {
type = mkOption {
type = types.enum [ "sqlite" ];
};
file = mkOption {
type = types.str;
description = '''';
};
};
});
databaseProviderPostgresql = types.submodule ({ ... }:
let
urlOptions = lib.${namespace}.options.mkUrlOptions {
host = {
description = ''
Hostname of the postgresql server
'';
};
port = {
default = 5432;
example = "5432";
description = ''
Port of the postgresql server
'';
};
protocol = mkOption {
default = "postgres";
example = "postgres";
};
};
in
{
options = {
type = mkOption {
type = types.enum [ "postgresql" ];
};
sslMode = mkOption {
type = types.enum [ "verify-ca" "verify-full" "require" "prefer" "allow" "disabled" ];
default = "verify-full";
example = "verify-ca";
description = ''
How to verify the server's ssl
| mode | eavesdropping protection | MITM protection | Statement |
|-------------|--------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| disable | No | No | I don't care about security, and I don't want to pay the overhead of encryption. |
| allow | Maybe | No | I don't care about security, but I will pay the overhead of encryption if the server insists on it. |
| prefer | Maybe | No | I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it. |
| require | Yes | No | I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want. |
| verify-ca | Yes | Depends on CA policy | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust. |
| verify-full | Yes | Yes | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify. |
[Source](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS)
'';
};
} // (urlOptions |> getAttrs [ "protocol" "host" "port" ]);
});
in in
{ {
options.${namespace}.services.security.vaultwarden = { options.${namespace}.services.security.vaultwarden = {
enable = mkEnableOption "enable vaultwarden"; enable = mkEnableOption "enable vaultwarden";
database = mkOption {
type = types.oneOf [
(types.addCheck databaseProviderSqlite (x: x ? type && x.type == "sqlite"))
(types.addCheck databaseProviderPostgresql (x: x ? type && x.type == "postgresql"))
null
];
default = null;
description = '''';
};
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -15,6 +89,8 @@ in
"d '/var/lib/vaultwarden' 0700 vaultwarden vaultwarden - -" "d '/var/lib/vaultwarden' 0700 vaultwarden vaultwarden - -"
]; ];
# systemd.services.vaultwarden.wants = [ "zitadelApplyTerraform.service" ];
services = { services = {
vaultwarden = { vaultwarden = {
enable = true; enable = true;
@ -26,8 +102,6 @@ in
SIGNUPS_ALLOWED = false; SIGNUPS_ALLOWED = false;
DOMAIN = "https://vault.kruining.eu"; DOMAIN = "https://vault.kruining.eu";
ADMIN_TOKEN = "";
DATABASE_URL = "postgres://localhost:5432/vaultwarden?sslmode=disable"; DATABASE_URL = "postgres://localhost:5432/vaultwarden?sslmode=disable";
WEB_VAULT_ENABLED = true; WEB_VAULT_ENABLED = true;
@ -41,9 +115,6 @@ in
SSO_ORGANIZATIONS_REVOCATION = true; SSO_ORGANIZATIONS_REVOCATION = true;
SSO_AUTHORITY = "https://auth.kruining.eu/"; SSO_AUTHORITY = "https://auth.kruining.eu/";
SSO_SCOPES = "email profile offline_access"; SSO_SCOPES = "email profile offline_access";
SSO_AUDIENCE_TRUSTED = "^333297815511892227$";
SSO_CLIENT_ID = "335178854421299459";
SSO_CLIENT_SECRET = "";
ROCKET_ADDRESS = "::1"; ROCKET_ADDRESS = "::1";
ROCKET_PORT = 8222; ROCKET_PORT = 8222;
@ -53,10 +124,14 @@ in
SMTP_PORT = 587; SMTP_PORT = 587;
SMTP_SECURITY = "starttls"; SMTP_SECURITY = "starttls";
SMTP_USERNAME = "chris@kruining.eu"; SMTP_USERNAME = "chris@kruining.eu";
SMTP_PASSWORD = "";
SMTP_FROM = "chris@kruining.eu"; SMTP_FROM = "chris@kruining.eu";
SMTP_FROM_NAME = "Chris' Vaultwarden"; SMTP_FROM_NAME = "Chris' Vaultwarden";
}; };
environmentFile = [
"/var/lib/zitadel/clients/nix_ulmo_vaultwarden"
config.sops.templates."vaultwarden/config.env".path
];
}; };
postgresql = { postgresql = {
@ -89,5 +164,54 @@ in
}; };
}; };
}; };
sops = {
secrets = {
"vaultwarden/email" = {
owner = config.users.users.vaultwarden.name;
group = config.users.users.vaultwarden.name;
key = "email/chris_kruining_eu";
restartUnits = [ "vaultwarden.service" ];
};
};
templates = {
"vaultwarden/config.env" = {
content = ''
SMTP_PASSWORD='${config.sops.placeholder."vaultwarden/email"}';
'';
owner = config.users.users.vaultwarden.name;
group = config.users.groups.vaultwarden.name;
};
temp-db-output.content =
let
config =
cfg.database
|> ({ type, ... }@db:
if type == "sqlite" then
{ inherit (db) type file; }
else if type == "postgresql" then
{
inherit (db) type;
url = lib.${namespace}.strings.toUrl {
inherit (db) protocol host port;
path = "vaultwarden";
query = {
sslmode = db.sslMode;
};
};
}
else
{}
)
|> concatMapAttrsStringSep "\n" (n: v: "${toUpper n}=${v}")
;
in
''
# GENERATED VALUES
${config}
'';
};
};
}; };
} }

View file

@ -5,6 +5,8 @@ mkShell {
bash bash
sops sops
just just
yq
pwgen
inputs.clan-core.packages.x86_64-linux.clan-cli inputs.clan-core.packages.x86_64-linux.clan-cli
]; ];
} }

View file

@ -147,15 +147,56 @@
media.enable = true; media.enable = true;
media.homer.enable = true; media.homer.enable = true;
media.nfs.enable = true; media.nfs.enable = true;
media.servarr = {
# radarr = {
# port = 2001;
# };
sonarr = {
enable = true;
# debug = true;
port = 2002;
rootFolders = [
"/var/media/series"
];
};
lidarr = {
enable = true;
debug = true;
port = 2003;
rootFolders = [
"/var/media/music"
];
};
prowlarr = {
enable = true;
debug = true;
port = 2004;
};
};
observability = { observability = {
grafana.enable = true; grafana.enable = true;
prometheus.enable = true; prometheus.enable = true;
loki.enable = true; loki.enable = true;
promtail.enable = true; promtail.enable = true;
# uptime-kuma.enable = true;
}; };
security.vaultwarden.enable = true; security.vaultwarden = {
enable = true;
database = {
# type = "sqlite";
# file = "/var/lib/vaultwarden/state.db";
type = "postgresql";
host = "localhost";
port = 5432;
sslMode = "disabled";
};
};
}; };
editor = { editor = {