add caldav/carddav server & redo some htpasswd stuff
This commit is contained in:
parent
ab830d4469
commit
65165b4a9d
3
home/apps/thunderbird.nix
Normal file
3
home/apps/thunderbird.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{pkgs, ...}: {
|
||||||
|
home.packages = with pkgs; [thunderbird];
|
||||||
|
}
|
58
hosts/hetzner-arm/containers/caldav/default.nix
Normal file
58
hosts/hetzner-arm/containers/caldav/default.nix
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
hostPath,
|
||||||
|
tree,
|
||||||
|
inputs,
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
containerAddresses = import "${hostPath}/data/containerAddresses.nix";
|
||||||
|
hostIP = containerAddresses.host;
|
||||||
|
containerIP = containerAddresses.containers.caldav;
|
||||||
|
in {
|
||||||
|
containers.caldav = {
|
||||||
|
autoStart = true;
|
||||||
|
privateNetwork = true;
|
||||||
|
hostAddress = hostIP;
|
||||||
|
localAddress = containerIP;
|
||||||
|
|
||||||
|
specialArgs = {
|
||||||
|
inherit inputs;
|
||||||
|
inherit tree;
|
||||||
|
inherit self;
|
||||||
|
inherit hostPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {...}: {
|
||||||
|
nixpkgs.pkgs = pkgs;
|
||||||
|
|
||||||
|
imports = with tree;
|
||||||
|
[
|
||||||
|
presets.nixos.containerBase
|
||||||
|
./secrets.nix
|
||||||
|
|
||||||
|
#./profiles/postgres.nix
|
||||||
|
#./profiles/restic.nix
|
||||||
|
]
|
||||||
|
++ (with hosts.hetzner-arm.containers.caldav.profiles; [
|
||||||
|
radicale
|
||||||
|
restic
|
||||||
|
]);
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [5232];
|
||||||
|
|
||||||
|
home-manager.users.root.home.stateVersion = "23.05";
|
||||||
|
system.stateVersion = "23.05";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."radicale.owo.monster" = {
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
locations."/".proxyPass = "http://${containerIP}:5232";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
31
hosts/hetzner-arm/containers/caldav/profiles/radicale.nix
Normal file
31
hosts/hetzner-arm/containers/caldav/profiles/radicale.nix
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{config, ...}: let
|
||||||
|
secrets = config.services.secrets.secrets;
|
||||||
|
in {
|
||||||
|
services.radicale = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
hosts = ["0.0.0.0:5232" "[::]:5232"];
|
||||||
|
};
|
||||||
|
auth = {
|
||||||
|
type = "htpasswd";
|
||||||
|
htpasswd_filename = "${secrets.radicale_htpasswd.path}";
|
||||||
|
htpasswd_encryption = "bcrypt";
|
||||||
|
};
|
||||||
|
rights = {
|
||||||
|
type = "owner_only";
|
||||||
|
};
|
||||||
|
storage = {
|
||||||
|
filesystem_folder = "/var/lib/radicale/collections";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.radicale.uid = 1000;
|
||||||
|
users.groups.radicale.gid = 1000;
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/lib/radicale - radicale radicale"
|
||||||
|
"d /var/lib/radicale/collections - radicale radicale"
|
||||||
|
];
|
||||||
|
}
|
39
hosts/hetzner-arm/containers/caldav/profiles/restic.nix
Normal file
39
hosts/hetzner-arm/containers/caldav/profiles/restic.nix
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
secrets = config.services.secrets.secrets;
|
||||||
|
in {
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
restic
|
||||||
|
(pkgs.writeShellScriptBin "restic-caldav" ''
|
||||||
|
env \
|
||||||
|
RESTIC_PASSWORD_FILE=${secrets.restic_password.path} \
|
||||||
|
$(cat ${secrets.restic_env.path}) \
|
||||||
|
${pkgs.restic}/bin/restic $@
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
services.restic.backups.caldav = {
|
||||||
|
user = "root";
|
||||||
|
paths = [
|
||||||
|
"/var/lib/radicale"
|
||||||
|
];
|
||||||
|
|
||||||
|
# repository is overrided in environmentFile to contain auth
|
||||||
|
# make sure to keep up to date when changing repository
|
||||||
|
repository = "rest:https://storage-restic.owo.monster/CalDAV";
|
||||||
|
passwordFile = "${secrets.restic_password.path}";
|
||||||
|
environmentFile = "${secrets.restic_env.path}";
|
||||||
|
|
||||||
|
pruneOpts = [
|
||||||
|
"--keep-last 50"
|
||||||
|
];
|
||||||
|
|
||||||
|
timerConfig = {
|
||||||
|
OnBootSec = "1m";
|
||||||
|
OnCalendar = "4h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
60
hosts/hetzner-arm/containers/caldav/secrets.nix
Normal file
60
hosts/hetzner-arm/containers/caldav/secrets.nix
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{pkgs, ...}: {
|
||||||
|
services.secrets = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
packages = with pkgs; [
|
||||||
|
apacheHttpd
|
||||||
|
];
|
||||||
|
|
||||||
|
vaultLogin = {
|
||||||
|
enable = true;
|
||||||
|
loginUsername = "hetzner-arm-container-caldav";
|
||||||
|
};
|
||||||
|
|
||||||
|
autoSecrets = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
requiredVaultPaths = [
|
||||||
|
"api-keys/data/caldav"
|
||||||
|
"api-keys/data/storage/restic/CalDAV"
|
||||||
|
"private-public-keys/data/restic/CalDAV"
|
||||||
|
];
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
vault_password = {
|
||||||
|
manual = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
radicale_htpasswd = {
|
||||||
|
user = "radicale";
|
||||||
|
group = "radicale";
|
||||||
|
fetchScript = ''
|
||||||
|
if [ -f "$secretFile" ]; then
|
||||||
|
rm "$secretFile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch "$secretFile"
|
||||||
|
|
||||||
|
data=$(kv_get "/api-keys/caldav" | base64)
|
||||||
|
for username in $(echo "$data" | base64 -d | jq -r ".data.data | keys | .[]"); do
|
||||||
|
password=$(echo "$data" | base64 -d | jq -r ".data.data.\"$username\"")
|
||||||
|
htpasswd -bB "$secretFile" "$username" "$password" 2>/dev/null
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
restic_password = {
|
||||||
|
fetchScript = ''
|
||||||
|
simple_get "/private-public-keys/restic/CalDAV" .password > "$secretFile"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
restic_env = {
|
||||||
|
fetchScript = ''
|
||||||
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/CalDAV" .restic)
|
||||||
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/CalDAV" > "$secretFile"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
{pkgs, ...}: {
|
{...}: {
|
||||||
services.secrets = {
|
services.secrets = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
|
@ -16,10 +16,6 @@
|
||||||
"private-public-keys/data/restic/Forgejo"
|
"private-public-keys/data/restic/Forgejo"
|
||||||
];
|
];
|
||||||
|
|
||||||
packages = with pkgs; [
|
|
||||||
apacheHttpd
|
|
||||||
];
|
|
||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
vault_password = {
|
vault_password = {
|
||||||
manual = true;
|
manual = true;
|
||||||
|
@ -32,9 +28,8 @@
|
||||||
};
|
};
|
||||||
restic_env = {
|
restic_env = {
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Forgejo" .username)
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Forgejo" .restic)
|
||||||
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Forgejo" .password)
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Forgejo" > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Forgejo" > "$secretFile"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,9 +36,8 @@
|
||||||
};
|
};
|
||||||
restic_env = {
|
restic_env = {
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Mail" .username)
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Mail" .restic)
|
||||||
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Mail" .password)
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Mail" > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Mail" > "$secretFile"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
private_mail_aliases = {
|
private_mail_aliases = {
|
||||||
|
|
|
@ -47,14 +47,13 @@ in {
|
||||||
inherit backupPrepareCommand;
|
inherit backupPrepareCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.postgresqlBackup = {
|
services.postgreSQLRemoteBackup = {
|
||||||
enable = true;
|
enable = true;
|
||||||
backupAll = false;
|
backupUser = "postgres";
|
||||||
databases = [
|
databases = [
|
||||||
"piped"
|
"piped"
|
||||||
"gotosocial"
|
"gotosocial"
|
||||||
"quassel"
|
"quassel"
|
||||||
];
|
];
|
||||||
compression = "zstd";
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,8 @@
|
||||||
};
|
};
|
||||||
restic_env = {
|
restic_env = {
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/PostgreSQL" .username)
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/PostgreSQL" .restic)
|
||||||
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/PostgreSQL" .password)
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/PostgreSQL" > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/PostgreSQL" > "$secretFile"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,9 +28,8 @@
|
||||||
};
|
};
|
||||||
restic_env = {
|
restic_env = {
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Quassel" .username)
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Quassel" .restic)
|
||||||
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Quassel" .password)
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Quassel" > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Quassel" > "$secretFile"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,9 +31,8 @@
|
||||||
};
|
};
|
||||||
restic_env = {
|
restic_env = {
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Social" .username)
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Social" .restic)
|
||||||
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Social" .password)
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Social" > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Social" > "$secretFile"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
env_secrets = {
|
env_secrets = {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
rclone_serve_webdav_main = 4200;
|
rclone_serve_webdav_main = 4200;
|
||||||
rclone_serve_webdav_media = 4201;
|
rclone_serve_webdav_media = 4201;
|
||||||
rclone_serve_webdav_music_ro = 4202;
|
rclone_serve_webdav_music_ro = 4202;
|
||||||
|
rclone_serve_webdav_public = 4202;
|
||||||
|
rclone_serve_webdav_uploads = 4202;
|
||||||
|
|
||||||
rclone_serve_restic_music = 4210;
|
rclone_serve_restic_music = 4210;
|
||||||
rclone_serve_restic_vault = 4211;
|
rclone_serve_restic_vault = 4211;
|
||||||
|
@ -10,7 +12,9 @@
|
||||||
rclone_serve_restic_postgresql = 4214;
|
rclone_serve_restic_postgresql = 4214;
|
||||||
rclone_serve_restic_mail = 4215;
|
rclone_serve_restic_mail = 4215;
|
||||||
rclone_serve_restic_forgejo = 4216;
|
rclone_serve_restic_forgejo = 4216;
|
||||||
|
rclone_serve_restic_caldav = 4217;
|
||||||
|
|
||||||
rclone_serve_http_music = 4220;
|
rclone_serve_http_music = 4220;
|
||||||
rclone_serve_http_public = 4221;
|
rclone_serve_http_public = 4221;
|
||||||
|
rclone_serve_http_uploads_public = 4221;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ in {
|
||||||
"/Main/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_main}";
|
"/Main/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_main}";
|
||||||
"/Media/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_media}";
|
"/Media/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_media}";
|
||||||
"/MusicRO/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_music_ro}";
|
"/MusicRO/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_music_ro}";
|
||||||
|
"/Uploads/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_webdav_uploads}";
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
client_max_body_size ${clientMaxBodySize};
|
client_max_body_size ${clientMaxBodySize};
|
||||||
|
@ -81,6 +82,7 @@ in {
|
||||||
locations = {
|
locations = {
|
||||||
"/Music/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_music}";
|
"/Music/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_music}";
|
||||||
"/Public/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_public}";
|
"/Public/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_public}";
|
||||||
|
"/Uploads/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_http_uploads_public}";
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
client_max_body_size ${clientMaxBodySize};
|
client_max_body_size ${clientMaxBodySize};
|
||||||
|
@ -98,6 +100,7 @@ in {
|
||||||
"/PostgreSQL/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_postgresql}";
|
"/PostgreSQL/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_postgresql}";
|
||||||
"/Mail/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_mail}";
|
"/Mail/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_mail}";
|
||||||
"/Forgejo/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_forgejo}";
|
"/Forgejo/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_forgejo}";
|
||||||
|
"/CalDAV/".proxyPass = "http://${containerIP}:${toString ports.rclone_serve_restic_caldav}";
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
client_max_body_size ${clientMaxBodySize};
|
client_max_body_size ${clientMaxBodySize};
|
||||||
|
|
|
@ -56,6 +56,27 @@ in {
|
||||||
"--baseurl=/MusicRO/"
|
"--baseurl=/MusicRO/"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
id = "public";
|
||||||
|
remote = "StorageBox:Public";
|
||||||
|
type = "webdav";
|
||||||
|
extraArgs = [
|
||||||
|
"--addr=0.0.0.0:${toString ports.rclone_serve_webdav_public}"
|
||||||
|
"--htpasswd=${secrets.webdav_media_htpasswd.path}"
|
||||||
|
"--baseurl=/Public/"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id = "uploads";
|
||||||
|
remote = "StorageBox:Uploads";
|
||||||
|
type = "webdav";
|
||||||
|
extraArgs = [
|
||||||
|
"--addr=0.0.0.0:${toString ports.rclone_serve_webdav_uploads}"
|
||||||
|
"--htpasswd=${secrets.webdav_uploads_htpasswd.path}"
|
||||||
|
"--baseurl=/Uploads/"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
id = "music-ro";
|
id = "music-ro";
|
||||||
remote = "StorageBox:Music";
|
remote = "StorageBox:Music";
|
||||||
|
@ -76,6 +97,16 @@ in {
|
||||||
"--read-only"
|
"--read-only"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
id = "uploads-public";
|
||||||
|
remote = "StorageBox:Uploads/Public";
|
||||||
|
type = "http";
|
||||||
|
extraArgs = [
|
||||||
|
"--addr=0.0.0.0:${toString ports.rclone_serve_http_uploads_public}"
|
||||||
|
"--baseurl=/Uploads/"
|
||||||
|
"--read-only"
|
||||||
|
];
|
||||||
|
}
|
||||||
{
|
{
|
||||||
id = "restic-music";
|
id = "restic-music";
|
||||||
remote = "StorageBox:Backups/Restic/Music";
|
remote = "StorageBox:Backups/Restic/Music";
|
||||||
|
@ -126,6 +157,16 @@ in {
|
||||||
"--baseurl=/PostgreSQL/"
|
"--baseurl=/PostgreSQL/"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
id = "restic-caldav";
|
||||||
|
remote = "StorageBox:Backups/Restic/CalDAV";
|
||||||
|
type = "restic";
|
||||||
|
extraArgs = [
|
||||||
|
"--addr=0.0.0.0:${toString ports.rclone_serve_restic_caldav}"
|
||||||
|
"--htpasswd=${secrets.restic_caldav_htpasswd.path}"
|
||||||
|
"--baseurl=/CalDAV/"
|
||||||
|
];
|
||||||
|
}
|
||||||
{
|
{
|
||||||
id = "restic-mail";
|
id = "restic-mail";
|
||||||
remote = "StorageBox:Backups/Restic/Mail";
|
remote = "StorageBox:Backups/Restic/Mail";
|
||||||
|
|
|
@ -31,9 +31,11 @@
|
||||||
"api-keys/data/storage/restic/PostgreSQL"
|
"api-keys/data/storage/restic/PostgreSQL"
|
||||||
"api-keys/data/storage/restic/Mail"
|
"api-keys/data/storage/restic/Mail"
|
||||||
"api-keys/data/storage/restic/Forgejo"
|
"api-keys/data/storage/restic/Forgejo"
|
||||||
|
"api-keys/data/storage/restic/CalDAV"
|
||||||
|
|
||||||
"api-keys/data/storage/webdav/main"
|
"api-keys/data/storage/webdav/Main"
|
||||||
"api-keys/data/storage/webdav/media"
|
"api-keys/data/storage/webdav/Media"
|
||||||
|
"api-keys/data/storage/webdav/Public"
|
||||||
|
|
||||||
"private-public-keys/data/rclone/Chaos-Media-Crypt"
|
"private-public-keys/data/rclone/Chaos-Media-Crypt"
|
||||||
];
|
];
|
||||||
|
@ -75,36 +77,46 @@
|
||||||
sed -i "s/$replace_password/$password/" "$3"
|
sed -i "s/$replace_password/$password/" "$3"
|
||||||
sed -i "s/$replace_salt/$salt/" "$3"
|
sed -i "s/$replace_salt/$salt/" "$3"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simple_get_htpasswd() {
|
||||||
|
if [ -f "$2" ]; then
|
||||||
|
rm "$2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch "$2"
|
||||||
|
|
||||||
|
data=$(kv_get "$1" | base64)
|
||||||
|
for username in $(echo "$data" | base64 -d | jq -r ".data.data | keys | .[]"); do
|
||||||
|
password=$(echo "$data" | base64 -d | jq -r ".data.data.\"$username\"")
|
||||||
|
htpasswd -b "$2" "$username" "$password" 2>/dev/null
|
||||||
|
done
|
||||||
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
vault_password = {manual = true;};
|
vault_password = {
|
||||||
|
manual = true;
|
||||||
|
};
|
||||||
|
|
||||||
restic_music_htpasswd = {
|
restic_music_htpasswd = {
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/Music" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/Music" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/Music" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
restic_vault_htpasswd = {
|
restic_vault_htpasswd = {
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/Vault" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/Vault" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/Vault" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
restic_social_htpasswd = {
|
restic_social_htpasswd = {
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/Social" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/Social" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/Social" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,9 +124,7 @@
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/Quassel" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/Quassel" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/Quassel" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -122,9 +132,7 @@
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/PostgreSQL" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/PostgreSQL" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/PostgreSQL" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,9 +140,7 @@
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/Mail" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/Mail" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/Mail" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,9 +148,15 @@
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/restic/Forgejo" .username)
|
simple_get_htpasswd "/api-keys/storage/restic/Forgejo" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/restic/Forgejo" .password)
|
'';
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
};
|
||||||
|
|
||||||
|
restic_caldav_htpasswd = {
|
||||||
|
user = "storage";
|
||||||
|
group = "storage";
|
||||||
|
fetchScript = ''
|
||||||
|
simple_get_htpasswd "/api-keys/storage/restic/CalDAV" "$secretFile"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,18 +164,28 @@
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/webdav/main" .username)
|
simple_get_htpasswd "/api-keys/storage/webdav/Main" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/webdav/main" .password)
|
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
webdav_media_htpasswd = {
|
webdav_media_htpasswd = {
|
||||||
user = "storage";
|
user = "storage";
|
||||||
group = "storage";
|
group = "storage";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
username=$(simple_get "/api-keys/storage/webdav/media" .username)
|
simple_get_htpasswd "/api-keys/storage/webdav/Media" "$secretFile"
|
||||||
password=$(simple_get "/api-keys/storage/webdav/media" .password)
|
'';
|
||||||
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
|
};
|
||||||
|
webdav_public_htpasswd = {
|
||||||
|
user = "storage";
|
||||||
|
group = "storage";
|
||||||
|
fetchScript = ''
|
||||||
|
simple_get_htpasswd "/api-keys/storage/webdav/Public" "$secretFile"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
webdav_uploads_htpasswd = {
|
||||||
|
user = "storage";
|
||||||
|
group = "storage";
|
||||||
|
fetchScript = ''
|
||||||
|
simple_get_htpasswd "/api-keys/storage/webdav/Uploads" "$secretFile"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,5 +8,6 @@
|
||||||
forgejo = "10.0.1.6";
|
forgejo = "10.0.1.6";
|
||||||
postgresql = "10.0.1.7";
|
postgresql = "10.0.1.7";
|
||||||
piped-fi = "10.0.1.8";
|
piped-fi = "10.0.1.8";
|
||||||
|
caldav = "10.0.1.9";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ in {
|
||||||
"piped-fi"
|
"piped-fi"
|
||||||
"mail"
|
"mail"
|
||||||
"forgejo"
|
"forgejo"
|
||||||
|
"caldav"
|
||||||
] (name: ./containers + "/${name}"))
|
] (name: ./containers + "/${name}"))
|
||||||
++ (with hosts.hetzner-arm.profiles; [
|
++ (with hosts.hetzner-arm.profiles; [
|
||||||
staticSites
|
staticSites
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
restic_music_env = {
|
restic_music_env = {
|
||||||
user = "chaos";
|
user = "chaos";
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
api_username=$(simple_get "/api-keys/storage/restic/Music" .username)
|
api_username=restic
|
||||||
api_password=$(simple_get "/api-keys/storage/restic/Music" .password)
|
api_password=$(simple_get "/api-keys/storage/restic/Music" ".$api_username")
|
||||||
restic_password=$(simple_get "/private-public-keys/restic/Music" .password)
|
restic_password=$(simple_get "/private-public-keys/restic/Music" .password)
|
||||||
|
|
||||||
echo > "$secretFile"
|
echo > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://''${api_username}:''${api_password}@storage-restic.owo.monster/Music" >> "$secretFile"
|
echo "RESTIC_REPOSITORY=rest:https://$api_username:$api_password@storage-restic.owo.monster/Music" >> "$secretFile"
|
||||||
echo "RESTIC_PASSWORD=''${restic_password}" >> "$secretFile"
|
echo "RESTIC_PASSWORD=''${restic_password}" >> "$secretFile"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
tree.modules.nixos.rcloneServe
|
tree.modules.nixos.rcloneServe
|
||||||
tree.modules.nixos.rcloneSync
|
tree.modules.nixos.rcloneSync
|
||||||
tree.modules.nixos.secrets
|
tree.modules.nixos.secrets
|
||||||
|
tree.modules.nixos.postgreSQLRemoteBackup
|
||||||
];
|
];
|
||||||
|
|
||||||
nixosUnstableSystem = nixpkgs-unstable.lib.nixosSystem;
|
nixosUnstableSystem = nixpkgs-unstable.lib.nixosSystem;
|
||||||
|
|
|
@ -65,9 +65,8 @@
|
||||||
};
|
};
|
||||||
restic_env = {
|
restic_env = {
|
||||||
fetchScript = ''
|
fetchScript = ''
|
||||||
RESTIC_USERNAME=$(simple_get "/api-keys/storage/restic/Vault" .username)
|
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Vault" .restic)
|
||||||
RESTIC_PASSWORD=$(simple_get "/api-keys/storage/restic/Vault" .password)
|
echo "RESTIC_REPOSITORY=rest:https://restic:$RESTIC_PASSWORD@storage-restic.owo.monster/Vault" > "$secretFile"
|
||||||
echo "RESTIC_REPOSITORY=rest:https://$RESTIC_USERNAME:$RESTIC_PASSWORD@storage-restic.owo.monster/Vault" > "$secretFile"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
165
modules/nixos/postgreSQLRemoteBackup.nix
Normal file
165
modules/nixos/postgreSQLRemoteBackup.nix
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (builtins) listToAttrs getAttr;
|
||||||
|
inherit (lib.modules) mkIf mkMerge;
|
||||||
|
inherit (lib.options) mkOption mkEnableOption mdDoc;
|
||||||
|
inherit (lib.strings) optionalString;
|
||||||
|
inherit (lib.attrsets) attrValues;
|
||||||
|
inherit (lib) types;
|
||||||
|
|
||||||
|
cfg = config.services.postgreSQLRemoteBackup;
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
# TODO: add host, port, user options
|
||||||
|
services.postgreSQLRemoteBackup = {
|
||||||
|
enable = mkEnableOption (mdDoc "PostgreSQL database dumps");
|
||||||
|
|
||||||
|
keepPrev = mkOption {
|
||||||
|
default = true;
|
||||||
|
type = types.bool;
|
||||||
|
description = mdDoc ''
|
||||||
|
Keep the previous run's backups but rename them to $name.prev
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
startAt = mkOption {
|
||||||
|
default = "4h";
|
||||||
|
type = with types; either (listOf str) str;
|
||||||
|
description = mdDoc ''
|
||||||
|
This option defines (see `systemd.time` for format) when the
|
||||||
|
databases should be dumped.
|
||||||
|
The default is run every 4 hours.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
backupUser = mkOption {
|
||||||
|
default = "root";
|
||||||
|
type = types.str;
|
||||||
|
description = mdDoc ''
|
||||||
|
User which will be used for backup job and files
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
databases = mkOption {
|
||||||
|
default = [];
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = mdDoc ''
|
||||||
|
List of database names to dump.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
location = mkOption {
|
||||||
|
default = "/var/backup/postgresql";
|
||||||
|
type = types.path;
|
||||||
|
description = mdDoc ''
|
||||||
|
Path of directory where the PostgreSQL database dumps will be placed.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
pgdumpOptions = mkOption {
|
||||||
|
type = types.separatedString " ";
|
||||||
|
default = "-C";
|
||||||
|
description = mdDoc ''
|
||||||
|
Command line options for pg_dump.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
compression = mkOption {
|
||||||
|
type = types.enum ["none" "zstd"];
|
||||||
|
default = "zstd";
|
||||||
|
description = mdDoc ''
|
||||||
|
The type of compression to use on the generated database dump.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
compressionLevel = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 9;
|
||||||
|
description = mdDoc ''
|
||||||
|
The compression level used when compression is enabled.
|
||||||
|
zstd accepts levels 1 to 19.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkMerge [
|
||||||
|
(mkIf cfg.enable {
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d '${cfg.location}' 0700 ${cfg.backupUser} - - -"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
(mkIf (cfg.enable) {
|
||||||
|
systemd.services = listToAttrs (map (db: {
|
||||||
|
name = "remotePostgreSQLBackup-${db}";
|
||||||
|
value = let
|
||||||
|
compressSuffixes = {
|
||||||
|
"none" = "";
|
||||||
|
"zstd" = ".zstd";
|
||||||
|
};
|
||||||
|
compressSuffix = getAttr cfg.compression compressSuffixes;
|
||||||
|
|
||||||
|
compressCmd = getAttr cfg.compression {
|
||||||
|
"none" = "cat";
|
||||||
|
"zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel}";
|
||||||
|
};
|
||||||
|
|
||||||
|
mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
|
||||||
|
curFile = mkSqlPath "" compressSuffix;
|
||||||
|
prevFile = mkSqlPath ".prev" compressSuffix;
|
||||||
|
prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes);
|
||||||
|
inProgressFile = mkSqlPath ".in-progress" compressSuffix;
|
||||||
|
in {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
description = "Backup of ${db} database(s)";
|
||||||
|
|
||||||
|
requires = mkIf (config.services.postgresql.enable) [
|
||||||
|
"postgresql.service"
|
||||||
|
];
|
||||||
|
|
||||||
|
path = [
|
||||||
|
pkgs.coreutils
|
||||||
|
(let
|
||||||
|
pgCfg = config.services.postgresql;
|
||||||
|
in
|
||||||
|
if pgCfg.enable
|
||||||
|
then pgCfg.package
|
||||||
|
else pkgs.postgresql)
|
||||||
|
];
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
set -e -o pipefail
|
||||||
|
|
||||||
|
umask 0077 # ensure backup is only readable by backup user
|
||||||
|
|
||||||
|
${optionalString (cfg.keepPrev) ''
|
||||||
|
if [ -e ${curFile} ]; then
|
||||||
|
rm -f ${toString prevFiles}
|
||||||
|
mv ${curFile} ${prevFile}
|
||||||
|
fi
|
||||||
|
''}
|
||||||
|
|
||||||
|
pg_dump ${cfg.pgdumpOptions} ${db} \
|
||||||
|
| ${compressCmd} \
|
||||||
|
> ${inProgressFile}
|
||||||
|
|
||||||
|
mv ${inProgressFile} ${curFile}
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.backupUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
startAt = cfg.startAt;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
cfg.databases);
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
|
@ -141,7 +141,7 @@ in
|
||||||
# All machines/containers with secrets.nix
|
# All machines/containers with secrets.nix
|
||||||
machines = rec {
|
machines = rec {
|
||||||
"hetzner-arm" = {
|
"hetzner-arm" = {
|
||||||
containers = ["storage" "music" "quassel" "social" "mail" "postgresql" "piped-fi" "forgejo"];
|
containers = ["storage" "music" "quassel" "social" "mail" "postgresql" "piped-fi" "forgejo" "caldav"];
|
||||||
sshAddress = "hetzner-arm.servers.genderfucked.monster";
|
sshAddress = "hetzner-arm.servers.genderfucked.monster";
|
||||||
};
|
};
|
||||||
"vault" = {
|
"vault" = {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
modules.nixos.rcloneServe
|
modules.nixos.rcloneServe
|
||||||
modules.nixos.rcloneSync
|
modules.nixos.rcloneSync
|
||||||
modules.nixos.secrets
|
modules.nixos.secrets
|
||||||
|
modules.nixos.postgreSQLRemoteBackup
|
||||||
])
|
])
|
||||||
++ [
|
++ [
|
||||||
# Default modules which are usually included in nixos.nix
|
# Default modules which are usually included in nixos.nix
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
home.apps.firefox
|
home.apps.firefox
|
||||||
home.apps.telegram
|
home.apps.telegram
|
||||||
|
home.apps.thunderbird
|
||||||
home.apps.quassel
|
home.apps.quassel
|
||||||
home.apps.session
|
home.apps.session
|
||||||
home.apps.toot-cli
|
home.apps.toot-cli
|
||||||
|
|
|
@ -22,8 +22,7 @@ in {
|
||||||
config = {
|
config = {
|
||||||
allowUnfree = true;
|
allowUnfree = true;
|
||||||
|
|
||||||
permittedInsecurePackages = [
|
permittedInsecurePackages = [];
|
||||||
];
|
|
||||||
};
|
};
|
||||||
overlays = [
|
overlays = [
|
||||||
(import ../../overlay)
|
(import ../../overlay)
|
||||||
|
|
Loading…
Reference in a new issue