summaryrefslogtreecommitdiffstats
path: root/nixos
diff options
context:
space:
mode:
authorSilvan Mosberger <infinisil@icloud.com>2018-08-03 16:39:12 +0200
committerGitHub <noreply@github.com>2018-08-03 16:39:12 +0200
commitd31f89df44666fcd0c854a1cc8da434929e73832 (patch)
tree6a6ef00c91a6493f3bfd2915e1f5aaf034f9ac2c /nixos
parent70e7235510930aa5d98b1cf0b8b4599ddb9c6b42 (diff)
parent7d7c36f8be6b064aa14d1004d6418f2b3e005e86 (diff)
Merge pull request #44127 from johanot/nixos-cfssl
nixos/cfssl: Add new module for cfssl
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/security/cfssl.nix209
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/cfssl.nix67
5 files changed, 280 insertions, 0 deletions
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 782f6c8f69df..f73660ed99de 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -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
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index f5d94baf173c..5f96336de672 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -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
diff --git a/nixos/modules/services/security/cfssl.nix b/nixos/modules/services/security/cfssl.nix
new file mode 100644
index 000000000000..1eb2f65ba602
--- /dev/null
+++ b/nixos/modules/services/security/cfssl.nix
@@ -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";
+ };
+ };
+}
diff --git a/nixos/release.nix b/nixos/release.nix
index 413f2bec54c7..007859259b17 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -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 {};
diff --git a/nixos/tests/cfssl.nix b/nixos/tests/cfssl.nix
new file mode 100644
index 000000000000..513ed8c45741
--- /dev/null
+++ b/nixos/tests/cfssl.nix
@@ -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');
+ '';
+})