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