summary refs log tree commit diff
path: root/nixos/modules/services/networking/gale.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/networking/gale.nix')
-rw-r--r--nixos/modules/services/networking/gale.nix182
1 files changed, 182 insertions, 0 deletions
diff --git a/nixos/modules/services/networking/gale.nix b/nixos/modules/services/networking/gale.nix
new file mode 100644
index 00000000000..3a5d9bd63c7
--- /dev/null
+++ b/nixos/modules/services/networking/gale.nix
@@ -0,0 +1,182 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gale;
+  # we convert the path to a string to avoid it being copied to the nix store,
+  # otherwise users could read the private key as all files in the store are
+  # world-readable
+  keyPath = toString cfg.keyPath;
+  # ...but we refer to the pubkey file using a path so that we can ensure the
+  # config gets rebuilt if the public key changes (we can assume the private key
+  # will never change without the public key having changed)
+  gpubFile = cfg.keyPath + "/${cfg.domain}.gpub";
+  home = "/var/lib/gale";
+  keysPrepared = cfg.keyPath != null && lib.pathExists cfg.keyPath;
+in
+{
+  options = {
+    services.gale = {
+      enable = mkEnableOption "the Gale messaging daemon";
+
+      user = mkOption {
+        default = "gale";
+        type = types.str;
+        description = "Username for the Gale daemon.";
+      };
+
+      group = mkOption {
+        default = "gale";
+        type = types.str;
+        description = "Group name for the Gale daemon.";
+      };
+
+      setuidWrapper = mkOption {
+        default = null;
+        description = "Configuration for the Gale gksign setuid wrapper.";
+      };
+
+      domain = mkOption {
+        default = "";
+        type = types.str;
+        description = "Domain name for the Gale system.";
+      };
+
+      keyPath = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          Directory containing the key pair for this Gale domain.  The expected
+          filename will be taken from the domain option with ".gpri" and ".gpub"
+          appended.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional text to be added to <filename>/etc/gale/conf</filename>.
+        '';
+      };
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.enable {
+       assertions = [{
+         assertion = cfg.domain != "";
+         message = "A domain must be set for Gale.";
+       }];
+
+       warnings = mkIf (!keysPrepared) [
+         "You must run gale-install in order to generate a domain key."
+       ];
+
+       system.activationScripts.gale = mkIf cfg.enable (
+         stringAfter [ "users" "groups" ] ''
+           chmod -R 755 ${home}
+           mkdir -m 0777 -p ${home}/auth/cache
+           mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub
+           mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub
+           mkdir -m 0755 -p ${home}/auth/trusted # ROOT
+           mkdir -m 0700 -p ${home}/.gale
+           mkdir -m 0700 -p ${home}/.gale/auth
+           mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri
+
+           ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT"
+           chown -R ${cfg.user}:${cfg.group} ${home}
+         ''
+       );
+
+       environment = {
+         etc = {
+           "gale/auth".source = home + "/auth"; # symlink /var/lib/gale/auth
+           "gale/conf".text = ''
+             GALE_USER ${cfg.user}
+             GALE_DOMAIN ${cfg.domain}
+             ${cfg.extraConfig}
+           '';
+         };
+
+         systemPackages = [ pkgs.gale ];
+       };
+
+       users.extraUsers = [{
+         name = cfg.user;
+         description = "Gale daemon";
+         uid = config.ids.uids.gale;
+         group = cfg.group;
+         home = home;
+         createHome = true;
+       }];
+
+       users.extraGroups = [{
+         name = cfg.group;
+         gid = config.ids.gids.gale;
+       }];
+    })
+    (mkIf (cfg.enable && keysPrepared) {
+       assertions = [
+         {
+           assertion = cfg.keyPath != null
+                    && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub");
+           message = "Couldn't find a Gale public key for ${cfg.domain}.";
+         }
+         {
+           assertion = cfg.keyPath != null
+                    && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri");
+           message = "Couldn't find a Gale private key for ${cfg.domain}.";
+         }
+       ];
+
+       services.gale.setuidWrapper = {
+         program = "gksign";
+         source = "${pkgs.gale}/bin/gksign";
+         owner = cfg.user;
+         group = cfg.group;
+         setuid = true;
+         setgid = false;
+       };
+
+       security.setuidOwners = [ cfg.setuidWrapper ];
+
+       systemd.services.gale-galed = {
+         description = "Gale messaging daemon";
+         wantedBy = [ "multi-user.target" ];
+         wants = [ "gale-gdomain.service" ];
+         after = [ "network.target" ];
+
+         preStart = ''
+           install -m 0640 ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/"
+           install -m 0644 ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub"
+           install -m 0644 ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub"
+           chown -R ${cfg.user}:${cfg.group} ${home}
+         '';
+
+         serviceConfig = {
+           Type = "forking";
+           ExecStart = "@${pkgs.gale}/bin/galed galed";
+           User = cfg.user;
+           Group = cfg.group;
+           PermissionsStartOnly = true;
+         };
+       };
+
+       systemd.services.gale-gdomain = {
+         description = "Gale AKD daemon";
+         wantedBy = [ "multi-user.target" ];
+         requires = [ "gale-galed.service" ];
+         after = [ "gale-galed.service" ];
+
+         serviceConfig = {
+           Type = "forking";
+           ExecStart = "@${pkgs.gale}/bin/gdomain gdomain";
+           User = cfg.user;
+           Group = cfg.group;
+         };
+       };
+    })
+  ];
+}