summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hosts/surtr/default.nix2
-rw-r--r--hosts/surtr/postgresql/default.nix (renamed from hosts/surtr/postgresql.nix)69
-rw-r--r--hosts/surtr/postgresql/pgbackrest.crt13
-rw-r--r--hosts/surtr/postgresql/pgbackrest.key26
-rw-r--r--hosts/surtr/ruleset.nft8
-rw-r--r--hosts/vidhar/default.nix2
-rw-r--r--hosts/vidhar/dns/zones/yggdrasil.soa5
-rw-r--r--hosts/vidhar/network/ruleset.nft6
-rw-r--r--hosts/vidhar/pgbackrest/ca/ca.crt12
-rw-r--r--hosts/vidhar/pgbackrest/ca/ca.key21
-rw-r--r--hosts/vidhar/pgbackrest/default.nix101
-rw-r--r--hosts/vidhar/pgbackrest/tls.crt12
-rw-r--r--hosts/vidhar/pgbackrest/tls.key26
-rw-r--r--modules/pgbackrest.nix192
14 files changed, 489 insertions, 6 deletions
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix
index 9ac087c3..f6200cf3 100644
--- a/hosts/surtr/default.nix
+++ b/hosts/surtr/default.nix
@@ -2,7 +2,7 @@
2{ 2{
3 imports = with flake.nixosModules.systemProfiles; [ 3 imports = with flake.nixosModules.systemProfiles; [
4 tmpfs-root qemu-guest openssh rebuild-machines zfs 4 tmpfs-root qemu-guest openssh rebuild-machines zfs
5 ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix 5 ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql
6 ./prometheus ./email ./vpn ./borg.nix ./etebase 6 ./prometheus ./email ./vpn ./borg.nix ./etebase
7 ]; 7 ];
8 8
diff --git a/hosts/surtr/postgresql.nix b/hosts/surtr/postgresql/default.nix
index c10c5084..9cf494ae 100644
--- a/hosts/surtr/postgresql.nix
+++ b/hosts/surtr/postgresql/default.nix
@@ -1,4 +1,4 @@
1{ pkgs, sources, config, ... }: 1{ pkgs, sources, config, flake, ... }:
2let 2let
3 versioning = sources.psql-versioning.src; 3 versioning = sources.psql-versioning.src;
4in { 4in {
@@ -8,6 +8,73 @@ in {
8 package = pkgs.postgresql_14; 8 package = pkgs.postgresql_14;
9 }; 9 };
10 10
11 services.pgbackrest = {
12 enable = true;
13 settings = {
14 "surtr" = {
15 pg1-path = config.services.postgresql.dataDir;
16
17 repo1-path = "/var/lib/pgbackrest";
18 repo1-retention-full-type = "time";
19 repo1-retention-full = 7;
20 repo1-retention-archive = 2;
21
22 repo2-host-type = "tls";
23 repo2-host = "pgbackrest.vidhar.yggdrasil";
24 repo2-host-ca-file = toString ../../vidhar/pgbackrest/ca/ca.crt;
25 repo2-host-cert-file = toString ./pgbackrest.crt;
26 repo2-host-key-file = config.sops.secrets."pgbackrest.key".path;
27 repo2-retention-full-type = "time";
28 repo2-retention-full = 14;
29 repo2-retention-archive = 7;
30 };
31
32 "global" = {
33 compress-type = "zst";
34 compress-level = 9;
35
36 archive-async = true;
37 spool-path = "/var/spool/pgbackrest";
38 };
39
40 "global:server" = {
41 tls-server-address = "2a03:4000:52:ada:1::";
42 tls-server-ca-file = toString ../../vidhar/pgbackrest/ca/ca.crt;
43 tls-server-cert-file = toString ./pgbackrest.crt;
44 tls-server-key-file = config.sops.secrets."pgbackrest.key".path;
45 tls-server-auth = ["vidhar.yggdrasil=surtr"];
46 };
47
48 "global:archive-push" = {
49 process-max = 2;
50 };
51 "global:archive-get" = {
52 process-max = 2;
53 };
54 };
55
56 tlsServer.enable = true;
57
58 backups."surtr-daily" = {
59 stanza = "surtr";
60 repo = "1";
61 timerConfig.OnCalendar = "daily";
62 };
63 };
64
65 sops.secrets."pgbackrest.key" = {
66 format = "binary";
67 sopsFile = ./pgbackrest.key;
68 owner = "postgres";
69 group = "postgres";
70 mode = "0400";
71 };
72
73 systemd.tmpfiles.rules = [
74 "d /var/lib/pgbackrest 0750 postgres postgres - -"
75 "d /var/spool/pgbackrest 0750 postgres postgres - -"
76 ];
77
11 systemd.services.migrate-postgresql = { 78 systemd.services.migrate-postgresql = {
12 after = [ "postgresql.service" ]; 79 after = [ "postgresql.service" ];
13 bindsTo = [ "postgresql.service" ]; 80 bindsTo = [ "postgresql.service" ];
diff --git a/hosts/surtr/postgresql/pgbackrest.crt b/hosts/surtr/postgresql/pgbackrest.crt
new file mode 100644
index 00000000..b4dc4d97
--- /dev/null
+++ b/hosts/surtr/postgresql/pgbackrest.crt
@@ -0,0 +1,13 @@
1-----BEGIN CERTIFICATE-----
2MIIB7zCCAW+gAwIBAgIPQAAAAGN7p/Q5SZ7JU43JMAUGAytlcTAfMR0wGwYDVQQD
3DBRwZ2JhY2tyZXN0LnlnZ2RyYXNpbDAeFw0yMjExMjExNjI2MTFaFw0zMjExMjEx
4NjMxMTFaMBoxGDAWBgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhABIl
5okEGkov33jgsrF0QA4CKQILbIWkZ2tn+UUhXxxyDo4HGMIHDMB8GA1UdIwQYMBaA
6FO+/yfEkwcLr+vNPIsyCW86UwJ3aMB0GA1UdDgQWBBQnVeShLYsqF35OmmzLJEV5
7dfenhjAOBgNVHQ8BAf8EBAMCBeAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggr
8BgEFBQcDAgYIKwYBBQUHAwEwRAYDVR0RBD0wO4IdcGdiYWNrcmVzdC5zdXJ0ci55
9Z2dkcmFzaWwubGmCGnBnYmFja3Jlc3Quc3VydHIueWdnZHJhc2lsMAUGAytlcQNz
10AJqqMDWN1Ym5XANRKWcCh09j0Rej3V64XZlOOP7qFF9Gh4QJXeCvDMjX4LOeRUmi
11lB8iosdRN9MSANI4kfwYBnzgn3BNMrvMI4faEOuVnd6X2ulsJdNbJNQzB3hRVsNf
12b+QNBV+PpTUgR4k9e1XWX+wwAA==
13-----END CERTIFICATE-----
diff --git a/hosts/surtr/postgresql/pgbackrest.key b/hosts/surtr/postgresql/pgbackrest.key
new file mode 100644
index 00000000..bc2af12d
--- /dev/null
+++ b/hosts/surtr/postgresql/pgbackrest.key
@@ -0,0 +1,26 @@
1{
2 "data": "ENC[AES256_GCM,data:Bg4fIAqIGLF1P1P583vQnHhjzrD8fdnS5tA/7SuSdBRJjVaRzB0bieEv+2i9WxgaStG9TTUSmClCVUsbR5gy7MoV6Br4AL17Y++R6wPpJbQJvtMMDJB2xg+THU/Ex61dendcWqPYh73Wn4U9uBE/wC1eVrShXRM=,iv:YG/foZwVcrzi6hdk7Vk0sYZ92LMbmiKg1SbAgPaeUNM=,tag:lAcoxUfQXB4vvc6XnIcA/g==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": null,
9 "lastmodified": "2022-11-21T14:30:27Z",
10 "mac": "ENC[AES256_GCM,data:Dsfc1XrGl4abSnDqRl/IwC11bVy+kHz1RaI0V/nkkaJ3fM/qTXPVc5mMoWCiPn1nz5BTABQRSnrf79qHc0wpZ1WUpn07yOf7JejJ/T/bUC7D8BuoVdWRh1og+NzWCEIwaGXg0Eo04yli+GXisdM3YVM9g3BrxYrSInjnNZFyB+Q=,iv:T5QprwIhB8ZWwmmfWVtxkXqbMB1onW+wX7GPIFMn+z0=,tag:zMi77nMepajhg2Djgz8rBA==,type:str]",
11 "pgp": [
12 {
13 "created_at": "2022-11-21T14:30:27Z",
14 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdAi3pfg9DA+1v5r5sEijbkdwmOopWh05IuhRJxuy1btyAw\nuo0iV7VpngK8tFcBHnmhx3QsxIJo/gU+xrOwczW3RoSGrWo9tV2FantQPRp6f1aS\n0lwBEJSxmTApD/YDu3M6WhxN49/ZVEXG+KQ/mOdoBo0ITGKa6No0btMolzJ0bCJU\n+/avVdlDdZzfXo9XP0iJUoqh+1yMn+XdnD5deGac8a/QGvXZkxsYQ8KpK9sONA==\n=QyKr\n-----END PGP MESSAGE-----\n",
15 "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
16 },
17 {
18 "created_at": "2022-11-21T14:30:27Z",
19 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdAYU2U/anEJ8JSiG7NBppmsFeogXN3ynOEdq2tHXf+mUww\nIS7kW1pqcGMjnf7RQNuL91Wek5GEk4T498IFadiYDImAfIdS5jeX2w7UvxWLX5OZ\n0lwBlnxOwkYRWZzAhB6jHthmk2zEc+0JKuFolXhrwXqsFwFGoLTO9fctJrV7ry0u\naM9DqXru+/cEUZJDSq5GYDQaxTjyaFMVwLVdfxrtFwc8YMlqU8vVoWTqLaUVYA==\n=Tg80\n-----END PGP MESSAGE-----\n",
20 "fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
21 }
22 ],
23 "unencrypted_suffix": "_unencrypted",
24 "version": "3.7.3"
25 }
26} \ No newline at end of file
diff --git a/hosts/surtr/ruleset.nft b/hosts/surtr/ruleset.nft
index 51fcd498..4993b6b7 100644
--- a/hosts/surtr/ruleset.nft
+++ b/hosts/surtr/ruleset.nft
@@ -82,6 +82,7 @@ table inet filter {
82 counter submissions-rx {} 82 counter submissions-rx {}
83 counter imaps-rx {} 83 counter imaps-rx {}
84 counter managesieve-rx {} 84 counter managesieve-rx {}
85 counter pgbackrest-rx {}
85 86
86 counter established-rx {} 87 counter established-rx {}
87 88
@@ -109,6 +110,7 @@ table inet filter {
109 counter submissions-tx {} 110 counter submissions-tx {}
110 counter imaps-tx {} 111 counter imaps-tx {}
111 counter managesieve-tx {} 112 counter managesieve-tx {}
113 counter pgbackrest-tx {}
112 114
113 counter tx {} 115 counter tx {}
114 116
@@ -149,7 +151,7 @@ table inet filter {
149 151
150 152
151 ct state invalid log level debug prefix "drop invalid input: " counter name invalid-rx drop 153 ct state invalid log level debug prefix "drop invalid input: " counter name invalid-rx drop
152 154
153 155
154 iifname lo counter name rx-lo accept 156 iifname lo counter name rx-lo accept
155 iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject 157 iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject
@@ -178,6 +180,7 @@ table inet filter {
178 tcp dport 465 counter name submissions-rx accept 180 tcp dport 465 counter name submissions-rx accept
179 tcp dport 993 counter name imaps-rx accept 181 tcp dport 993 counter name imaps-rx accept
180 tcp dport 4190 counter name managesieve-rx accept 182 tcp dport 4190 counter name managesieve-rx accept
183 iifname yggdrasil tcp dport 8432 counter name pgbackrest-rx accept
181 184
182 ct state {established, related} counter name established-rx accept 185 ct state {established, related} counter name established-rx accept
183 186
@@ -222,7 +225,8 @@ table inet filter {
222 tcp sport 465 counter name submissions-tx accept 225 tcp sport 465 counter name submissions-tx accept
223 tcp sport 993 counter name imaps-tx accept 226 tcp sport 993 counter name imaps-tx accept
224 tcp sport 4190 counter name managesieve-tx accept 227 tcp sport 4190 counter name managesieve-tx accept
225 228 tcp sport 8432 counter name pgbackrest-tx accept
229
226 230
227 counter name tx 231 counter name tx
228 } 232 }
diff --git a/hosts/vidhar/default.nix b/hosts/vidhar/default.nix
index fc04d3f5..5c23dea2 100644
--- a/hosts/vidhar/default.nix
+++ b/hosts/vidhar/default.nix
@@ -4,7 +4,7 @@ with lib;
4 4
5{ 5{
6 imports = with flake.nixosModules.systemProfiles; [ 6 imports = with flake.nixosModules.systemProfiles; [
7 ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg 7 ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest
8 tmpfs-root zfs 8 tmpfs-root zfs
9 initrd-all-crypto-modules default-locale openssh rebuild-machines 9 initrd-all-crypto-modules default-locale openssh rebuild-machines
10 build-server 10 build-server
diff --git a/hosts/vidhar/dns/zones/yggdrasil.soa b/hosts/vidhar/dns/zones/yggdrasil.soa
index 3d9d4d83..045e49f8 100644
--- a/hosts/vidhar/dns/zones/yggdrasil.soa
+++ b/hosts/vidhar/dns/zones/yggdrasil.soa
@@ -1,7 +1,7 @@
1$ORIGIN yggdrasil. 1$ORIGIN yggdrasil.
2$TTL 300 2$TTL 300
3@ IN SOA vidhar.yggdrasil. root.yggdrasil.li. ( 3@ IN SOA vidhar.yggdrasil. root.yggdrasil.li. (
4 2022101601 ; serial 4 2022112101 ; serial
5 300 ; refresh 5 300 ; refresh
6 300 ; retry 6 300 ; retry
7 300 ; expire 7 300 ; expire
@@ -16,8 +16,11 @@ sif IN AAAA 2a03:4000:52:ada:1:2::
16 16
17grafana.vidhar IN CNAME vidhar.yggdrasil. 17grafana.vidhar IN CNAME vidhar.yggdrasil.
18prometheus.vidhar IN CNAME vidhar.yggdrasil. 18prometheus.vidhar IN CNAME vidhar.yggdrasil.
19pgbackrest.vidhar IN CNAME vidhar.yggdrasil.
19nfsroot.vidhar IN CNAME vidhar.lan.yggdrasil. 20nfsroot.vidhar IN CNAME vidhar.lan.yggdrasil.
20 21
22pgbackrest.surtr IN CNAME surtr.yggdrasil.
23
21 24
22vidhar.lan IN A 10.141.0.1 25vidhar.lan IN A 10.141.0.1
23 26
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft
index 473f8a20..da3a9048 100644
--- a/hosts/vidhar/network/ruleset.nft
+++ b/hosts/vidhar/network/ruleset.nft
@@ -87,6 +87,7 @@ table inet filter {
87 counter samba-rx {} 87 counter samba-rx {}
88 counter http-rx {} 88 counter http-rx {}
89 counter tftp-rx {} 89 counter tftp-rx {}
90 counter pgbackrest-rx {}
90 91
91 counter established-rx {} 92 counter established-rx {}
92 93
@@ -114,6 +115,7 @@ table inet filter {
114 counter samba-tx {} 115 counter samba-tx {}
115 counter http-tx {} 116 counter http-tx {}
116 counter tftp-tx {} 117 counter tftp-tx {}
118 counter pgbackrest-tx {}
117 119
118 counter tx {} 120 counter tx {}
119 121
@@ -189,6 +191,8 @@ table inet filter {
189 191
190 iifname { lan, mgmt } udp dport 69 counter name tftp-rx accept 192 iifname { lan, mgmt } udp dport 69 counter name tftp-rx accept
191 193
194 iifname yggdrasil tcp dport 8432 counter name pgbackrest-rx accept
195
192 ct state {established, related} counter name established-rx accept 196 ct state {established, related} counter name established-rx accept
193 197
194 198
@@ -235,6 +239,8 @@ table inet filter {
235 udp sport 69 counter name tftp-tx accept 239 udp sport 69 counter name tftp-tx accept
236 udp dport 69 counter name tftp-tx accept 240 udp dport 69 counter name tftp-tx accept
237 241
242 tcp sport 8432 counter name pgbackrest-tx accept
243
238 244
239 counter name tx 245 counter name tx
240 } 246 }
diff --git a/hosts/vidhar/pgbackrest/ca/ca.crt b/hosts/vidhar/pgbackrest/ca/ca.crt
new file mode 100644
index 00000000..6be81a1d
--- /dev/null
+++ b/hosts/vidhar/pgbackrest/ca/ca.crt
@@ -0,0 +1,12 @@
1-----BEGIN CERTIFICATE-----
2MIIBrjCCAS6gAwIBAgIUXY4sW3OoefBUAZqzZIvlv284d64wBQYDK2VxMB8xHTAb
3BgNVBAMMFHBnYmFja3Jlc3QueWdnZHJhc2lsMB4XDTIyMTEyMTE0MTUzMloXDTMy
4MTEyMTE0MjAzMlowHzEdMBsGA1UEAwwUcGdiYWNrcmVzdC55Z2dkcmFzaWwwQzAF
5BgMrZXEDOgDeYXclDR4mFv4plX6mKS1j5VxF1bFgoQbAuqb/c7KMZe/RxNiiyp82
6ZCAfIaNMIFV3lMMc/j7VkICjYzBhMB8GA1UdIwQYMBaAFO+/yfEkwcLr+vNPIsyC
7W86UwJ3aMB0GA1UdDgQWBBTvv8nxJMHC6/rzTyLMglvOlMCd2jAOBgNVHQ8BAf8E
8BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXEDcwCJifNBFrOgzYYnZtvR6jig
9OJp3JQRDCpIeFegO2Hnt8CN/y0dLsGI9OS9LsKq7/2NlHGUfqBpoPACQJcI+DDgd
10EZcU+ibTfJmYOu8E3ZbMnbuB22MS7+WPcqAy4Jq/P0C8Ifz83VubogwgcPlLLRiC
11FgA=
12-----END CERTIFICATE-----
diff --git a/hosts/vidhar/pgbackrest/ca/ca.key b/hosts/vidhar/pgbackrest/ca/ca.key
new file mode 100644
index 00000000..4c92fb3f
--- /dev/null
+++ b/hosts/vidhar/pgbackrest/ca/ca.key
@@ -0,0 +1,21 @@
1{
2 "data": "ENC[AES256_GCM,data:wSkqm/wM9f4HixP3obg6kA1d4cpNOMAnEsfNO5O47LKGZOpAmONTSqfVrLPoL3ZiLacYIuAYWk5hR/n0MkRinrHAmI/HHh/66G4LoIX2HZU2QmdsTJh4sVRbby8S/rfEVAlmJ10JYL2nZvyEt4JANmGC1WARXtR7eIGEU7Cv0SmAdXv9VsDYDxorupU4//gid7CpFj4cjS/5c2Y8,iv:Ix1Zg68ewK5QPqsWj+7Lxeete5AHPJHKWx+M4Z1M4Uk=,tag:aS2kAsTbmF1iYxwdvH528Q==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": null,
9 "lastmodified": "2022-11-21T14:20:32Z",
10 "mac": "ENC[AES256_GCM,data:9iFROHIjheIRb2dTR2VAyZLsM+z6RiPMQPV3qwgGvJfeSGEFWsv9Jg7lBhWAJvWKfEZVptClnGAMbUh2bGTkLbT1JOy10xJsVGk5FrUpPuYT3stJeynNKxfloeoF9WKSIdSLx3blO0bZzqyjmCxR2rJk8FtslWqJUEJsHtYhnyY=,iv:XJT56EroPUlWnWlPIp/vsJIzO3FxZAsZbf0knxXHvuw=,tag:k8zThN9xS6pHq+waAy/HQQ==,type:str]",
11 "pgp": [
12 {
13 "created_at": "2022-11-21T14:20:31Z",
14 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdA0laIXY2D02+/42Fkyxp/H/4DRxpxKGdqoRfFv5LwhAQw\n3M7DZeg0b8rWgC9BL17w54PY1EekyMzW/IxyRTyV0ffYXmn1IJ9VuqMXMteP+i/A\n0lwBdJIPACe5A0IfAMwcguzAB9kwuIkMykvaE9OjtcR/HFF8VU86GoPM0Gc/kUNS\nPbABAy6OuxFZEvziiT56EJ+gbb7u1JlwIrX7zjVAKWeKxQQyFd2gLDIczlD6uw==\n=gmbw\n-----END PGP MESSAGE-----\n",
15 "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
16 }
17 ],
18 "unencrypted_suffix": "_unencrypted",
19 "version": "3.7.3"
20 }
21} \ No newline at end of file
diff --git a/hosts/vidhar/pgbackrest/default.nix b/hosts/vidhar/pgbackrest/default.nix
new file mode 100644
index 00000000..49644e51
--- /dev/null
+++ b/hosts/vidhar/pgbackrest/default.nix
@@ -0,0 +1,101 @@
1{ config, flake, ... }:
2
3let
4 surtrRepoCfg = flake.nixosConfigurations."surtr".config.services.pgbackrest.settings.surtr;
5in {
6 config = {
7 services.pgbackrest = {
8 enable = true;
9 tlsServer = {
10 enable = true;
11
12 user = "pgbackrest";
13 group = "pgbackrest";
14 };
15
16 settings = {
17 "surtr" = {
18 pg1-host-type = "tls";
19 pg1-host = "pgbackrest.surtr.yggdrasil";
20 pg1-host-ca-file = toString ./ca/ca.crt;
21 pg1-host-cert-file = toString ./tls.crt;
22 pg1-host-key-file = config.sops.secrets."pgbackrest.key".path;
23 inherit (surtrRepoCfg) pg1-path;
24
25 # repo1-host-type = "tls";
26 # repo1-host = "pgbackrest.surtr.yggdrasil";
27 # repo1-host-ca-file = toString ./ca/ca.crt;
28 # repo1-host-cert-file = toString ./tls.crt;
29 # repo1-host-key-file = config.sops.secrets."pgbackrest.key".path;
30 # repo1-retention-full-type = "time";
31 # repo1-retention-full = 7;
32 # repo1-retention-archive = 2;
33
34 repo2-path = "/var/lib/pgbackrest";
35 repo2-retention-full-type = "time";
36 repo2-retention-full = 14;
37 repo2-retention-archive = 7;
38 };
39
40 "global" = {
41 compress-type = "zst";
42 compress-level = 9;
43
44 archive-async = true;
45 spool-path = "/var/spool/pgbackrest";
46 };
47
48 "global:server" = {
49 tls-server-address = "2a03:4000:52:ada:1:1::";
50 tls-server-ca-file = toString ./ca/ca.crt;
51 tls-server-cert-file = toString ./tls.crt;
52 tls-server-key-file = config.sops.secrets."pgbackrest.key".path;
53 tls-server-auth = ["surtr.yggdrasil=surtr"];
54 };
55
56 "global:archive-push" = {
57 process-max = 6;
58 };
59 "global:archive-get" = {
60 process-max = 6;
61 };
62 };
63
64 backups."surtr-daily" = {
65 stanza = "surtr";
66 repo = "2";
67 user = "pgbackrest";
68 group = "pgbackrest";
69 timerConfig.OnCalendar = "daily Europe/Berlin";
70 };
71 };
72
73 systemd.tmpfiles.rules = [
74 "d /var/lib/pgbackrest 0750 pgbackrest pgbackrest - -"
75 "d /var/spool/pgbackrest 0750 pgbackrest pgbackrest - -"
76 ];
77
78 users = {
79 users.pgbackrest = {
80 name = "pgbackrest";
81 group = "pgbackrest";
82 isSystemUser = true;
83 home = "/var/lib/pgbackrest";
84 };
85 groups.pgbackrest = {};
86 };
87
88 systemd.services."pgbackrest-tls-server".serviceConfig = {
89 StateDirectory = [ "pgbackrest" ];
90 StateDirectoryMode = "0750";
91 };
92
93 sops.secrets."pgbackrest.key" = {
94 format = "binary";
95 sopsFile = ./tls.key;
96 owner = "pgbackrest";
97 group = "pgbackrest";
98 mode = "0400";
99 };
100 };
101}
diff --git a/hosts/vidhar/pgbackrest/tls.crt b/hosts/vidhar/pgbackrest/tls.crt
new file mode 100644
index 00000000..e807d423
--- /dev/null
+++ b/hosts/vidhar/pgbackrest/tls.crt
@@ -0,0 +1,12 @@
1-----BEGIN CERTIFICATE-----
2MIIB0jCCAVKgAwIBAgIPQAAAAGN7p+4PBkv3Tn05MAUGAytlcTAfMR0wGwYDVQQD
3DBRwZ2JhY2tyZXN0LnlnZ2RyYXNpbDAeFw0yMjExMjExNjI2MDVaFw0zMjExMjEx
4NjMxMDVaMBsxGTAXBgNVBAMMEHZpZGhhci55Z2dkcmFzaWwwKjAFBgMrZXADIQDy
5Wj+rp1Nvyj5TiIdmVV7HW0LUnX2aIQSd8eh5B54BaaOBqDCBpTAfBgNVHSMEGDAW
6gBTvv8nxJMHC6/rzTyLMglvOlMCd2jAdBgNVHQ4EFgQUXU/P0Nq4GmxaL3V8Mq39
7YqggieEwDgYDVR0PAQH/BAQDAgXgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI
8KwYBBQUHAwEGCCsGAQUFBwMCMCYGA1UdEQQfMB2CG3BnYmFja3Jlc3QudmlkaGFy
9LnlnZ2RyYXNpbDAFBgMrZXEDcwBa1HCz42U2W8lhL3iFQJp/ZoPGm7Iluibvvnh/
10h8ka4mhIcx8mtYp0L04Lte9JWEx+MgOOso6Tk4Bh7xPjJY1uUkwP9ZwsrsJPqIj1
111nwtHtUiNr3L4IpJkEo3s/52S41KiaiZ0cXnFE2b8pwLTHIJAwA=
12-----END CERTIFICATE-----
diff --git a/hosts/vidhar/pgbackrest/tls.key b/hosts/vidhar/pgbackrest/tls.key
new file mode 100644
index 00000000..6ab308ac
--- /dev/null
+++ b/hosts/vidhar/pgbackrest/tls.key
@@ -0,0 +1,26 @@
1{
2 "data": "ENC[AES256_GCM,data:LnaklO60F6ZXJh0mYwG0e9LTU5qmZWKq2/0YxXeH1QAnEcJIWnrTWwQegL3UJYMf3kOqKJmAcc2VX1nrxe+GRAUUwgVojxS+VFxeSjACNnpe0Zgfgj5ps3GJME3gpmfey+fgnbIFkI8w5UpRtvz7Evj6dJHMGTE=,iv:Q5rIm2GFjJT0ensa+5ILN/yNhjHyxFhZh5q6hh8hDW0=,tag:bCGcF2v+JnWexJb4C35dWA==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": null,
9 "lastmodified": "2022-11-21T14:21:06Z",
10 "mac": "ENC[AES256_GCM,data:OQnaCFEsi5Xka2L7KoC0UX0L+NtihG1hk7koxH51WiiL/JF1NrOs7PpgNbhVzqiAPWlBF1X/2ZhWyEZris9iVZ9RKa1lgF2VXjuwVHZNGA9G9Dr0ipriupOEdQABRA2MM0PlfdW7CdbzxmBcA4uwfL3m4b0uMB87A/cRG8mSm3U=,iv:2yuhHIjWRHipcOx+2hFUx2RJG/L/icGMH0QxR9w+MTM=,tag:pnwNVPzyqu4t6AklWd6HGA==,type:str]",
11 "pgp": [
12 {
13 "created_at": "2022-11-21T14:21:06Z",
14 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdARaz8S4iFbM7+9cUv/WGQDsbnv51AKznQzs3W31w4Cy0w\nh3UzddwF0lH57GYBnVN6S8h5zEjbtz6tRHVsim6ltnVGmsT+t+fmEbASoPF0mvmc\n0lwB9JoMB9l32cFeCQ6Y1Hxryvu/FeL+iXe+7zouKpW67HQ235+Zx5481xxOg1fy\nwmDb+iZ9R+iNO5twraf1BOG+3y8yrJpZV7SZq4H958Kk35QnHlRiPqDfkx9NEg==\n=GAV2\n-----END PGP MESSAGE-----\n",
15 "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
16 },
17 {
18 "created_at": "2022-11-21T14:21:06Z",
19 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DbYDvGI0HDr0SAQdAgjL9+LcR5m5vHngB9DWE2zfkjsQDsIKrEw2RLKrKdVMw\nQ5B131gL7QKEfAc0vd+HQDANo/pfB9ArI/lNkVvlBYfbO8paadJWvDt9fdmOtJ9J\n0lwBcT1xLhPxCrUVEY1Clsv4y3liNZ78iOBuqaOx0W1A7CQonM2B9ghTDq4bsEE0\n8CxD/mNCn/D8WOqu4dJg6wvIzkk6faSBCbxBjmzTcJ6oDj9RdnnnZ6M/uNWw7g==\n=jZqN\n-----END PGP MESSAGE-----\n",
20 "fp": "A1C7C95E6CAF0A965CB47277BCF50A89C1B1F362"
21 }
22 ],
23 "unencrypted_suffix": "_unencrypted",
24 "version": "3.7.3"
25 }
26} \ No newline at end of file
diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix
new file mode 100644
index 00000000..41a7b381
--- /dev/null
+++ b/modules/pgbackrest.nix
@@ -0,0 +1,192 @@
1{ config, pkgs, lib, utils, ... }:
2
3with utils;
4with lib;
5
6let
7 cfg = config.services.pgbackrest;
8 settingsFormat = pkgs.formats.ini {
9 listsAsDuplicateKeys = true;
10 mkKeyValue = lib.generators.mkKeyValueDefault {
11 mkValueString = v: with builtins;
12 let err = t: v: abort
13 ("mkValueString: " +
14 "${t} not supported: ${toPretty {} v}");
15 in if isInt v then toString v
16 # convert derivations to store paths
17 else if lib.isDerivation v then toString v
18 # we default to not quoting strings
19 else if isString v then v
20 # isString returns "1", which is not a good default
21 else if true == v then "y"
22 # here it returns to "", which is even less of a good default
23 else if false == v then "n"
24 else if null == v then "null"
25 # if you have lists you probably want to replace this
26 else if isList v then err "lists" v
27 # same as for lists, might want to replace
28 else if isAttrs v then err "attrsets" v
29 # functions can’t be printed of course
30 else if isFunction v then err "functions" v
31 # Floats currently can't be converted to precise strings,
32 # condition warning on nix version once this isn't a problem anymore
33 # See https://github.com/NixOS/nix/pull/3480
34 else if isFloat v then libStr.floatToString v
35 else err "this value is" (toString v);
36 } "=";
37 };
38
39 loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"];
40 inherit (utils.systemdUtils.unitOptions) unitOption;
41in {
42 options = {
43 services.pgbackrest = {
44 enable = mkEnableOption "pgBackRest";
45
46 package = mkPackageOption pkgs "pgbackrest" {};
47
48 configurePostgresql = {
49 enable = mkEnableOption "configuring PostgreSQL for sending WAL to pgBackRest" // {
50 default = config.services.postgresql.enable;
51 defaultText = literalExpression "config.systemd.services.postgresql.enable";
52 };
53
54 stanza = mkOption {
55 type = types.str;
56 default = config.networking.hostName;
57 };
58 };
59
60 settings = mkOption {
61 type = types.submodule {
62 freeformType = settingsFormat.type;
63
64 options = {
65 global.log-level-console = mkOption {
66 type = loglevelType;
67 default = "detail";
68 };
69 global.log-level-file = mkOption {
70 type = loglevelType;
71 default = "off";
72 };
73 global.log-level-stderr = mkOption {
74 type = loglevelType;
75 default = "warn";
76 };
77
78 global.log-subprocess = mkOption {
79 type = types.bool;
80 default = true;
81 };
82 global.log-timestamp = mkOption {
83 type = types.bool;
84 default = false;
85 };
86 };
87 };
88 default = {};
89 description = ''
90 Configuration for pgBackRest
91 '';
92 };
93
94 tlsServer = {
95 enable = mkEnableOption "pgBackRest TLS Server";
96
97 user = mkOption {
98 type = types.str;
99 default = "postgres";
100 };
101 group = mkOption {
102 type = types.str;
103 default = "postgres";
104 };
105 };
106
107 backups = mkOption {
108 type = types.attrsOf (types.submodule ({ name, ... }: {
109 options = {
110 type = mkOption {
111 type = types.enum ["full" "incr" "diff"];
112 default = "full";
113 };
114
115 stanza = mkOption {
116 type = types.str;
117 default = cfg.configurePostgresql.stanza;
118 };
119 repo = mkOption {
120 type = types.nullOr (types.strMatching "^[0-9]+$");
121 };
122
123 user = mkOption {
124 type = types.str;
125 default = "postgres";
126 };
127 group = mkOption {
128 type = types.str;
129 default = "postgres";
130 };
131
132 timerConfig = mkOption {
133 type = types.attrsOf unitOption;
134 };
135 };
136 }));
137 default = {};
138 };
139 };
140 };
141
142 config = mkIf cfg.enable {
143 environment.systemPackages = [ cfg.package ];
144
145 services.postgresql.settings = mkIf cfg.configurePostgresql.enable {
146 archive_command = "pgbackrest --stanza ${escapeSystemdExecArg cfg.configurePostgresql.stanza} archive-push %p";
147 archive_mode = true;
148 max_wal_senders = mkDefault 3;
149 wal_level = "replica";
150 };
151
152 systemd.services = {
153 postgresql.path = mkIf cfg.configurePostgresql.enable [ cfg.package ];
154 pgbackrest-tls-server = mkIf cfg.tlsServer.enable {
155 description = "pgBackRest TLS-Server";
156 wantedBy = [ "multi-user.target" ];
157 after = [ "network.target" ];
158
159 restartTriggers = [ config.environment.etc."pgbackrest/pgbackrest.conf".source ];
160
161 unitConfig = {
162 StartLimitIntervalSec = 0;
163 };
164
165 serviceConfig = {
166 Type = "simple";
167 Restart = "always";
168 RestartSec = 1;
169 User = cfg.tlsServer.user;
170 Group = cfg.tlsServer.group;
171 ExecStart = "${cfg.package}/bin/pgbackrest server";
172 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
173 };
174 };
175 } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" {
176 description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})";
177 serviceConfig = {
178 Type = "oneshot";
179 ExecStart = "${cfg.package}/bin/pgbackrest --type ${escapeSystemdExecArg backupCfg.type} --stanza ${escapeSystemdExecArg backupCfg.stanza}${optionalString (!(isNull backupCfg.repo)) " --repo ${backupCfg.repo}"} backup";
180 User = backupCfg.user;
181 Group = backupCfg.group;
182 };
183 }) cfg.backups;
184
185 systemd.timers = mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" {
186 wantedBy = [ "timers.target" ];
187 inherit (backupCfg) timerConfig;
188 }) cfg.backups;
189
190 environment.etc."pgbackrest/pgbackrest.conf".source = settingsFormat.generate "pgbackrest.conf" cfg.settings;
191 };
192}