1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.gale; 7 # we convert the path to a string to avoid it being copied to the nix store, 8 # otherwise users could read the private key as all files in the store are 9 # world-readable 10 keyPath = toString cfg.keyPath; 11 # ...but we refer to the pubkey file using a path so that we can ensure the 12 # config gets rebuilt if the public key changes (we can assume the private key 13 # will never change without the public key having changed) 14 gpubFile = cfg.keyPath + "/${cfg.domain}.gpub"; 15 home = "/var/lib/gale"; 16 keysPrepared = cfg.keyPath != null && lib.pathExists cfg.keyPath; 17in 18{ 19 options = { 20 services.gale = { 21 enable = mkEnableOption "the Gale messaging daemon"; 22 23 user = mkOption { 24 default = "gale"; 25 type = types.str; 26 description = "Username for the Gale daemon."; 27 }; 28 29 group = mkOption { 30 default = "gale"; 31 type = types.str; 32 description = "Group name for the Gale daemon."; 33 }; 34 35 setuidWrapper = mkOption { 36 default = null; 37 description = "Configuration for the Gale gksign setuid wrapper."; 38 }; 39 40 domain = mkOption { 41 default = ""; 42 type = types.str; 43 description = "Domain name for the Gale system."; 44 }; 45 46 keyPath = mkOption { 47 default = null; 48 type = types.nullOr types.path; 49 description = '' 50 Directory containing the key pair for this Gale domain. The expected 51 filename will be taken from the domain option with ".gpri" and ".gpub" 52 appended. 53 ''; 54 }; 55 56 extraConfig = mkOption { 57 type = types.lines; 58 default = ""; 59 description = '' 60 Additional text to be added to <filename>/etc/gale/conf</filename>. 61 ''; 62 }; 63 }; 64 }; 65 66 config = mkMerge [ 67 (mkIf cfg.enable { 68 assertions = [{ 69 assertion = cfg.domain != ""; 70 message = "A domain must be set for Gale."; 71 }]; 72 73 warnings = mkIf (!keysPrepared) [ 74 "You must run gale-install in order to generate a domain key." 75 ]; 76 77 system.activationScripts.gale = mkIf cfg.enable ( 78 stringAfter [ "users" "groups" ] '' 79 chmod 755 ${home} 80 mkdir -m 0777 -p ${home}/auth/cache 81 mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub 82 mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub 83 mkdir -m 0755 -p ${home}/auth/trusted # ROOT 84 mkdir -m 0700 -p ${home}/.gale 85 mkdir -m 0700 -p ${home}/.gale/auth 86 mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri 87 88 ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT" 89 chown ${cfg.user}:${cfg.group} ${home} ${home}/auth ${home}/auth/* 90 chown ${cfg.user}:${cfg.group} ${home}/.gale ${home}/.gale/auth ${home}/.gale/auth/private 91 '' 92 ); 93 94 environment = { 95 etc = { 96 "gale/auth".source = home + "/auth"; # symlink /var/lib/gale/auth 97 "gale/conf".text = '' 98 GALE_USER ${cfg.user} 99 GALE_DOMAIN ${cfg.domain} 100 ${cfg.extraConfig} 101 ''; 102 }; 103 104 systemPackages = [ pkgs.gale ]; 105 }; 106 107 users.extraUsers = [{ 108 name = cfg.user; 109 description = "Gale daemon"; 110 uid = config.ids.uids.gale; 111 group = cfg.group; 112 home = home; 113 createHome = true; 114 }]; 115 116 users.extraGroups = [{ 117 name = cfg.group; 118 gid = config.ids.gids.gale; 119 }]; 120 }) 121 (mkIf (cfg.enable && keysPrepared) { 122 assertions = [ 123 { 124 assertion = cfg.keyPath != null 125 && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub"); 126 message = "Couldn't find a Gale public key for ${cfg.domain}."; 127 } 128 { 129 assertion = cfg.keyPath != null 130 && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri"); 131 message = "Couldn't find a Gale private key for ${cfg.domain}."; 132 } 133 ]; 134 135 services.gale.setuidWrapper = { 136 program = "gksign"; 137 source = "${pkgs.gale}/bin/gksign"; 138 owner = cfg.user; 139 group = cfg.group; 140 setuid = true; 141 setgid = false; 142 }; 143 144 security.setuidOwners = [ cfg.setuidWrapper ]; 145 146 systemd.services.gale-galed = { 147 description = "Gale messaging daemon"; 148 wantedBy = [ "multi-user.target" ]; 149 wants = [ "gale-gdomain.service" ]; 150 after = [ "network.target" ]; 151 152 preStart = '' 153 install -m 0640 -o ${cfg.user} -g ${cfg.group} ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/" 154 install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub" 155 install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub" 156 ''; 157 158 serviceConfig = { 159 Type = "forking"; 160 ExecStart = "@${pkgs.gale}/bin/galed galed"; 161 User = cfg.user; 162 Group = cfg.group; 163 PermissionsStartOnly = true; 164 }; 165 }; 166 167 systemd.services.gale-gdomain = { 168 description = "Gale AKD daemon"; 169 wantedBy = [ "multi-user.target" ]; 170 requires = [ "gale-galed.service" ]; 171 after = [ "gale-galed.service" ]; 172 173 serviceConfig = { 174 Type = "forking"; 175 ExecStart = "@${pkgs.gale}/bin/gdomain gdomain"; 176 User = cfg.user; 177 Group = cfg.group; 178 }; 179 }; 180 }) 181 ]; 182}