Configuration management is an essential part of securing your infrastructure because it can make sure that it is set up correctly. It is essential that configuration management only enhance security, and not weaken it. Unfortunately, the status-quo of secret management in puppet is pretty poor.
In the worst (and most common) case, plain text passwords are found in manifests. If the module author tried harder, sometimes these password strings are pre-hashed (and sometimes salted) and fed directly into the consumer. (This isn’t always possible without modifying the software you’re managing.)
On better days, these strings are kept separate from the code in unencrypted yaml files, and if the admin is smart enough to store their configurations in git, they hopefully separated out the secrets into a separate repository. Of course none of these solutions are very convincing to someone who puts security at the forefront.
This article describes how I use puppet to correctly and securely setup FreeIPA.
FreeIPA is an excellent piece of software that combines LDAP and Kerberos with an elegant web ui and command line interface. It can also glue in additional features like NTP. It is essential for any infrastructure that wants single sign on, and unified identity management and security. It is a key piece of infrastructure since you can use it as a cornerstone, and build out your infrastructures from that centrepiece. (I hope to make the puppet-ipa module at least half as good as what the authors have done with FreeIPA core.)
Passing a secret into the FreeIPA server for installation is simply not possible without it touching puppet. The way I work around this limitation is by generating the dm_password on the FreeIPA server at install time! This typically looks like:
/usr/sbin/ipa-server-install --hostname='ipa.example.com' --domain='example.com' --realm='EXAMPLE.COM' --ds-password=`/usr/bin/pwgen 16 1 | /usr/bin/tee >( /usr/bin/gpg --homedir '/var/lib/puppet/tmp/ipa/gpg/' --encrypt --trust-model always --recipient '24090D66' > '/var/lib/puppet/tmp/ipa/gpg/dm_password.gpg' ) | /bin/cat | /bin/cat` --admin-password=`/usr/bin/pwgen 16 1 | /usr/bin/tee >( /usr/bin/gpg --homedir '/var/lib/puppet/tmp/ipa/gpg/' --encrypt --trust-model always --recipient '24090D66' > '/var/lib/puppet/tmp/ipa/gpg/admin_password.gpg' ) | /bin/cat | /bin/cat` --idstart=16777216 --no-ntp --selfsign --unattended
This command is approximately what puppet generates. The interesting part is:
--ds-password=`/usr/bin/pwgen 16 1 | /usr/bin/tee >( /usr/bin/gpg --homedir '/var/lib/puppet/tmp/ipa/gpg/' --encrypt --trust-model always --recipient '24090D66' > '/var/lib/puppet/tmp/ipa/gpg/dm_password.gpg' ) | /bin/cat | /bin/cat`
If this is hard to follow, here is the synopsis:
pwgencommand is used generate a password.
- The password is used for installation.
- The password is encrypted with the users GPG key and saved to a file for retrieval.
- The encrypted password is (optionally) sent out via email to the admin.
Note that the email portion wasn’t shown since it makes the command longer.
Where did my GPG key come from?
Any respectable FreeIPA admin should already have their own GPG key. If they don’t, they probably shouldn’t be managing a security appliance. You can either pass the public key to
gpg_publickey or specify a keyserver with
gpg_keyserver. In either case you must supply a valid recipient (-r) string to
gpg_recipient. In my case, I use my keyid of
24090D66, which can be used to find my key on the public keyservers. In either case, puppet knows how to import it and use it correctly. A security audit is welcome!
You’ll be pleased to know that I deliberately included the options to use your own keyserver, or to specify your public key manually if you don’t want it stored on any key servers.
But, I want a different password!
It’s recommended that you use the secure password that has been generated for you. There are a few options if you don’t like this approach:
- The puppet module allows you to specify the password as a string. This isn’t recommended, but it is useful for testing and compatibility with legacy puppet environments that don’t care about security.
- You can use the secure password initially to authenticate with your FreeIPA server, and then change the password to the one you desire. Doing this is outside the scope of this article, and you should consult the FreeIPA documentation.
- You can use puppet to regenerate a new password for you. This hasn’t been implemented yet, but will be coming eventually.
- You can use the interactive password helper. This takes the place of the
pwgencommand. This will be implemented if there is enough demand. During installation, the admin will be able to connect to a secure console to specify the password.
Other suggestions will be considered.
What about the admin password?
admin_password is generated following the same process that was used for the
dm_password. The chance that the two passwords match is probably about:
1/((((26*2)+10)^16)^2) = ~4.4e-58
In other words, very unlikely.
Testing this easily:
Testing this out is quite straightforward. This process has been integrated with vagrant for easy testing. Start by setting up vagrant if you haven’t already:
Once you are comfortable with vagrant, follow these steps for using Puppet-IPA:
git clone --recursive https://github.com/purpleidea/puppet-ipa cd vagrant/ vagrant status # edit the puppet-ipa.yaml file to add your keyid in the recipient field # if you do not add a keyid, then a password of 'password' will be used # this default is only used in the vagrant development environment vagrant up puppet vagrant up ipa
You should now have a working FreeIPA server. Login as root with:
Hope you enjoyed this.