From 7e97353075b4acee96488d022e456f80f4f903ed Mon Sep 17 00:00:00 2001
From: Gregor Kleen <gkleen@yggdrasil.li>
Date: Thu, 10 Nov 2022 09:15:50 +0100
Subject: surtr: etebase

---
 hosts/surtr/default.nix                            |   3 +-
 hosts/surtr/dns/default.nix                        |   4 +-
 .../dns/keys/app.etesync.yggdrasil.li_acme.yaml    |  26 +++++
 .../surtr/dns/keys/etesync.yggdrasil.li_acme.yaml  |  26 +++++
 hosts/surtr/dns/zones/li.yggdrasil.soa             |  14 ++-
 hosts/surtr/etebase/default.nix                    | 128 +++++++++++++++++++++
 hosts/surtr/etebase/secret.txt                     |  26 +++++
 hosts/surtr/postgresql.nix                         |  13 +++
 hosts/surtr/tls/tsig_keys/app.etesync.yggdrasil.li |  26 +++++
 hosts/surtr/tls/tsig_keys/etesync.yggdrasil.li     |  26 +++++
 overlays/etesync-web.nix                           |  41 +++++++
 11 files changed, 329 insertions(+), 4 deletions(-)
 create mode 100644 hosts/surtr/dns/keys/app.etesync.yggdrasil.li_acme.yaml
 create mode 100644 hosts/surtr/dns/keys/etesync.yggdrasil.li_acme.yaml
 create mode 100644 hosts/surtr/etebase/default.nix
 create mode 100644 hosts/surtr/etebase/secret.txt
 create mode 100644 hosts/surtr/tls/tsig_keys/app.etesync.yggdrasil.li
 create mode 100644 hosts/surtr/tls/tsig_keys/etesync.yggdrasil.li
 create mode 100644 overlays/etesync-web.nix

diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix
index cebb2b6c..9ac087c3 100644
--- a/hosts/surtr/default.nix
+++ b/hosts/surtr/default.nix
@@ -2,7 +2,8 @@
 {
   imports = with flake.nixosModules.systemProfiles; [
     tmpfs-root qemu-guest openssh rebuild-machines zfs
-    ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix ./prometheus ./email ./vpn ./borg.nix
+    ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix
+    ./prometheus ./email ./vpn ./borg.nix ./etebase
   ];
 
   config = {
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix
index 5cba23d9..e0637b3b 100644
--- a/hosts/surtr/dns/default.nix
+++ b/hosts/surtr/dns/default.nix
@@ -48,7 +48,7 @@ in {
       unitConfig.RequiresMountsFor = [ "/var/lib/knot" ];
       serviceConfig.LoadCredential = map ({name, ...}: "${name}:${config.sops.secrets.${name}.path}") knotKeys;
     };
-    
+
     services.knot = {
       enable = true;
       keyFiles = map ({name, ...}: "/run/credentials/knot.service/${name}") knotKeys;
@@ -159,7 +159,7 @@ in {
         ${concatMapStringsSep "\n" mkZone [
           { domain = "yggdrasil.li";
             addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; };
-            acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li"];
+            acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "app.etesync.yggdrasil.li"];
           }
           { domain = "nights.email";
             addACLs = { "nights.email" = ["ymir_acme_acl"]; };
diff --git a/hosts/surtr/dns/keys/app.etesync.yggdrasil.li_acme.yaml b/hosts/surtr/dns/keys/app.etesync.yggdrasil.li_acme.yaml
new file mode 100644
index 00000000..f8e0794d
--- /dev/null
+++ b/hosts/surtr/dns/keys/app.etesync.yggdrasil.li_acme.yaml
@@ -0,0 +1,26 @@
+{
+	"data": "ENC[AES256_GCM,data:YW/R3Bi4IDGNBxtUFh9h/9i/kQaQTVQN019NDNQsGVBOFQSZxvy8+RBEfmZO1bvAYbBuQ72ksb3+dckupm8BQaO4lxsCZpGcPmDrWpYal4hirJAtiJ374j9jGTFVF0x7z6lb8B3aZ5Ztkov6ZxLLiXAEZ1owufKCYeqyemzuEUPPvrfAvF14vg3kqcr2OfeLE7XdMMMu1/ive5C2QGsKekRqJNDbO2iiWDaTFCY3N9Rqja895Of9lzUGNjsWhnNsZLzpEvm/NPFKAmStRq24XGk/KIxGoxBCLZYoCaqZNJ0=,iv:xWYRqkW8Oyple4EQegxx3Y+fwlm1ghm9pbP59UmM1bk=,tag:371XtqRpcbCLcDSJ0xtGgA==,type:str]",
+	"sops": {
+		"kms": null,
+		"gcp_kms": null,
+		"azure_kv": null,
+		"hc_vault": null,
+		"age": null,
+		"lastmodified": "2022-11-09T19:02:47Z",
+		"mac": "ENC[AES256_GCM,data:1/v1EB5lz/cwKcUuOPVVXPBtEnTmFrZj0hTGv5uQEVU9fd66muY3J6HPEvS68g/YBaaYy6V2QLc2lDwbu9amaukqE1Mq7sv51kSPp7jQs7u91BKfN5K3OtCipFxG1fwjqY4k7zliaYESLwrQWXEhFz3k/nPT9xD/nDNc/czQi3I=,iv:zNUpuirl9gZp/kr/NdO3a6ibjX6Itc0JBpu/xxTpMAI=,tag:0twXpUS+/YCpSxZBfojQ0g==,type:str]",
+		"pgp": [
+			{
+				"created_at": "2022-11-09T19:02:47Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdAwgUrKA64oejQmFVmq/vVXUtB0cA1QFTD9tYjc47x+zUw\nwClB436nZMlbuVAltWoMwaW6SOF2I6pcl10j1mU2tSBTnAFmhYUKstYNN1QaBcsj\n0l4By0ALjyRuRkvhZI1Tx3pUJ25P4mGux5dIYPbM+tDcb8hwfmCBig6NG47HH3xp\nPxWXzP6LNFkAAzpZidkv9RaI1XDezbqweMHVTOMfgnaQR35bIbFKDBEd/Y7AvGOT\n=P2yg\n-----END PGP MESSAGE-----\n",
+				"fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
+			},
+			{
+				"created_at": "2022-11-09T19:02:47Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdAbP0iUr2BYsdWpD0m2W4S8aTz8t4dp9mY23qAY5vbGV4w\nxETSJs6Luv32fHpG+kUFkNKIkkpte7Yq3qtxpFoIKroZAGR3/mXB2f0Nd+BKbDZy\n0l4Baouvj8guk0BxywGDyW3V88qMphaGxAwgVsZSiZ9++HxhGHu2fAozJdsJNNtv\njtQI/IM6TaR5/Ib5NxEZ2zR1AguaoI7iDIPhiLUwZmzk95/+xbNwo/bVjHXyh6vA\n=zxXy\n-----END PGP MESSAGE-----\n",
+				"fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
+			}
+		],
+		"unencrypted_suffix": "_unencrypted",
+		"version": "3.7.3"
+	}
+}
\ No newline at end of file
diff --git a/hosts/surtr/dns/keys/etesync.yggdrasil.li_acme.yaml b/hosts/surtr/dns/keys/etesync.yggdrasil.li_acme.yaml
new file mode 100644
index 00000000..1c588b07
--- /dev/null
+++ b/hosts/surtr/dns/keys/etesync.yggdrasil.li_acme.yaml
@@ -0,0 +1,26 @@
+{
+	"data": "ENC[AES256_GCM,data:9VkwZFnF/WJZx4eHBV0psppNd+XbtCO3flQeO9YIVLYA7Hlyu7YZKkILgQDheHN/KjKfuRXsXUNjojEGgkyzU3Hc03LUQkrF4dFP99/Fqwjl9TUWKHPPxCXKPzEuEpJI3krwFOLWoD++aGmQKzAW4vG9oMF4vErkzUAchxfvVnC6TiswuSAsOF34/A3JP4dZKo78iMf90MhXSrqzQ60tUSrSGUBipBne40a5kVHw6Jc4N5zUemnYAInftIvQ+8VKhxhIxLIPrYslM159w0HgTta2Jio+6UHq,iv:UwDkUeaXY6IrVJf4BxPy52ssE32AiKkpWSOj8JeZrTw=,tag:Jdz4tOhu41kjGbBOMqQC1A==,type:str]",
+	"sops": {
+		"kms": null,
+		"gcp_kms": null,
+		"azure_kv": null,
+		"hc_vault": null,
+		"age": null,
+		"lastmodified": "2022-11-09T15:58:56Z",
+		"mac": "ENC[AES256_GCM,data:bLVoRyiCj/t39dC62YuhwDlpVdniufta6wie+bTD3CmC7RxFrSVTIuRZbKlYgue+sxhtIsG2AaO4/FrpFGm9i3tQAi47wHMhr4NRtxXYALAiBKgREjap1q19ePMeN9vdbdxB2SsnnJBhlRAsZzyFqoeKuo67pEWWPuwJz3QXSGI=,iv:fmr313AD4xbQHNP94HLzKzVTGdL7E0m0u4F/oQay/2w=,tag:gs7GWUWuCISO0WVu/C+wuQ==,type:str]",
+		"pgp": [
+			{
+				"created_at": "2022-11-09T15:58:56Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdATu1XElbAp1jN1ON1K5dOrePlVtucKDXpu1316bi0pQsw\n8YHSJkrIS0LaAGSPnZkNtxXMOWNcmLrbUhDwLcLnmYG2VSv4oaOhgHJ7qHxlwFTM\n0l4B67lzysh5ah1XEQMn5J/tERwHp9S2s5vN61olviMetrlAV6n03JTHjMSsV2nZ\nM5JflAbE3amxEdlAIcKyRh5pcTz1cnwEk5dVQMN6to8alhBOsEd2j40S7ixvuAmB\n=UUbW\n-----END PGP MESSAGE-----\n",
+				"fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
+			},
+			{
+				"created_at": "2022-11-09T15:58:56Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdANUUZ//nrQaWaN09s/He7ZvgVDBNSoSoor5PPpeFkogYw\nxtwRVqp4/bqkiBDk0Szgjna98hnC0LKLfiO1zDDzSZ1c8NhUSo2mI52qnq6PAkOZ\n0l4BlYEjEcCYhuZJrGErzFnxWdPVUlTy/DOVN8AWwJCgvvbKKL0R4As7gwyoGg8a\nAPYgA4J9p62dlTCTHFXZNdQ6Iml/sBcgafcWAq5B6anQ6bmFGUF7s/+ntT5Ergr9\n=LVUN\n-----END PGP MESSAGE-----\n",
+				"fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
+			}
+		],
+		"unencrypted_suffix": "_unencrypted",
+		"version": "3.7.3"
+	}
+}
\ No newline at end of file
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa
index 1a4e4656..1bb10662 100644
--- a/hosts/surtr/dns/zones/li.yggdrasil.soa
+++ b/hosts/surtr/dns/zones/li.yggdrasil.soa
@@ -1,7 +1,7 @@
 $ORIGIN yggdrasil.li.
 $TTL 3600
 @ IN SOA ns.yggdrasil.li. root.yggdrasil.li. (
-  2022072800 ; serial
+  2022110904 ; serial
   10800      ; refresh
   3600       ; retry
   604800     ; expire
@@ -53,6 +53,18 @@ _acme-challenge.surtr  	IN	NS	ns.yggdrasil.li.
 
 prometheus.surtr	IN	CNAME	surtr.yggdrasil.li.
 
+etesync                 IN      A       202.61.241.61
+etesync                 IN      AAAA    2a03:4000:52:ada::
+etesync                 IN      MX      0 surtr.yggdrasil.li
+etesync                 IN      TXT     "v=spf1 redirect=surtr.yggdrasil.li"
+_acme-challenge.etesync	IN	NS	ns.yggdrasil.li.
+
+app.etesync             IN      A       202.61.241.61
+app.etesync             IN      AAAA    2a03:4000:52:ada::
+app.etesync             IN      MX      0 surtr.yggdrasil.li
+app.etesync             IN      TXT     "v=spf1 redirect=surtr.yggdrasil.li"
+_acme-challenge.app.etesync IN	NS	ns.yggdrasil.li.
+
 vidhar                  IN      AAAA    2a03:4000:52:ada:4:1::
 vidhar                  IN      MX      0 ymir.yggdrasil.li
 vidhar                  IN      TXT     "v=spf1 redirect=yggdrasil.li"
diff --git a/hosts/surtr/etebase/default.nix b/hosts/surtr/etebase/default.nix
new file mode 100644
index 00000000..3c71bed0
--- /dev/null
+++ b/hosts/surtr/etebase/default.nix
@@ -0,0 +1,128 @@
+{ config, pkgs, ... }:
+
+{
+  config = {
+    services.etebase-server = {
+      enable = true;
+      port = null;
+      unixSocket = "/run/etebase-server/etebase-server.sock";
+      user = "etebase";
+      settings = {
+        allowed_hosts.allowed_host1 = "etesync.yggdrasil.li";
+        global.secret_file = config.sops.secrets."etebase-server-secret.txt".path;
+        database = {
+          engine = "django.db.backends.postgresql";
+          name = "etebase";
+          user = "etebase";
+        };
+      };
+    };
+
+    systemd.services.etebase-server = {
+      serviceConfig = {
+        RuntimeDirectory = "etebase-server";
+      };
+    };
+
+    sops.secrets."etebase-server-secret.txt" = {
+      format = "binary";
+      sopsFile = ./secret.txt;
+      owner = config.services.etebase-server.user;
+      group = config.services.etebase-server.user;
+      restartUnits = ["etebase-server.service"];
+    };
+
+    security.acme.domains = {
+      "etesync.yggdrasil.li".certCfg = {
+        postRun = ''
+          ${pkgs.systemd}/bin/systemctl try-restart nginx.service
+        '';
+      };
+      "app.etesync.yggdrasil.li".certCfg = {
+        postRun = ''
+          ${pkgs.systemd}/bin/systemctl try-restart nginx.service
+        '';
+      };
+    };
+
+    services.nginx = {
+      upstreams."etebase" = {
+        servers = {
+          "unix://${config.services.etebase-server.unixSocket}" = {};
+        };
+      };
+
+      virtualHosts = {
+        "etesync.yggdrasil.li" = {
+          forceSSL = true;
+          sslCertificate = "/run/credentials/nginx.service/etesync.yggdrasil.li.pem";
+          sslCertificateKey = "/run/credentials/nginx.service/etesync.yggdrasil.li.key.pem";
+          sslTrustedCertificate = "/run/credentials/nginx.service/etesync.yggdrasil.li.chain.pem";
+          extraConfig = ''
+            client_max_body_size 100M;
+            charset utf-8;
+          '';
+
+          locations = {
+            "/static/" = {
+              alias = "${config.services.etebase-server.settings.global.static_root}/";
+            };
+            "= /".return = "301 https://app.etesync.yggdrasil.li";
+            "/".extraConfig = ''
+              proxy_pass http://etebase;
+
+              proxy_http_version 1.1;
+              proxy_set_header Upgrade $http_upgrade;
+              proxy_set_header Connection "upgrade";
+
+              proxy_redirect off;
+              proxy_set_header Host $host;
+              proxy_set_header X-Real-IP $remote_addr;
+              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+              proxy_set_header X-Forwarded-Host $server_name;
+            '';
+          };
+        };
+
+        "app.etesync.yggdrasil.li" = {
+          forceSSL = true;
+          sslCertificate = "/run/credentials/nginx.service/app.etesync.yggdrasil.li.pem";
+          sslCertificateKey = "/run/credentials/nginx.service/app.etesync.yggdrasil.li.key.pem";
+          sslTrustedCertificate = "/run/credentials/nginx.service/app.etesync.yggdrasil.li.chain.pem";
+
+          locations."/".alias = "${pkgs.etesync-web}/";
+        };
+      };
+    };
+
+    systemd.services.nginx = {
+      serviceConfig = {
+        ReadPaths = [
+          config.services.etebase-server.settings.global.static_root
+          pkgs.etesync-web
+        ];
+        LoadCredential = [
+          "etesync.yggdrasil.li.key.pem:${config.security.acme.certs."etesync.yggdrasil.li".directory}/key.pem"
+          "etesync.yggdrasil.li.pem:${config.security.acme.certs."etesync.yggdrasil.li".directory}/fullchain.pem"
+          "etesync.yggdrasil.li.chain.pem:${config.security.acme.certs."etesync.yggdrasil.li".directory}/chain.pem"
+
+          "app.etesync.yggdrasil.li.key.pem:${config.security.acme.certs."app.etesync.yggdrasil.li".directory}/key.pem"
+          "app.etesync.yggdrasil.li.pem:${config.security.acme.certs."app.etesync.yggdrasil.li".directory}/fullchain.pem"
+          "app.etesync.yggdrasil.li.chain.pem:${config.security.acme.certs."app.etesync.yggdrasil.li".directory}/chain.pem"
+        ];
+      };
+    };
+
+    users = {
+      users.${config.services.etebase-server.user} = {
+        isSystemUser = true;
+        group = config.services.etebase-server.user;
+        home = config.services.etebase-server.dataDir;
+      };
+
+      groups.${config.services.etebase-server.user} = {
+        members = [ "nginx" ];
+      };
+    };
+  };
+}
diff --git a/hosts/surtr/etebase/secret.txt b/hosts/surtr/etebase/secret.txt
new file mode 100644
index 00000000..acedb549
--- /dev/null
+++ b/hosts/surtr/etebase/secret.txt
@@ -0,0 +1,26 @@
+{
+	"data": "ENC[AES256_GCM,data:0iCyumWJXIVl/YnDZPCVeGM9FP4mGJ8A6Kp8nTXCZQfNOfXzvHRlJVXKlPtYuYD3/sXb,iv:gKJoiuXJIvL0/Eu48OM/7YPnX4p/3Bi8u/GvvNNSeg8=,tag:7XKIlfZ7ZimZ3wE0qVqU5w==,type:str]",
+	"sops": {
+		"kms": null,
+		"gcp_kms": null,
+		"azure_kv": null,
+		"hc_vault": null,
+		"age": null,
+		"lastmodified": "2022-11-09T15:30:57Z",
+		"mac": "ENC[AES256_GCM,data:zb9S3tgUEja6IfCvrh6AJkzoiqAj5RyBtEvHHV7RkANGHxRer79YdDJW39I4qrg2WC8odr5CyJF3sVqw4fUeUeeq0QAJYupJVmINBqIaFcy6f5XtFDpHRNPmHT1WwrN6t5o8pqb4cv8H7JRfjySxlwFNmItgrQIQn6QBqE2ZkEc=,iv:BTzROI/DxqCmRYzsRkMrj+kTG3KTLP+nAF4z0l/dRbU=,tag:S+w0+XL55PBiHWkUKtDggQ==,type:str]",
+		"pgp": [
+			{
+				"created_at": "2022-11-09T14:03:17Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdAfsNj4UmCNc1Qo5hi1YLaRjoeoudRZwNgVfaQTMsOPA8w\nfuIRUgq9Mybq4Frp4U/l86LwekOIwiF5tk1hPcK2HrmHG2z/ewr6WnrhczjFy+Qi\n0lwBMEtZWrD4h8GdTwan7E/jDLytEZYjDmXK72Ep5PubyO86H1BKy4Da5YIZw4Bc\nq3RaJ65wcp1EwIJ7gbEvG7a1a00AjFhXIwtsT/DhKTBy/OwPj9w4mFJ5rka8FQ==\n=2FIT\n-----END PGP MESSAGE-----\n",
+				"fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
+			},
+			{
+				"created_at": "2022-11-09T14:03:17Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdATs6pQrq07RGgFTTrNTI26pt3WSSF8tg9ywhepFvxfyUw\nItZrRfQUi42Yj6UC0GuxNmVYcS/Ogv7SngtM+22kofS476gfhkHT45/9gMhqve0D\n0lwBPaW0UHfU8Z3tbA6aRpMSYF20Srvvqfs2Q+PFSEWDFXx06RqpmH72LrhI3uYm\nbK9LykI7ucQAGJSSkHJQEbvEqyv1CMFGdDHkI1LyAetmcqgPZH8JRPx3LDagyg==\n=EsHC\n-----END PGP MESSAGE-----\n",
+				"fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
+			}
+		],
+		"unencrypted_suffix": "_unencrypted",
+		"version": "3.7.3"
+	}
+}
\ No newline at end of file
diff --git a/hosts/surtr/postgresql.nix b/hosts/surtr/postgresql.nix
index 7013ae97..c10c5084 100644
--- a/hosts/surtr/postgresql.nix
+++ b/hosts/surtr/postgresql.nix
@@ -24,6 +24,7 @@ in {
         psql postgres postgres -eXf ${pkgs.writeText "schema.sql" ''
           CREATE DATABASE "matrix-synapse" WITH TEMPLATE "template0" ENCODING "UTF8" LOCALE "C";
           CREATE DATABASE "email" WITH TEMPLATE "template0" ENCODING "UTF8" LOCALE "C";
+          CREATE DATABASE "etebase" WITH TEMPLATE "template0" ENCODING "UTF8" LOCALE "C";
         ''}
 
         psql matrix-synapse postgres -eXf ${pkgs.writeText "matrix-synapse.sql" ''
@@ -153,6 +154,18 @@ in {
           GRANT SELECT ON ALL TABLES IN SCHEMA public TO "postfix-ccert-sender-policy";
           COMMIT;
         ''}
+
+        psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" ''
+          \i ${versioning + "/install.versioning.sql"}
+
+          BEGIN;
+          SELECT _v.register_patch('000-user', null, null);
+
+          CREATE USER "etebase";
+          GRANT ALL PRIVILEGES ON DATABASE "etebase" TO "etebase";
+          GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "etebase";
+          COMMIT;
+        ''}
       '';
     };
   };
diff --git a/hosts/surtr/tls/tsig_keys/app.etesync.yggdrasil.li b/hosts/surtr/tls/tsig_keys/app.etesync.yggdrasil.li
new file mode 100644
index 00000000..a50469a0
--- /dev/null
+++ b/hosts/surtr/tls/tsig_keys/app.etesync.yggdrasil.li
@@ -0,0 +1,26 @@
+{
+	"data": "ENC[AES256_GCM,data:rlLDETp/eY1duDhCCWSe8fOrpk3rQFnbOH8D5XxWp7MIZa1xPqKSvbG8qRgc,iv:wLunCVQdM9y1f2/bJAL6HJxECmzFbZXlpNfNZukBSNc=,tag:dwvzIwQ/fECIq6YJXFJutA==,type:str]",
+	"sops": {
+		"kms": null,
+		"gcp_kms": null,
+		"azure_kv": null,
+		"hc_vault": null,
+		"age": null,
+		"lastmodified": "2022-11-09T19:02:47Z",
+		"mac": "ENC[AES256_GCM,data:9yn9o50V7+e09RBZfNgjaPeoDDv0cdSZSSH5QV9RJUaFbV/5razGbqtDa3aASor2o9aGRdxV8aTS8r0HUnXBvAtKvj957PgRprf7D9J3iU9iHmitrEStuRIQTz1u9rbxxPxi45Cp136n6XcVoRUrIO9XmpzYZ5lPSGCu3CXyk98=,iv:8HPj8B9nRzlBryt+gPNvSsl6YoF4zl3VvI5+aZ4UkLU=,tag:GJKnHL5mt0rO73HUCxC8Qw==,type:str]",
+		"pgp": [
+			{
+				"created_at": "2022-11-09T19:02:47Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdAAEW7XxblC2ra6d6tKyiJczKy6sX8iCQzzJq1uenZH1Ew\ngRZp56DapGmV1+Ihb2tasyVRTl07QLc4dP+OmO1/pKNnMLaPk4djy5YWNyGvNyUK\n0l4BSfhJmO+Jxwq21VCefaA+sFr1bkLaQUILzyr33QSXrwnunwj4BV3pKIvXT0mB\ncJdyoXQlZbHkGxLxo/0qxfpERfeGluOSA/J59Qf4oAGT5GkTqfyFkNMmQJFb/kNn\n=cIev\n-----END PGP MESSAGE-----\n",
+				"fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
+			},
+			{
+				"created_at": "2022-11-09T19:02:47Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdARJp9uSk59SGgYyNwybohjzbjTak/OdgPogdlHM4ui0ow\nNIONcLnzKHX7NFv0BIcwJ8iG5/R2JQ/CKkHi1c5D8RWi6fHEcGYeGk78VDaUT5vi\n0l4BT9vPO/DWHQxw+C7XlUTAwUD3g78W2AkV8H46fMaUBQNITkcXdV1E4T3oNBkv\n/IVY+C1l8NpxzVHYQdo+BRICZ3CKpRXci3ZwQK00epXd6uPyUEpWrVh8bN22oxJT\n=aSHb\n-----END PGP MESSAGE-----\n",
+				"fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
+			}
+		],
+		"unencrypted_suffix": "_unencrypted",
+		"version": "3.7.3"
+	}
+}
\ No newline at end of file
diff --git a/hosts/surtr/tls/tsig_keys/etesync.yggdrasil.li b/hosts/surtr/tls/tsig_keys/etesync.yggdrasil.li
new file mode 100644
index 00000000..36b088ee
--- /dev/null
+++ b/hosts/surtr/tls/tsig_keys/etesync.yggdrasil.li
@@ -0,0 +1,26 @@
+{
+	"data": "ENC[AES256_GCM,data:90Fxrx/+2S1Rtud04u/SirJl7I8+e/e0GkBgFUN88wdH6IVRv5//1dDeHien,iv:r+LL6DT45HMrV5f577rQg5pBYsmMUQloc53P8A0bwt4=,tag:FYsnwM7x9u5eujBt6vfeMw==,type:str]",
+	"sops": {
+		"kms": null,
+		"gcp_kms": null,
+		"azure_kv": null,
+		"hc_vault": null,
+		"age": null,
+		"lastmodified": "2022-11-09T15:58:57Z",
+		"mac": "ENC[AES256_GCM,data:jnayMaU/b7Ga0LY8aTT83ZfveBpyZQONYxZg7m7wtQsJ9R9fBz8Hj8RCTe/kQHI9J6QjDkM0BRtQjKWkth3BJMyzsLpBWvxdYen3AVROs/MHaX9rQ2MlKbZT6sQHiOgJaYiKem6cogMmLgQvb23I56gJNPGaM+0av6evCyu9+Oo=,iv:eiJQQChxu9ncxt8v1DXFFCRHMBuOnjOkOAVLv2tZjgk=,tag:14R6xM+2jIN03ZnleF788Q==,type:str]",
+		"pgp": [
+			{
+				"created_at": "2022-11-09T15:58:56Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdAKFpHfMQJnP8nDjHzxTxavExHX5z7JE3xPL6RCAJIX3kw\nbZ01Kd8gS3K4o69Nmfq8pXnPi6Oth7cuU4sQMN6TDz7/TCbyGSfdeh69A6d5WiU3\n0lwBNIuAyXvDIbtfOO3hqlQSzyBI0FBdj95DkyDu9el5KFHgD9VYm+of//pcdFV6\nVvoRQV2Cgb7kfzRQJxb//XqGZ1X/+TeETAoHVeEwCTCyi205tdH7eKJ21oGgQQ==\n=ovuM\n-----END PGP MESSAGE-----\n",
+				"fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
+			},
+			{
+				"created_at": "2022-11-09T15:58:56Z",
+				"enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdA0Dcxxnlrr5jyhG3c2391EURXEHWCozH1dZwVXjE9pkQw\nL4WrL9LBnUBNgNXse83Va20k1VQxZUvOQ/xlLhCoFgJX/oa3++BIzuZSA2/Uh/yv\n0lwBmpMYnHdoFYxlxLX5xYE9wo7cye/eNHcoZeP/InOGOEkQc2dbIari/Y4z1+2Q\n18Z8eCRD/iLCbFXJmH+/pHhQhjzWM+p08DSxQqKAfYhEN/cAs6e2T9Mp85wU1A==\n=X+4i\n-----END PGP MESSAGE-----\n",
+				"fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
+			}
+		],
+		"unencrypted_suffix": "_unencrypted",
+		"version": "3.7.3"
+	}
+}
\ No newline at end of file
diff --git a/overlays/etesync-web.nix b/overlays/etesync-web.nix
new file mode 100644
index 00000000..2d4c23ea
--- /dev/null
+++ b/overlays/etesync-web.nix
@@ -0,0 +1,41 @@
+{ final, prev, ... }: {
+  etesync-web = final.mkYarnPackage rec {
+    pname = "etesync-web";
+    version = "0.6.1";
+
+    patches = [
+      (final.fetchpatch {
+        name = "fix-server-url-usage.patch";
+        url = "https://github.com/etesync/etesync-web/commit/7a03f8c69c12527d537ce9cb012a5e86a578aa9d.patch";
+        hash = "sha256-qPwV+K3jiqAXZl2gYMzcNIcuG0raOyX/YjRWd7/5kU8=";
+      })
+    ];
+
+    src = final.fetchFromGitHub {
+      owner = "etesync";
+      repo = "etesync-web";
+      rev = "v${version}";
+      hash = "sha256-ZQpbeEIDj7cB0Y62uIw3qSClvwE4buVKh6ZPMfS53dY=";
+    };
+
+    buildPhase = ''
+      runHook preBuild
+
+      REACT_APP_DEFAULT_API_PATH=https://etesync.yggdrasil.li \
+        NODE_OPTIONS=--openssl-legacy-provider \
+        yarn --offline build
+
+      runHook postBuild
+    '';
+
+    installPhase = ''
+      runHook preInstall
+
+      cp -r deps/etesync-web/build $out
+
+      runHook postInstall
+    '';
+
+    distPhase = "true";
+  };
+}
-- 
cgit v1.2.3