summary refs log tree commit diff stats
path: root/system
diff options
context:
space:
mode:
Diffstat (limited to 'system')
-rw-r--r--system/default.nix6
-rw-r--r--system/disks/default.nix82
-rw-r--r--system/file_system_layouts/default.nix53
-rw-r--r--system/impermanence/default.nix23
-rw-r--r--system/impermanence/mods/acme.nix5
-rw-r--r--system/impermanence/mods/fail2ban.nix10
-rw-r--r--system/impermanence/mods/keycloak.nix5
-rw-r--r--system/impermanence/mods/mail.nix34
-rw-r--r--system/impermanence/mods/minecraft.nix10
-rw-r--r--system/impermanence/mods/nix-sync.nix10
-rw-r--r--system/impermanence/mods/openssh.nix21
-rw-r--r--system/impermanence/mods/users.nix28
-rw-r--r--system/mail/default.nix40
-rw-r--r--system/secrets/default.nix12
-rw-r--r--system/secrets/keycloak/passwd.tix17
-rw-r--r--system/secrets/secrets.nix12
-rw-r--r--system/services/acme/default.nix38
-rw-r--r--system/services/acme/domains.nixbin130 -> 0 bytes
-rw-r--r--system/services/default.nix8
-rw-r--r--system/services/fail2ban/default.nix3
-rw-r--r--system/services/git-sync/default.nix104
-rw-r--r--system/services/keycloak/default.nix2
-rw-r--r--system/services/mail/default.nix41
-rw-r--r--system/services/mail/users.nix (renamed from system/mail/users.nix)bin486 -> 486 bytes
-rw-r--r--system/services/matrix/default.nix1
-rw-r--r--system/services/minecraft/default.nix2
-rw-r--r--system/services/nginx/default.nix8
-rw-r--r--system/services/nginx/hosts.nixbin630 -> 976 bytes
-rw-r--r--system/services/nix-sync/default.nix262
-rw-r--r--system/services/openssh/default.nix17
-rw-r--r--system/services/opensshd/default.nix13
-rw-r--r--system/users/default.nix95
32 files changed, 654 insertions, 308 deletions
diff --git a/system/default.nix b/system/default.nix
index d67ada2..14f0748 100644
--- a/system/default.nix
+++ b/system/default.nix
@@ -1,7 +1,9 @@
-{config, ...}: {
+{...}: {
   imports = [
-    ./file_system_layouts
+    ./disks
+    ./impermanence
     ./packages
+    ./secrets
     ./services
     ./users
   ];
diff --git a/system/disks/default.nix b/system/disks/default.nix
new file mode 100644
index 0000000..f4e6935
--- /dev/null
+++ b/system/disks/default.nix
@@ -0,0 +1,82 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.system.disks;
+  defaultMountOptions = ["compress-force=zstd:15"];
+in {
+  options.system.disks = {
+    disk = mkOption {
+      type = lib.types.path;
+      example = literalExpression "/dev/disk/by-id/ata-WDC_WD10SDRW-11A0XS0_WD-WXP2A901KJN5";
+      description = lib.mdDoc "Path to the disk";
+    };
+  };
+
+  config = {
+    disko.devices = {
+      disk.main = {
+        device = cfg.disk;
+        content = {
+          type = "table";
+          format = "gpt";
+          partitions = [
+            {
+              name = "boot";
+              start = "0";
+              end = "1M";
+              part-type = "primary";
+              flags = ["bios_grub"];
+            }
+            {
+              name = "root";
+              # leave space for the grub aka BIOS boot
+              start = "1M";
+              end = "100%";
+              part-type = "primary";
+              bootable = true;
+              content = {
+                type = "btrfs";
+                extraArgs = ["-f" "--label nixos"]; # f: Override existing partitions
+                subvolumes = {
+                  "nix" = {
+                    mountpoint = "/nix";
+                    mountOptions = defaultMountOptions;
+                  };
+                  "persistent-storage" = {
+                    mountpoint = "/srv";
+                    mountOptions = defaultMountOptions;
+                  };
+                  "persistent-storage@snapshots" = {
+                    mountpoint = "/srv/.snapshots";
+                    mountOptions = defaultMountOptions;
+                  };
+                  "boot" = {
+                    mountpoint = "/boot";
+                    mountOptions = defaultMountOptions;
+                  };
+                };
+              };
+            }
+          ];
+        };
+      };
+      nodev = {
+        "/" = {
+          fsType = "tmpfs";
+          mountOptions = ["defaults" "size=2G" "mode=755"];
+        };
+      };
+    };
+    fileSystems = {
+      "/srv" = {
+        neededForBoot = true;
+      };
+      "/boot" = {
+        neededForBoot = true;
+      };
+    };
+  };
+}
diff --git a/system/file_system_layouts/default.nix b/system/file_system_layouts/default.nix
deleted file mode 100644
index 4cd9ff1..0000000
--- a/system/file_system_layouts/default.nix
+++ /dev/null
@@ -1,53 +0,0 @@
-{
-  modulesPath,
-  config,
-  lib,
-  ...
-}:
-with lib; let
-  cfg = config.system.fileSystemLayouts;
-in {
-  options.system.fileSystemLayouts = {
-    mainDisk = mkOption {
-      type = lib.types.path;
-      example = literalExpression "/dev/disk/by-uuid/0442cb6d-f13a-4635-b487-fa76189774c5";
-      description = lib.mdDoc "Path to the main disk";
-    };
-  };
-  config = {
-    fileSystems = {
-      "/" = {
-        device = "tmpfs";
-        fsType = "tmpfs";
-        options = ["defaults" "size=2G" "mode=755"];
-      };
-      "/nix" = {
-        device = cfg.mainDisk;
-        fsType = "btrfs";
-        options = ["subvol=nix" "compress-force=zstd"];
-      };
-      "/srv" = {
-        device = cfg.mainDisk;
-        fsType = "btrfs";
-        options = ["subvol=storage" "compress-force=zstd"];
-      };
-      "/boot" = {
-        device = cfg.mainDisk;
-        options = ["subvol=boot" "compress-force=zstd"];
-      };
-
-      "/etc/nixos" = {
-        device = "/srv/nix-config";
-        options = ["bind"];
-      };
-      "/var/lib/acme" = {
-        device = "/srv/acme";
-        options = ["bind"];
-      };
-      "/var/lib/postgresql" = {
-        device = "/srv/postgresql";
-        options = ["bind"];
-      };
-    };
-  };
-}
diff --git a/system/impermanence/default.nix b/system/impermanence/default.nix
new file mode 100644
index 0000000..198eeba
--- /dev/null
+++ b/system/impermanence/default.nix
@@ -0,0 +1,23 @@
+{...}: {
+  # TODO: Only activate them if their module is also active
+  imports = [
+    ./mods/acme.nix
+    ./mods/keycloak.nix
+    ./mods/mail.nix
+    ./mods/minecraft.nix
+    ./mods/nix-sync.nix
+    ./mods/openssh.nix
+    ./mods/users.nix
+  ];
+
+  environment.persistence."/srv" = {
+    hideMounts = true;
+    directories = [
+      "/etc/nixos"
+      "/var/log"
+    ];
+    files = [
+      "/etc/machine-id"
+    ];
+  };
+}
diff --git a/system/impermanence/mods/acme.nix b/system/impermanence/mods/acme.nix
new file mode 100644
index 0000000..b16171e
--- /dev/null
+++ b/system/impermanence/mods/acme.nix
@@ -0,0 +1,5 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    "/var/lib/acme"
+  ];
+}
diff --git a/system/impermanence/mods/fail2ban.nix b/system/impermanence/mods/fail2ban.nix
new file mode 100644
index 0000000..a817876
--- /dev/null
+++ b/system/impermanence/mods/fail2ban.nix
@@ -0,0 +1,10 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    {
+      directory = "/var/lib/fail2ban";
+      user = "fail2ban";
+      group = "fail2ban";
+      mode = "0700";
+    }
+  ];
+}
diff --git a/system/impermanence/mods/keycloak.nix b/system/impermanence/mods/keycloak.nix
new file mode 100644
index 0000000..63b02f5
--- /dev/null
+++ b/system/impermanence/mods/keycloak.nix
@@ -0,0 +1,5 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    "/var/lib/postgresql"
+  ];
+}
diff --git a/system/impermanence/mods/mail.nix b/system/impermanence/mods/mail.nix
new file mode 100644
index 0000000..18151ad
--- /dev/null
+++ b/system/impermanence/mods/mail.nix
@@ -0,0 +1,34 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    {
+      directory = "/var/lib/mail/backup";
+      user = "virtualMail";
+      group = "virtualMail";
+      mode = "0700";
+    }
+    {
+      directory = "/var/lib/mail/sieve";
+      user = "virtualMail";
+      group = "virtualMail";
+      mode = "0700";
+    }
+    {
+      directory = "/var/lib/mail/vmail";
+      user = "virtualMail";
+      group = "virtualMail";
+      mode = "0700";
+    }
+    {
+      directory = "/var/lib/mail/dkim";
+      user = "opendkim";
+      group = "opendkim";
+      mode = "0700";
+    }
+    {
+      directory = "/var/lib/rspamd";
+      user = "rspamd";
+      group = "rspamd";
+      mode = "0700";
+    }
+  ];
+}
diff --git a/system/impermanence/mods/minecraft.nix b/system/impermanence/mods/minecraft.nix
new file mode 100644
index 0000000..2a02626
--- /dev/null
+++ b/system/impermanence/mods/minecraft.nix
@@ -0,0 +1,10 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    {
+      directory = "/var/lib/minecraft";
+      user = "minecraft";
+      group = "minecraft";
+      mode = "0700";
+    }
+  ];
+}
diff --git a/system/impermanence/mods/nix-sync.nix b/system/impermanence/mods/nix-sync.nix
new file mode 100644
index 0000000..11449ea
--- /dev/null
+++ b/system/impermanence/mods/nix-sync.nix
@@ -0,0 +1,10 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    {
+      directory = "/var/lib/nix-sync";
+      user = "nix-sync";
+      group = "nix-sync";
+      mode = "0700";
+    }
+  ];
+}
diff --git a/system/impermanence/mods/openssh.nix b/system/impermanence/mods/openssh.nix
new file mode 100644
index 0000000..0373a83
--- /dev/null
+++ b/system/impermanence/mods/openssh.nix
@@ -0,0 +1,21 @@
+{...}: {
+  /*
+   FIXME:
+    This results in a boot error, as the `/var/lib/sshd` directory is only mounted _after_ the stage 2 init and with it the system activation.
+    Agenix needs the sshd hostkey however to decrypt the secrets and such we have to ensure that this directory is mounted _before_ the system activation.
+    Alas the only way I see to achieve that is to store the ssh hostkey directly on /srv, which is mounted before (it's marked as 'neededForBoot' after all).
+
+    It should be possible to achieve this with impermanence however, as `/var/log` is mounted in the stage 1 init; The problem is that I have no idea _why_ only
+    this is mounted and nothing else.
+
+
+  environment.persistence."/srv".directories = [
+    {
+      directory = "/var/lib/sshd";
+      user = "root";
+      group = "root";
+      mode = "0755";
+    }
+  ];
+  */
+}
diff --git a/system/impermanence/mods/users.nix b/system/impermanence/mods/users.nix
new file mode 100644
index 0000000..0692b00
--- /dev/null
+++ b/system/impermanence/mods/users.nix
@@ -0,0 +1,28 @@
+{...}: {
+  environment.persistence."/srv".directories = [
+    {
+      directory = "/home";
+      user = "root";
+      group = "root";
+      mode = "0755";
+    }
+    {
+      directory = "/home/sils";
+      user = "sils";
+      group = "sils";
+      mode = "0700";
+    }
+    {
+      directory = "/home/soispha";
+      user = "soispha";
+      group = "soispha";
+      mode = "0700";
+    }
+    {
+      directory = "/home/nightingale";
+      user = "nightingale";
+      group = "nightingale";
+      mode = "0700";
+    }
+  ];
+}
diff --git a/system/mail/default.nix b/system/mail/default.nix
deleted file mode 100644
index 1086f6e..0000000
--- a/system/mail/default.nix
+++ /dev/null
@@ -1,40 +0,0 @@
-# vim: ts=2
-{lib, ...}: let
-  all_admins = [
-    "sils@vhack.eu"
-    "soispha@vhack.eu"
-    "nightingale@vhack.eu"
-  ];
-  users = import ./users.nix {};
-in
-  lib.recursiveUpdate {
-    enable = true;
-    fqdn = "server1.vhack.eu";
-
-    useFsLayout = true;
-
-    extraVirtualAliases = {
-      "abuse@vhack.eu" = all_admins;
-      "postmaster@vhack.eu" = all_admins;
-      "admin@vhack.eu" = all_admins;
-    };
-
-    mailDirectory = "/srv/mail/vmail";
-    dkimKeyDirectory = "/srv/mail/dkim";
-    sieveDirectory = "/srv/mail/sieve";
-    backup.snapshotRoot = "/srv/mail/backup";
-
-    enableImap = false;
-    enableImapSsl = true;
-    enablePop3 = false;
-    enablePop3Ssl = true;
-    # SMTP
-    enableSubmission = false;
-    enableSubmissionSsl = true;
-    openFirewall = true;
-
-    keyFile = "/var/lib/acme/server1.vhack.eu/key.pem";
-    certificateScheme = "acme";
-    certificateFile = "/var/lib/acme/server1.vhack.eu/fullchain.pem";
-  }
-  users
diff --git a/system/secrets/default.nix b/system/secrets/default.nix
new file mode 100644
index 0000000..3d92fe8
--- /dev/null
+++ b/system/secrets/default.nix
@@ -0,0 +1,12 @@
+{...}: {
+  age = {
+    secrets = {
+      keycloak = {
+        file = ./keycloak/passwd.tix;
+        mode = "700";
+        owner = "root";
+        group = "root";
+      };
+    };
+  };
+}
diff --git a/system/secrets/keycloak/passwd.tix b/system/secrets/keycloak/passwd.tix
new file mode 100644
index 0000000..b5c36cd
--- /dev/null
+++ b/system/secrets/keycloak/passwd.tix
@@ -0,0 +1,17 @@
+-----BEGIN AGE ENCRYPTED FILE-----
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGeFZ2Uk10Z1lrNUV6SWtu
+UFZLVFR2dWJsMnNZTkE0Q3loa1c0c3ZwTjJvCjhHcm9XVk1jWENYNG5VNVV2RlZp
+SVZROEFrU2tNV1dDYmNOdmEzanoyd2sKLT4gWDI1NTE5IHFOTEhFUUtGOWMzWjVO
+MGs2a0tsYlZSZGI3NXdBRktKNDgxdzRmQTI1emsKeVJzbHJhR3h5NDVwM2pqcFFW
+UzROY2toa0ZBL1p2elRmeHdUTFNhRm91bwotPiBzc2gtZWQyNTUxOSBPRDhUNGcg
+NG42WHBrcVh0cjJYKzMwR3FkTHN0VTZUY0xyZVQvMjR3azdtZ0V4M0dpRQpxSzlj
+anVRVndxdWhaQldZeHJ0WVFCNzZJMHFnaG5KRDRQa003enoyME1FCi0+ICZ2LjlB
+SS1ncmVhc2Ugfj51dWUyIG5XOExyNmR2IFNVdmkKK3QwSmRRCi0tLSBqb3FPRWtV
+dUdLcWV4R1VnOGZmNGxBY0dVbTRZN094V0dIeWpZTWp1V1QwCpu/WQunSMxbtxwz
+uiFrDcdAa3H3+2gIFHmktFbHZX9XNC2ri99G7nqQ9SoBvnpRFhhAiw0LSWnq2lqr
+rMQeug/z0sooWO6R2H17aLHXwxz82Spm7eUlc3nMz243U0SChz7OnPDgHBgLztqJ
+3zOMub3inn83jR7Pg+GjEuI26tqZUp6107CKzvBWI8ePsa7MW19UdmEVplewxrDq
+fjNsmid3+NNJ0LjC3gGUppHtTJW9ikTvaDMtS5Ysn94JoS6xRzVgE2LBswGBiQBJ
+ZDV+9het3ijDyljk3pjPRDKoWPEctT/cWLczEMK5vHqVt3pr/IAWqJTfXVkebP6Q
+6QdZrUKl/xxKmApVRx9K0wQAhfwlKKTHpW3hFFNivZtSg0hSaF4=
+-----END AGE ENCRYPTED FILE-----
diff --git a/system/secrets/secrets.nix b/system/secrets/secrets.nix
new file mode 100644
index 0000000..cdaf50d
--- /dev/null
+++ b/system/secrets/secrets.nix
@@ -0,0 +1,12 @@
+let
+  soispha = "age1mshh4ynzhhzhff25tqwkg4j054g3xwrfznh98ycchludj9wjj48qn2uffn";
+  sils = "age1vuhaey7kd9l76y6f9weeqmde3s4kjw38869ju6u3027yece2r3rqssjxst";
+
+  server1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnqsfIZjelH7rcvFvnLR5zUZuC8thsBupBlvjcMRBUm";
+in {
+  "keycloak/passwd.tix".publicKeys = [
+    soispha
+    sils
+    server1
+  ];
+}
diff --git a/system/services/acme/default.nix b/system/services/acme/default.nix
deleted file mode 100644
index 0a0c4ce..0000000
--- a/system/services/acme/default.nix
+++ /dev/null
@@ -1,38 +0,0 @@
-{lib, ...}: let
-  domains = import ./domains.nix {};
-
-  virtualHosts = builtins.listToAttrs (
-    builtins.map (domain_name: {
-      name = "acmechallenge.${domain_name}";
-      value = {
-        serverAliases = ["*.${domain_name}"];
-        locations."/.well-known/acme-challenge" = {
-          root = "/var/lib/acme/.challenges";
-        };
-        locations."/" = {
-          return = "301 https://$host$request_uri";
-        };
-      };
-    })
-    domains
-  );
-  certs = lib.attrsets.genAttrs domains (
-    domain_name: {
-      webroot = "/var/lib/acme/.challenges";
-      group = "nginx";
-    }
-  );
-in {
-  users.users.nginx.extraGroups = ["acme"];
-
-  services.nginx = {
-    enable = true;
-    inherit virtualHosts;
-  };
-
-  security.acme = {
-    acceptTerms = true;
-    defaults.email = "admin@vhack.eu";
-    inherit certs;
-  };
-}
diff --git a/system/services/acme/domains.nix b/system/services/acme/domains.nix
deleted file mode 100644
index 8f0930d..0000000
--- a/system/services/acme/domains.nix
+++ /dev/null
Binary files differdiff --git a/system/services/default.nix b/system/services/default.nix
index 19a531f..7bf26c3 100644
--- a/system/services/default.nix
+++ b/system/services/default.nix
@@ -1,14 +1,14 @@
 {...}: {
   imports = [
-    ./acme
+    ./fail2ban
     ./keycloak
+    ./mail
     ./matrix
     ./minecraft
     ./nginx
     ./nix
-    ./opensshd
+    ./nix-sync
+    ./openssh
     ./rust-motd
-    ./fail2ban
-    ./git-sync
   ];
 }
diff --git a/system/services/fail2ban/default.nix b/system/services/fail2ban/default.nix
index 5aee097..43fd674 100644
--- a/system/services/fail2ban/default.nix
+++ b/system/services/fail2ban/default.nix
@@ -1,4 +1,3 @@
-# vim: ts=2
 {...}: {
   services.fail2ban = {
     enable = true;
@@ -8,7 +7,7 @@
       logtarget = SYSLOG
       socket    = /run/fail2ban/fail2ban.sock
       pidfile   = /run/fail2ban/fail2ban.pid
-      dbfile    = /srv/fail2ban/fail2ban.sqlite3
+      dbfile    = /var/lib/fail2ban/db.sqlite3
     '';
     bantime-increment = {
       enable = true;
diff --git a/system/services/git-sync/default.nix b/system/services/git-sync/default.nix
deleted file mode 100644
index 776ca60..0000000
--- a/system/services/git-sync/default.nix
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-Taken from:
-https://github.com/nix-community/home-manager/blob/9ba7b3990eb1f4782ea3f5fe7ac4f3c88dd7a32c/modules/services/git-sync.nix
-*/
-{
-  config,
-  lib,
-  pkgs,
-  ...
-}: let
-  cfg = config.services.git-sync;
-
-  mkUnit = name: repo: {
-    Unit.Description = "Git Sync ${name}";
-
-    Install.WantedBy = ["default.target"];
-
-    Service = {
-      Environment = [
-        "PATH=${lib.makeBinPath (with pkgs; [openssh git])}"
-        "GIT_SYNC_DIRECTORY=${repo.path}"
-        "GIT_SYNC_COMMAND=${cfg.package}/bin/git-sync"
-        "GIT_SYNC_REPOSITORY=${repo.uri}"
-        "GIT_SYNC_INTERVAL=${toString repo.interval}"
-      ];
-      ExecStart = "${cfg.package}/bin/git-sync-on-inotify";
-      Restart = "on-abort";
-    };
-  };
-
-  services =
-    lib.mapAttrs' (name: repo: {
-      name = "git-sync-${name}";
-      value = mkUnit name repo;
-    })
-    cfg.repositories;
-
-  repositoryType = lib.types.submodule ({name, ...}: {
-    options = {
-      name = lib.mkOption {
-        internal = true;
-        default = name;
-        type = lib.types.str;
-        description = "The name that should be given to this unit.";
-      };
-
-      path = lib.mkOption {
-        type = lib.types.path;
-        description = "The path at which to sync the repository";
-      };
-
-      uri = lib.mkOption {
-        type = lib.types.str;
-        example = "git+ssh://user@example.com:/~[user]/path/to/repo.git";
-        description = ''
-          The URI of the remote to be synchronized. This is only used in the
-          event that the directory does not already exist. See
-          <link xlink:href="https://git-scm.com/docs/git-clone#_git_urls"/>
-          for the supported URIs.
-        '';
-      };
-
-      interval = lib.mkOption {
-        type = lib.types.int;
-        default = 500;
-        description = ''
-          The interval, specified in seconds, at which the synchronization will
-          be triggered even without filesystem changes.
-        '';
-      };
-    };
-  });
-in {
-  options = {
-    services.git-sync = {
-      enable = lib.mkEnableOption "git-sync services";
-
-      package = lib.mkOption {
-        type = lib.types.package;
-        default = pkgs.git-sync;
-        defaultText = lib.literalExpression "pkgs.git-sync";
-        description = ''
-          Package containing the <command>git-sync</command> program.
-        '';
-      };
-
-      repositories = lib.mkOption {
-        type = with lib.types; attrsOf repositoryType;
-        description = ''
-          The repositories that should be synchronized.
-        '';
-      };
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    assertions = [
-      (lib.hm.assertions.assertPlatform "services.git-sync" pkgs
-        lib.platforms.linux)
-    ];
-
-    systemd.user.services = services;
-  };
-}
diff --git a/system/services/keycloak/default.nix b/system/services/keycloak/default.nix
index dfeabc3..5f21b90 100644
--- a/system/services/keycloak/default.nix
+++ b/system/services/keycloak/default.nix
@@ -31,7 +31,7 @@
       createLocally = true;
 
       username = "keycloak";
-      passwordFile = "/srv/keycloak/password";
+      passwordFile = "${config.age.secrets.keycloak.path}";
     };
 
     settings = {
diff --git a/system/services/mail/default.nix b/system/services/mail/default.nix
new file mode 100644
index 0000000..382a87f
--- /dev/null
+++ b/system/services/mail/default.nix
@@ -0,0 +1,41 @@
+{lib, ...}: let
+  all_admins = [
+    "sils@vhack.eu"
+    "soispha@vhack.eu"
+    "nightingale@vhack.eu"
+  ];
+  users = import ./users.nix {};
+in {
+  mailserver =
+    lib.recursiveUpdate {
+      enable = true;
+      fqdn = "server1.vhack.eu";
+
+      useFsLayout = true;
+
+      extraVirtualAliases = {
+        "abuse@vhack.eu" = all_admins;
+        "postmaster@vhack.eu" = all_admins;
+        "admin@vhack.eu" = all_admins;
+      };
+
+      mailDirectory = "/var/lib/mail/vmail";
+      dkimKeyDirectory = "/var/lib/mail/dkim";
+      sieveDirectory = "/var/lib/mail/sieve";
+      backup.snapshotRoot = "/var/lib/mail/backup";
+
+      enableImap = false;
+      enableImapSsl = true;
+      enablePop3 = false;
+      enablePop3Ssl = true;
+      # SMTP
+      enableSubmission = false;
+      enableSubmissionSsl = true;
+      openFirewall = true;
+
+      keyFile = "/var/lib/acme/server1.vhack.eu/key.pem";
+      certificateScheme = "acme";
+      certificateFile = "/var/lib/acme/server1.vhack.eu/fullchain.pem";
+    }
+    users;
+}
diff --git a/system/mail/users.nix b/system/services/mail/users.nix
index f3264a1..f3264a1 100644
--- a/system/mail/users.nix
+++ b/system/services/mail/users.nix
Binary files differdiff --git a/system/services/matrix/default.nix b/system/services/matrix/default.nix
index d49fda2..e35c129 100644
--- a/system/services/matrix/default.nix
+++ b/system/services/matrix/default.nix
@@ -55,7 +55,6 @@ in {
     enable = true;
     dataDir = "/srv/matrix/data";
     configFile = "/srv/matrix";
-    extraConfigFiles = ["/srv/matrix/oid/config.yaml"];
     settings = {
       media_store_path = "/srv/matrix/media_store";
       server_name = "vhack.eu";
diff --git a/system/services/minecraft/default.nix b/system/services/minecraft/default.nix
index e69ffb1..e659af0 100644
--- a/system/services/minecraft/default.nix
+++ b/system/services/minecraft/default.nix
@@ -7,7 +7,7 @@
     enable = true;
     declarative = true;
     eula = true;
-    dataDir = "/srv/minecraft";
+    dataDir = "/var/lib/minecraft";
     openFirewall = true;
     jvmOpts = "-Xmx8192M -Xms8192M";
     whitelist = {
diff --git a/system/services/nginx/default.nix b/system/services/nginx/default.nix
index 6753fb0..404c167 100644
--- a/system/services/nginx/default.nix
+++ b/system/services/nginx/default.nix
@@ -2,6 +2,14 @@
   imports = [
     ./hosts.nix
   ];
+  security.acme = {
+    acceptTerms = true;
+    defaults = {
+      email = "admin@vhack.eu";
+      webroot = "/var/lib/acme/acme-challenge";
+    };
+  };
+
   networking.firewall = {
     allowedTCPPorts = [80 443];
   };
diff --git a/system/services/nginx/hosts.nix b/system/services/nginx/hosts.nix
index 684bb68..1590756 100644
--- a/system/services/nginx/hosts.nix
+++ b/system/services/nginx/hosts.nix
Binary files differdiff --git a/system/services/nix-sync/default.nix b/system/services/nix-sync/default.nix
new file mode 100644
index 0000000..44348c0
--- /dev/null
+++ b/system/services/nix-sync/default.nix
@@ -0,0 +1,262 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}: let
+  cfg = config.services.nix-sync;
+
+  mkTimer = name: repo: {
+    description = "Nix sync ${name} timer";
+    wantedBy = ["timers.target"];
+    timerConfig = {
+      OnActiveSec = repo.interval;
+    };
+    after = ["network-online.target"];
+  };
+
+  parents = path: let
+    split_path = builtins.split "/" path;
+    filename = builtins.elemAt split_path (builtins.length split_path - 1);
+  in
+    lib.strings.removeSuffix "/" (builtins.replaceStrings [filename] [""] path);
+  esa = lib.strings.escapeShellArg;
+  mkUnit = name: repo: let
+    optionalPathSeparator =
+      if lib.strings.hasPrefix "/" repo.path
+      then ""
+      else "/";
+    repoCachePath = cfg.cachePath + optionalPathSeparator + repo.path;
+    execStartScript = pkgs.writeScript "nix-sync-exec" ''
+      #! /usr/bin/env dash
+      export XDG_CACHE_HOME="$CACHE_DIRECTORY";
+      cd ${esa repoCachePath};
+
+      git fetch
+      origin="$(git rev-parse @{u})";
+      branch="$(git rev-parse @)";
+
+      if ! [ "$origin" = "$branch" ]; then
+        git pull;
+
+        out_paths=$(mktemp);
+        nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths";
+        [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1)
+        out_path="$(cat "$out_paths")";
+        rm ${esa repo.path};
+        ln -s "$out_path" ${esa repo.path};
+        rm "$out_paths";
+      fi
+    '';
+    execStartPreScript = ''
+      export XDG_CACHE_HOME="$CACHE_DIRECTORY";
+
+      if ! [ -d ${esa repoCachePath}/.git ]; then
+          mkdir --parents ${esa repoCachePath};
+          git clone ${esa repo.uri} ${esa repoCachePath};
+
+          out_paths=$(mktemp);
+          nix build ${esa repoCachePath} --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths";
+          [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1)
+          out_path="$(cat "$out_paths")";
+          ln -s "$out_path" ${esa repo.path};
+          rm "$out_paths";
+      fi
+
+      if ! [ -L ${esa repo.path} ]; then
+        cd ${esa repoCachePath};
+
+        git pull;
+
+        out_paths=$(mktemp);
+        nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths";
+        [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1)
+        out_path="$(cat "$out_paths")";
+
+        [ -d ${esa repo.path} ] && rm -d ${esa repo.path};
+        [ -e ${esa repo.path} ] && rm ${esa repo.path};
+
+        ln -s "$out_path" ${esa repo.path};
+        rm "$out_paths";
+      fi
+    '';
+  in {
+    description = "Nix Sync ${name}";
+    wantedBy = ["default.target"];
+    after = ["network.target"];
+    path = with pkgs; [openssh git nix mktemp coreutils dash];
+    preStart = execStartPreScript;
+
+    serviceConfig = {
+      ExecStart = execStartScript;
+      Restart = "on-abort";
+      # User and group
+      User = cfg.user;
+      Group = cfg.group;
+      # Runtime directory and mode
+      RuntimeDirectory = "nix-sync";
+      RuntimeDirectoryMode = "0750";
+      # Cache directory and mode
+      CacheDirectory = "nix-sync";
+      CacheDirectoryMode = "0750";
+      # Logs directory and mode
+      LogsDirectory = "nix-sync";
+      LogsDirectoryMode = "0750";
+      # Proc filesystem
+      ProcSubset = "all";
+      ProtectProc = "invisible";
+      # New file permissions
+      UMask = "0027"; # 0640 / 0750
+      # Capabilities
+      AmbientCapabilities = ["CAP_CHOWN"];
+      CapabilityBoundingSet = ["CAP_CHOWN"];
+      # Security
+      NoNewPrivileges = true;
+      # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html)
+      ReadWritePaths = ["${esa (parents repo.path)}" "-${esa repoCachePath}" "-${esa cfg.cachePath}"];
+      ReadOnlyPaths = ["/nix"];
+      ProtectSystem = "strict";
+      ProtectHome = true;
+      PrivateTmp = true;
+      PrivateDevices = true;
+      ProtectHostname = true;
+      ProtectClock = true;
+      ProtectKernelTunables = true;
+      ProtectKernelModules = true;
+      ProtectKernelLogs = true;
+      ProtectControlGroups = true;
+      RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
+      RestrictNamespaces = true;
+      LockPersonality = true;
+      MemoryDenyWriteExecute = true;
+      RestrictRealtime = true;
+      RestrictSUIDSGID = true;
+      RemoveIPC = true;
+      PrivateMounts = true;
+      # System Call Filtering
+      SystemCallArchitectures = "native";
+      SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"];
+    };
+  };
+
+  services =
+    lib.mapAttrs' (name: repo: {
+      name = "nix-sync-${name}";
+      value = mkUnit name repo;
+    })
+    cfg.repositories;
+  timers =
+    lib.mapAttrs' (name: repo: {
+      name = "nix-sync-${name}";
+      value = mkTimer name repo;
+    })
+    cfg.repositories;
+
+  # generate the websites directory, so systemd can mount it read write
+  generatedDirectories =
+    lib.mapAttrsToList (
+      _: repo: "d ${esa (parents repo.path)} 0755 ${cfg.user} ${cfg.group}"
+    )
+    cfg.repositories;
+
+  repositoryType = lib.types.submodule ({name, ...}: {
+    options = {
+      name = lib.mkOption {
+        internal = true;
+        default = name;
+        type = lib.types.str;
+        description = "The name that should be given to this unit.";
+      };
+
+      path = lib.mkOption {
+        type = lib.types.str;
+        description = "The path at which to sync the repository";
+      };
+
+      uri = lib.mkOption {
+        type = lib.types.str;
+        example = "ssh://user@example.com:/~[user]/path/to/repo.git";
+        description = ''
+          The URI of the remote to be synchronized. This is only used in the
+          event that the directory does not already exist. See
+          <link xlink:href="https://git-scm.com/docs/git-clone#_git_urls"/>
+          for the supported URIs.
+        '';
+      };
+
+      interval = lib.mkOption {
+        type = lib.types.int;
+        default = 500;
+        description = ''
+          The interval, specified in seconds, at which the synchronization will
+          be triggered.
+        '';
+      };
+    };
+  });
+in {
+  options = {
+    services.nix-sync = {
+      enable = lib.mkEnableOption "nix-sync services";
+
+      user = lib.mkOption {
+        type = lib.types.str;
+        default = "nix-sync";
+        description = lib.mdDoc "User account under which nix-sync units runs.";
+      };
+
+      group = lib.mkOption {
+        type = lib.types.str;
+        default = "nix-sync";
+        description = lib.mdDoc "Group account under which nix-sync units runs.";
+      };
+
+      cachePath = lib.mkOption {
+        type = lib.types.str;
+        default = "/var/lib/nix-sync";
+        description = lib.mdDoc ''
+          Where to cache git directories. Should not end with a slash ("/")
+        '';
+      };
+
+      repositories = lib.mkOption {
+        type = with lib.types; attrsOf repositoryType;
+        description = ''
+          The repositories that should be synchronized.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !lib.strings.hasSuffix "/" cfg.cachePath;
+        message = "Your cachePath ('${cfg.cachePath}') ends with a slash ('/'), please use: '${lib.strings.removeSuffix "/" cfg.cachePath}'.";
+      }
+    ];
+
+    systemd.tmpfiles.rules =
+      generatedDirectories;
+
+    systemd.services = services;
+    systemd.timers = timers;
+    users.users =
+      if cfg.user == "nix-sync"
+      then {
+        nix-sync = {
+          group = "${cfg.group}";
+          isSystemUser = true;
+        };
+      }
+      else lib.warnIf (cfg.user != "nix-sync") "The user (${cfg.user}) is not \"nix-sync\", thus you are responible for generating it.";
+    users.groups =
+      if cfg.group == "nix-sync"
+      then {
+        nix-sync = {
+          members = ["${cfg.user}"];
+        };
+      }
+      else lib.warnIf (cfg.group != "nix-sync") "The group (${cfg.group}) is not \"nix-sync\", thus you are responible for generating it.";
+  };
+}
diff --git a/system/services/openssh/default.nix b/system/services/openssh/default.nix
new file mode 100644
index 0000000..46b7ffd
--- /dev/null
+++ b/system/services/openssh/default.nix
@@ -0,0 +1,17 @@
+{...}: {
+  services.openssh = {
+    enable = true;
+    settings.PasswordAuthentication = false;
+    hostKeys = [
+      {
+        # See the explanation for this in /system/impermanence/mods/openssh.nix
+        # path = "/var/lib/sshd/ssh_host_ed25519_key";
+
+        # FIXME: Remove this workaround
+        path = "/srv/var/lib/sshd/ssh_host_ed25519_key";
+        rounds = 1000;
+        type = "ed25519";
+      }
+    ];
+  };
+}
diff --git a/system/services/opensshd/default.nix b/system/services/opensshd/default.nix
deleted file mode 100644
index 1bb37ee..0000000
--- a/system/services/opensshd/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{...}: {
-  services.openssh = {
-    enable = true;
-    settings.PasswordAuthentication = false;
-    hostKeys = [
-      {
-        path = "/srv/sshd/ssh_host_ed25519_key";
-        rounds = 1000;
-        type = "ed25519";
-      }
-    ];
-  };
-}
diff --git a/system/users/default.nix b/system/users/default.nix
index 3555221..822c94b 100644
--- a/system/users/default.nix
+++ b/system/users/default.nix
@@ -1,56 +1,53 @@
 {pkgs, ...}: {
-  users.mutableUsers = false;
-  users.defaultUserShell = pkgs.zsh;
+  users = {
+    mutableUsers = false;
+    defaultUserShell = pkgs.zsh;
+    users = {
+      root = {
+        initialHashedPassword = null; # to lock root
+        openssh.authorizedKeys.keys = [];
+      };
 
-  users.users = {
-    root = {
-      #uid = 0;
-      initialHashedPassword = null; # to lock root
-      openssh.authorizedKeys.keys = [
-      ];
-    };
+      sils = {
+        name = "sils";
+        isNormalUser = true;
+        home = "/home/sils";
+        initialHashedPassword = "$y$jFT$KpFnahVCE9JbE.5P3us8o.$ZzSxCusWqe3sL7b6DLgOXNNUf114tiiptM6T8lDxtKC";
+        uid = 1000;
+        extraGroups = [
+          "wheel"
+        ];
+        openssh.authorizedKeys.keys = [
+          "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG63gxw8JePmrC8Fni0pLV4TnPBhCPmSV9FYEdva+6s7 sils"
+        ];
+      };
 
-    sils = {
-      name = "sils";
-      isNormalUser = true;
-      home = "/srv/home/sils";
-      initialHashedPassword = "$y$jFT$KpFnahVCE9JbE.5P3us8o.$ZzSxCusWqe3sL7b6DLgOXNNUf114tiiptM6T8lDxtKC";
-      uid = 1000;
-      extraGroups = [
-        "wheel"
-      ];
-      openssh.authorizedKeys.keys = [
-        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG63gxw8JePmrC8Fni0pLV4TnPBhCPmSV9FYEdva+6s7 sils"
-      ];
-    };
+      soispha = {
+        name = "soispha";
+        isNormalUser = true;
+        home = "/home/soispha";
+        initialHashedPassword = "$y$jFT$3.8XmUyukZvpExMUxDZkI.$IVrJgm8ysNDF/0vDD2kF6w73ozXgr1LMVRNN4Bq7pv1";
+        uid = 1001;
+        extraGroups = [
+          "wheel"
+        ];
+        openssh.authorizedKeys.keys = [
+          "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBFuTNNn71Rhfnop2cdz3r/RhWWlCePnSBOhTBbu2ME soispha"
+        ];
+      };
 
-    soispha = {
-      name = "soispha";
-      isNormalUser = true;
-      home = "/srv/home/soispha";
-      initialHashedPassword = "$y$jFT$3.8XmUyukZvpExMUxDZkI.$IVrJgm8ysNDF/0vDD2kF6w73ozXgr1LMVRNN4Bq7pv1";
-      uid = 1001;
-      extraGroups = [
-        "wheel"
-      ];
-      openssh.authorizedKeys.keys = [
-        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBFuTNNn71Rhfnop2cdz3r/RhWWlCePnSBOhTBbu2ME soispha"
-      ];
-    };
-
-    nightingale = {
-      name = "nightingale";
-      isNormalUser = true;
-      home = "/srv/home/nightingale";
-      initialHashedPassword = null; # TODO CHANGE
-      uid = 1002;
-      extraGroups = [
-        "wheel"
-      ];
-      openssh.authorizedKeys.keys = [
-      ];
+      nightingale = {
+        name = "nightingale";
+        isNormalUser = true;
+        home = "/home/nightingale";
+        initialHashedPassword = null; # TODO CHANGE
+        uid = 1002;
+        extraGroups = [
+          "wheel"
+        ];
+        openssh.authorizedKeys.keys = [
+        ];
+      };
     };
   };
 }
-# vim: ts=2
-