- based on module originally written by @srhb - complies with available options in cfssl v1.3.2 - uid and gid 299 reserved in ids.nix - added simple nixos test casewip/yesman
parent
812f4749ae
commit
7d7c36f8be
@ -0,0 +1,209 @@ |
||||
{ config, lib, pkgs, ... }: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
cfg = config.services.cfssl; |
||||
in { |
||||
options.services.cfssl = { |
||||
enable = mkEnableOption "the CFSSL CA api-server"; |
||||
|
||||
dataDir = mkOption { |
||||
default = "/var/lib/cfssl"; |
||||
type = types.path; |
||||
description = "Cfssl work directory."; |
||||
}; |
||||
|
||||
address = mkOption { |
||||
default = "127.0.0.1"; |
||||
type = types.str; |
||||
description = "Address to bind."; |
||||
}; |
||||
|
||||
port = mkOption { |
||||
default = 8888; |
||||
type = types.ints.u16; |
||||
description = "Port to bind."; |
||||
}; |
||||
|
||||
ca = mkOption { |
||||
defaultText = "\${cfg.dataDir}/ca.pem"; |
||||
type = types.str; |
||||
description = "CA used to sign the new certificate -- accepts '[file:]fname' or 'env:varname'."; |
||||
}; |
||||
|
||||
caKey = mkOption { |
||||
defaultText = "file:\${cfg.dataDir}/ca-key.pem"; |
||||
type = types.str; |
||||
description = "CA private key -- accepts '[file:]fname' or 'env:varname'."; |
||||
}; |
||||
|
||||
caBundle = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Path to root certificate store."; |
||||
}; |
||||
|
||||
intBundle = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Path to intermediate certificate store."; |
||||
}; |
||||
|
||||
intDir = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Intermediates directory."; |
||||
}; |
||||
|
||||
metadata = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = '' |
||||
Metadata file for root certificate presence. |
||||
The content of the file is a json dictionary (k,v): each key k is |
||||
a SHA-1 digest of a root certificate while value v is a list of key |
||||
store filenames. |
||||
''; |
||||
}; |
||||
|
||||
remote = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.str; |
||||
description = "Remote CFSSL server."; |
||||
}; |
||||
|
||||
configFile = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.str; |
||||
description = "Path to configuration file. Do not put this in nix-store as it might contain secrets."; |
||||
}; |
||||
|
||||
responder = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Certificate for OCSP responder."; |
||||
}; |
||||
|
||||
responderKey = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.str; |
||||
description = "Private key for OCSP responder certificate. Do not put this in nix-store."; |
||||
}; |
||||
|
||||
tlsKey = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.str; |
||||
description = "Other endpoint's CA private key. Do not put this in nix-store."; |
||||
}; |
||||
|
||||
tlsCert = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Other endpoint's CA to set up TLS protocol."; |
||||
}; |
||||
|
||||
mutualTlsCa = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Mutual TLS - require clients be signed by this CA."; |
||||
}; |
||||
|
||||
mutualTlsCn = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.str; |
||||
description = "Mutual TLS - regex for whitelist of allowed client CNs."; |
||||
}; |
||||
|
||||
tlsRemoteCa = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "CAs to trust for remote TLS requests."; |
||||
}; |
||||
|
||||
mutualTlsClientCert = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Mutual TLS - client certificate to call remote instance requiring client certs."; |
||||
}; |
||||
|
||||
mutualTlsClientKey = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Mutual TLS - client key to call remote instance requiring client certs. Do not put this in nix-store."; |
||||
}; |
||||
|
||||
dbConfig = mkOption { |
||||
default = null; |
||||
type = types.nullOr types.path; |
||||
description = "Certificate db configuration file. Path must be writeable."; |
||||
}; |
||||
|
||||
logLevel = mkOption { |
||||
default = 1; |
||||
type = types.enum [ 0 1 2 3 4 5 ]; |
||||
description = "Log level (0 = DEBUG, 5 = FATAL)."; |
||||
}; |
||||
}; |
||||
|
||||
config = { |
||||
users.extraGroups.cfssl = { |
||||
gid = config.ids.gids.cfssl; |
||||
}; |
||||
|
||||
users.extraUsers.cfssl = { |
||||
description = "cfssl user"; |
||||
createHome = true; |
||||
home = cfg.dataDir; |
||||
group = "cfssl"; |
||||
uid = config.ids.uids.cfssl; |
||||
}; |
||||
|
||||
systemd.services.cfssl = mkIf cfg.enable { |
||||
description = "CFSSL CA API server"; |
||||
wantedBy = [ "multi-user.target" ]; |
||||
after = [ "network.target" ]; |
||||
|
||||
serviceConfig = { |
||||
WorkingDirectory = cfg.dataDir; |
||||
StateDirectory = cfg.dataDir; |
||||
StateDirectoryMode = 700; |
||||
Restart = "always"; |
||||
User = "cfssl"; |
||||
|
||||
ExecStart = with cfg; let |
||||
opt = n: v: optionalString (v != null) ''-${n}="${v}"''; |
||||
in |
||||
lib.concatStringsSep " \\\n" [ |
||||
"${pkgs.cfssl}/bin/cfssl serve" |
||||
(opt "address" address) |
||||
(opt "port" (toString port)) |
||||
(opt "ca" ca) |
||||
(opt "ca-key" caKey) |
||||
(opt "ca-bundle" caBundle) |
||||
(opt "int-bundle" intBundle) |
||||
(opt "int-dir" intDir) |
||||
(opt "metadata" metadata) |
||||
(opt "remote" remote) |
||||
(opt "config" configFile) |
||||
(opt "responder" responder) |
||||
(opt "responder-key" responderKey) |
||||
(opt "tls-key" tlsKey) |
||||
(opt "tls-cert" tlsCert) |
||||
(opt "mutual-tls-ca" mutualTlsCa) |
||||
(opt "mutual-tls-cn" mutualTlsCn) |
||||
(opt "mutual-tls-client-key" mutualTlsClientKey) |
||||
(opt "mutual-tls-client-cert" mutualTlsClientCert) |
||||
(opt "tls-remote-ca" tlsRemoteCa) |
||||
(opt "db-config" dbConfig) |
||||
(opt "loglevel" (toString logLevel)) |
||||
]; |
||||
}; |
||||
}; |
||||
|
||||
services.cfssl = { |
||||
ca = mkDefault "${cfg.dataDir}/ca.pem"; |
||||
caKey = mkDefault "${cfg.dataDir}/ca-key.pem"; |
||||
}; |
||||
}; |
||||
} |
@ -0,0 +1,67 @@ |
||||
import ./make-test.nix ({ pkgs, ...} : { |
||||
name = "cfssl"; |
||||
|
||||
machine = { config, lib, pkgs, ... }: |
||||
{ |
||||
networking.firewall.allowedTCPPorts = [ config.services.cfssl.port ]; |
||||
|
||||
services.cfssl.enable = true; |
||||
systemd.services.cfssl.after = [ "cfssl-init.service" ]; |
||||
|
||||
systemd.services.cfssl-init = { |
||||
description = "Initialize the cfssl CA"; |
||||
wantedBy = [ "multi-user.target" ]; |
||||
serviceConfig = { |
||||
User = "cfssl"; |
||||
Type = "oneshot"; |
||||
WorkingDirectory = config.services.cfssl.dataDir; |
||||
}; |
||||
script = with pkgs; '' |
||||
${cfssl}/bin/cfssl genkey -initca ${pkgs.writeText "ca.json" (builtins.toJSON { |
||||
hosts = [ "ca.example.com" ]; |
||||
key = { |
||||
algo = "rsa"; size = 4096; }; |
||||
names = [ |
||||
{ |
||||
C = "US"; |
||||
L = "San Francisco"; |
||||
O = "Internet Widgets, LLC"; |
||||
OU = "Certificate Authority"; |
||||
ST = "California"; |
||||
} |
||||
]; |
||||
})} | ${cfssl}/bin/cfssljson -bare ca |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
testScript = |
||||
let |
||||
cfsslrequest = with pkgs; writeScript "cfsslrequest" '' |
||||
curl -X POST -H "Content-Type: application/json" -d @${csr} \ |
||||
http://localhost:8888/api/v1/cfssl/newkey | ${cfssl}/bin/cfssljson /tmp/certificate |
||||
''; |
||||
csr = pkgs.writeText "csr.json" (builtins.toJSON { |
||||
CN = "www.example.com"; |
||||
hosts = [ "example.com" "www.example.com" ]; |
||||
key = { |
||||
algo = "rsa"; |
||||
size = 2048; |
||||
}; |
||||
names = [ |
||||
{ |
||||
C = "US"; |
||||
L = "San Francisco"; |
||||
O = "Example Company, LLC"; |
||||
OU = "Operations"; |
||||
ST = "California"; |
||||
} |
||||
]; |
||||
}); |
||||
in |
||||
'' |
||||
$machine->waitForUnit('cfssl.service'); |
||||
$machine->waitUntilSucceeds('${cfsslrequest}'); |
||||
$machine->succeed('ls /tmp/certificate-key.pem'); |
||||
''; |
||||
}) |
Loading…
Reference in new issue