summary refs log tree commit diff
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
parent70e7235510930aa5d98b1cf0b8b4599ddb9c6b42 (diff)
parent7d7c36f8be6b064aa14d1004d6418f2b3e005e86 (diff)
downloadnixlib-d31f89df44666fcd0c854a1cc8da434929e73832.tar
nixlib-d31f89df44666fcd0c854a1cc8da434929e73832.tar.gz
nixlib-d31f89df44666fcd0c854a1cc8da434929e73832.tar.bz2
nixlib-d31f89df44666fcd0c854a1cc8da434929e73832.tar.lz
nixlib-d31f89df44666fcd0c854a1cc8da434929e73832.tar.xz
nixlib-d31f89df44666fcd0c854a1cc8da434929e73832.tar.zst
nixlib-d31f89df44666fcd0c854a1cc8da434929e73832.zip
Merge pull request #44127 from johanot/nixos-cfssl
nixos/cfssl: Add new module for cfssl
-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');
+    '';
+})