Managing the network with Cfengine
Big Engine

© Kit-Wai-Chan, Fotolia
Automate admin tasks with the powerful Cfengine framework.
Cfengine [1] is a flexible framework for automating system administration tasks. With Cfengine, you can manage one machine or a heterogeneous network. The first version of Cfengine was released more than 15 years ago by Mark Burgess, a professor at Oslo University. According to usage estimates, Cfengine has managed more than 1 million computers over the years. Version 3 of the Cfengine framework rolls out some new capabilities and does away with all the old historical layers. The developers have even retooled the language so that all elements are handled in a uniform way.
To show what is possible with Cfengine 3, I introduce various Cfengine components in a running example. To follow along, you need two networked Linux machines that I call PolicyServer and Client. The end goal is to have the client machine running a fully configured and managed Apache web server, with no manual configuration required, other than installing Cfengine.
The basic model I use will store and distribute all of the policy code centrally from a single server. Cfengine can be used many ways because it is very flexible, but this is a common design, and it serves many sys admins well. PolicyServer will hold and make available the central repository of Cfengine code, and the Client machine will receive the Apache configuration.
Design Principles
Cfengine is built around a number of design principles. In general, the language is descriptive rather than iterative: As much as is possible, you are attempting to describe the "what" of the system rather than the "how." In practice, this approach usually means that Cfengine actions are idempotent; that is, applying the same function twice will result in the same result. This characteristic is important because Cfengine continually monitors the state of your nodes and, depending on how you write your policy, corrects any divergences.
Another principle that Cfengine adheres to is the "pull" architecture, which means that clients request new policy code from a server. This behavior is in contrast to the "push" system, which requires a central node to connect periodically to all clients to configure them. The use of a pull architecture allows you to configure a machine that is down or not yet built because any changes will be picked up automatically when the machine comes onto the network. Cfengine has the facilities to do a push if you really need it, but even these features are built around an underlying pull mechanism. The pull principle also has important implications for the autonomy of the configured node: If the Cfengine server crashes, or if it is unavailable to the client for some reason, the client can continue to use its cached policy until the next time it can connect successfully.
Downloading and Building
First, install Cfengine on both the client and server. Packages are available for many popular Linux distributions, or you can build the tool from source code.
CFengine 3 has very few build-time dependencies and even fewer run-time dependencies (only OpenSSL libcrypto and Berkeley DB libdb). Although not strictly necessary, the Perl-compatible regular expression library (libpcre) also contributes significantly to Cfengine.
If you are building Cfengine from source, first obtain the latest Cfengine 2 and 3 tarballs from the project website [2]. Also, you need Flex, Bison, and Make to compile Cfengine, as well as the static library libcfengine from Cfengine 2. Once you have the dependencies in place, first build Cfengine 2, then Cfengine 3 (you can use the same procedure for both):
./configure make sudo make install # Or use su
By default, files are installed to /usr/local, but you can change this by adding the --prefix=/some/other/path argument to configure, although you need to make sure that the Cfengine build process can find libcfengine. All the binaries are installed in sbin under the relevant prefix (by default, /usr/local/sbin). The next version of Cfengine, 3.0.1, due for release later this year, will not require libcfengine, so this is just an intermediate measure.
Hello, World
With the software safely installed on the server and client, you are ready for a first look at Cfengine in action. A simple "Hello, World" example will demonstrate a working Cfengine 3 program First, run cf-key with no arguments. This command creates some dot files in your home directory and generates a keypair (which you won't need right away, but it is necessary for remote copies).
This "Hello, World" program will be written for cf-agent, which is the program that does the bulk of the configuration work in Cfengine. cf-agent monitors system state and applies corrective action when necessary. Much as perl and sh binaries are interpreters for Perl and the Bourne shell, respectively, you can think of cf-agent as a command interpreter for the Cfengine language. By default, as an unprivileged user, cf-agent reads and executes code in ~/.cfagent/input/promises.cf. So, with your favorite editor, create that file and enter:
body common control { bundlesequence => { "hello" }; } bundle agent hello { reports: linux:: # This is a comment "Hello, world."; }
White space does not matter in the Cfengine language, so you can indent this code as you see fit. With no arguments at the shell, go ahead and run it with cf-agent. It doesn't matter which of the two machines you run this on because you have installed the software on both.
As you can see, the two main entities present in the code are a body named control and a bundle named hello. Bundles are the primary statement aggregation construct in Cfengine (in the same way that a function is the primary construct of C, although bundles are not functions in a mathematical sense). Bodies are groupings of parameters. Both the body and the bundle specify which component of Cfengine they are to be consumed by; in the case of the control body, the consumer is common, a special keyword meaning the Cfengine suite as a whole, and in the case, of the hello bundle, the consumer is agent, which refers to the Cfengine binary cf-agent.
The name of the bundle, hello, is referenced in bundlesequence, which is a special directive that tells cf-agent what code to execute, and in what order. The special token reports is a promise type – one of many kinds of statements that you can make about how you want your system to function.
Bundles are made up of promises. In this case, as you can probably guess, reports is a way to generate output. The next token, linux, followed by a double colon, is a class. Later, I will explain classes in more detail, but for now, just know that code following this class will only execute on a Linux node.
Config Files
Now that cf-agent is up and running, the next step is to configure the cf-serverd daemon on PolicyServer so that the client can download an updated policy. cf-serverd functions as a secure file server that provides external access to the cf-agent running on a specific node.
On PolicyServer, designate a directory as the canonical policy repository. Here, I use /srv/cf-serverd, but you can select whatever location fits best in your environment. (This should not be /var/cfengine. PolicyServer will probably also be a client; that is, the server will update its policy and evaluate it with cf-agent.)
Within your central repository, create an inputs directory (I am mirroring the contents of the working directory /var/cfengine, but this is the only subdirectory that I care about for now). In /srv/cf-serverd, you need to create four files. The first step is to create cf-serverd.cf (Listing 1). This file will control which machines can connect to the server and which files they will have access to, and it also will have some cf-serverd--specific configuration variables.
Listing 1
cf-serverd.cf
Now, create update.cf, which will contain code that the client runs to synchronize its local policy to the central policy in the repository (Listing 2).
Listing 2
update.cf
Listing 2 introduces some Cfengine variables. Unlike past versions, variables are now all dynamic types (before, they were all just strings). Other variable types include slist (a list of strings), real (a number with decimal precision), and int (an integer). Variables are substituted (as in the shell) with ${variablename}.
Listing 3 shows the file promises.cf. The final configuration file is failsafe.cf, which simply contains the following:
body common control { bundlesequence => { "update" }; inputs => { "update.cf" }; }
Listing 3
promises.cf
The special promises.cf and failsafe.cf files are basically just dispatches specifying what other code cf-agent should execute. The names for cf-serverd.cf and update.cf I made up myself (you can call them whatever you want, but I suggest names that are similarly suggestive). The hard-coded entry point for cf-agent is promises.cf. All of the code you want to run needs to be either in this file or referenced by this file. Strictly speaking, failsafe.cf is not required, but if promises.cf does not parse, cf-agent will fall back to failsafe.cf, so it is a good idea to make sure that a very simple, known, good failsafe.cf is available.
failsafe.cf merely attempts to update the policy files from the server. Because I designed failsafe.cf to get only the most recent policy, it also functions as a bootstrap procedure for the Cfengine client. Thus, to configure the client initially, you only need to copy failsafe.cf (and any files it references) onto the client.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Find SysAdmin Jobs
News
-
MNT Seeks Financial Backing for New Seven-Inch Linux Laptop
MNT Pocket Reform is a tiny laptop that is modular, upgradable, recyclable, reusable, and ships with Debian Linux.
-
Ubuntu Flatpak Remix Adds Flatpak Support Preinstalled
If you're looking for a version of Ubuntu that includes Flatpak support out of the box, there's one clear option.
-
Gnome 44 Release Candidate Now Available
The Gnome 44 release candidate has officially arrived and adds a few changes into the mix.
-
Flathub Vying to Become the Standard Linux App Store
If the Flathub team has any say in the matter, their product will become the default tool for installing Linux apps in 2023.
-
Debian 12 to Ship with KDE Plasma 5.27
The Debian development team has shifted to the latest version of KDE for their testing branch.
-
Planet Computers Launches ARM-based Linux Desktop PCs
The firm that originally released a line of mobile keyboards has taken a different direction and has developed a new line of out-of-the-box mini Linux desktop computers.
-
Ubuntu No Longer Shipping with Flatpak
In a move that probably won’t come as a shock to many, Ubuntu and all of its official spins will no longer ship with Flatpak installed.
-
openSUSE Leap 15.5 Beta Now Available
The final version of the Leap 15 series of openSUSE is available for beta testing and offers only new software versions.
-
Linux Kernel 6.2 Released with New Hardware Support
Find out what's new in the most recent release from Linus Torvalds and the Linux kernel team.
-
Kubuntu Focus Team Releases New Mini Desktop
The team behind Kubuntu Focus has released a new NX GEN 2 mini desktop PC powered by Linux.