1 { config, lib, pkgs, ... }:
3 cfg = config.services.step-ca;
4 settingsFormat = (pkgs.formats.json { });
7 meta.maintainers = [ ];
11 enable = lib.mkEnableOption "the smallstep certificate authority server";
12 openFirewall = lib.mkEnableOption "opening the certificate authority server port";
13 package = lib.mkOption {
14 type = lib.types.package;
15 default = pkgs.step-ca;
16 defaultText = lib.literalExpression "pkgs.step-ca";
17 description = "Which step-ca package to use.";
19 address = lib.mkOption {
21 example = "127.0.0.1";
23 The address (without port) the certificate authority should listen at.
24 This combined with {option}`services.step-ca.port` overrides {option}`services.step-ca.settings.address`.
28 type = lib.types.port;
31 The port the certificate authority should listen on.
32 This combined with {option}`services.step-ca.address` overrides {option}`services.step-ca.settings.address`.
35 settings = lib.mkOption {
36 type = with lib.types; attrsOf anything;
38 Settings that go into {file}`ca.json`. See
39 [the step-ca manual](https://smallstep.com/docs/step-ca/configuration)
40 for more information. The easiest way to
41 configure this module would be to run `step ca init`
42 to generate {file}`ca.json` and then import it using
44 [This article](https://smallstep.com/docs/step-cli/basic-crypto-operations#run-an-offline-x509-certificate-authority)
45 may also be useful if you want to customize certain aspects of
46 certificate generation for your CA.
47 You need to change the database storage path to {file}`/var/lib/step-ca/db`.
50 The {option}`services.step-ca.settings.address` option
51 will be ignored and overwritten by
52 {option}`services.step-ca.address` and
53 {option}`services.step-ca.port`.
57 intermediatePasswordFile = lib.mkOption {
58 type = lib.types.path;
59 example = "/run/keys/smallstep-password";
61 Path to the file containing the password for the intermediate
62 certificate private key.
65 Make sure to use a quoted absolute path instead of a path literal
66 to prevent it from being copied to the globally readable Nix
74 config = lib.mkIf config.services.step-ca.enable (
76 configFile = settingsFormat.generate "ca.json" (cfg.settings // {
77 address = cfg.address + ":" + toString cfg.port;
84 assertion = !lib.isStorePath cfg.intermediatePasswordFile;
86 <option>services.step-ca.intermediatePasswordFile</option> points to
87 a file in the Nix store. You should use a quoted absolute path to
93 systemd.packages = [ cfg.package ];
95 # configuration file indirection is needed to support reloading
96 environment.etc."smallstep/ca.json".source = configFile;
98 systemd.services."step-ca" = {
99 wantedBy = [ "multi-user.target" ];
100 restartTriggers = [ configFile ];
102 ConditionFileNotEmpty = ""; # override upstream
108 Environment = "HOME=%S/step-ca";
109 WorkingDirectory = ""; # override upstream
110 ReadWritePaths = ""; # override upstream
112 # LocalCredential handles file permission problems arising from the use of DynamicUser.
113 LoadCredential = "intermediate_password:${cfg.intermediatePasswordFile}";
116 "" # override upstream
117 "${cfg.package}/bin/step-ca /etc/smallstep/ca.json --password-file \${CREDENTIALS_DIRECTORY}/intermediate_password"
120 # ProtectProc = "invisible"; # not supported by upstream yet
121 # ProcSubset = "pid"; # not supported by upstream yet
122 # PrivateUsers = true; # doesn't work with privileged ports therefore not supported by upstream
125 StateDirectory = "step-ca";
129 users.users.step-ca = {
130 home = "/var/lib/step-ca";
135 users.groups.step-ca = {};
137 networking.firewall = lib.mkIf cfg.openFirewall {
138 allowedTCPPorts = [ cfg.port ];