6.2. CFengine

CFengine is then used both to distribute files from the central server to the other servers and to carry out some monitoring tasks and some system checkups such as filesystem permissions scheme, old files and directories cleaning up and creation of new files and directories (policies implementation).

CFengine offers a homogeneous array of functions other softwares already provide (rsync, netsaint/nagios, ecc.), thus allowing for many different ways to configure the system. Concerning the configuration described in this howto, we have chosen to implement two of them:

distribution

the copying of whole files in the /etc directory, both common to all of the servers and specific to only one of them. This is the main way the static content gets spread out and works through a specific procol of the cfservd daemon, including its own authentication mechanism (that let us avoid the installation of ssh keys or rsh enabling).

editing

the modification of configuration files. CFengine includes some very powerful directives allowing to modify the content of text files: it is possible for example to comment specific lines in a file depending on a series of regexp or to include specific paragraphs in some files.

These two ways can be combined as preferred, making it possible to schedule specific event for each server on a set of files centrally distributed. The very same CFengine configuration files are managed in the same way. We will see below how to do it, with some practical examples.

CFengine is configured through some files including a series of rules that are applied depending on the dynamic "classes" active at a specific moment. The rules are divided into sections that are applied one after another and with different syntaxes: for example, some sections only check whether a file exists or not (and its checksum), some other only clean directories from old files, or modify text files, etc. Classes are defined for every other attribute specific to the servers: the name, the operative system, the day and time.

To study more in-depth CFegning configuration, we recommend you to read these documents:

6.2.1. CFengine configuration structure

The configuration of the different servers in the network is managed centrally, by modifying a series of files in a single location (the central repository of the configuration). Each server keeps locally a copy of the most updated version of this repository, so that it knows "what to do" even in case it has problems connecting to the main server. This also allows for a quick change of the server working as main repository.

In this example, the configuration files copies are installed under the /configfiles directory. The servers are called test1, test2 and test3.

If you take a look at the /configfiles directory you can see it is structured like this:


        \--+ configfiles
           |--+ cfengine
           |  \--- inputs
           |  \--- ppkeys
           |--+ ring0
           |  |--+ common
           |  |  \--- ... 
           |  |--+ test1
           |  |  \--- ... 
           |  |--+ test2
           |  |  \--- ... 
           |  \--+ test3
           |     \--- ... 
           \--+ ring1
              \--+ common
                 \--- ... 

      

These directories include files that are copied (recursively if necessary) in the /etc directory of the system: the common directory contains the files that are common to all servers, while each testN directory contains files specific to the namesake server. If a file is present both in the generic directory and in the specific directory, the latter is the one that gets copied. The division into a ring0 and a ring1 is due to the fact that between one layer and the the other there are more differences than similarities.

How configuration data are generated for the various servers

The cfengine directory contains CFengine configuration and is handled separately. Files in cfengine/inputs check completely the way CFengine works and have to be modified by hand (on the main repository!) so as to change server specific parametres. Through these files you can also set the policies on the various systems, in order to make some routine tasks automatic.

6.2.2. An example on how to configure a service

While it is fairly easy to understand how to configure a service that only has static configuration files (i.e. copying its config files in the /configfiles/ringN/common/something or /configfiles/ringN/HOST/something directories directly), it could be useful to show how to configure CFengine for a service needing a specific configuration for each host.

6.2.2.1. Tinc CFenging configuration

We have already discussed about the Tinc software used to implement the VPNs corresponding to the differnet trust rings. The basic configuration has already been discussed in Capitolo 2.2: VPN>.

After this basic configuration, we need to do a bunch of modifications to these basic configuration files, depending on the host, a task best suited to CFengine.

The CFengine configuration we have implemented handles the copying of the /configfiles/ringN/common/tinc file on every single box (check the comment in the cf.sysconfig file below). In order to manage the further modifications needed, we have created a cf.tinc.ringN file in the /configfiles/cfengine/inputs directory for each VPN ring. One of these is presented below commented as needed:


control:

    vpn_ring0_name = ( ring0 )
    vpn_ring0_cf_dir = ( /etc/tinc/$(vpn_ring0_name) )

	

This is the "control" section handling the global variables specific to the whole configuration of the service.


    
links:

        $(vpn_ring0_cf_dir)/rsa_key.priv ->! $(vpn_ring0_cf_dir)/hosts/$(host).priv
	

The "links" section allows us to specify the symbolic link we want to create. Here we tell CFengine to create a different link to the private key (/etc/tinc/ring0/rsa_key.priv) for each single box.


files:

        $(vpn_ring0_cf_dir)/hosts/*.priv mode=400 o=root action=fixplain
        $(vpn_ring0_cf_dir)/tinc-up mode=755 o=root action=fixall
        $(vpn_ring0_cf_dir)/tinc-down mode=755 o=root action=fixall
	

The "files" section includes various checks that are to be done on files. For example here we check that the two specified files have the appropriate permissions and that they are owned by the specified user (action=fixall implies that they are to be fixed if needed). We also tell CFengine to ensure that the private keys can be read only by root.


editfiles:

        { /etc/tinc/nets.boot
          AutoCreate
          AppendIfNoSuchLine "$(vpn_name)" 
        }
	

The "editfiles" section tells CFengine to edit the text files generally containing the system configuration. In this case the /etc/tinc/nets.boot file in Debian systems includes a list of the VPN to be activated at boottime: the command AppendIfNoSuchLine adds a line including our VPN if it is not already in the file. If the file does not exist it will be created (AutoCreate).

  

        { $(vpn_ring0_cf_dir)/tinc.conf
          CommentLinesContaining "ConnectTo = $(host)"
          DeleteLinesStarting "Name ="
          AppendIfNoSuchLine "Name = $(host)"
        }
	

The commands to handle text files usually have very verbose names. The previous series of command ensures that the configuration file includes only one line with the server name and that in each server the VPN service won't try to connect to itself.


        { $(vpn_ring0_cf_dir)/tinc-up
          BeginGroupIfNoSuchLine "# added by cfengine; do not edit"
            Append "# added by cfengine; do not edit"
            Append "/sbin/ifconfig $(dollar)INTERFACE $(vpn_ring0_ip) 
                    netmask 255.255.0.0 up"
          EndGroup
        }
	

Here we create the file activating the IP interface of the VPN, once the connection has been established (the last Append is a single line broken up for the need of this document layout). The editing of the file is "protected", that is to say the file won't be edited twice through the checking of comments.


   !ring0_gw::
        { $(vpn_ring0_cf_dir)/tinc-up
          AppendIfNoSuchLine "/sbin/ip route add 172.17.0.0/16 via 172.16.1.1"
        }
	

This last part of the file adds a route for the ring1 VPN to every server apart from the one working as a gateway between the two rings (ring0_gw), which does not need this route.

6.2.3. Current CFengine configuration

The files in the /configfiles/cfengine/inputs directory completely control the behaviour of the CFengine software. They are fully modular, so that each file includes all the directive concerning a particular subsystem (hoping to gather all of the directives specifically connected in a single file).

We have already suggested how these files are divided into "sections" (specifying different types of actions to be carried out) and how they are managed by a system of dynamic "classes" (different on each server). It will now be useful to check how the various modules work and what they are needed for:

cfagent.conf

This is the main cfagent configuration file. It includes the definition of some crucial variables (as the domain and the admin email address) and all the following configuration files (in the import section). A crucial parametre included in this file is the sequence by which the various sections are executed:

        actionsequence =
            (
            resolve
            copy
            directories
            files
            links
            editfiles
            shellcommands
            tidy
            disable
            )
	

cf.cfengine

This file mantains a local copy of the /configfiles directory. It also installs correctly all the public keys needed by CFengine.

cf.linux

This file includes the specific configuration for Linux system and it is executed only if the host is included in the "linux" class. It does not contain anything particularly cunning, but it is rich in examples.

cf.sysconfig

This file implements the copying of configuration files in /etc, starting from the common directory (/configfiles/common) and reaching each specific directory (/configfiles/NAME). Files are copied from the main repository keeping their permissions. To understand how easy it is to program such a task in cfengine, it can make sense to check the part of the file handling it.


control:

    any::

        rconfigdir = ( "$(configdir)/$(ring)" )
        configpath = ( "$(rconfigdir)/common:$(rconfigdir)/$(host)" )

copy:

    any::

        $(configpath)           dest=/etc/
                                recurse=inf
                                timestamps=preserve
                                backup=timestamp

	    

The lines specify that CFengine has to copy all the files (thanks to the recurse=inf directive) of the two directories specified by the configpath variable in the /etc directory, keeping permissions and timestamps, as well as backup modified or deleted files in the /var/lib/cfengine2/repository directory. The inform=true line allows for notification to the administrators for each modification CFengine does to a file.

CFengine also keeps a checksum database (Tripwire style) of the binary files on the system.

cf.tinc.ring0, cf.tinc.ring1

These files detail Tinc configuration (see paragraph 2.2.3).

cf.apache, cf.mysql, cf.postfix, cf.bind, ...

These files detail the configuration of other services. It is useful to know that if you want to understand when to restart a service, you should keep a temporary directory where to store the /configfiles content, change it and compare it to the /etc files: if the files have been modified one can then define a class to restart the service.

6.2.4. Automatic updates

In order to configure a system for remote configurations update (and for transferring files from the main repository to the local ones), we need to enable the cfservd and cfexecd software on every server. The host authentication is handled by an autonomous RSA key system that we need to check when it is first setup (see Appendice A>).

Another thing to check is the access list configuration in the /configfile/cfengine/inputs/cfservd.conf file.

CFengine works using a pull model. It is the clients that request the main server to update their configurations. cfexecd allows the automatic execution of cfagent at regular times (it basically replaces crontab). By default the time span is one hour.

In case we want to force an instant configuration update on all servers (push), for example because we have just changed a configuration file and we want it to be spread out, we can use the cfrun command that tells each server to run cfagent immediately.

6.2.5. Security

As a sidenote to the aspects detailed in the CFengine Tutorial, we can see how CFengine is subject to the huge problems of implementing an appropriate trust system and depends heavily on the control of the key distribution for security. This is in any case the best possible solution we can develop, without losing flexibility.

In any case, CFengine behaviour is non-destructive and heavily monitored (any --verbose option will produce an almost obnoxious amount of monitoring information), and it generally generates an increase in the overall security of the system, since it forces a common and verified policy for all servers participating in the network.