Chef offers a great many useful features, including the ability to manage and create user accounts. The password for a local user account can be specified either in clear text or as a password hash.
According to the documentation linked above, generating an appropriate password hash for 10.8+ requires the use of a specific Ruby function:
OpenSSL::PKCS5::pbkdf2_hmac( password, salt, iterations, 128, OpenSSL::Digest::SHA512.new )
However, when trying to generate such a hash using this tool on 10.10.5, I discovered a problem:
irb(main):026:0> OpenSSL::PKCS5::pbkdf2_hmac( irb(main):027:1* password, irb(main):028:1* salt, irb(main):029:1* iterations, irb(main):030:1* 128, irb(main):031:1* OpenSSL::Digest::SHA512.new irb(main):032:1> ) NotImplementedError: pbkdf2_hmac() function is unimplemented on this machine from (irb):26:in `pbkdf2_hmac' from (irb):26 from /usr/bin/irb:12:in `<main>'
Well, that's not very nice.
The issue is that the version of OpenSSL on OS X for the last several years is still 0.9.8zg. That version simply doesn't have an implementation of the pbkdf2_hmac() that Ruby wants to use. However, Python does, thanks to hashlib.
To recreate the same process in Python that the Chef documentation recommends for generating a 10.8+ password hash, use the following steps:
import hashlib import binascii import os password = b'password' salt = os.urandom(32) chef_salt = binascii.hexlify(salt) iterations = 25000 hex = hashlib.pbkdf2_hmac('sha512', password, salt, 25000, 128) chef_password_hash = binascii.hexlify(hex)
Let's break down what happened there. First, we set the password to our password string. In Python 2, the b before a string doesn't really do anything.
The salt is a random 32-bit string. In Python, this comes out in binary form:
>>> salt = os.urandom(32) >>> salt 'M\xde\xf6\x9fp\xd7$\x128\x9a\xc2!\xad\x1a\xe6\x9bE\xf8N\n\xd0\x18\xf6Ez\xf5@\xe0\xd1\r\xe6a'
Chef, however, requires this in a hexadecimal form:
>>> binascii.hexlify(salt) '4ddef69f70d72412389ac221ad1ae69b45f84e0ad018f6457af540e0d10de661'
We use 25,000 iterations as a nice arbitrary number, but you should use anything above 10,000 to be at least minimally safe. Of course this is a local user account on a service machine in my context, so I’m not entirely worried about its security as much.
Once we have all the variables, we can use the actual
pbkdf2_hmac() function. In the example above, we’re using the SHA-512 digest, with a derived key length of 128 as the Chef documentation suggests. Once again, the result of that command is binary data, and Chef requires a hexadecimal representation, so we turn to our trusty friend
This allows us to create the Chef user block we need:
user 'myuser' do gid 'staff' home '/Users/myuser' shell '/bin/bash' password 'e6a8a452c0a9edb7f80703657b91fae74191d3b83982687ca00b83741ad775410178542ffc176abe6db9dc46053bc7ed36c91c1f43f82ba1dedc12de929f81cca868e223a25f3f16728e9f92c02e4421e9f73d73edb5e23e5d0cf1784243e8c79307ee5e61b411c9f116c450af8112e519fa15cfb50f5e7a8c1e6a78fb7cbc0e' salt 'eb30e9c1946f086b4cd84679c1ee81235edea080b28b1ce4d39341794fad1ccd' iterations 25_000 supports manage_home: true action :create end
I’m told this same technique can also generate password hashes to be used with Puppet as well, although I haven’t tested it personally.