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.users.${cfg.user} = {
108 description = "Gale daemon";
109 uid = config.ids.uids.gale;
110 group = cfg.group;
111 home = home;
112 createHome = true;
113 };
114
115 users.groups = [{
116 name = cfg.group;
117 gid = config.ids.gids.gale;
118 }];
119 })
120 (mkIf (cfg.enable && keysPrepared) {
121 assertions = [
122 {
123 assertion = cfg.keyPath != null
124 && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub");
125 message = "Couldn't find a Gale public key for ${cfg.domain}.";
126 }
127 {
128 assertion = cfg.keyPath != null
129 && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri");
130 message = "Couldn't find a Gale private key for ${cfg.domain}.";
131 }
132 ];
133
134 services.gale.setuidWrapper = {
135 program = "gksign";
136 source = "${pkgs.gale}/bin/gksign";
137 owner = cfg.user;
138 group = cfg.group;
139 setuid = true;
140 setgid = false;
141 };
142
143 security.wrappers.gksign = cfg.setuidWrapper;
144
145 systemd.services.gale-galed = {
146 description = "Gale messaging daemon";
147 wantedBy = [ "multi-user.target" ];
148 wants = [ "gale-gdomain.service" ];
149 after = [ "network.target" ];
150
151 preStart = ''
152 install -m 0640 -o ${cfg.user} -g ${cfg.group} ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/"
153 install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub"
154 install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub"
155 '';
156
157 serviceConfig = {
158 Type = "forking";
159 ExecStart = "@${pkgs.gale}/bin/galed galed";
160 User = cfg.user;
161 Group = cfg.group;
162 PermissionsStartOnly = true;
163 };
164 };
165
166 systemd.services.gale-gdomain = {
167 description = "Gale AKD daemon";
168 wantedBy = [ "multi-user.target" ];
169 requires = [ "gale-galed.service" ];
170 after = [ "gale-galed.service" ];
171
172 serviceConfig = {
173 Type = "forking";
174 ExecStart = "@${pkgs.gale}/bin/gdomain gdomain";
175 User = cfg.user;
176 Group = cfg.group;
177 };
178 };
179 })
180 ];
181}