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.ints.between 0 5;
158 description = "Log level (0 = DEBUG, 5 = FATAL).";
159 };
160
161 disable = lib.mkOption {
162 default = null;
163 type = lib.types.nullOr lib.types.commas;
164 description = "Endpoints to disable (comma-separated list)";
165 };
166 };
167
168 config = lib.mkIf cfg.enable {
169 users.groups.cfssl = {
170 gid = config.ids.gids.cfssl;
171 };
172
173 users.users.cfssl = {
174 description = "cfssl user";
175 home = cfg.dataDir;
176 group = "cfssl";
177 uid = config.ids.uids.cfssl;
178 };
179
180 systemd.services.cfssl = {
181 description = "CFSSL CA API server";
182 wantedBy = [ "multi-user.target" ];
183 after = [ "network.target" ];
184
185 serviceConfig = lib.mkMerge [
186 {
187 WorkingDirectory = cfg.dataDir;
188 Restart = "always";
189 User = "cfssl";
190 Group = "cfssl";
191
192 ExecStart =
193 with cfg;
194 let
195 opt = n: v: lib.optionalString (v != null) ''-${n}="${v}"'';
196 in
197 lib.concatStringsSep " \\\n" [
198 "${pkgs.cfssl}/bin/cfssl serve"
199 (opt "address" address)
200 (opt "port" (toString port))
201 (opt "ca" ca)
202 (opt "ca-key" caKey)
203 (opt "ca-bundle" caBundle)
204 (opt "int-bundle" intBundle)
205 (opt "int-dir" intDir)
206 (opt "metadata" metadata)
207 (opt "remote" remote)
208 (opt "config" configFile)
209 (opt "responder" responder)
210 (opt "responder-key" responderKey)
211 (opt "tls-key" tlsKey)
212 (opt "tls-cert" tlsCert)
213 (opt "mutual-tls-ca" mutualTlsCa)
214 (opt "mutual-tls-cn" mutualTlsCn)
215 (opt "mutual-tls-client-key" mutualTlsClientKey)
216 (opt "mutual-tls-client-cert" mutualTlsClientCert)
217 (opt "tls-remote-ca" tlsRemoteCa)
218 (opt "db-config" dbConfig)
219 (opt "loglevel" (toString logLevel))
220 (opt "disable" disable)
221 ];
222 }
223 (lib.mkIf (cfg.dataDir == options.services.cfssl.dataDir.default) {
224 StateDirectory = baseNameOf cfg.dataDir;
225 StateDirectoryMode = 700;
226 })
227 ];
228 };
229
230 services.cfssl = {
231 ca = lib.mkDefault "${cfg.dataDir}/ca.pem";
232 caKey = lib.mkDefault "${cfg.dataDir}/ca-key.pem";
233 };
234 };
235}