git.delta.rocks / jrsonnet / refs/commits / 5b343db89280

difftreelog

source

modules/nixos/secrets.nix6.3 KiBsourcehistory
1{2  lib,3  fleetLib,4  config,5  pkgs,6  ...7}:8let9  inherit (builtins)10    hashString11    elemAt12    length13    toJSON14    filter15    ;16  inherit (lib.stringsWithDeps) stringAfter;17  inherit (lib.options) mkOption literalExpression;18  inherit (lib.lists) optional;19  inherit (lib.attrsets) mapAttrs mapAttrsToList;20  inherit (lib.modules) mkIf mkMerge;21  inherit (lib.types)22    submodule23    str24    attrsOf25    nullOr26    unspecified27    lazyAttrsOf28    uniq29    functionTo30    package31    listOf32    bool33    ;34  inherit (fleetLib.strings) decodeRawSecret;3536  sysConfig = config;37  secretPartDataType = submodule {38    options = {39      raw = mkOption {40        type = str;41        internal = true;42        description = "Encoded & Encrypted secret part data, passed from fleet.nix";43      };44    };45  };46  secretDataType = submodule {47    freeformType = lazyAttrsOf secretPartDataType;48    options = {49      shared = mkOption {50        description = "Is this secret owned by this machine, or propagated from shared secrets";51        default = false;52      };53    };54  };55  secretPartType =56    secretName:57    submodule (58      { config, ... }:59      let60        partName = config._module.args.name;61      in62      {63        options = {64          encrypted = mkOption {65            type = bool;66            description = "Is this secret part supposed to be encrypted?";67          };6869          hash = mkOption {70            type = str;71            description = "Hash of secret in encoded format";72          };73          path = mkOption {74            type = str;75            description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";76          };77          stablePath = mkOption {78            type = str;79            description = "Path to secret part, stable path (users are expected to watch for file changes/re-read secret on demand)";80          };81          data = mkOption {82            type = str;83            description = "Secret public data (only available for plaintext)";84          };85        };86        config =87          let88            raw = sysConfig.data.secrets.${secretName}.${partName}.raw;89          in90          {91            hash = hashString "sha1" raw;92            data = decodeRawSecret raw;93            path = "/run/secrets/${secretName}/${config.hash}-${partName}";94            stablePath = "/run/secrets/${secretName}/${partName}";95          };96      }97    );98  secretType = submodule (99    {100      config,101      ...102    }:103    let104      secretName = config._module.args.name;105    in106    {107      options = {108        parts = mkOption {109          type = lazyAttrsOf (secretPartType secretName);110          description = "Definition of secret parts";111          default = {};112        };113        generator = mkOption {114          type = uniq (nullOr (functionTo package));115          description = "Derivation to evaluate for secret generation";116          default = null;117        };118        mode = mkOption {119          type = str;120          description = "Secret mode";121          default = "0440";122        };123        owner = mkOption {124          type = str;125          description = "Owner of the secret";126          default = "root";127        };128        group = mkOption {129          type = str;130          description = "Group of the secret";131          default = sysConfig.users.users.${config.owner}.group;132          defaultText = literalExpression "config.users.users.$${owner}.group";133        };134        expectedGenerationData = mkOption {135          type = unspecified;136          description = "Data that gets embedded into secret part";137          default = null;138        };139      };140      config.parts = mkMerge [141        (mkIf (config.generator != null && config.generator ? parts) config.generator.parts)142        (mapAttrs (_: _: {}) (removeAttrs sysConfig.data.secrets.${secretName} ["shared"]))143      ];144    }145  );146  processPart = secretName: partName: part: {147    inherit (part) path stablePath;148    raw = config.data.secrets.${secretName}.${partName}.raw;149  };150  processSecret =151    secretName: secret:152    {153      inherit (secret.definition) group mode owner;154      parts = (mapAttrs (processPart secretName) (155        secret.definition.parts156      ));157    };158  secretsData = (mapAttrs (processSecret) config.secrets);159  secretsFile = pkgs.writeTextFile {160    name = "secrets.json";161    text = toJSON secretsData;162  };163  useSysusers =164    (config.systemd ? sysusers && config.systemd.sysusers.enable)165    || (config ? userborn && config.userborn.enable);166in167{168  options = {169    data.secrets = mkOption {170      type = attrsOf secretDataType;171      default = { };172      description = "Host-local secret data";173    };174    secrets = mkOption {175      type = attrsOf secretType;176      default = { };177      apply = v: (mapAttrs (_: secret: secret.parts // {definition = secret;}) v);178      description = "Host-local secrets";179    };180    system.secretsData = mkOption {181      type = unspecified;182      default = { };183      description = "secrets.json contents";184    };185  };186  config = {187    system = { inherit secretsData; };188    environment.systemPackages = [ pkgs.fleet-install-secrets ];189190    systemd.services.fleet-install-secrets = mkIf useSysusers {191      wantedBy = [ "sysinit.target" ];192      after = [ "systemd-sysusers.service" ];193      restartTriggers = [194        secretsFile195      ];196      aliases = [197        "sops-install-secrets"198        "agenix-install-secrets"199      ];200201      unitConfig.DefaultDependencies = false;202203      serviceConfig = {204        Type = "oneshot";205        RemainAfterExit = true;206        ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";207      };208    };209    system.activationScripts.decryptSecrets = mkIf (!useSysusers) (210      stringAfter211        (212          [213            # secrets are owned by user/group, thus we need to refer to those214            "users"215            "groups"216            "specialfs"217          ]218          # nixos-impermanence compatibility: secrets are encrypted by host-key,219          # but with impermanence we expect that the host-key is installed by220          # persist-file activation script.221          ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")222        )223        ''224          1>&2 echo "setting up secrets"225          ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}226        ''227    );228  };229}