Merge pull request #44127 from johanot/nixos-cfssl
nixos/cfssl: Add new module for cfssl
This commit is contained in:
commit
d31f89df44
5 changed files with 280 additions and 0 deletions
|
@ -323,6 +323,7 @@
|
|||
mapred = 296;
|
||||
hadoop = 297;
|
||||
hydron = 298;
|
||||
cfssl = 299;
|
||||
|
||||
# When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
|
||||
|
||||
|
@ -606,6 +607,7 @@
|
|||
mapred = 296;
|
||||
hadoop = 297;
|
||||
hydron = 298;
|
||||
cfssl = 299;
|
||||
|
||||
# When adding a gid, make sure it doesn't match an existing
|
||||
# uid. Users and groups with the same name should have equal
|
||||
|
|
|
@ -622,6 +622,7 @@
|
|||
./services/search/hound.nix
|
||||
./services/search/kibana.nix
|
||||
./services/search/solr.nix
|
||||
./services/security/cfssl.nix
|
||||
./services/security/clamav.nix
|
||||
./services/security/fail2ban.nix
|
||||
./services/security/fprintd.nix
|
||||
|
|
209
nixos/modules/services/security/cfssl.nix
Normal file
209
nixos/modules/services/security/cfssl.nix
Normal file
|
@ -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";
|
||||
};
|
||||
};
|
||||
}
|
|
@ -256,6 +256,7 @@ in rec {
|
|||
tests.buildbot = callTest tests/buildbot.nix {};
|
||||
tests.cadvisor = callTestOnMatchingSystems ["x86_64-linux"] tests/cadvisor.nix {};
|
||||
tests.ceph = callTestOnMatchingSystems ["x86_64-linux"] tests/ceph.nix {};
|
||||
tests.cfssl = callTestOnMatchingSystems ["x86_64-linux"] tests/cfssl.nix {};
|
||||
tests.chromium = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/chromium.nix {}).stable or {};
|
||||
tests.cjdns = callTest tests/cjdns.nix {};
|
||||
tests.cloud-init = callTest tests/cloud-init.nix {};
|
||||
|
|
67
nixos/tests/cfssl.nix
Normal file
67
nixos/tests/cfssl.nix
Normal file
|
@ -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 a new issue