Local user management

There are many approaches to managing users. You can edit system files like /etc/passwd directly, you can use commands on some systems like useradd. However the easiest, and preferred way is to use CFEngine's native users type promise.

Ensuring a local user has a specific password

This example shows ensuring that the local users root is managed if there is a specific password hash defined.

body file control
{
  # This policy uses parts of the standard library.
  inputs => { "$(sys.libdir)/users.cf" };
}

bundle agent main
{
  vars:
      # This is the hashed password for 'vagrant'
    debian_8::
      "root_hash"
        string => "$6$1nRTeNoE$DpBSe.eDsuZaME0EydXBEf.DAwuzpSoIJhkhiIAPgRqVKlmI55EONfvjZorkxNQvK2VFfMm9txx93r2bma/4h/";

  users:
    linux::
      "root"
        policy => "present",
        password => hashed_password( $(root_hash) ),
        if => isvariable("root_hash");
}

This policy can be found in /var/cfengine/share/doc/examples/local_user_password.cf and downloaded directly from github.

root@debian-jessie:/core/examples# grep root /etc/shadow
root:!:16791:0:99999:7:::
root@debian-jessie:/core/examples# cf-agent -KIf ./local_user_password.cf
    info: User promise repaired
root@debian-jessie:/core/examples# grep root /etc/shadow
root:$6$1nRTeNoE$DpBSe.eDsuZaME0EydXBEf.DAwuzpSoIJhkhiIAPgRqVKlmI55EONfvjZorkxNQvK2VFfMm9txx93r2bma/4h/:16791:0:99999:7:::

Ensuring local users are present

This example shows ensuring that the local users jack and jill are present on all linux systems using the native users type promise.

body file control
{
  # This policy uses parts of the standard library.
  inputs => { "$(sys.libdir)/files.cf" };
}

bundle agent main
{
  vars:
    "users" slist => { "jack", "jill" };
    "skel" string => "/etc/skel";

  users:
    linux::
      "$(users)"
        home_dir => "/home/$(users)",
        policy => "present",
        home_bundle => home_skel( $(users), $(skel) );
}

bundle agent home_skel(user, skel)
{
  files:
    "/home/$(user)/."
      create => "true",
      copy_from => seed_cp( $(skel) ),
      depth_search => recurse( "inf" );
}

This policy can be found in /var/cfengine/share/doc/examples/local_users_present.cf and downloaded directly from github.

Lets check the environment to see that the users do not currently exist.

root@debian-jessie:/CFEngine/core/examples# egrep "jack|jill" /etc/passwd
root@debian-jessie:/core/examples# ls -al /home/{jack,jill}
ls: cannot access /home/jack: No such file or directory
ls: cannot access /home/jill: No such file or directory

Let's run the policy and inspect the state of the system afterwards.

root@debian-jessie:/core/examples# cf-agent -KIf ./users_present.cf
    info: Created directory '/home/jack/.'
    info: Copying from 'localhost:/etc/skel/.bashrc'
    info: Copying from 'localhost:/etc/skel/.profile'
    info: Copying from 'localhost:/etc/skel/.bash_logout'
    info: User promise repaired
    info: Created directory '/home/jill/.'
    info: Copying from 'localhost:/etc/skel/.bashrc'
    info: Copying from 'localhost:/etc/skel/.profile'
    info: Copying from 'localhost:/etc/skel/.bash_logout'
    info: User promise repaired
root@debian-jessie:/core/examples# egrep "jack|jill" /etc/passwd
jack:x:1001:1001::/home/jack:/bin/sh
jill:x:1002:1002::/home/jill:/bin/sh
root@debian-jessie:/core/examples# ls -al /home/{jack,jill}
/home/jack:
total 20
drwxr-xr-x 2 root root 4096 Dec 22 16:37 .
drwxr-xr-x 5 root root 4096 Dec 22 16:37 ..
-rw-r--r-- 1 root root  220 Dec 22 16:37 .bash_logout
-rw-r--r-- 1 root root 3515 Dec 22 16:37 .bashrc
-rw-r--r-- 1 root root  675 Dec 22 16:37 .profile

/home/jill:
total 20
drwxr-xr-x 2 root root 4096 Dec 22 16:37 .
drwxr-xr-x 5 root root 4096 Dec 22 16:37 ..
-rw-r--r-- 1 root root  220 Dec 22 16:37 .bash_logout
-rw-r--r-- 1 root root 3515 Dec 22 16:37 .bashrc
-rw-r--r-- 1 root root  675 Dec 22 16:37 .profile

Ensuring local users are locked

This example shows ensuring that the local users jack and jill are locked if they are present on linux systems using the native users type promise.

bundle agent main
{
  vars:
    "users" slist => { "jack", "jill" };

  users:
    linux::
      "$(users)"
        policy => "locked";
}

This policy can be found in /var/cfengine/share/doc/examples/local_users_locked.cf and downloaded directly from github.

This output shows the state of the /etc/shadow file before running the example policy:

root@debian-jessie:/core/examples# egrep "jack|jill" /etc/shadow
jack:x:16791:0:99999:7:::
jill:x:16791:0:99999:7:::
root@debian-jessie:/core/examples# cf-agent -KIf ./local_users_locked.cf
    info: User promise repaired
    info: User promise repaired
root@debian-jessie:/core/examples# egrep "jack|jill" /etc/shadow
jack:!x:16791:0:99999:7::1:
jill:!x:16791:0:99999:7::1:

Ensuring local users are absent

This example shows ensuring that the local users jack and jill are absent on linux systems using the native users type promise.

bundle agent main
{
  vars:
    "users" slist => { "jack", "jill" };

  users:
    linux::
      "$(users)"
        policy => "absent";
}

This policy can be found in /var/cfengine/share/doc/examples/local_users_absent.cf and downloaded directly from github.

Before activating the example policy, lets inspect the current state of the system.

root@debian-jessie:/core/examples# egrep "jack|jill" /etc/passwd
jack:x:1001:1001::/home/jack:/bin/sh
jill:x:1002:1002::/home/jill:/bin/sh
root@debian-jessie:/core/examples# ls -al /home/{jack,jill}
/home/jack:
total 20
drwxr-xr-x 2 root root 4096 Dec 22 16:37 .
drwxr-xr-x 5 root root 4096 Dec 22 16:37 ..
-rw-r--r-- 1 root root  220 Dec 22 16:37 .bash_logout
-rw-r--r-- 1 root root 3515 Dec 22 16:37 .bashrc
-rw-r--r-- 1 root root  675 Dec 22 16:37 .profile

/home/jill:
total 20
drwxr-xr-x 2 root root 4096 Dec 22 16:37 .
drwxr-xr-x 5 root root 4096 Dec 22 16:37 ..
-rw-r--r-- 1 root root  220 Dec 22 16:37 .bash_logout
-rw-r--r-- 1 root root 3515 Dec 22 16:37 .bashrc
-rw-r--r-- 1 root root  675 Dec 22 16:37 .profile

From the above output we can see that the local users jack and jill are present, and that they both have home directories.

Now lets activate the example policy and insepect the result.

root@debian-jessie:/core/examples# cf-agent -KIf ./local_users_absent.cf
    info: User promise repaired
    info: User promise repaired
root@debian-jessie:/core/examples# egrep "jack|jill" /etc/passwd
root@debian-jessie:/core/examples# ls -al /home/{jack,jill}
/home/jack:
total 20
drwxr-xr-x 2 root root 4096 Dec 22 16:37 .
drwxr-xr-x 5 root root 4096 Dec 22 16:37 ..
-rw-r--r-- 1 root root  220 Dec 22 16:37 .bash_logout
-rw-r--r-- 1 root root 3515 Dec 22 16:37 .bashrc
-rw-r--r-- 1 root root  675 Dec 22 16:37 .profile

/home/jill:
total 20
drwxr-xr-x 2 root root 4096 Dec 22 16:37 .
drwxr-xr-x 5 root root 4096 Dec 22 16:37 ..
-rw-r--r-- 1 root root  220 Dec 22 16:37 .bash_logout
-rw-r--r-- 1 root root 3515 Dec 22 16:37 .bashrc
-rw-r--r-- 1 root root  675 Dec 22 16:37 .profile

From the above output we can see that the local users jack and jill were removed from the system as desired. Note that their home directories remain, and if we wanted them to be purged we would have to have a separate promise to perform that cleanup.

Local group management

CFEngine does not currently have a native groups type promise so you will need to either edit the necessary files using files type promises, or arrange for the proper commands to be run in order to create or delete groups.

Ensure a local group is present

Add lines to the password file, and users to group if they are not already there.

This example uses the native operating system commands to show ensuring that a group is present.

body file control
{
  # This policy uses parts of the standard library.
  inputs => { "$(sys.libdir)/paths.cf" };
}

bundle agent main
{
  classes:
      "group_cfengineers_absent"
        not => groupexists("cfengineers");

  commands:
    linux.group_cfengineers_absent::
      "$(paths.groupadd)"
        args => "cfengineers";
}

This policy can be found in /var/cfengine/share/doc/examples/local_group_present.cf and downloaded directly from github.

First lets inspect the current state of the system.

root@debian-jessie:/core/examples# grep cfengineers /etc/group

Now lets activate the example policy and check the resulting state of the system.

root@debian-jessie:/core/examples# cf-agent -KIf ./local_group_present.cf
    info: Executing 'no timeout' ... '/usr/sbin/groupadd cfengineers'
    info: Completed execution of '/usr/sbin/groupadd cfengineers'
root@debian-jessie:/CFEngine/core2.git/examples# grep cfengineers /etc/group
cfengineers:x:1001:

Ensureing a user is a member of a secondary group

This example shows using the native users type promise to ensure that a user is a member of a particular group.

bundle agent main
{
  users:
    linux::
      "jill"
        policy => "present",
        groups_secondary => { "cfengineers" };
}

This policy can be found in /var/cfengine/share/doc/examples/local_user_secondary_group_member.cf and downloaded directly from github.

First lets inspect the current state of the system

root@debian-jessie:/core/examples# grep jill /etc/passwd
root@debian-jessie:/core/examples# grep jill /etc/group

Now lets actiavte the example policy and inspect the resulting state.

root@debian-jessie:/core/examples# cf-agent -KIf ./local_user_secondary_group_member.cf
    info: User promise repaired
root@debian-jessie:/core/examples# grep jill /etc/passwd
jill:x:1001:1002::/home/jill:/bin/sh
root@debian-jessie:/core/examples# grep jill /etc/group
cfengineers:x:1001:jill
jill:x:1002:

It's important to remember we made no promise about the presence of the cfengineers group in the above example. We can see what would happen when the cfengineers group was not present.

root@debian-jessie:/core/examples# grep cfengineers /etc/group
root@debian-jessie:/core/examples# cf-agent -KIf ./local_user_secondary_group_member.cf
usermod: group 'cfengineers' does not exist
   error: Command returned error while modifying user 'jill'. (Command line: '/usr/sbin/usermod -G "cfengineers" jill')
    info: User promise not kept

Get a list of users

body common control
{
      bundlesequence  => { test };
}

bundle agent test
{
  vars:
      "allusers" slist => getusers("zenoss,mysql,at","12,0");
  reports:
    linux::
      "Found user $(allusers)";
}