Skyler Grey | 82ea805 | 2024-06-08 22:56:00 +0000 | [diff] [blame^] | 1 | # SPDX-FileCopyrightText: 2024 Clicks Codes |
| 2 | # |
| 3 | # SPDX-License-Identifier: GPL-3.0-only |
| 4 | |
| 5 | { |
| 6 | lib, |
| 7 | pkgs, |
| 8 | config, |
| 9 | ... |
| 10 | }: |
| 11 | let |
| 12 | cfg = config.clicks.security.sops; |
| 13 | |
| 14 | guessFormat = |
| 15 | extension: |
| 16 | if extension == "json" then |
| 17 | "json" |
| 18 | else if extension == "yaml" || extension == "yml" then |
| 19 | "yaml" |
| 20 | else if extension == "env" then |
| 21 | "dotenv" |
| 22 | else if extension == "ini" then |
| 23 | "ini" |
| 24 | else |
| 25 | "binary"; |
| 26 | |
| 27 | getExtension = |
| 28 | filePath: |
| 29 | let |
| 30 | pathParts = builtins.split ''\.'' (builtins.toString filePath); |
| 31 | numPathParts = builtins.length pathParts; |
| 32 | in |
| 33 | builtins.elemAt pathParts (numPathParts - 1); |
| 34 | in |
| 35 | { |
| 36 | options.clicks.secrets = |
| 37 | let |
| 38 | generateNonBinarySopsPaths = |
| 39 | file: keys: |
| 40 | lib.lists.forEach keys (key: { |
| 41 | name = key; |
| 42 | value = config.sops.secrets."${lib.clicks.secrets.name file}:${key}".path; |
| 43 | }); |
| 44 | in |
| 45 | lib.mkOption { |
| 46 | type = lib.types.attrsOf ( |
| 47 | lib.types.submodule ( |
| 48 | { ... }@submodule: |
| 49 | { |
| 50 | options = { |
| 51 | file = lib.mkOption { |
| 52 | type = lib.types.pathInStore; |
| 53 | description = "The store path to your secrets file"; |
| 54 | }; |
| 55 | group = lib.mkOption { |
| 56 | type = lib.types.str; |
| 57 | description = "The user the secret should be owned by."; |
| 58 | default = "root"; |
| 59 | }; |
| 60 | keys = lib.mkOption { |
| 61 | type = lib.types.nullOr (lib.types.listOf lib.types.str); |
| 62 | description = "List of keys to pull from the structured data."; |
| 63 | default = null; |
| 64 | }; |
| 65 | neededForUsers = lib.mkEnableOption "This secret is needed for users"; |
| 66 | paths = lib.mkOption { |
| 67 | type = lib.types.nullOr (lib.types.attrsOf lib.types.str); |
| 68 | description = "Automatically populated with the SOPS paths to your keys, null if you are using binary secrets"; |
| 69 | default = |
| 70 | if guessFormat (getExtension submodule.config.file) != "binary" then |
| 71 | builtins.listToAttrs (generateNonBinarySopsPaths submodule.config.file submodule.config.keys) |
| 72 | else |
| 73 | null; |
| 74 | }; |
| 75 | path = lib.mkOption { |
| 76 | type = lib.types.nullOr lib.types.str; |
| 77 | description = "Populated automatically with the SOPS path of the secret, null if you are using non binary secrets"; |
| 78 | default = |
| 79 | if guessFormat (getExtension submodule.config.file) == "binary" then |
| 80 | config.sops.secrets.${lib.clicks.secrets.name submodule.config.file}.path |
| 81 | else |
| 82 | null; |
| 83 | }; |
| 84 | }; |
| 85 | } |
| 86 | ) |
| 87 | ); |
| 88 | description = ""; |
| 89 | default = { }; |
| 90 | }; |
| 91 | |
| 92 | config = |
| 93 | let |
| 94 | generateBinarySopsSecret = secret: { |
| 95 | name = lib.clicks.secrets.name secret.value.file; |
| 96 | value = { |
| 97 | mode = "0400"; |
| 98 | owner = config.users.users.root.name; |
| 99 | group = config.users.groups.${secret.value.group}.name; |
| 100 | sopsFile = secret.value.file; |
| 101 | format = guessFormat (getExtension secret.value.file); |
| 102 | inherit (secret.value) neededForUsers; |
| 103 | }; |
| 104 | }; |
| 105 | |
| 106 | generateNonBinarySopsSecrets = |
| 107 | secret: |
| 108 | lib.lists.forEach secret.value.keys (key: { |
| 109 | name = "${lib.clicks.secrets.name secret.value.file}:${key}"; |
| 110 | value = { |
| 111 | mode = "0040"; |
| 112 | owner = config.users.users.root.name; |
| 113 | group = config.users.groups.${secret.value.group}.name; |
| 114 | sopsFile = secret.value.file; |
| 115 | format = guessFormat (getExtension secret.value.file); |
| 116 | inherit (secret.value) neededForUsers; |
| 117 | inherit key; |
| 118 | }; |
| 119 | }); |
| 120 | |
| 121 | secretsAsList = lib.attrsets.attrsToList config.clicks.secrets; |
| 122 | |
| 123 | secretsAsSops = lib.pipe secretsAsList [ |
| 124 | (map ( |
| 125 | secret: |
| 126 | if guessFormat (getExtension secret.value.file) == "binary" then |
| 127 | generateBinarySopsSecret secret |
| 128 | else |
| 129 | generateNonBinarySopsSecrets secret |
| 130 | )) |
| 131 | lib.flatten |
| 132 | builtins.listToAttrs |
| 133 | ]; |
| 134 | in |
| 135 | { |
| 136 | sops.secrets = secretsAsSops; |
| 137 | }; |
| 138 | } |