1{ config, lib, pkgs, ... }:
2
3# openafsBin, openafsSrv, mkCellServDB
4with import ./lib.nix { inherit config lib pkgs; };
5
6let
7 inherit (lib) concatStringsSep literalExpression mkIf mkOption mkEnableOption
8 optionalString types;
9
10 bosConfig = pkgs.writeText "BosConfig" (''
11 restrictmode 1
12 restarttime 16 0 0 0 0
13 checkbintime 3 0 5 0 0
14 '' + (optionalString cfg.roles.database.enable ''
15 bnode simple vlserver 1
16 parm ${openafsSrv}/libexec/openafs/vlserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.vlserverArgs}
17 end
18 bnode simple ptserver 1
19 parm ${openafsSrv}/libexec/openafs/ptserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.ptserverArgs}
20 end
21 '') + (optionalString cfg.roles.fileserver.enable ''
22 bnode dafs dafs 1
23 parm ${openafsSrv}/libexec/openafs/dafileserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.fileserverArgs}
24 parm ${openafsSrv}/libexec/openafs/davolserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.volserverArgs}
25 parm ${openafsSrv}/libexec/openafs/salvageserver ${cfg.roles.fileserver.salvageserverArgs}
26 parm ${openafsSrv}/libexec/openafs/dasalvager ${cfg.roles.fileserver.salvagerArgs}
27 end
28 '') + (optionalString (cfg.roles.database.enable && cfg.roles.backup.enable && (!cfg.roles.backup.enableFabs)) ''
29 bnode simple buserver 1
30 parm ${openafsSrv}/libexec/openafs/buserver ${cfg.roles.backup.buserverArgs} ${optionalString useBuCellServDB "-cellservdb /etc/openafs/backup/"}
31 end
32 '') + (optionalString (cfg.roles.database.enable &&
33 cfg.roles.backup.enable &&
34 cfg.roles.backup.enableFabs) ''
35 bnode simple buserver 1
36 parm ${lib.getBin pkgs.fabs}/bin/fabsys server --config ${fabsConfFile} ${cfg.roles.backup.fabsArgs}
37 end
38 ''));
39
40 netInfo = if (cfg.advertisedAddresses != []) then
41 pkgs.writeText "NetInfo" ((concatStringsSep "\nf " cfg.advertisedAddresses) + "\n")
42 else null;
43
44 buCellServDB = pkgs.writeText "backup-cellServDB-${cfg.cellName}"
45 (mkCellServDB cfg.cellName cfg.roles.backup.cellServDB);
46
47 useBuCellServDB = (cfg.roles.backup.cellServDB != []) && (!cfg.roles.backup.enableFabs);
48
49 cfg = config.services.openafsServer;
50
51 udpSizeStr = toString cfg.udpPacketSize;
52
53 fabsConfFile = pkgs.writeText "fabs.yaml" (builtins.toJSON ({
54 afs = {
55 aklog = cfg.package + "/bin/aklog";
56 cell = cfg.cellName;
57 dumpscan = cfg.package + "/bin/afsdump_scan";
58 fs = cfg.package + "/bin/fs";
59 pts = cfg.package + "/bin/pts";
60 vos = cfg.package + "/bin/vos";
61 };
62 k5start.command = (lib.getBin pkgs.kstart) + "/bin/k5start";
63 } // cfg.roles.backup.fabsExtraConfig));
64
65in {
66
67 options = {
68
69 services.openafsServer = {
70
71 enable = mkOption {
72 default = false;
73 type = types.bool;
74 description = lib.mdDoc ''
75 Whether to enable the OpenAFS server. An OpenAFS server needs a
76 complex setup. So, be aware that enabling this service and setting
77 some options does not give you a turn-key-ready solution. You need
78 at least a running Kerberos 5 setup, as OpenAFS relies on it for
79 authentication. See the Guide "QuickStartUnix" coming with
80 `pkgs.openafs.doc` for complete setup
81 instructions.
82 '';
83 };
84
85 advertisedAddresses = mkOption {
86 type = types.listOf types.str;
87 default = [];
88 description = lib.mdDoc "List of IP addresses this server is advertised under. See NetInfo(5)";
89 };
90
91 cellName = mkOption {
92 default = "";
93 type = types.str;
94 description = lib.mdDoc "Cell name, this server will serve.";
95 example = "grand.central.org";
96 };
97
98 cellServDB = mkOption {
99 default = [];
100 type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
101 description = lib.mdDoc "Definition of all cell-local database server machines.";
102 };
103
104 package = mkOption {
105 default = pkgs.openafs;
106 defaultText = literalExpression "pkgs.openafs";
107 type = types.package;
108 description = lib.mdDoc "OpenAFS package for the server binaries";
109 };
110
111 roles = {
112 fileserver = {
113 enable = mkOption {
114 default = true;
115 type = types.bool;
116 description = lib.mdDoc "Fileserver role, serves files and volumes from its local storage.";
117 };
118
119 fileserverArgs = mkOption {
120 default = "-vattachpar 128 -vhashsize 11 -L -rxpck 400 -cb 1000000";
121 type = types.str;
122 description = lib.mdDoc "Arguments to the dafileserver process. See its man page.";
123 };
124
125 volserverArgs = mkOption {
126 default = "";
127 type = types.str;
128 description = lib.mdDoc "Arguments to the davolserver process. See its man page.";
129 example = "-sync never";
130 };
131
132 salvageserverArgs = mkOption {
133 default = "";
134 type = types.str;
135 description = lib.mdDoc "Arguments to the salvageserver process. See its man page.";
136 example = "-showlog";
137 };
138
139 salvagerArgs = mkOption {
140 default = "";
141 type = types.str;
142 description = lib.mdDoc "Arguments to the dasalvager process. See its man page.";
143 example = "-showlog -showmounts";
144 };
145 };
146
147 database = {
148 enable = mkOption {
149 default = true;
150 type = types.bool;
151 description = lib.mdDoc ''
152 Database server role, maintains the Volume Location Database,
153 Protection Database (and Backup Database, see
154 `backup` role). There can be multiple
155 servers in the database role for replication, which then need
156 reliable network connection to each other.
157
158 Servers in this role appear in AFSDB DNS records or the
159 CellServDB.
160 '';
161 };
162
163 vlserverArgs = mkOption {
164 default = "";
165 type = types.str;
166 description = lib.mdDoc "Arguments to the vlserver process. See its man page.";
167 example = "-rxbind";
168 };
169
170 ptserverArgs = mkOption {
171 default = "";
172 type = types.str;
173 description = lib.mdDoc "Arguments to the ptserver process. See its man page.";
174 example = "-restricted -default_access S---- S-M---";
175 };
176 };
177
178 backup = {
179 enable = mkEnableOption (lib.mdDoc ''
180 Backup server role. When using OpenAFS built-in buserver, use in conjunction with the
181 `database` role to maintain the Backup
182 Database. Normally only used in conjunction with tape storage
183 or IBM's Tivoli Storage Manager.
184
185 For a modern backup server, enable this role and see
186 {option}`enableFabs`.
187 '');
188
189 enableFabs = mkEnableOption (lib.mdDoc ''
190 FABS, the flexible AFS backup system. It stores volumes as dump files, relying on other
191 pre-existing backup solutions for handling them.
192 '');
193
194 buserverArgs = mkOption {
195 default = "";
196 type = types.str;
197 description = lib.mdDoc "Arguments to the buserver process. See its man page.";
198 example = "-p 8";
199 };
200
201 cellServDB = mkOption {
202 default = [];
203 type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
204 description = lib.mdDoc ''
205 Definition of all cell-local backup database server machines.
206 Use this when your cell uses less backup database servers than
207 other database server machines.
208 '';
209 };
210
211 fabsArgs = mkOption {
212 default = "";
213 type = types.str;
214 description = lib.mdDoc ''
215 Arguments to the fabsys process. See
216 {manpage}`fabsys_server(1)` and
217 {manpage}`fabsys_config(1)`.
218 '';
219 };
220
221 fabsExtraConfig = mkOption {
222 default = {};
223 type = types.attrs;
224 description = lib.mdDoc ''
225 Additional configuration parameters for the FABS backup server.
226 '';
227 example = literalExpression ''
228 {
229 afs.localauth = true;
230 afs.keytab = config.sops.secrets.fabsKeytab.path;
231 }
232 '';
233 };
234 };
235 };
236
237 dottedPrincipals= mkOption {
238 default = false;
239 type = types.bool;
240 description = lib.mdDoc ''
241 If enabled, allow principal names containing (.) dots. Enabling
242 this has security implications!
243 '';
244 };
245
246 udpPacketSize = mkOption {
247 default = 1310720;
248 type = types.int;
249 description = lib.mdDoc ''
250 UDP packet size to use in Bytes. Higher values can speed up
251 communications. The default of 1 MB is a sufficient in most
252 cases. Make sure to increase the kernel's UDP buffer size
253 accordingly via `net.core(w|r|opt)mem_max`
254 sysctl.
255 '';
256 };
257
258 };
259
260 };
261
262 config = mkIf cfg.enable {
263
264 assertions = [
265 { assertion = cfg.cellServDB != [];
266 message = "You must specify all cell-local database servers in config.services.openafsServer.cellServDB.";
267 }
268 { assertion = cfg.cellName != "";
269 message = "You must specify the local cell name in config.services.openafsServer.cellName.";
270 }
271 ];
272
273 environment.systemPackages = [ openafsBin ];
274
275 environment.etc = {
276 bosConfig = {
277 source = bosConfig;
278 target = "openafs/BosConfig";
279 mode = "0644";
280 };
281 cellServDB = {
282 text = mkCellServDB cfg.cellName cfg.cellServDB;
283 target = "openafs/server/CellServDB";
284 mode = "0644";
285 };
286 thisCell = {
287 text = cfg.cellName;
288 target = "openafs/server/ThisCell";
289 mode = "0644";
290 };
291 buCellServDB = {
292 enable = useBuCellServDB;
293 text = mkCellServDB cfg.cellName cfg.roles.backup.cellServDB;
294 target = "openafs/backup/CellServDB";
295 };
296 };
297
298 systemd.services = {
299 openafs-server = {
300 description = "OpenAFS server";
301 after = [ "network.target" ];
302 wantedBy = [ "multi-user.target" ];
303 restartIfChanged = false;
304 unitConfig.ConditionPathExists = [
305 "|/etc/openafs/server/KeyFileExt"
306 ];
307 preStart = ''
308 mkdir -m 0755 -p /var/openafs
309 ${optionalString (netInfo != null) "cp ${netInfo} /var/openafs/netInfo"}
310 ${optionalString useBuCellServDB "cp ${buCellServDB}"}
311 '';
312 serviceConfig = {
313 ExecStart = "${openafsBin}/bin/bosserver -nofork";
314 ExecStop = "${openafsBin}/bin/bos shutdown localhost -wait -localauth";
315 };
316 };
317 };
318 };
319}