Tutorial

How To Install and Use CFEngine Community Edition on Ubuntu 20.04

Published on February 2, 2023
    How To Install and Use CFEngine Community Edition on Ubuntu 20.04

    The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program.

    Introduction

    CFEngine is a scalable configuration management tool for your IT infrastructure. CFEngine allows you to use the same tool and policy language to manage everything in your infrastructure: Linux, Mac, Windows, BSD, Solaris, IBM AIX, HP-UX, and others. You install an agent (written in C) on each machine you want to manage. CFEngine supports many platforms and is useful for resource-constrained environments such as large infrastructures or small Internet of Things (IoT) devices.

    In this tutorial, you will install CFEngine Community Edition 3.21 on Ubuntu 20.04, write sample policy files, and automate your policy runs.

    Prerequisites

    To complete this tutorial, you will need:

    • One Ubuntu 20.04 server with a non-root sudo user. To set this up, follow our guide Initial Server Setup with Ubuntu 20.04.
    • A way of running shell commands (including sudo), such as ssh.
    • A text editor. This tutorial uses nano.

    Step 1 — Installing CFEngine

    In this step, you will install CFEngine using apt and the package repositories. The main advantage of using apt for installation is that you can use apt to update CFEngine later.

    There are other ways to install CFEngine for different use cases:

    If you use one of these alternatives to install CFEngine Community or Enterprise, skip to Step 2 — Starting CFEngine after installing or go to Step 3 — Creating Your First Policy after bootstrapping.

    Adding CFEngine’s Public Key

    To install CFEngine using apt, you need apt to be able to access the repository. The first step is to add the CFEngine public GPG key to the apt key collection so that apt can trust packages from the repository.

    Download the key with this command:

    1. wget https://cfengine.com/pub/gpg.key -O ~/cfengine-gpg.key

    Then use apt-key to add it:

    1. sudo apt-key add ~/cfengine-gpg.key

    Adding CFEngine’s Package Repositories

    Next, you need to add the CFEngine repository as a source to apt with the following command:

    1. sudo sh -c "echo 'deb https://cfengine-package-repos.s3.amazonaws.com/pub/apt/packages stable main' > /etc/apt/sources.list.d/cfengine-community.list"

    This command adds the CFEngine repository to a file in the apt sources list directory.

    Without the sh argument and -c option, this command would get a Permission denied error because the redirection of the output is not performed by sudo. To address this issue, you use sh to run a shell with sudo and give the command to it by using the -c option.

    Now, run the update command so that apt can see the new packages from the CFEngine repository:

    1. sudo apt update

    Installing the Package with apt

    With the CFEngine package now registered with apt, you can use apt to install CFEngine:

    1. sudo apt install cfengine-community

    If you receive a warning about the package being built on a different (older) operating system, you can ignore it. The package still works.

    Verifying the Installation

    When the installation is complete, you will see the components that make up CFEngine in /var/cfengine/bin. Out of all of these, the agent (cf-agent) is the one you will interact with most of the time. This binary evaluates the policy you write, makes changes to the system, and records information about what it did. By default, the agent will wake up every five minutes, fetch new policy if necessary, and enforce it.

    Use the following command to check that the agent was installed successfully:

    1. sudo cf-agent --version

    The version number will print to the screen:

    Output
    CFEngine Core 3.21.0

    Note: At the time of this writing, the current version is 3.21.0. Your version number may differ.

    If the cf-agent command doesn’t work, check that /var/cfengine/bin/ is in your PATH variable and that cf-agent exists in that directory. You can also check the installation log file in /var/log/CFEngine-Install.log for some hints about what went wrong.

    Step 2 — Starting CFEngine

    To start CFEngine, you need to bootstrap the agent. Use the following command to do that:

    1. sudo cf-agent --bootstrap 127.0.0.1

    This command tells the agent to start the CFEngine components and to fetch policy from this machine, 127.0.0.1.

    In the future, you will want multiple machines to fetch policy from the same server. In that case, 127.0.0.1 will be replaced by the IP address of that server. However, for getting started and learning about CFEngine, you’ll use one machine and 127.0.0.1 in this tutorial.

    You now have CFEngine installed and running on your server. The next step is to start writing policy.

    Step 3 — Creating Your First Policy

    To automate a system administration task using CFEngine, you create a policy file, which is written in CFEngine’s own Domain Specific Language (DSL). To learn more about the policy language, such as what it can do and how it’s structured, see the CFEngine reference documentation.

    Policy files differ from scripts in that they are declarative: you describe the desired state for the system, and CFEngine will check if that’s already the case and will make changes only if necessary. Some examples of things you might want to express in policy are:

    • Ensure the user sammy exists.
    • Keep the curl package updated (and installed).
    • Ensure telnet is not installed.
    • Render a script file from a template and some data.
    • Stop a process, like httpd, if it’s running.
    • Enforce strict permissions on the /usr/bin folder.

    In all of these cases, you would write policy and put it in the correct place on your server (/var/cfengine/masterfiles). Then CFEngine will:

    • Automate distribution of the policy file by transferring it to your other bootstrapped hosts.
    • Enforce your requirements regularly (running the policy every five minutes by default). For example, if someone deleted the user sammy, or changed the permissions on /usr/bin, these changes would be restored automatically within five minutes.
    • Only make changes, such as writing to a file, if it’s necessary (that is, if the state is not already as desired).
    • Handle many platform differences for you (for example, the commands used to install packages, create users, and other operations vary depending on the operating system).

    In this step, you’ll create a “Hello World” policy to output the text “Hello!” to the terminal.

    Use nano or your favorite text editor to create a new file, ~/hello_world.cf:

    1. nano ~/hello_world.cf

    Within the file, add the following:

    ~/hello_world.cf
    bundle agent main
    {
      reports:
        "Hello!";
    }
    

    CFEngine policy is declarative and is not evaluated from top to bottom.

    In CFEngine policy language, each statement you make about what you are managing is called a promise. Promises are organized into bundles. Bundles are useful because they allow you to choose which parts of the policy are run (and which parts aren’t) and what order they run in.

    In this policy, reports is a promise type, responsible for printing the message to the terminal. The agent keyword, after bundle, signifies that this bundle is for the cf-agent component. The name of the bundle is main. The bundle sequence specifies the bundles to evaluate. By default, it includes the main bundle, so you don’t need to change it.

    Save and close the file. Using nano, press CTRL+X to exit, Y to save, and ENTER to confirm the filename and close the file.

    Since CFEngine typically runs as the root user, it tries to prevent unauthorized changes to the system by requiring strict permissions on policy files. (Allowing other users to edit the policy CFEngine runs would allow them to make changes to the system as a privileged user). You can use chmod to edit the file permissions and thus prevent other users from editing this file:

    1. chmod 600 hello_world.cf

    To run the policy, use the following command:

    1. sudo cf-agent ~/hello_world.cf

    The CFEngine agent finds your bundle (because of the main name), looks at the promises inside of it, and evaluates them. When evaluating promises, the agent checks whether it needs to make changes to the system. In this case, there was just one promise, and the agent determined that it needs to print a message to the terminal to satisfy it.

    You’ll receive the following output:

    Output
    R: Hello!

    By default, CFEngine keeps track of what has been done and skips promises evaluated recently (within the last minute). If you try to rerun the policy, it won’t print anything. You can disable this locking with the --no-lock command line option:

    1. sudo cf-agent --no-lock hello_world.cf

    You’ve created and run your first policy. Although it is useful to be able to output to the console, this policy doesn’t actually change the system. You’ll do that in the next step.

    Step 4 — Writing Policy to Manage the Contents of a Text File

    In the previous step, you created a policy to print a message to the terminal. However, a more realistic use case is ensuring that a file with some specific content exists on all the hosts in your infrastructure. In this step, you will write a policy that creates a text file (/tmp/my_file.txt) with the contents Hello, CFEngine!.

    Open a new policy file, ~/file_management.cf:

    1. nano ~/file_management.cf

    Add the following content:

    ~/file_management.cf
    bundle agent manage_my_file
    {
      files:
        "/tmp/my_file.txt"
          content => "Hello, CFEngine!$(const.n)";
    }
    

    The bundle’s name is manage_my_file instead of main. As you write more policy, you should give each bundle a unique and descriptive name. There can only be one main bundle.

    Because this policy manages files, the promise type is files. The content attribute is used for files promises, stating what the content of that file should be. The last part of the string, $(const.n), expands a special variable const.n, which results in a new line at the end of the file.

    Save and close the file.

    As before, set the strict permissions on the policy file:

    1. chmod 600 file_management.cf

    Now run the file with some additional command line options:

    1. sudo cf-agent --no-lock --info ~/file_management.cf --bundle manage_my_file

    Specifying the bundle with --bundle manage_my_file is necessary since there is no longer a main bundle.

    The --info option makes CFEngine print informational messages about the changes it’s making to the system.

    The output from this command will contain information about these changes:

    Output
    info: Using command line specified bundlesequence info: Created file '/tmp/my_file.txt', mode 0600 info: Updated file '/tmp/my_file.txt' with content 'Hello, CFEngine!

    This output indicates that CFEngine created a text file called my_file.txt in the /tmp directory with the contents Hello, CFEngine!.

    If you run the same command again, the messages about creating and updating the file will no longer display. CFEngine recognizes that the contents of the file are already correct, and it doesn’t make any changes.

    Note: --no-lock and --info are commonly used together when writing and testing policy files. To save some typing, there are shortcuts available; -KI is equivalent to --no-lock --info.

    Now that you have a working system policy, you may want to run it without your supervision. You’ll do that in the next step.

    Step 5 — Automating Policy Runs

    You probably don’t want to run policy manually from the command line all the time. CFEngine includes automation features to handle that.

    In this step, you will automate policy runs by putting your policy files in the expected location and updating a JSON file. The JSON file periodically tells the CFEngine component what to do in the background without your having to run commands explicitly and manually from the command line.

    Use the following command to copy the policy file you created in the previous step to the recommended location:

    1. sudo cp file_management.cf /var/cfengine/masterfiles/services/

    All CFEngine policy is inside /var/cfengine/masterfiles/. This includes policy you didn’t write that came with CFEngine. To keep your custom policy separate from the default policy, it is recommended to put your policy files inside the services/ subdirectory.

    When the CFEngine agents fetch new policy files, they copy them from this directory on the hub. Even if you are just using one machine, the agent still works in the same way: it will look for files in /var/cfengine/masterfiles and copy them to /var/cfengine/inputs. For new users, it’s best to use these paths. Customizing the paths or putting policy files in other locations requires more work since you’d have to ensure that permissions as well as copying and finding the files work correctly.

    Next, create an augments JSON file to specify where the policy file is and what bundle should be run:

    1. sudo nano /var/cfengine/masterfiles/def.json

    Add the following content to that file:

    /var/cfengine/masterfiles/def.json
    {
      "inputs": [ "services/file_management.cf" ],
      "vars": {
        "control_common_bundlesequence_end": [ "manage_my_file" ]
      }
    }
    

    There are two steps necessary to make your policy run regularly (every five minutes): make sure the agent finds and reads the file and ensure the agent runs your bundle.

    The inputs key in def.json tells the agent what policy files to read. In this case, the agent will read the policy you created in the last step, file_management.cf.

    The vars key can be used to define variables. The control_common_bundlesequence_end variable is used in the default policy, so any bundle names you put there will be added to the end of the bundlesequence and evaluated after all the default bundles. Together, these two pieces of information mean that cf-agent knows what policy files to read and which bundles within them to evaluate without having to specify these things from the command line.

    At this point, you are editing policy inside /var/cfengine/masterfiles/ and automation takes care of the rest. More specifically, cf-agent periodically wakes up and fetches the new policy files you’ve written. The agent will read and evaluate the policy, enforcing all the promises and making changes to the machines as necessary (such as editing files and creating users).

    Based on the policy you’ve written in this tutorial, every time the agent runs, it will ensure /tmp/my_file.txt exists with the content you specified in the policy file.

    Save and close the file.

    To confirm that the automation is working, delete the text file created when you first ran the file management policy:

    1. sudo rm /tmp/my_file.txt

    After five minutes, you can confirm whether CFEngine re-created my_file.txt in the background:

    1. cat /tmp/my_file.txt
    Output
    Hello, CFEngine.

    Alternatively, you can also force the file creation to happen sooner:

    1. sudo rm /tmp/my_file.txt ; sudo cf-agent -Kf update.cf ; sudo cf-agent -KI

    The rm command deletes the file so CFEngine will see that changes are necessary.

    The first cf-agent command updates the policy file, copying it from /var/cfengine/masterfiles to /var/cfengine/inputs.

    The last cf-agent command enforces your policy, which makes it look for the /tmp/my_file.txt file, and create and edit it if necessary.

    In this case, you are running the agent immediately after deleting the file, so cf-agent should print that it created the file. (The chances of an agent run happening in the background between these commands are slim to none.)

    Note: The command sudo cf-agent -Kf update.cf ; sudo cf-agent -KI is similar to the command CFEngine runs by default every five minutes. So, running this command should have the same result as waiting five minutes if CFEngine is working correctly. The first agent run updates the policy and the second one evaluates the policy and makes changes to the system.

    In this step, you’ve automated your first system administration task with CFEngine. While this example focused on creating and editing a file, the process for another task would be identical: write the policy, put it in the correct directory, and update the def.json file accordingly.

    Conclusion

    You’ve now installed and started CFEngine on a single server. You wrote your first policy and set it up to run automatically.

    As a next step, check out the CFEngine official documentation, such as this tutorial on managing files: Create, Modify, and Delete files.

    If you have any questions or need help, feel free to post in the Q&A section of CFEngine’s GitHub Discussions.

    Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

    Learn more about our products

    About the authors

    Default avatar

    Technical Editor


    Still looking for an answer?

    Ask a questionSearch for more help

    Was this helpful?
     
    Leave a comment
    

    This textbox defaults to using Markdown to format your answer.

    You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

    Try DigitalOcean for free

    Click below to sign up and get $200 of credit to try our products over 60 days!

    Sign up

    Join the Tech Talk
    Success! Thank you! Please check your email for further details.

    Please complete your information!

    Featured on Community

    Get our biweekly newsletter

    Sign up for Infrastructure as a Newsletter.

    Hollie's Hub for Good

    Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

    Become a contributor

    Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

    Welcome to the developer cloud

    DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

    Learn more