Managing Infrastructure with Puppet - James Loope [11]
Next we declare a define that takes a bunch of arguments describing our user and set a custom variable $username to the name of the resource, for clarity’s sake.
Then a user type declaration is made, passing the parameters from the define in to describe the user we want. The minimum member declaration specifies that the user can be a member of groups outside of this declaration and Puppet will not remove any groups added to the user manually.
Finally, we ensure the ownership of the user’s home and add the .ssh directory and populate the authorized_keys. This will allow us to manage the user’s login credentials to our servers:
# modules/users/manifests/init.pp
# imports
import "people"
class rubyshadow {
package { "libshadow-ruby1.8":
ensure => installed,
}
}
define useraccount ( $ensure = present, $uid, $pgroup = users,
$groups, $password, $fullname, $homefs, $shell ) {
# Grab the username from the resource name
$username = $name
# define the user
user { $username:
ensure => $ensure,
uid => $uid,
gid => $pgroup,
groups => $groups,
listitemship => minimum,
comment => $fullname,
home => "${homefs}/$username",
shell => $shell,
allowdupe => false,
password => $password,
}
# Ensure the ownership and perms of the user home
file { "${homefs}/${username}":
ensure => directory,
owner => $home_owner,
group => $home_group,
mode => 750,
require => User["${username}"],
}
# Create a dir for the ssh pubkey
file { "${homefs}/${username}/.ssh":
ensure => directory,
owner => $home_owner,
group => $home_group,
mode => 700,
require => File["${homefs}/${username}"],
}
# Add the users pubkeys
file { "${homefs}/${username}/.ssh/authorized_keys":
ensure => present,
owner => $home_owner,
group => $home_group,
mode => 600,
require => File["${homefs}/${username}/.ssh"],
source => "puppet:///modules/users/${username}/.ssh/authorized_keys",
}
}
Declaring Users
In this file, we’ve created a class called people that includes our rubyshadow class, sets up a couple of default values, and checks the home directory for sanity. Then we create a virtual resource named alice from our defined useraccount type. Alice has a couple of group memberships and her password parameter is supplied with a hash. This hash can either be mined out of a shadow file or generated with the mkpasswd utility. Bob is also present, and he’s a member of the db group:
# modules/users/people.pp
class people {
# include our rubyshadow class
include rubyshadow
# set some defaults
$shell = "/bin/bash"
$homefs = "/home"
# make sure that /home is correct
file { $homefs:
ensure => directory,
owner => "root",
group => "root",
mode => 2755
}
@useraccount { "alice":
ensure => "present",
uid => "1001",
pgroup => "users",
groups => ["db", "web", "admin"],
fullname => "Alice",
homefs => $homefs,
shell => $shell,
password => '$6$V38meAAms5qFW$iTX0EpsGGlWxqkVByPw75zF8QbVNMTLPyY8Hk6RykfTDR
cCTegRtjCpssZPJsUXRJJihgWHX.a0xaeuszjPii0',
}
@useraccount { "bob":
ensure => "present",
uid => "1002",
pgroup => "users",
groups => ["db"],
fullname => "Bob",
homefs => $homefs,
shell => $shell,
password => '$6$CiljlJAsBzc.fm7Q$dlo0/DsoVUD.MBeItUPrb8m5TkRmFSpQZP3smK9yTFV
dIyn4ib54PvohmkSn93WvPKUIXwODEUIjumCmsQ7rd0',
}
}
I’ve made Alice and Bob virtual resources here, because I may not want to have useraccounts on all of my nodes. If I were to have declared a real resource, every node that includes the people class would have had her user created. This way, I can realize only the users in the web group on nodes A.example.com and both users in the web and db groups on B.example.com.
* * *
Warning
It may seem obvious, but it must be said: Your Puppet manifests need to be kept secure. They will often contain secrets such as user password hashes and database credentials. Even if you can distribute those pieces out of band, the classes themselves are a road map to your system configuration and would be a security breach