{"id":16288083,"url":"https://github.com/jdelic/dynamicsecrets","last_synced_at":"2025-03-20T03:30:34.518Z","repository":{"id":151274412,"uuid":"148281128","full_name":"jdelic/dynamicsecrets","owner":"jdelic","description":"Solves the initial secret introduction problem for Salt","archived":false,"fork":false,"pushed_at":"2023-10-08T12:19:00.000Z","size":21,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T22:48:38.935Z","etag":null,"topics":["salt","salt-master","saltstack","secret-mana"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jdelic.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-09-11T07:52:36.000Z","updated_at":"2023-10-08T12:19:04.000Z","dependencies_parsed_at":"2023-04-07T05:01:31.437Z","dependency_job_id":null,"html_url":"https://github.com/jdelic/dynamicsecrets","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdelic%2Fdynamicsecrets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdelic%2Fdynamicsecrets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdelic%2Fdynamicsecrets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdelic%2Fdynamicsecrets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jdelic","download_url":"https://codeload.github.com/jdelic/dynamicsecrets/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244047609,"owners_count":20389205,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["salt","salt-master","saltstack","secret-mana"],"created_at":"2024-10-10T19:47:14.469Z","updated_at":"2025-03-20T03:30:34.190Z","avatar_url":"https://github.com/jdelic.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Salt Dynamic Secrets Module and Pillar\n======================================\n\nThis module solves the *Initial Secret Introduction Problem* for Salt. You\nassign secrets to nodes (or grains) and then use them in your states through the\n``dynamicsecrets`` pillar. Secrets can be configured to represent\n\n* cryptographically secure UUIDs,\n* cryptographically secure strings of random characters suitable for passwords\n* base64-encoded random bytes\n* RSA keys\n* or Consul ACL tokens\n\nSecrets can be constant across the cluster or even differ for each salt-minion\n(\"per-host\").\n\nThe idea behind this is to solve the following problem: To manage secrets in a\ncluster you have few options. You can pre-share them, which is a bad idea,\nespecially if those secrets are then unnecessarily shared across cluster\ninstances (multiple Salt installations using the same secrets). Also, your OPS\nteam might have access to all the secrets all the time, making them difficult to\nrotate when someone leaves. On the other end of the spectrum you can use a tool\nlike `Hashicorp Vault \u003cvault_\u003e`__, but that raises the question of how you build\nthe cluster up to the point where a Vault instance is available.\n\nDynamicsecrets aims at exactly that small space inbetween installing your Salt\nmaster and checking out your Salt configuration from your git repository and\ninstalling your cluster to the point where Vault is available. A real-world\nusage scenario for a Salt configuration that does exactly this is my very own\n`Jdelic's Saltshaker \u003csaltshaker_\u003e`__.\n\nThe generated secrets are all kept in an *unencrypted* SQLite database in\n``/etc/salt``. This is important, you **must** protect that database. Ideally,\nyou only use these secrets to bootstrap yourself into a cluster that then stores\nand generates the more important secrets in a software like Vault.\n\n\nInstallation\n------------\nFirst, place the *Salt execution module* into your Salt configuration. Example\ndirectory structure:\n\n.. code-block::\n\n    srv/salt-modules/modules/dynamicsecrets.py\n    srv/salt-modules/pillar/dynamicsecrets.py\n    srv/salt/top.sls\n    srv/salt/...\n    srv/pillar/top.sls\n    srv/pillar/...\n\nThen add a ``ext_pillar`` configuration to your salt-master:\n\n.. code-block:: yaml\n\n    # Extension modules\n    extension_modules: /srv/salt-modules\n\n    # set up access to Consul server\n    dynamicsecrets.consul_url: http://169.254.1.1:8500/\n    # You can either set a static token (not recommended)\n    #     dynamicsecrets.consul_token: 12345678-abcd-...\n    # or reference another dynamicsecrets secret as the ACL master token to use\n    # to create new ACL tokens\n    dynamicsecrets.consul_token_secret: consul-acl-master-token\n\n    ext_pillar:\n        - dynamicsecrets:\n            config:\n                approle-auth-token:\n                    type: uuid\n                concourse-encryption:\n                    length: 32\n                concourse-hostkey:\n                    length: 2048\n                    type: rsa\n                consul-acl-token:\n                    type: consul-acl-token\n                    unique-per-host: True\n                consul-acl-master-token:\n                    type: uuid\n                consul-encryptionkey:\n                    encode: base64\n                    length: 16\n            grainmapping:\n                roles:\n                    authserver:\n                        - approle-auth-token\n            hostmapping:\n                '*':\n                    - consul-acl-token\n\n\nIn the above example, every node that has the grain ``roles:authserver`` can\naccess ``pillar['dynamicsecrets']['approle-auth-token']`` which is a UUID\nconstant over all salt-minions and every node can access\n``pillar['dynamicsecrets']['consul-acl-token']`` which is a UUID that is\ndifferent for each salt-minion (and in my case used to create a Consul ACL for\neach salt-minion by firing an event to the salt-master when the minion boots).\n\nFor ``type: password`` the Pillar will simply contain the random password\nstring.\n\nFor ``type: uuid`` the Pillar will return a UUID4 built from a secure random\nsource (as long as the OS provides one).\n\nFor ``type: rsa`` the Pillar will return a ``dict`` that has the following\nproperties:\n\n* ``public_pem`` the public key in OpenSSL PEM encoding\n* ``public`` the public key in `ssh-rsa` format\n* ``key`` the private key in PEM encoding\n\nFor ``type: consul-acl-token`` the Pillar will return a ``dict`` that has the\nfollowing properties:\n\n* ``accessor_id`` the accessor id of the ACL token (if ``firstrun`` is\n  ``False``)\n* ``secret_id`` the secret id of the ACL token (if ``firstrun`` is ``False``)\n* ``firstrun`` a boolean flag that shows if the salt-master had a Consul server\n  available to create ACL tokens. When a cluster is first started, this allows\n  your Salt configuration to detect the chicken+egg problem of knowing when\n  you're bootstrapping.\n\n\nConsuL ACL tokens\n-----------------\nIf you want to use the Consul ACL token support in ``dynamicsecrets`` then your\nsalt-master **must** have access to a Consul server node and know a ACL master\ntoken. ``dynamicsecrets`` talks directly to the Consul ACL API to create ACL\ntokens with *no attached policy whatsoever*. You are then supposed to use Salt\nto update the ACL tokens with your policies as they become available.\n\nThis is most easily done by using a Salt Reactor. An example can be found\n`in this consul-acl Reactor \u003cconsul_reactor_\u003e`__ and its associated\n`salt-master configuration \u003creactor_config_\u003e`__.\n\n\nUsage\n-----\nAs shown above, an `ext_pillar \u003cext_pillar_\u003e`__ ends up in the ``pillar``\ndictionary. Salt minions therefore get rendered pillars that can freely\nreference ``pillar['dynamicsecrets']`` or ``__pillar__['dynamicsecrets']``,\ndepending on the use-case. On the salt-master, where the module is executed,\nyour code can also use the dynamicsecrets Salt execution module. So in\n``pydsl`` states, reactors or in your own modules you can directly interface\nwith the module like this:\n\n.. code-block:: python\n\n    # get or create a secret for a specific host in a reactor\n    # Note: in a reactor SLS, data['id'] is the salt-minion's ID\n    salt['dynamicsecrets'].get_or_create(\n        {\n            \"type\": \"uuid\",\n        },\n        'consul-acl-token',\n        host=data['id']\n    )\n\n    # get all secrets stored under a key (for all hosts)\n    for sekrit in salt['dynamicsecrets'].loadall(\n        'consul-acl-token):\n        ...\n\n    if salt['dynamicsecrets'].exists('consul-master-token',\n        host=\"saltmaster\"):\n        ...\n\n\nThe Salt execution module can also be executed using the Salt client:\n\n.. code-block:: shell\n\n    $ salt 'saltmaster' dynamicsecrets.load consul-acl-token host=saltmaster\n\n\nFuture enhancements\n-------------------\nWith a bit of work this could possibly use pysqlcipher to encrypt its backing\ndatabase.\n\n.. _vault: https://vaultproject.io/\n.. _saltshaker: https://github.com/jdelic/saltshaker/\n.. _ext_pillar:\n   https://docs.saltstack.com/en/latest/topics/development/external_pillars.html\n.. _consul_reactor:\n   https://github.com/jdelic/saltshaker/blob\n   /231fc14c7521f44c83f76ad7de67fa062bd9aca8/srv/salt/orchestrate\n   /consul-node-setup.sls\n.. _reactor_config:\n   https://github.com/jdelic/saltshaker/blob\n   /231fc14c7521f44c83f76ad7de67fa062bd9aca8/etc/salt-master/master.d\n   /saltshaker.conf#L131\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdelic%2Fdynamicsecrets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjdelic%2Fdynamicsecrets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdelic%2Fdynamicsecrets/lists"}