2

In a chef recipe invoked by chef-solo / vagrant I'm trying to write a .htpasswd file from an object of users. I specify the users in vagrantfile like this...

  chef.json = {
      :apache => {
          ...
          :my_users => {
              :john => "test",
              :matt => "test2"
          }
          ...

My chef recipe looks like this at the moment:

file "/etc/apache2/.htpasswd" do
  content "john:n5MfEoHOIQkKg"
  owner "#{node['apache']['user']}"
  group "#{node['apache']['group']}"
  mode '0644'
  action :create
end

As you can see I have hard coded John's credentials in there - however, I'm not a Ruby dev and I'm missing some very basic knowledge here...

How can I write all user credentials in the node['apache']['my_users'] attribute (defined in the chef.json) in a loop into the file while creating the password hash for each clear text password?

Note: I'm trying to avoid using a template for this simple file.

phpPhil
  • 906
  • 1
  • 9
  • 28
  • Why are you "trying to avoid using a template"? If your input is cleartext, have to make the calculation to hash the credential somewhere if not in a template, then in your recipe code. If you have multiple users a template would be a way to loop over all of them. (otherwise you have loop and build the string in ruby). A template seems like an appropriate solution. – Charlie Apr 24 '15 at 04:39
  • Well, I thought a template would be overkill for this sort of thing. But if you don't agree I'm open for suggestions and happy to do this using templates. Either way, I would not have an idea on how to add the call to `htpasswd` into the constellation. – phpPhil Apr 24 '15 at 04:54
  • So there are a number of ways you could implement this, calling `htpasswd` to manage the file and its contents (probably looking at creating an [execute resource](https://docs.chef.io/resource_execute.html) for [each](http://ruby-doc.org/core-2.2.0/Hash.html#method-i-each) username / password pair. The other way is to manage the file completely in ruby. One way to do this would be to build a large string and pass it to the file resource as contents, the other would be to use a template to generate the file. – Charlie Apr 24 '15 at 05:04
  • 1
    Also searching about, not sure how good this is, but it looks like someone wrote a htpasswd lwrp: https://supermarket.chef.io/cookbooks/htpasswd – Charlie Apr 24 '15 at 05:05
  • @Charlie: Thanks! The LWRP works well - see my post. – phpPhil Apr 24 '15 at 06:34

3 Answers3

2

I got this working using the LWRP Charlie suggested.

First step is to modify the definition of users to be a proper array:

chef.json = {
    :apache => {
        ...
        :my_users => [ 
            { :username => "john", :password => "test1" },
            { :username => "matt", :password => "test2" }
        ]
        ...

I include the htpasswd dependency to metadata and bershelf. Then in my recipe I create the users in a loop using the htpasswd call:

node[:apache][:my_users].each do |user|
  htpasswd "/etc/apache2/.htpasswd" do
    user user['username']
    password user['password']
  end
end
phpPhil
  • 906
  • 1
  • 9
  • 28
0

The htpasswd man page looks like it uses MD5 hashing on the passwords.

Perhaps you can generate md5 hashes in your recipe's Ruby code?

Noah Gibbs
  • 765
  • 3
  • 10
  • Yeah, I know it creates hashes with an Apache specific implementation of MD5. Running the command creates the hash for me :) That's exactly what my question is about - I don't know enough Ruby to create the loop, execute the htpasswd and write it to the file. Sounds pretty basic and probably is for a Ruby dev, but not for me. – phpPhil Apr 24 '15 at 03:35
0

You can do it the native way, it requires htpasswd to be installed:

execute 'set password' do
  sensitive true
  command "htpasswd -cb /etc/htpasswd.users #{user} #{password}"
  creates '/etc/htpasswd.users'
end

file '/etc/htpasswd.users' do
  owner 'www-data'
  group 'www-data'
  mode 0o600
end
sekrett
  • 1,205
  • 1
  • 15
  • 17