- Notifications
You must be signed in to change notification settings - Fork0
Solves the initial secret introduction problem for Salt
License
jdelic/dynamicsecrets
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This module solves theInitial Secret Introduction Problem for Salt. Youassign secrets to nodes (or grains) and then use them in your states through thedynamicsecrets
pillar. Secrets can be configured to represent
- cryptographically secure UUIDs,
- cryptographically secure strings of random characters suitable for passwords
- base64-encoded random bytes
- RSA keys
- or Consul ACL tokens
Secrets can be constant across the cluster or even differ for each salt-minion("per-host").
The idea behind this is to solve the following problem: To manage secrets in acluster you have few options. You can pre-share them, which is a bad idea,especially if those secrets are then unnecessarily shared across clusterinstances (multiple Salt installations using the same secrets). Also, your OPSteam might have access to all the secrets all the time, making them difficult torotate when someone leaves. On the other end of the spectrum you can use a toollikeHashicorp Vault, but that raises the question of how you buildthe cluster up to the point where a Vault instance is available.
Dynamicsecrets aims at exactly that small space inbetween installing your Saltmaster and checking out your Salt configuration from your git repository andinstalling your cluster to the point where Vault is available. A real-worldusage scenario for a Salt configuration that does exactly this is my very ownJdelic's Saltshaker.
The generated secrets are all kept in anunencrypted SQLite database in/etc/salt
. This is important, youmust protect that database. Ideally,you only use these secrets to bootstrap yourself into a cluster that then storesand generates the more important secrets in a software like Vault.
First, place theSalt execution module into your Salt configuration. Exampledirectory structure:
srv/salt-modules/modules/dynamicsecrets.pysrv/salt-modules/pillar/dynamicsecrets.pysrv/salt/top.slssrv/salt/...srv/pillar/top.slssrv/pillar/...
Then add aext_pillar
configuration to your salt-master:
# Extension modulesextension_modules:/srv/salt-modules# set up access to Consul serverdynamicsecrets.consul_url:http://169.254.1.1:8500/# You can either set a static token (not recommended)# dynamicsecrets.consul_token: 12345678-abcd-...# or reference another dynamicsecrets secret as the ACL master token to use# to create new ACL tokensdynamicsecrets.consul_token_secret:consul-acl-master-tokenext_pillar: -dynamicsecrets:config:approle-auth-token:type:uuidconcourse-encryption:length:32concourse-hostkey:length:2048type:rsaconsul-acl-token:type:consul-acl-tokenunique-per-host:Trueconsul-acl-master-token:type:uuidconsul-encryptionkey:encode:base64length:16grainmapping:roles:authserver: -approle-auth-tokenhostmapping:'*': -consul-acl-token
In the above example, every node that has the grainroles:authserver
canaccesspillar['dynamicsecrets']['approle-auth-token']
which is a UUIDconstant over all salt-minions and every node can accesspillar['dynamicsecrets']['consul-acl-token']
which is a UUID that isdifferent for each salt-minion (and in my case used to create a Consul ACL foreach salt-minion by firing an event to the salt-master when the minion boots).
Fortype: password
the Pillar will simply contain the random passwordstring.
Fortype: uuid
the Pillar will return a UUID4 built from a secure randomsource (as long as the OS provides one).
Fortype: rsa
the Pillar will return adict
that has the followingproperties:
public_pem
the public key in OpenSSL PEM encodingpublic
the public key in ssh-rsa formatkey
the private key in PEM encoding
Fortype: consul-acl-token
the Pillar will return adict
that has thefollowing properties:
accessor_id
the accessor id of the ACL token (iffirstrun
isFalse
)secret_id
the secret id of the ACL token (iffirstrun
isFalse
)firstrun
a boolean flag that shows if the salt-master had a Consul serveravailable to create ACL tokens. When a cluster is first started, this allowsyour Salt configuration to detect the chicken+egg problem of knowing whenyou're bootstrapping.
If you want to use the Consul ACL token support indynamicsecrets
then yoursalt-mastermust have access to a Consul server node and know a ACL mastertoken.dynamicsecrets
talks directly to the Consul ACL API to create ACLtokens withno attached policy whatsoever. You are then supposed to use Saltto update the ACL tokens with your policies as they become available.
This is most easily done by using a Salt Reactor. An example can be foundin this consul-acl Reactor and its associatedsalt-master configuration.
As shown above, anext_pillar ends up in thepillar
dictionary. Salt minions therefore get rendered pillars that can freelyreferencepillar['dynamicsecrets']
or__pillar__['dynamicsecrets']
,depending on the use-case. On the salt-master, where the module is executed,your code can also use the dynamicsecrets Salt execution module. So inpydsl
states, reactors or in your own modules you can directly interfacewith the module like this:
# get or create a secret for a specific host in a reactor# Note: in a reactor SLS, data['id'] is the salt-minion's IDsalt['dynamicsecrets'].get_or_create( {"type":"uuid", },'consul-acl-token',host=data['id'])# get all secrets stored under a key (for all hosts)forsekritinsalt['dynamicsecrets'].loadall( 'consul-acl-token): ...ifsalt['dynamicsecrets'].exists('consul-master-token',host="saltmaster"): ...
The Salt execution module can also be executed using the Salt client:
$ salt'saltmaster' dynamicsecrets.load consul-acl-token host=saltmaster
With a bit of work this could possibly use pysqlcipher to encrypt its backingdatabase.