Compare commits

...

11 Commits

23 changed files with 276 additions and 1548 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ result*
*.bak
*.qcow2
*.raw
*.img
*.img
*.log

713
flake.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,6 @@
inputs = {
flake-utils-plus.url = "github:gytis-ivaskevicius/flake-utils-plus/v1.4.0";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# 6.1.55 kernel breaks podman. wait for fix
nixpkgs-pinned.url = "github:nixos/nixpkgs/9eebdbb7182caf58dbbc11a4c221c23e867cca08";
nixpkgs-master.url = "github:nixos/nixpkgs/master";
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-23.11";
flake-registry = {
@ -17,11 +15,15 @@
inputs.nixpkgs.follows = "nixpkgs";
};
impermanence.url = "github:nix-community/impermanence";
aagl.url = "github:ezKEa/aagl-gtk-on-nix";
aagl = {
url = "github:ezKEa/aagl-gtk-on-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
arkenfox-userjs = {
url = "github:arkenfox/user.js";
flake = false;
};
ataraxiasjel-nur.url = "github:AtaraxiaSjel/nur";
attic.url = "github:zhaofengli/attic";
base16.url = "github:AtaraxiaSjel/base16-nix";
base16-tokyonight-scheme = {
@ -33,7 +35,6 @@
inputs.nixpkgs.follows = "nixpkgs";
};
deploy-rs.url = "github:serokell/deploy-rs";
devenv.url = "github:cachix/devenv";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
@ -43,9 +44,7 @@
inputs.nixpkgs.follows = "nixpkgs"; # MESA/OpenGL HW workaround
};
hyprpaper = {
# TODO: return to upstream after fix merge
url = "github:AtaraxiaSjel/hyprpaper/fix-nix";
# url = "github:hyprwm/hyprpaper";
url = "github:hyprwm/hyprpaper";
inputs.nixpkgs.follows = "nixpkgs";
};
mms.url = "github:mkaito/nixos-modded-minecraft-servers";
@ -66,20 +65,12 @@
url = "github:nix-community/nixos-generators";
inputs.nixpkgs.follows = "nixpkgs";
};
nur.url = "github:nix-community/NUR";
# nur.url = "github:nix-community/NUR";
prismlauncher.url = "github:AtaraxiaSjel/PrismLauncher/develop";
rnix-lsp = {
url = "github:nix-community/rnix-lsp";
inputs.nixpkgs.follows = "nixpkgs";
};
rycee = {
url = "gitlab:rycee/nur-expressions";
flake = false;
};
simple-nixos-mailserver = {
url = "gitlab:simple-nixos-mailserver/nixos-mailserver";
inputs.nixpkgs.follows = "nixpkgs";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
@ -127,7 +118,10 @@
"webhooks.patch"
];
sharedOverlays = [ flake-utils-plus.overlay inputs.sops-nix.overlays.default ];
channelsConfig = { allowUnfree = true; android_sdk.accept_license = true; };
channelsConfig = {
allowUnfree = true; android_sdk.accept_license = true;
permittedInsecurePackages = [ "electron-25.9.0" ];
};
channels.unstable.input = nixpkgs;
channels.unstable.patches = patchesPath [ "zen-kernels.patch" "ydotoold.patch" ] ++ sharedPatches;
channels.stable.input = inputs.nixpkgs-stable;
@ -211,7 +205,7 @@
name = "aliases";
packages = with pkgs; [
rebuild update-vscode upgrade upgrade-hyprland
nixfmt nixpkgs-fmt statix vulnix deadnix git deploy-rs
nixfmt-rfc-style nixpkgs-fmt statix vulnix deadnix git deploy-rs
fup-repl ssh-to-pgp sops
];
};

View File

@ -1,4 +1,6 @@
{ config, inputs, ... }: {
imports = [ inputs.ataraxiasjel-nur.nixosModules.rustic ];
sops.secrets.rustic-nas-pass.sopsFile = inputs.self.secretsDir + /rustic.yaml;
sops.secrets.rclone-rustic-backups.sopsFile = inputs.self.secretsDir + /rustic.yaml;
services.rustic.backups = rec {

View File

@ -21,7 +21,7 @@ in {
customProfiles.hoyolab
customProfiles.inpx-web
customProfiles.it-tools
customProfiles.joplin-server
# customProfiles.joplin-server
customProfiles.media-stack
customProfiles.minio
customProfiles.nginx

View File

@ -1,4 +1,6 @@
{ config, inputs, ... }: {
imports = [ inputs.ataraxiasjel-nur.nixosModules.rustic ];
sops.secrets.rustic-vps-pass.sopsFile = inputs.self.secretsDir + /rustic.yaml;
sops.secrets.rclone-rustic-backups.sopsFile = inputs.self.secretsDir + /rustic.yaml;
services.rustic.backups = rec {
@ -31,7 +33,7 @@
ignore-devid = true;
group-by = "label";
sources = [{
source = "/srv/marzban /srv/nextcloud/config /srv/nextcloud/data";
source = "/srv/marzban";
}];
};
forget = {

View File

@ -1,5 +1,6 @@
{ config, pkgs, lib, inputs, ... }:
{ config, pkgs, inputs, ... }:
let
inherit (pkgs.hostPlatform) system;
cert-key = config.sops.secrets."cert.key".path;
cert-pem = config.sops.secrets."cert.pem".path;
nginx-conf = config.sops.secrets."nginx.conf".path;
@ -25,16 +26,6 @@ in {
};
virtualisation.oci-containers.containers = {
nextcloud = {
autoStart = true;
image = "docker.io/nextcloud:stable";
ports = [ "9765:80" ];
volumes = [
"/srv/nextcloud/html:/var/www/html"
"/srv/nextcloud/config:/var/www/html/config"
"/srv/nextcloud/data:/var/www/html/data"
];
};
marzban = {
autoStart = true;
image = "ghcr.io/gozargah/marzban:v0.4.1";
@ -57,10 +48,20 @@ in {
};
};
services.ocis = {
enable = true;
package = inputs.ataraxiasjel-nur.packages.${system}.ocis-bin;
configDir = "/srv/ocis/config";
baseDataPath = "/srv/ocis/data";
environment = {
OCIS_INSECURE = "false";
OCIS_URL = "https://cloud.ataraxiadev.com";
PROXY_HTTP_ADDR = "127.0.0.1:9200";
PROXY_TLS = "false";
};
};
systemd.tmpfiles.rules = [
"d /srv/marzban 0755 root root -"
"d /srv/nextcloud/html 0755 33 33 -"
"d /srv/nextcloud/config 0755 33 33 -"
"d /srv/nextcloud/data 0755 33 33 -"
];
}

View File

@ -1,320 +0,0 @@
# Thanks for original module, anpin! https://gist.github.com/anpin/ecbdb6625400908856ef9482eca3380c
{ config, pkgs, lib, ... }:
let
cfg = config.services.authentik;
databaseActuallyCreateLocally = cfg.database.createLocally
&& cfg.database.host == "/run/postgresql";
inherit (lib)
mkIf mkEnableOption mkOption types mdDoc literalExpression optional attrsets;
inherit (attrsets) optionalAttrs;
inherit (types) str bool port submodule package nullOr path enum;
hostWithPort = h: p: "${h}:${toString p}";
authentikBaseService = {
after = [ "network.target" ]
++ optional databaseActuallyCreateLocally "postgresql.service";
wantedBy = [ "multi-user.target" ];
path = [ cfg.package ];
environment = let
listenAddress = hostWithPort cfg.listen.address;
in {
AUTHENTIK_REDIS__HOST = cfg.redis.host;
AUTHENTIK_REDIS__PORT = toString cfg.redis.port;
AUTHENTIK_POSTGRESQL__HOST = cfg.database.host;
AUTHENTIK_POSTGRESQL__PORT = mkIf (cfg.database.port != null) "${toString cfg.database.port}";
AUTHENTIK_POSTGRESQL__USER = cfg.database.user;
AUTHENTIK_POSTGRESQL__NAME = cfg.database.name;
AUTHENTIK_LISTEN__HTTP = listenAddress cfg.listen.http;
AUTHENTIK_LISTEN__HTTPS = listenAddress cfg.listen.https;
# initial password for admin user
AUTHENTIK_BOOTSTRAP_PASSWORD = cfg.defaultPassword;
# disable outbound connections
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
AUTHENTIK_ERROR_REPORTING__ENABLED = "false";
AUTHENTIK_DISABLE_STARTUP_ANALYTICS = "true";
AUTHENTIK_AVATARS = "initials";
AUTHENTIK_LOG_LEVEL = cfg.logLevel;
};
serviceConfig = {
User = "authentik";
Group = "authentik";
EnvironmentFile = cfg.environmentFile;
WorkingDirectory = cfg.package;
DynamicUser = true;
RuntimeDirectory = "authentik";
NoNewPrivileges = true;
PrivateTmp = true;
ProtectHome = true;
ProtectSystem = "strict";
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
SystemCallFilter= "~@cpu-emulation @keyring @module @obsolete @raw-io @reboot @swap @sync";
ConfigurationDirectory = "authentik";
StateDirectoryMode = "0750";
};
};
in {
options.services.authentik = {
enable = mkEnableOption "Enables Authentik service";
package = mkOption {
type = package;
default = pkgs.authentik;
defaultText = literalExpression "pkgs.authentik";
description = mdDoc "Authentik package to use.";
};
defaultPassword = mkOption {
description = mdDoc "Default admin password. Only read on first startup.";
type = str;
default = "change-me";
};
logLevel = mkOption {
description = mdDoc
"Log level for the server and worker containers. Setting the log level to trace will include sensitive details in logs, so it shouldn't be used in most cases.";
type = enum [ "trace" "debug" "info" "warning" "error" ];
default = "info";
};
listen = mkOption {
description = mdDoc "Listen ports";
default = { };
type = submodule {
options = {
http = mkOption {
description = mdDoc "HTTP port.";
type = port;
default = 9000;
};
https = mkOption {
description = mdDoc "HTTPS port.";
type = port;
default = 9443;
};
address = mkOption {
description = mdDoc "Address to listen on.";
type = str;
default = "0.0.0.0";
};
};
};
};
redis = {
createLocally = mkOption {
description = mdDoc "Configure local Redis server for Authentik.";
type = bool;
default = true;
};
host = mkOption {
description = mdDoc "Redis host.";
type = str;
default = "127.0.0.1";
};
port = mkOption {
description = mdDoc "Redis port.";
type = port;
default = 31637;
};
};
ssl = {
cert = mkOption {
type = nullOr path;
default = null;
};
key = mkOption {
type = nullOr path;
default = null;
};
name = mkOption {
type = str;
default = "SSL from NIXOS";
};
};
environmentFile = mkOption {
type = nullOr path;
default = null;
example = "/var/lib/authentik/secrets/db-password";
description = mdDoc ''
Environment variables including :
- Secret key used for cookie signing and unique user IDs, don't change this after the first install.
'';
};
database = {
createLocally = mkOption {
description =
mdDoc "Configure local PostgreSQL database server for authentik.";
type = bool;
default = true;
};
host = mkOption {
type = str;
default = "/run/postgresql";
example = "192.168.23.42";
description = mdDoc "Database host address or unix socket.";
};
port = mkOption {
type = nullOr port;
default = if cfg.database.createLocally then null else 5432;
defaultText = literalExpression ''
if config.database.createLocally then null else 5432
'';
description = mdDoc "Database host port.";
};
name = mkOption {
type = str;
default = "authentik";
description = mdDoc "Database name.";
};
user = mkOption {
type = str;
default = "authentik";
description = mdDoc "Database user.";
};
};
outposts = mkOption {
type = submodule {
options = {
ldap = mkOption {
type = submodule {
options = {
enable =
mkEnableOption (lib.mdDoc "the authentik ldap outpost");
package = mkOption {
type = path;
default = pkgs.authentik-outposts.ldap;
};
host = mkOption {
type = str;
default = if cfg.outposts.ldap.insecure then
"http://127.0.0.1:${toString cfg.listen.http}"
else
"https://127.0.0.1:${toString cfg.listen.https}";
};
insecure = mkOption {
type = bool;
default = false;
};
environmentFile = mkOption {
type = nullOr path;
default = null;
example = "/var/lib/authentik-ldap/secrets/env";
description = mdDoc ''
Environment variables including :
- API TOKEN
'';
};
listen = mkOption {
description = mdDoc "Listen ports";
default = { };
type = submodule {
options = {
ldap = mkOption {
description = mdDoc "LDAP port.";
type = port;
default = 3389;
};
ldaps = mkOption {
description = mdDoc "LDAPS port.";
type = port;
default = 6636;
};
address = mkOption {
description = mdDoc "Address to listen on.";
type = str;
default = "0.0.0.0";
};
};
};
};
};
};
};
};
};
default = { ldap = { enable = false; }; };
};
};
config = mkIf cfg.enable {
users.users.authentik = {
isSystemUser = true;
home = cfg.package;
group = "authentik";
};
users.groups.authentik = { };
services.postgresql = mkIf databaseActuallyCreateLocally {
enable = true;
ensureUsers = [{
name = cfg.database.name;
ensureDBOwnership = true;
}];
ensureDatabases = [ cfg.database.name ];
};
services.redis.servers.authentik =
mkIf (cfg.redis.createLocally && cfg.redis.host == "127.0.0.1") {
enable = true;
port = cfg.redis.port;
bind = "127.0.0.1";
};
systemd.services.authentik-server = authentikBaseService // {
serviceConfig = authentikBaseService.serviceConfig // {
ExecStart = "${cfg.package}/bin/ak server";
};
};
systemd.services.authentik-worker = authentikBaseService // {
serviceConfig = authentikBaseService.serviceConfig // {
ExecStart = "${cfg.package}/bin/ak worker";
};
};
# systemd.services.authentik-ssl-import = authentikBaseService // {
# before = [ "authentik-server.service" ];
# serviceConfig = authentikBaseService.serviceConfig // {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStart = ''
# ${cfg.package}/bin/ak import_certificate --name "${cfg.ssl.name}" --certificate "${cfg.ssl.cert}" --private-key "${cfg.ssl.key}"'';
# };
# };
systemd.services.authentik-ldap-outpost = let
ldapCfg = cfg.outposts.ldap;
in mkIf ldapCfg.enable (authentikBaseService // {
description = "authentik ldap outpost";
environment = let listenAddress = hostWithPort ldapCfg.listen.address;
in {
AUTHENTIK_HOST = ldapCfg.host;
AUTHENTIK_LISTEN__LDAP = listenAddress ldapCfg.listen.ldap;
AUTHENTIK_LISTEN__LDAPS = listenAddress ldapCfg.listen.ldaps;
} // optionalAttrs ldapCfg.insecure { AUTHENTIK_INSECURE = "true"; };
serviceConfig = authentikBaseService.serviceConfig // {
ExecStart = "${cfg.outposts.ldap.package}/bin/ldap";
EnvironmentFile = ldapCfg.environmentFile;
};
});
};
}

View File

@ -58,13 +58,13 @@ with lib;
HEADSCALE_CLI_ADDRESS = "wg.ataraxiadev.com:443";
};
script = ''
auth_key=$(headscale preauthkeys create -e ${cfg.expire} -u ${cfg.user} -o json ${optionalString cfg.ephemeral "--ephemeral"} | jq -r .key)
if [ "$auth_key" = "null" ]; then
echo "Cannot retrieve auth key." >&2
exit 1
else
echo $auth_key > "${cfg.outPath}"
fi
while true; do
auth_key=$(headscale preauthkeys create -e ${cfg.expire} -u ${cfg.user} -o json ${optionalString cfg.ephemeral "--ephemeral"} | jq -r .key)
[[ "$auth_key" = "null" ]] || break
echo "Cannot retrieve auth key. Will try again after 5 seconds." >&2
sleep 5
done
echo $auth_key > "${cfg.outPath}"
'';
serviceConfig = {
EnvironmentFile = config.sops.secrets.headscale-api-env.path;

View File

@ -1,80 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.kes;
format = pkgs.formats.yaml { };
configFile = format.generate "config.yaml" cfg.settings;
port = strings.toInt (lists.last (strings.splitString ":" cfg.settings.address));
in
{
options.services.kes = {
enable = mkEnableOption (mdDoc "Minio's Key Managament Server");
package = mkOption {
type = types.package;
description = mdDoc "Which package to use for the kes instance.";
default = pkgs.minio-kes;
};
environmentFile = mkOption {
type = with types; nullOr str;
default = null;
description = lib.mdDoc ''
File in the format of an EnvironmentFile as described by systemd.exec(5).
'';
};
settings = mkOption {
type = format.type;
default = { address = "0.0.0.0:7373"; };
example = literalExpression ''
{
address = "0.0.0.0:7373";
cache = {
expiry = {
any = "5m0s";
unused = "20s";
};
};
}
'';
description = mdDoc ''
KES Configuration.
Refer to <https://github.com/minio/kes/blob/master/server-config.yaml>
for details on supported values.
'';
};
};
config = mkIf cfg.enable {
systemd.services.kes = {
description = "KES";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
path = [ cfg.package ];
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart = "${cfg.package}/bin/kes server --config ${configFile}";
User = "kes";
Group = "kes";
# WorkingDirectory = "/etc/kes";
AmbientCapabilities = mkIf (port < 1024) ["CAP_NET_BIND_SERVICE"];
LimitNOFILE = 65536;
ProtectProc = "invisible";
SendSIGKILL = "no";
TasksMax = "infinity";
TimeoutStopSec = "infinity";
} // optionalAttrs (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
};
};
environment.systemPackages = [ cfg.package ];
users.groups.kes = { };
users.users.kes = {
description = "KES user";
group = "kes";
isSystemUser = true;
};
};
}

View File

@ -1,159 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.ocis;
format = pkgs.formats.yaml { };
linkConfigs = confDir: lib.pipe cfg.settings [
(lib.attrsets.mapAttrs (n: v: format.generate "${n}.yaml" v))
(lib.mapAttrsToList (n: v: "ln -sf ${v} ${confDir}/${n}.yaml"))
(lib.concatStringsSep "\n")
];
mkExport = { arg, value }: "export ${arg}=${value}";
adminpass = {
arg = "ADMIN_PASSWORD";
value = ''"$(<"${toString cfg.adminpassFile}")"'';
};
in
{
options.services.ocis = {
enable = mkEnableOption (lib.mdDoc "ownCloud Infinite Scale Stack");
package = mkOption {
type = types.package;
description = lib.mdDoc "Which package to use for the ocis instance.";
default = pkgs.ocis-bin;
};
configDir = mkOption {
default = "/var/lib/ocis/.config";
type = types.path;
description = lib.mdDoc "The config directory. Set OCIS_CONFIG_DIR env variable.";
};
baseDataPath = mkOption {
default = "/var/lib/ocis";
type = types.path;
description = lib.mdDoc "The base data directory. Set OCIS_BASE_DATA_PATH env variable.";
};
environment = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
example = lib.literalExpression ''
{
OCIS_URL = "https://localhost:9200";
}
'';
description = lib.mdDoc "Environment variables to pass to ocis instance.";
};
environmentFile = mkOption {
type = with types; nullOr str;
default = null;
description = lib.mdDoc ''
File in the format of an EnvironmentFile as described by systemd.exec(5).
'';
};
adminpassFile = mkOption {
type = with types; nullOr str;
default = null;
description = lib.mdDoc ''
The full path to a file that contains the admin's password. Must be
readable by user `ocis`. The password is set only in the initial
setup of Ocis by the systemd service `ocis-init.service`.
'';
};
settings = mkOption {
type = with types; attrsOf format.type;
default = { };
example = lib.literalExpression ''
{
auth-bearer = {
tracing = {
enabled = true;
};
};
proxy = {
user_oidc_claim = "preferred_username";
user_cs3_claim = "username";
};
}
'';
description = lib.mdDoc ''
OCIS configuration. Refer to
<https://doc.owncloud.com/ocis/next/deployment/services/services.html>
for details on supported values.
'';
};
};
config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d '${cfg.configDir}' - ocis ocis - -"
"d '${cfg.baseDataPath}' - ocis ocis - -"
];
systemd.services.ocis-init = rec {
before = [ "ocis-server.service" ];
requiredBy = [ "ocis-server.service" ];
path = [ cfg.package ];
environment = {
OCIS_CONFIG_DIR = cfg.configDir;
OCIS_BASE_DATA_PATH = cfg.baseDataPath;
} // cfg.environment;
script = ''
${lib.optionalString (cfg.settings != { }) "${linkConfigs environment.OCIS_CONFIG_DIR}"}
if [ ! -f "$OCIS_CONFIG_DIR/ocis.yaml" ]; then
${
lib.optionalString (cfg.adminpassFile != null) ''
if [ ! -r "${cfg.adminpassFile}" ]; then
echo "adminpassFile ${cfg.adminpassFile} is not readable by ocis:ocis! Aborting..."
exit 1
fi
if [ -z "$(<${cfg.adminpassFile})" ]; then
echo "adminpassFile ${cfg.adminpassFile} is empty!"
exit 1
fi
${mkExport adminpass}
''
}
ocis init
fi
'';
serviceConfig = {
Type = "simple";
StateDirectory = "ocis";
User = "ocis";
Group = "ocis";
} // optionalAttrs (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
};
};
systemd.services.ocis-server = {
description = "ownCloud Infinite Scale Stack";
wantedBy = [ "multi-user.target" ];
path = [ cfg.package ];
environment = {
OCIS_CONFIG_DIR = cfg.configDir;
OCIS_BASE_DATA_PATH = cfg.baseDataPath;
OCIS_URL = "https://localhost:9200";
PROXY_HTTP_ADDR = "127.0.0.1:9200";
} // cfg.environment;
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart = "${cfg.package}/bin/ocis server";
User = "ocis";
Group = "ocis";
LimitNOFILE = 65536;
} // optionalAttrs (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
};
};
environment.systemPackages = [ cfg.package ];
users.groups.ocis = { };
users.users.ocis = {
description = "Ocis Daemon User";
group = "ocis";
isSystemUser = true;
};
};
}

View File

@ -19,6 +19,7 @@ with lib;
}));
default = { };
};
imports = [ inputs.ataraxiasjel-nur.nixosModules.rustic ];
config = mkIf (config.backups.postgresql != { }) {
sops.secrets.rclone-postgresql-backups.sopsFile = inputs.self.secretsDir + /rustic.yaml;
sops.secrets.rustic-postgresql-pass.sopsFile = inputs.self.secretsDir + /rustic.yaml;

View File

@ -1,324 +0,0 @@
{ config, lib, pkgs, utils, ... }:
with lib;
let
# Type for a valid systemd unit option. Needed for correctly passing "timerConfig" to "systemd.timers"
inherit (utils.systemdUtils.unitOptions) unitOption;
settingsFormat = pkgs.formats.toml {};
in
{
options.services.rustic.backups = mkOption {
description = lib.mdDoc ''
Periodic backups to create with Rustic.
'';
type = types.attrsOf (types.submodule ({ config, name, ... }: {
options = {
settings = mkOption {
type = settingsFormat.type;
default = {};
description = lib.mdDoc ''
'';
};
environmentFile = mkOption {
type = with types; nullOr str;
default = null;
description = lib.mdDoc ''
file containing the credentials to access the repository, in the
format of an EnvironmentFile as described by systemd.exec(5)
'';
};
extraEnvironment = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
example = lib.literalExpression ''
{
http_proxy = "http://server:12345";
}
'';
description = lib.mdDoc "Environment variables to pass to rustic.";
};
rcloneOptions = mkOption {
type = with types; nullOr (attrsOf (oneOf [ str bool ]));
default = null;
description = lib.mdDoc ''
Options to pass to rclone to control its behavior.
See <https://rclone.org/docs/#options> for
available options. When specifying option names, strip the
leading `--`. To set a flag such as
`--drive-use-trash`, which does not take a value,
set the value to the Boolean `true`.
'';
example = {
bwlimit = "10M";
drive-use-trash = "true";
};
};
rcloneConfigFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc ''
Path to the file containing rclone configuration. This file
must contain configuration for the remote specified in this backup
set and also must be readable by root. Options set in
`rcloneConfig` will override those set in this
file.
'';
};
timerConfig = mkOption {
type = types.attrsOf unitOption;
default = {
OnCalendar = "daily";
Persistent = true;
};
description = lib.mdDoc ''
When to run the backup. See {manpage}`systemd.timer(5)` for details.
'';
example = {
OnCalendar = "00:05";
RandomizedDelaySec = "5h";
Persistent = true;
};
};
user = mkOption {
type = types.str;
default = "root";
description = lib.mdDoc ''
As which user the backup should run.
'';
example = "postgresql";
};
extraBackupArgs = mkOption {
type = types.listOf types.str;
default = [ ];
description = lib.mdDoc ''
Extra arguments passed to rustic backup.
'';
example = [
"--exclude-file=/etc/nixos/rustic-ignore"
];
};
extraOptions = mkOption {
type = types.listOf types.str;
default = [ ];
description = lib.mdDoc ''
Extra extended options to be passed to the rustic --option flag.
'';
example = [
"sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'"
];
};
backup = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Start backup.
'';
};
prune = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Start prune.
'';
};
initialize = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Create the repository if it doesn't exist.
'';
};
initializeOpts = mkOption {
type = types.listOf types.str;
default = [ ];
description = lib.mdDoc ''
A list of options for 'rustic init'.
'';
example = [
"--set-version 2"
];
};
checkOpts = mkOption {
type = types.listOf types.str;
default = [ ];
description = lib.mdDoc ''
A list of options for 'rustic check', which is run after
pruning.
'';
example = [
"--with-cache"
];
};
pruneOpts = mkOption {
type = types.listOf types.str;
default = [ ];
description = lib.mdDoc ''
A list of options for 'rustic prune', which is run before
pruning.
'';
example = [
"--repack-cacheable-only=false"
];
};
backupCommandPrefix = mkOption {
type = types.str;
default = "";
description = lib.mdDoc ''
Prefix for backup command.
'';
};
backupCommandSuffix = mkOption {
type = types.str;
default = "";
description = lib.mdDoc ''
Suffix for backup command.
'';
};
backupPrepareCommand = mkOption {
type = with types; nullOr str;
default = null;
description = lib.mdDoc ''
A script that must run before starting the backup process.
'';
};
backupCleanupCommand = mkOption {
type = with types; nullOr str;
default = null;
description = lib.mdDoc ''
A script that must run after finishing the backup process.
'';
};
package = mkOption {
type = types.package;
default = pkgs.rustic-rs;
defaultText = literalExpression "pkgs.rustic-rs";
description = lib.mdDoc ''
Rustic package to use.
'';
};
createWrapper = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to generate and add a script to the system path, that has the same environment variables set
as the systemd service. This can be used to e.g. mount snapshots or perform other opterations, without
having to manually specify most options.
'';
};
};
}));
default = { };
};
config = {
systemd.services =
mapAttrs'
(name: backup:
let
profile = settingsFormat.generate "${name}.toml" backup.settings;
extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
rusticCmd = "${backup.package}/bin/rustic -P ${lib.strings.removeSuffix ".toml" profile}${extraOptions}";
# Helper functions for rclone remotes
rcloneAttrToOpt = v: "RCLONE_" + toUpper (builtins.replaceStrings [ "-" ] [ "_" ] v);
toRcloneVal = v: if lib.isBool v then lib.boolToString v else v;
in
nameValuePair "rustic-backups-${name}" ({
environment = backup.extraEnvironment // {
# not %C, because that wouldn't work in the wrapper script
RUSTIC_CACHE_DIR = "/var/cache/rustic-backups-${name}";
} // optionalAttrs (backup.rcloneConfigFile != null) {
RCLONE_CONFIG = backup.rcloneConfigFile;
} // optionalAttrs (backup.rcloneOptions != null) (mapAttrs'
(name: value:
nameValuePair (rcloneAttrToOpt name) (toRcloneVal value)
)
backup.rcloneOptions);
path = [ config.programs.ssh.package pkgs.rclone ];
restartIfChanged = false;
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
script = ''
${optionalString (backup.backup) ''
${backup.backupCommandPrefix} ${rusticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${backup.backupCommandSuffix}
''}
${optionalString (backup.prune) ''
${rusticCmd} forget --prune ${concatStringsSep " " backup.pruneOpts}
${rusticCmd} check ${concatStringsSep " " backup.checkOpts}
''}
'';
serviceConfig = {
Type = "oneshot";
User = backup.user;
RuntimeDirectory = "rustic-backups-${name}";
CacheDirectory = "rustic-backups-${name}";
CacheDirectoryMode = "0700";
PrivateTmp = true;
} // optionalAttrs (backup.environmentFile != null) {
EnvironmentFile = backup.environmentFile;
};
} // optionalAttrs (backup.initialize || backup.backupPrepareCommand != null) {
preStart = ''
${optionalString (backup.backupPrepareCommand != null) ''
${pkgs.writeScript "backupPrepareCommand" backup.backupPrepareCommand}
''}
${optionalString (backup.initialize) ''
${rusticCmd} init ${concatStringsSep " " backup.initializeOpts} || true
''}
'';
} // optionalAttrs (backup.backupCleanupCommand != null) {
postStop = ''
${optionalString (backup.backupCleanupCommand != null) ''
${pkgs.writeScript "backupCleanupCommand" backup.backupCleanupCommand}
''}
'';
})
)
config.services.rustic.backups;
systemd.timers =
mapAttrs'
(name: backup: nameValuePair "rustic-backups-${name}" {
wantedBy = [ "timers.target" ];
timerConfig = backup.timerConfig;
})
config.services.rustic.backups;
# generate wrapper scripts, as described in the createWrapper option
environment.systemPackages = lib.mapAttrsToList (name: backup: let
profile = settingsFormat.generate "${name}.toml" backup.settings;
extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
rusticCmd = "${backup.package}/bin/rustic -P ${lib.strings.removeSuffix ".toml" profile}${extraOptions}";
in pkgs.writeShellScriptBin "rustic-${name}" ''
set -a # automatically export variables
${lib.optionalString (backup.environmentFile != null) "source ${backup.environmentFile}"}
# set same environment variables as the systemd service
${lib.pipe config.systemd.services."rustic-backups-${name}".environment [
(lib.filterAttrs (_: v: v != null))
(lib.mapAttrsToList (n: v: "${n}=${v}"))
(lib.concatStringsSep "\n")
]}
exec ${rusticCmd} $@
'') (lib.filterAttrs (_: v: v.createWrapper) config.services.rustic.backups);
};
}

View File

@ -62,6 +62,7 @@ with config.deviceSpecific; {
# libreoffice
obs-studio
obs-studio-plugins.obs-vkcapture
obsidian
pinta
qbittorrent
sonixd
@ -92,6 +93,7 @@ with config.deviceSpecific; {
".config/kdeconnect"
".config/libreoffice"
".config/obs-studio"
".config/obsidian"
".config/pcmanfm"
# ".config/Pinta"
".config/qBittorrent"

View File

@ -123,12 +123,12 @@ in
"license.extension" = ".md";
"license.year" = "auto";
"nix.enableLanguageServer" = true;
"nix.formatterPath" = "${pkgs.nixfmt}/bin/nixfmt";
"nix.formatterPath" = "${pkgs.nixfmt-rfc-style}/bin/nixfmt";
"nix.serverPath" = "${pkgs.nil}/bin/nil";
"nix.serverSettings" = {
"nil" = {
"formatting" = {
"command" = ["${pkgs.nixfmt}/bin/nixfmt"];
"command" = ["${pkgs.nixfmt-rfc-style}/bin/nixfmt"];
};
};
};

View File

@ -9,16 +9,16 @@ let
config = config.nixpkgs.config;
localSystem = { inherit system; };
};
nur = import inputs.nur {
nurpkgs = import inputs.nixpkgs {
system = "x86_64-linux";
};
};
# nur = import inputs.nur {
# nurpkgs = import inputs.nixpkgs {
# system = "x86_64-linux";
# };
# };
in
with lib; {
nixpkgs.overlays = [
nur.repos.ataraxiasjel.overlays.default
nur.repos.ataraxiasjel.overlays.grub2-argon2
inputs.ataraxiasjel-nur.overlays.default
inputs.ataraxiasjel-nur.overlays.grub2-argon2
inputs.deploy-rs.overlay
inputs.hyprland.overlays.default
(final: prev:

View File

@ -1,4 +1,6 @@
{ config, inputs, ... }: {
imports = [ inputs.ataraxiasjel-nur.nixosModules.authentik ];
sops.secrets.authentik-env.sopsFile = inputs.self.secretsDir + /home-hypervisor/authentik.yaml;
sops.secrets.authentik-ldap.sopsFile = inputs.self.secretsDir + /home-hypervisor/authentik.yaml;
sops.secrets.authentik-env.restartUnits = [ "authentik-server.service" "authentik-worker.service" ];

View File

@ -40,11 +40,14 @@ in {
owner = "headscale";
restartUnits = [ "headscale.service" ];
};
systemd.services.headscale.after = lib.mkIf config.services.authentik.enable [
"authentik-server.service"
"authentik-worker.service"
"nginx.service"
];
systemd.services.headscale = {
serviceConfig.TimeoutStopSec = 10;
after = lib.mkIf config.services.authentik.enable [
"authentik-server.service"
"authentik-worker.service"
"nginx.service"
];
};
persist.state.directories = [ "/var/lib/headscale" ];
}

View File

@ -1,72 +1,30 @@
{ config, lib, pkgs, ... }:
let
backend = config.virtualisation.oci-containers.backend;
nas-path = "/media/nas/containers";
pod-name = "homepage-pod";
pod-dns = "192.168.0.1";
open-ports = [
"127.0.0.1:3000:3000/tcp"
];
in {
virtualisation.oci-containers.containers = {
homepage = {
autoStart = true;
image = "ghcr.io/gethomepage/homepage:v0.8.0";
environment = {
PUID = "1000";
PGID = "100";
};
extraOptions = [ "--pod=${pod-name}" ];
volumes = [
"${nas-path}/homepage/config:/app/config"
"${nas-path}/homepage/icons:/app/public/icons"
"${nas-path}/homepage/images:/app/public/images"
];
};
docker-proxy = {
autoStart = true;
image = "ghcr.io/tecnativa/docker-socket-proxy:0.1.1";
environment = {
CONTAINERS = "1";
SERVICES = "0";
TASKS = "0";
POST = "0";
};
extraOptions = [ "--pod=${pod-name}" ];
volumes = [
"${nas-path}/homepage/config:/app/config"
"${nas-path}/homepage/icons:/app/public/icons"
"${nas-path}/homepage/images:/app/public/images"
"/var/run/docker.sock:/var/run/docker.sock:ro"
];
};
{ config, inputs, ... }: {
imports = [ inputs.ataraxiasjel-nur.nixosModules.homepage ];
services.homepage-dashboard = {
enable = true;
listenPort = 3000;
dataDir = "/srv/homepage";
};
systemd.tmpfiles.rules = [
"d ${nas-path}/homepage/config 0755 1000 100 -"
"d ${nas-path}/homepage/icons 0755 1000 100 -"
"d ${nas-path}/homepage/images 0755 1000 100 -"
systemd.tmpfiles.rules = let
cfg = config.services.homepage-dashboard;
in [
"d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group} -"
];
systemd.services."podman-create-${pod-name}" = let
portsMapping = lib.concatMapStrings (port: " -p " + port) open-ports;
start = pkgs.writeShellScript "create-pod-${pod-name}" ''
podman pod exists ${pod-name} || podman pod create -n ${pod-name} ${portsMapping} --dns ${pod-dns}
'';
stop = "podman pod rm -i -f ${pod-name}";
in rec {
path = [ pkgs.coreutils config.virtualisation.podman.package ];
before = [
"${backend}-homepage.service"
"${backend}-docker-proxy.service"
];
requiredBy = before;
partOf = before;
serviceConfig = {
Type = "oneshot";
RemainAfterExit = "yes";
ExecStart = start;
ExecStop = stop;
virtualisation.oci-containers.containers.docker-proxy = {
autoStart = true;
image = "ghcr.io/tecnativa/docker-socket-proxy:0.1.1";
environment = {
CONTAINERS = "1";
SERVICES = "0";
TASKS = "0";
POST = "0";
};
ports = [ "127.0.0.1:2375:2375/tcp" ];
volumes = [
"/var/run/docker.sock:/var/run/docker.sock:ro"
];
};
}

View File

@ -13,6 +13,8 @@ let
restartUnits = [ "kes.service" ];
};
in {
imports = [ inputs.ataraxiasjel-nur.nixosModules.kes ];
sops.secrets.minio-credentials = minio-secret;
sops.secrets.kes-vault-env = kes-secret;
sops.secrets.kes-key = kes-secret;

View File

@ -5,6 +5,8 @@
sopsFile = inputs.self.secretsDir + /home-hypervisor/ocis.yaml;
restartUnits = [ "ocis-server.service" ];
};
imports = [ inputs.ataraxiasjel-nur.nixosModules.ocis ];
services.ocis = {
enable = true;
configDir = "/var/lib/ocis";

View File

@ -24,10 +24,16 @@ in {
wantedBy = [ "multi-user.target" ];
partOf = [ "vault.service" ];
after = [ "vault.service" ];
path = [ pkgs.curl ];
path = [ pkgs.curl pkgs.jq ];
script = ''
set -aeuo pipefail
set -a
source ${config.sops.secrets.vault-keys-env.path}
while true; do
initialized=$(curl -s ${api-addr}/v1/sys/health | jq -r '.initialized')
[[ "$initialized" = "true" ]] && break
echo "Vault has not been initialized yet. Will try again after 5 seconds." >&2
sleep 5
done
curl -H "Content-Type: application/json" --data "{\"key\":\"$VAULT_KEY1\"}" ${api-addr}/v1/sys/unseal >/dev/null 2>&1
curl -H "Content-Type: application/json" --data "{\"key\":\"$VAULT_KEY2\"}" ${api-addr}/v1/sys/unseal >/dev/null 2>&1
curl -H "Content-Type: application/json" --data "{\"key\":\"$VAULT_KEY3\"}" ${api-addr}/v1/sys/unseal >/dev/null 2>&1

File diff suppressed because one or more lines are too long