Automate Linux with cfengine

By Carla Schroder | Sep 13, 2005 | Print this Page
http://www.enterprisenetworkingplanet.com/netsysm/article.php/3548416/Automate-Linux-with-cfengine.htm

Last week we installed, configured and tested a cfengine server. Today we'll set up clients and use cfengine to monitor key system files, run unattended and push out changes to the client hosts.

We need one more server configuration file, /var/lib/cfengine2/inputs/cfrun.hosts. This file does not need to be copied to clients, so don't put a copy in /masterfiles/input. This file is simple: your domain name, and a list of clients that the server will push out changes to. Remember to double-check your filepaths, this example is on Debian:

##################
#/var/lib/cfengine2/inputs/cfrun.hosts
##################
domain = carla.com
# Clients
stinkpad.carla.com

Client Configuration
Now it's time to install cfengine on a client machine and configure it. The easy way is to copy cfagent.conf, cfservd.conf and update.conf from the server into /var/lib/cfengine2/inputs on the client.

Syntax Checker
You can run the syntax checker anytime:

# cfagent -p

If there are no errors it exits silently. Add the -v switch to generate voluminous output.

Now fire up the cfengine daemons on server and client. On Debian:

# /etc/init.d/cfengine2 start

On Red Hat/Fedora/et al:

# /etc/init.d/cfservd start

Distributing Keys
cfengine works on a two-way trust: Keys must be exchanged from the client to the server, and the server to the client. If you installed from RPMs or apt-get, the installer created two encryption keys and stored them in /var/[whatever]/ppkeys. Exchanging keys is as simple as connecting manually from the server:

# cfrun stinkpad.carla.com
cfrun(0): .......... [ Hailing stinkpad.carla.com ] ..........
Connecting to server stinkpad.carla.com to port 0 with options
Loaded /var/lib/cfengine2/ppkeys/root-192.168.1.10.pub
Connect to stinkpad.carla.com = 192.168.1.10 on port cfengine
Updating last-seen time for stinkpad.carla.com
Loaded /var/lib/cfengine2/ppkeys/root-192.168.1.10.pub
...............................................................
cfrun:windbag.carla.com: Strong authentication of server=stinkpad.carla.com connection confirmed

You may also automate the key exchange by adding this line to the control section of cfservd.conf:

TrustKeysFrom = ( 192.168.1.0/24 )

Or connect manually from the client:

# cfagent -qv

You may need to run cfrun several times to make everything happen, because it always checks update.conf on the server first and downloads any changed or new files. Then it executes cfagent.conf, which may also need a couple of runs, depending on what actions are taken. Running cfrun again looks something like this:

# cfrun stinkpad.carla.com
cfrun(0): .......... [ Hailing stinkpad.carla.com ] ..........

cfengine::
    Update of image /var/lib/cfengine2/cfagent.conf from master
    /var/lib/cfengine2/masterfiles/inputs/cfagent.conf on windbag.carla.com
cfengine:stinkpad: Object /tmp/testfile had permission 777, changed it to 600

And Now, the Fun Stuff
Ok, that was a bit of work to get going. Now it's all gravy. All you have to do is edit files in the /masterfiles/input directory on the server, and all changes will be copied out to the clients and the server as well. cfagent.conf is where the action is; let's add some rules to it to make our lives easier. These rules monitor file permissions and ownership on files, and change them back if someone messes with them. This is very handy for things like key system files, and for Web files, which won't display if they are not world-readable:

files:
    /etc/passwd mode=644 owner=root group=root action=fixall
    /etc/shadow mode=640 owner=root group=shadow action=fixall
    /var/lib/http mode=644 r=4 owner=httpadmin group=httpadmins action=fixall

Use the files: for monitoring existing files and directories. The directories keyword is for creating new directories. The r=4 directive means "recurse no more than four levels in the /var/lib/http directory". r=0 means "no recursion", so it pays attention only to the top-level directory, and r=inf means keep going until you hit bottom. Specifying a number is a simple safety precaution.

You can run shell commands, like this one for updating the locate database:

shellcommands:
    "/usr/bin/updatedb"

You can create and enforce symlinks, using the syntax linkname -> object to link to:

links:
    /var/cfengine -> /var/lib/cfengine
    /var/http/homefiles -> /var/lib/http/public/home

Automating cfengine
It's fun to push the button and watch things happen. It's also fun to set up cfengine to run unattended, and just take care of business. cfengine will check processes that need to be running, and start them if they're not:

processes:
    "cfservd" restart "/var/cfengine/bin/cfservd"
    "cfexecd" restart "/var/cfengine/bin/cfexecd"

cfengine can be scheduled with either cfexecd or cron. Add these lines to cfagent.conf to have cfexecd wake up cfagent five minutes past every hour:

control:
    schedule = ( Min00_05 )

You can also run it from /etc/crontab. This entry checks all /etc/crontab files to make sure this entry exists, and if it doesn't, it adds it. It runs cfexecd hourly, on the hour:

editfiles:
    { /etc/crontab
    AppendIfNoSuchLine
    "0 * * * * root /usr/local/sbin/cfexecd -F"
}

The -F switch tells cfexecd to run in non-daemon mode. cfengine will mail reports to admins, this goes under the control: section:

smtpserver = ( mail.carla.com )
sysadm = ( carla@carla.com )

Classes
Classes are what makes cfengine work across mixed environments. You can configure actions for groups of hosts based on the operating system. Note the double colons indicating the class name:

copy:
    # Copy OS specific files
solaris::
    /var/patchdir dest=$(workdir)/inputs/ server=solaris.carla.com
hp-ux::
    /var/patchdir dest=$(workdir)/inputs/ server=hpux.carla.com

See the cfengine Reference for a complete listing of built-in classes. The reserved operating system classes are ultrix, sun4, sun3, hpux, hpux10, aix, solaris, osf, irix4, irix, irix64, sco, freebsd, netbsd, openbsd, bsd4_3, newsos, solarisx86, aos, nextstep, bsdos, linux, debian, cray, unix_sv, GnU, and NT.

You may also define your own classes. A common method is to test for the presence of a certain file, then assume that the host belongs to a certain class based on that:

classes:
    # Assume systems with httpd.conf are web servers
    web_server = (
        '/usr/bin/test -f /etc/httpd/httpd.conf'

Then you can have cfengine monitor only the web_server class for correct file permissions in the /etc/httpd/ directory:

files:
   web_server::
        /etc/httpd/ owner=httpadmin group=httpadmins mode=0644 action=fixall recurse=4

You don't want to hassle with a DNS server? Hosts files were good enough for my granny, and by dang they're good enough for me. Use cfengine to keep all hosts files on your network synchronized. This example completely rewrites /etc/hosts every time it is changed, which suits us nervous types just fine:

editfiles:
  any::
    { /etc/hosts
     EmptyEntireFilePlease
    Append "127.0.0.1 localhost.localdomain localhost"
    Append "192.168.1.1 windbag.carla.com windbag"
    Append "192.168.1.1 stinkpad.carla.com stinkpad"
}

That's just a snippet of the power of cfengine. Getting up and running is the hard part; now you can study the Tutorial and Reference manual and learn all kinds of creative ways to automate your network chores.

Resources