sous-chefs / users

Development repository for the users cookbook
https://supermarket.chef.io/cookbooks/users
Apache License 2.0
138 stars 217 forks source link

Add support for Encrypted Data Bag content #72

Closed lonniev closed 3 years ago

lonniev commented 9 years ago

It would be great to have the option of supplying an encrypted data bag or a set of such bags to the users recipe.

I have a UseCase where I automate the creation of users and their logins. For this, I need to create their initial passphrase-less private id_rsa ssh keys. Currently, I do this in a custom recipe that reads an encrypted data bag to get the encrypted content of the id_rsa file. It reads the json attribute, decrypts it, creates the ~/.ssh/id_rsa file, and stuffs it with the right content. (The recipe manages the .ssh directory and the config file as well.)

It would be great to delegate all this to the existing users_manage resource. That resource, as you know, does the right stuff for any found ssh_private_key attribute in the users' bags. However, it expects that private key value to be plaintext.

So, if the manage resource either would look in a separate encrypted bag for such sensitive keys or would allow the entire user's bag to be encrypted, then it could manage all this ssh cruft.

lonniev commented 9 years ago

Here's the concept gist. This looks for all users in the bags that users currently uses that have a true value for has_private_ssh. For each of those, it loads their key values from the encrypted private_keys bag that shares their users:id name.

Some of the steps are redundant with what manage_users will do and the code is in the midst of debug--but it expresses the goal.

# for each user with an has_private_ssh entry, create their ssh identity files
search( "users", "has_private_ssh:true AND NOT action:remove") do |ssh_user|

  ssh_user['username'] ||= ssh_user['id']

  ssh_keys = Chef::EncryptedDataBagItem.load( "private_keys", ssh_user['id'] )

  if ( ssh_keys.empty? )

    log "message" do
        message "user #{ssh_user['id']} missing from data_bags/private_keys."
        level :warn
    end

    next
  end

  sshDir = Pathname.new( ssh_user['home'] ).join( ".ssh" )
  idFile = sshDir.join( "id_rsa" )

  directory sshDir.to_s do
    owner ssh_user['username']
    group ssh_user['username']
    mode 0700
    recursive true

    action :create
  end

  file sshDir.join( "config" ).to_s do
    owner ssh_user['username']
    group ssh_user['username']
    mode 0600

      content <<-EOT
Host *
  StrictHostKeyChecking no
  IdentityFile #{idFile}
  IdentitiesOnly yes
EOT
  end

  file idFile.to_s do
    owner ssh_user['username']
    group ssh_user['username']
    mode 0600

    content ssh_keys['private']
  end

  file idFile.sub_ext( ".pub" ).to_s do
    owner ssh_user['username']
    group ssh_user['username']
    mode 0644

    content ssh_keys['public']
  end

end
SkyEyes commented 9 years ago

What about something like this?

use_inline_resources if defined?(use_inline_resources)
def whyrun_supported?
  true
end
def userlist
  secret_key = Chef::EncryptedDataBagItem.load_secret(Chef::Config[:encrypted_data_bag_secret])
  dec_users = []
  data_bag(:users).each do |user|
    dec_users << Chef::EncryptedDataBagItem.load("users", "#{user}", secret_key)
  end
  return dec_users
end
def initialize(*args)
  super
  @action = :create
end
def chef_solo_search_installed?
  klass = ::Search::const_get('Helper')
  return klass.is_a?(Class)
rescue NameError
  return false
end

action :remove do
  userlist.each do |rm_user|
    if rm_user['groups'].first == "#{new_resource.search_group}" and rm_user['action'] == "remove"
      user "#{rm_user['id']}" do
        action :remove
      end
    end
  end
end

action :create do
  security_group = Array.new

  userlist.each do |u|
    if u['groups'].first == "#{new_resource.search_group}" and not u['action'] == "remove"

      security_group << u['id']

      if node['apache'] and node['apache']['allowed_openids']
        Array(u['openid']).compact.each do |oid|
          node.default['apache']['allowed_openids'] << oid unless node['apache']['allowed_openids'].include?(oid)
        end
      end
      # Set home_basedir based on platform_family
      case node['platform_family']
      when 'mac_os_x'
        home_basedir = '/Users'
      when 'debian', 'rhel', 'fedora', 'arch', 'suse', 'freebsd'
        home_basedir = '/home'
      end
      # Set home to location in data bag,
      # or a reasonable default ($home_basedir/$user).
      if u['home']
        home_dir = u['home']
      else
        home_dir = "#{home_basedir}/#{u['id']}"
      end
      # The user block will fail if the group does not yet exist.
      # See the -g option limitations in man 8 useradd for an explanation.
      # This should correct that without breaking functionality.
      if u['gid'] and u['gid'].kind_of?(Numeric)
        group u['id'] do
          gid u['gid']
        end
      end
      # Create user object.
      # Do NOT try to manage null home directories.
      user "#{u['id']}" do
        uid u['uid']
        if u['gid']
          gid u['gid']
        end
        shell u['shell']
        comment u['comment']
        password u['password'] if u['password']
        if home_dir == "/dev/null"
          supports :manage_home => false
        else
          supports :manage_home => true
        end
        home home_dir
        action u['action'] if u['action']
      end
      Chef::Log.debug("Managing home files for #{u['id']}")
      directory "#{home_dir}/.ssh" do
        owner u['id']
        group u['gid'] || u['id']
        mode "0700"
      end
      if u['ssh_keys']
        template "#{home_dir}/.ssh/authorized_keys" do
          source "authorized_keys.erb"
          cookbook new_resource.cookbook
          owner u['id']
          group u['gid'] || u['id']
          mode "0600"
          variables :ssh_keys => u['ssh_keys']
        end
      end
      if u['ssh_private_key']
        key_type = u['ssh_private_key'].include?("BEGIN RSA PRIVATE KEY") ? "rsa" : "dsa"
        template "#{home_dir}/.ssh/id_#{key_type}" do
          source "private_key.erb"
          cookbook new_resource.cookbook
          owner u['id']
          group u['gid'] || u['id']
          mode "0400"
          variables :private_key => u['ssh_private_key']
        end
      end
      if u['ssh_public_key']
        key_type = u['ssh_public_key'].include?("ssh-rsa") ? "rsa" : "dsa"
        template "#{home_dir}/.ssh/id_#{key_type}.pub" do
          source "public_key.pub.erb"
          cookbook new_resource.cookbook
          owner u['id']
          group u['gid'] || u['id']
          mode "0400"
          variables :public_key => u['ssh_public_key']
        end
      end
    else
      Chef::Log.debug("Not managing home files for #{u['id']}")
    end
  end
  group new_resource.group_name do
    if new_resource.group_id
      gid new_resource.group_id
    end
    members security_group
  end
gravesb commented 9 years ago

A lot of things are moving to support chef-vault now. Any thoughts on allowing that for the encrypted data bag pieces?

yl013512 commented 9 years ago

Submitted a PR: https://github.com/opscode-cookbooks/users/pull/91

SkyEyes commented 9 years ago

Even edited little my code :) Nice to see :) Let's wait for merge ^)

bimp commented 8 years ago

@yl013512 so is your forked version essentially work with the existing "users_manage" resource but with a encrypted data bag of users via chef-vault?

SkyEyes commented 8 years ago

It must. Just try from fork

bimp commented 8 years ago

@yl013512 tried your fork with a chef-vault encrypted users item and get the following error. Any ideas?

10.41.82.124 Recipe Compile Error in /var/chef/cache/cookbooks/users/providers/manage.rb 10.41.82.124 ================================================================================ 10.41.82.124 10.41.82.124 NameError 10.41.82.124 --------- 10.41.82.124 uninitialized constant Chef::Provider::ChefVaultItem 10.41.82.124 10.41.82.124 Cookbook Trace: 10.41.82.124 --------------- 10.41.82.124 /var/chef/cache/cookbooks/users/providers/manage.rb:20:in `class_from_file' 10.41.82.124 10.41.82.124 Relevant File Content: 10.41.82.124 ---------------------- 10.41.82.124 /var/chef/cache/cookbooks/users/providers/manage.rb: 10.41.82.124 10.41.82.124 13: # 10.41.82.124 14: # Unless required by applicable law or agreed to in writing, software 10.41.82.124 15: # distributed under the License is distributed on an "AS IS" BASIS, 10.41.82.124 16: # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10.41.82.124 17: # See the License for the specific language governing permissions and 10.41.82.124 18: # limitations under the License. 10.41.82.124 19: # 10.41.82.124 20>> include ChefVaultItem 10.41.82.124 21: 10.41.82.124 22: use_inline_resources if defined?(use_inline_resources) 10.41.82.124 23: 10.41.82.124 24: def whyrun_supported? 10.41.82.124 25: true 10.41.82.124 26: end 10.41.82.124 27: 10.41.82.124 28: def initialize(*args) 10.41.82.124 29: super

djessich commented 7 years ago

Any ideas when Chef Vault support will be available for this cookbook?

nkadel-skyhook commented 7 years ago

I think it would need to be an option for the recipe that calls the data_bag, associated with a different data_bag directory.

frankruegamer commented 5 years ago

Is there any update on the support of encrypted data bag items?

frank-m commented 3 years ago

Hi, from version 6.0.0 the data source has been moved outside of the users_manage resource. So you can now initialize your encrypted data bag in the recipe and then pass it to the manage resource.

See https://github.com/sous-chefs/users/blob/master/upgrading.md for details with a normal databag. This can easily be adapted to an encrypted data bag or any data source you want.