1{
2 config,
3 options,
4 lib,
5 pkgs,
6 ...
7}:
8let
9 cfg = config.services.cfssl;
10in
11{
12 options.services.cfssl = {
13 enable = lib.mkEnableOption "the CFSSL CA api-server";
14
15 dataDir = lib.mkOption {
16 default = "/var/lib/cfssl";
17 type = lib.types.path;
18 description = ''
19 The work directory for CFSSL.
20
21 ::: {.note}
22 If left as the default value this directory will automatically be
23 created before the CFSSL server starts, otherwise you are
24 responsible for ensuring the directory exists with appropriate
25 ownership and permissions.
26 :::
27 '';
28 };
29
30 address = lib.mkOption {
31 default = "127.0.0.1";
32 type = lib.types.str;
33 description = "Address to bind.";
34 };
35
36 port = lib.mkOption {
37 default = 8888;
38 type = lib.types.port;
39 description = "Port to bind.";
40 };
41
42 ca = lib.mkOption {
43 defaultText = lib.literalExpression ''"''${cfg.dataDir}/ca.pem"'';
44 type = lib.types.str;
45 description = "CA used to sign the new certificate -- accepts '[file:]fname' or 'env:varname'.";
46 };
47
48 caKey = lib.mkOption {
49 defaultText = lib.literalExpression ''"file:''${cfg.dataDir}/ca-key.pem"'';
50 type = lib.types.str;
51 description = "CA private key -- accepts '[file:]fname' or 'env:varname'.";
52 };
53
54 caBundle = lib.mkOption {
55 default = null;
56 type = lib.types.nullOr lib.types.path;
57 description = "Path to root certificate store.";
58 };
59
60 intBundle = lib.mkOption {
61 default = null;
62 type = lib.types.nullOr lib.types.path;
63 description = "Path to intermediate certificate store.";
64 };
65
66 intDir = lib.mkOption {
67 default = null;
68 type = lib.types.nullOr lib.types.path;
69 description = "Intermediates directory.";
70 };
71
72 metadata = lib.mkOption {
73 default = null;
74 type = lib.types.nullOr lib.types.path;
75 description = ''
76 Metadata file for root certificate presence.
77 The content of the file is a json dictionary (k,v): each key k is
78 a SHA-1 digest of a root certificate while value v is a list of key
79 store filenames.
80 '';
81 };
82
83 remote = lib.mkOption {
84 default = null;
85 type = lib.types.nullOr lib.types.str;
86 description = "Remote CFSSL server.";
87 };
88
89 configFile = lib.mkOption {
90 default = null;
91 type = lib.types.nullOr lib.types.str;
92 description = "Path to configuration file. Do not put this in nix-store as it might contain secrets.";
93 };
94
95 responder = lib.mkOption {
96 default = null;
97 type = lib.types.nullOr lib.types.path;
98 description = "Certificate for OCSP responder.";
99 };
100
101 responderKey = lib.mkOption {
102 default = null;
103 type = lib.types.nullOr lib.types.str;
104 description = "Private key for OCSP responder certificate. Do not put this in nix-store.";
105 };
106
107 tlsKey = lib.mkOption {
108 default = null;
109 type = lib.types.nullOr lib.types.str;
110 description = "Other endpoint's CA private key. Do not put this in nix-store.";
111 };
112
113 tlsCert = lib.mkOption {
114 default = null;
115 type = lib.types.nullOr lib.types.path;
116 description = "Other endpoint's CA to set up TLS protocol.";
117 };
118
119 mutualTlsCa = lib.mkOption {
120 default = null;
121 type = lib.types.nullOr lib.types.path;
122 description = "Mutual TLS - require clients be signed by this CA.";
123 };
124
125 mutualTlsCn = lib.mkOption {
126 default = null;
127 type = lib.types.nullOr lib.types.str;
128 description = "Mutual TLS - regex for whitelist of allowed client CNs.";
129 };
130
131 tlsRemoteCa = lib.mkOption {
132 default = null;
133 type = lib.types.nullOr lib.types.path;
134 description = "CAs to trust for remote TLS requests.";
135 };
136
137 mutualTlsClientCert = lib.mkOption {
138 default = null;
139 type = lib.types.nullOr lib.types.path;
140 description = "Mutual TLS - client certificate to call remote instance requiring client certs.";
141 };
142
143 mutualTlsClientKey = lib.mkOption {
144 default = null;
145 type = lib.types.nullOr lib.types.path;
146 description = "Mutual TLS - client key to call remote instance requiring client certs. Do not put this in nix-store.";
147 };
148
149 dbConfig = lib.mkOption {
150 default = null;
151 type = lib.types.nullOr lib.types.path;
152 description = "Certificate db configuration file. Path must be writeable.";
153 };
154
155 logLevel = lib.mkOption {
156 default = 1;
157 type = lib.types.enum [
158 0
159 1
160 2
161 3
162 4
163 5
164 ];
165 description = "Log level (0 = DEBUG, 5 = FATAL).";
166 };
167
168 disable = lib.mkOption {
169 default = null;
170 type = lib.types.nullOr lib.types.commas;
171 description = "Endpoints to disable (comma-separated list)";
172 };
173 };
174
175 config = lib.mkIf cfg.enable {
176 users.groups.cfssl = {
177 gid = config.ids.gids.cfssl;
178 };
179
180 users.users.cfssl = {
181 description = "cfssl user";
182 home = cfg.dataDir;
183 group = "cfssl";
184 uid = config.ids.uids.cfssl;
185 };
186
187 systemd.services.cfssl = {
188 description = "CFSSL CA API server";
189 wantedBy = [ "multi-user.target" ];
190 after = [ "network.target" ];
191
192 serviceConfig = lib.mkMerge [
193 {
194 WorkingDirectory = cfg.dataDir;
195 Restart = "always";
196 User = "cfssl";
197 Group = "cfssl";
198
199 ExecStart =
200 with cfg;
201 let
202 opt = n: v: lib.optionalString (v != null) ''-${n}="${v}"'';
203 in
204 lib.concatStringsSep " \\\n" [
205 "${pkgs.cfssl}/bin/cfssl serve"
206 (opt "address" address)
207 (opt "port" (toString port))
208 (opt "ca" ca)
209 (opt "ca-key" caKey)
210 (opt "ca-bundle" caBundle)
211 (opt "int-bundle" intBundle)
212 (opt "int-dir" intDir)
213 (opt "metadata" metadata)
214 (opt "remote" remote)
215 (opt "config" configFile)
216 (opt "responder" responder)
217 (opt "responder-key" responderKey)
218 (opt "tls-key" tlsKey)
219 (opt "tls-cert" tlsCert)
220 (opt "mutual-tls-ca" mutualTlsCa)
221 (opt "mutual-tls-cn" mutualTlsCn)
222 (opt "mutual-tls-client-key" mutualTlsClientKey)
223 (opt "mutual-tls-client-cert" mutualTlsClientCert)
224 (opt "tls-remote-ca" tlsRemoteCa)
225 (opt "db-config" dbConfig)
226 (opt "loglevel" (toString logLevel))
227 (opt "disable" disable)
228 ];
229 }
230 (lib.mkIf (cfg.dataDir == options.services.cfssl.dataDir.default) {
231 StateDirectory = baseNameOf cfg.dataDir;
232 StateDirectoryMode = 700;
233 })
234 ];
235 };
236
237 services.cfssl = {
238 ca = lib.mkDefault "${cfg.dataDir}/ca.pem";
239 caKey = lib.mkDefault "${cfg.dataDir}/ca-key.pem";
240 };
241 };
242}