UNIX Password, Roles & Node Management: Generator Details

Generator Details

The generator,, is one of the three core scripts in Password Management. It ensures the correct users are built into the work files on a per-node basis quickly and sanely.

Required Arguments

You must specify one of the following three arguments, followed by a list of names:

-R list
-R obtains the list of nodes to work on by role name. list in this context is one or more role names. You can specify as many roles as you want, or let list be empty which indicates all roles are to be built.


  # ./ -R AdminUnixUsers DevUsers HelpdeskAdmins

-E list
-E obtains the list of nodes to work on by environment. list in this context is one or more valid environments. You can specify as many environments as you want, or let list be empty which indicates all environments are to be built.


  # ./ -E prod/database prod/www ga/atlanta/facility01/misc/async

-N list
-N obtains the list of nodes to work on from list. list in this context is one ore more active inventory nodes. You can specify as many nodes as you want, or let list be empty which indicates all active nodes are to be built.


  # ./ -N

Additional Options

Turn on debugging. Extra information will be shown in the logs. Note that -d and -v do not imply each other.

Turn on verbose. Extra data will be shown to STDOUT. Note that -d and -v do not imply each other.

Show basic usage of and exit.

Clears out any work files that might be lingering around. This is useful if you want to start with a clear slate.

Audit the work. Use this option in conjunction with -R, -E, or -N as above. The steps to decide the users for the node will be shown, but no work will actually be done. This is quite useful for when you want to see why (or why not) users were installed onto a node or why they have the sudo permissions they do.

How users are chosen for installation onto nodes

Translate list into nodenames.
Regardless of what we want to specify to build - roles, environments, nodes - we always build by nodes. The first step is therefore to translate the arguments given into a node list.

If -R is specified, then the roles are translated into environments (via Covad::Pwman::Roles::pwman_get_envs_by_role).

Those environments (or the contents of -E, if it was specified) are translated into nodes (via Covad::Pwman::Search::pwman_find_nodes_by_tag).

Those nodes (or the contents of -N, if it was specified) becomes the nodelist we work from.

Loop through the nodes
Perform the following for each node needed:
Get the node's +ENV tags
This is parsed from the node's NCF (via Covad::Pwman::Parsers::pwman_parse_ncf).

Build the roles for the node
Roles are based on +ENV environment settings. Covad::Pwman::Roles::pwman_role_builder does all of this for us,

Each environment specified by the +ENV tag is examined in the following manner:

The outcome of this is a large-ish hashreference that looks a bit like this:

 $ref = {
        user_list => {
                usera => { undef, },
                userb => {
                        'role-name' => 'env-set-by',
                        'role-name' => 'env-set-by',
                group-user => {
                        '_data' => 'group-name',
                hack-user => {
                        '_data' => 'hack-user:x:200:200:hack-user:homedir:/bin/sh',
        is_exclusive => 'env',
        no_inherit => [

If a user exists in the user_list hash, then that user must be installed onto the node we're working on. If the user has any role attributes assigned to it (as in userb, above) then those attributes are interpreted to be role names whose sudo directives need to apply to this user. 'groups' users have a special attribute _data, which is a scalar containing the name of the group file from which they were referenced; this group file can be found in pwman.groups_dir. 'hack' users also have a special attribute _data, which is a scalar containing the actual passwd entry that should be used for this user.

Plugin Details uses two types of plugins: obtain and gen. They're detailed below, along with their phases.

obtain plugins take the task of translating usernames into user entries. They query an authorative source of user data, like NIS or LDAP or a RDBMS or something, and if the username is found, populate a hashref with that user's information.

Each obtain plugin must be able to retrieve the following information for a user given a username:

home directory
default shell

obtain_pre plugins are run, one at a time, in alphabetical order. Each plugin loops through the list of users and tries to resolve them into a user entry. If no entry can be found, the obtain_pre plugins skip it and go on to the next one. User entries are cached by username, so if a user is already resolved by a prior obtain_pre plugin it is not re-resolved, because it's already in the cache. This cache is nothing more elaborate than a hashref indexed by username. Entries that already exist (that have already been looked up and resolved) are by default skipped on sequential plugins. This behavior can be changed with the property. Changing this property to undef or unset will ignore a user entry's presence if the user exists in a seperate authentication source, and the last source with a matching username will be used. This property is enabled by default and should not be unset unless you're clear about what users are in which data source. Examples:

single-sourced obtainment
You have a single authorative source of users - let's say it's a RDMBS database of all users at your organization. You only need to have one active obtain plugin: obtain_post_rdbms. The ordering is very simple:
...resolves the username from the authorative datasource.

If the user isn't found, then the entry is not generated. has no effect here, because there is only one source to work through.

single-sourced obtainment with hacks
Say you've got a single authorative data source of users (let's say NIS). You also have a few accounts you don't want in NIS. This might be, say, root. Or you might have many different root accounts, broken down by environment (so you have one root password for your development systems, and one root password for your colocated servers in Georgia, and one root password for your customer-facing nodes, and so one). These are called hacks. They act as overrides to prevent the accidental removal of a critical user account. A hack entry looks like a line directly out of a functional /etc/passwd file:

and is located in the directory defined by the pwman.hacks_dir property. Hacks files can be called whatever you want. In our above example on different root passwords, you might call them root-dev, root-colo-ga, and root-dmz. The only thing that would be different between these three hacks files would be the crypt()ed password string in the second field.

In order to translate these 'hacks' entries into actual entries, we use a plugin, and we call it obtain_pre_hacks. The only thing this plugin does is split up the line read by the role parser, and re-assigns it to the user hashref in the proper format.

The plugin ordering looks now like this:

This takes the actual 'hacks' entry as read by the role builder, and builds the user entry accordingly.

Entries that are not hacks still must be resolved, and they must come from NIS. Hack entries are ignored (since they have already been resolved), so it doesn't matter what their password or account status (or even existance) in NIS is.

multi-sourced obtainment
Say you've got two seperate data sources: a corporate NIS system with all UNIX users, and a corporate ActiveDirectory system (with SFU installed) with all employees. Amazingly, you want to ensure that usernames that exist in AD are installed as well as users in NIS. For our example, we'll say that the NIS system is to be used for only system users (daemon, bin, lp, etc) and that actual employee user accounts must come from AD. The ordering of the obtain plugins should look like this, with set to true:
obtain_pre_hacks resolve direct 'hack' entries into user entries

obtain_pre_nis resolve the system users from NIS

...the final fallthrough, to get resolve employees to UNIX accounts

Note that if there are duplicate user accounts in NIS or AD, the account in NIS will win, because use_first_match is in effect. Changing use_first_match to unset will reverse that behavior - the duplicate account in AD will be used. However, this will also override any 'hack' entries and that might not be desirable.

You can stack as many obtain plugins as you feel you need to. (I honestly don't believe you should have more than 1 authorative source, but there may be times when this is unavoidable, and so the flexibility exists).

gen plugins work exclusively with the generated work files: .passwd, .shadow, .sudoers, and .digest. These work files are always prefixed with the nodename of the node that is currently being generated (eg, and are written to by the obtain plugins into the work directory specified by pwman.work_dir. gen_pre phase plugins have the opportunity to modify user data before it's written out. gen_format phase plugins actually write the data (you can think of them as a glorified conditional sprintf). gen_post phase plugins can perform checking on the generated work files after they've been written.

See Also

the Covad::Pwman manpage, the Covad::Pwman::Parsers manpage, the Covad::Pwman::Roles manpage


Jon Gilbert <>


$Id: pwman_docs_generator.pod,v 1.2 2005/10/20 08:46:34 jgilbertsjc Exp $ Logo