Do you maintain multiple policy sets? Do you leverage policy written by others? Ever wished for an easier way to upgrade your policy framework? cfbs
can help to improve all of these cases.
cfbs
is a command line tool that aims to help simplify managing a policy set and working with CFEngine Build, a website for finding and sharing modules. A policy set usually - but not always - builds on top of some base, like the Masterfiles Policy Framework (MPF). Custom policy is added on top, and you’re off to the races. When a new version of CFEngine is released, the best practice is to upgrade the MPF (assuming you are using it) as the first step in the upgrade process. If you have not modified the MPF, re-integrating the custom policy on top of the new MPF is a relatively straightforward process. If you have modified the MPF it’s 1) something you need to know that you did and 2) a process of managing your diffs against the newer version of the MPF.
For the case of a non-modified MPF upgrade, cfbs
makes this very straightforward using cfbs upgrade
. For the case of a modified MPF, cfbs
provides an ordered series of steps that can better help to keep track of and maintain modifications.
Let’s take a look at the process of initializing a cfbs
project with the MPF and adding some custom policy files. After installing cfbs
from the Python Package Index (pip), running cfbs init
is all you need to do. You’ll be prompted with a few questions (defaults can be accepted by pressing ENTER
when prompted).
cfbs init
Please enter the name of this CFEngine Build project [Example project]:
Please enter the description of this CFEngine Build project [Example description]:
Do you want cfbs to initialize a git repository and make commits to it? [YES/y/no/n]
Please enter user name to use for git commits [Nick Anderson]:
Please enter user email to use for git commits [nick.anderson@northern.tech]:
Initialized empty Git repository in /home/nickanderson/northern.tech/cfengine/src/website/test/.git/
{
"name": "Example project",
"description": "Example description",
"type": "policy-set",
"git": true,
"build": []
}
The default commit message is 'Initialized a new CFEngine Build project' - edit it? [yes/y/NO/n]
Committing using git:
[main (root-commit) 64d9389] Initialized a new CFEngine Build project
1 file changed, 7 insertions(+)
create mode 100644 cfbs.json
Initialized an empty project called 'Example project' in 'cfbs.json'
Do you wish to build on top of the default policy set, masterfiles? (Recommended) [YES/y/no/n]:
Added module: masterfiles
The default commit message is 'Added module 'masterfiles'' - edit it? [yes/y/NO/n]
Committing using git:
[main da7fbe0] Added module 'masterfiles'
1 file changed, 16 insertions(+), 1 deletion(-)
At this point, we still don’t have a working policy set, because we need to build it. For that, we use the cfbs build
command.
cfbs build
Modules:
001 masterfiles @ 80374429aa8d9f1d5afe952727ae5659caf5e9ef (Downloaded)
Steps:
001 masterfiles : run 'EXPLICIT_VERSION=3.21.4 EXPLICIT_RELEASE=1 ./prepare.sh -y'
001 masterfiles : copy './' 'masterfiles/'
Generating tarball...
Build complete, ready to deploy 🐿
-> Directory: out/masterfiles
-> Tarball: out/masterfiles.tgz
To install on this machine: sudo cfbs install
To deploy on remote hub(s): cf-remote deploy
By now, our project directory contains the project file (cfbs.json
), and the out
directory where our completed build can be found in out/masterfiles
contains a built policy set. If you’re familiar with the MPF, it should look familiar.
ls
cfbs.json out
ls out/masterfiles | columns -c 50
cfbs.json cfe_internal
controls inventory
lib modules
promises.cf services
standalone_self_upgrade.cf templates
update.cf
With a working base policy set, we can add some custom policy files to the project. Then, we can run cfbs add
to integrate it into the project.
cat > hello-world.cf << EOF
bundle agent hello_world
#@ Emit hello world
{
reports:
"Hello World!";
}
EOF
cfbs add ./hello-world.cf
Which bundle should be evaluated (added to bundle sequence)?
1. ./hello-world.cf:hello_world (default)
2. (None)
[1/2]: 1
Added module: ./hello-world.cf
The default commit message is 'Added module './hello-world.cf'' - edit it? [yes/y/NO/n]
Committing using git:
[main bc51c13] Added module './hello-world.cf'
2 files changed, 17 insertions(+)
create mode 100644 hello-world.cf
As you can see from the above output, cfbs
prompted us to ask if the bundle should be added to the bundle sequence, since we want that bundle to run, not just use it from other policy we selected the appropriate option. We selected the default commit message and the module was added. We can now see that reflected in the cfbs status
command output.
cfbs status
Name: Example project
Description: Example description
File: cfbs.json
Modules:
001 masterfiles @ 80374429aa8d9f1d5afe952727ae5659caf5e9ef (Downloaded)
002 ./hello-world.cf @ local (Copied)
Now, when we build the project using the cfbs build
command, we will find the hello-world.cf
policy file in the result.
find out/masterfiles -name "hello-world.cf"
out/masterfiles/services/cfbs/hello-world.cf
Where that file lands in the resulting policy set is governed by the project file (cfbs.json
), let’s have a look.
{
"name": "Example project",
"description": "Example description",
"type": "policy-set",
"git": true,
"build": [
{
"name": "masterfiles",
"description": "Official CFEngine Masterfiles Policy Framework (MPF).",
"tags": ["supported", "base"],
"repo": "https://github.com/cfengine/masterfiles",
"by": "https://github.com/cfengine",
"version": "3.21.4",
"commit": "80374429aa8d9f1d5afe952727ae5659caf5e9ef",
"added_by": "cfbs add",
"steps": [
"run EXPLICIT_VERSION=3.21.4 EXPLICIT_RELEASE=1 ./prepare.sh -y",
"copy ./ ./"
]
},
{
"name": "./hello-world.cf",
"description": "Local policy file added using cfbs command line",
"tags": ["local"],
"added_by": "cfbs add",
"steps": [
"copy ./hello-world.cf services/cfbs/hello-world.cf",
"policy_files services/cfbs/hello-world.cf",
"bundles hello_world"
]
}
]
}
Note the policy_files
build step for ./hello-world.cf
targets services/cfbs/hello-world.cf
. You can adjust the build steps to get the desired layout.
If you manage just a single policy set, the benefits may not be immediately apparent, but that’s not all cfbs
does. It’s also a great way to find and integrate policy written by others.
For example, there is a lynis
module providing policy to install and run CISOfy Lynis as well as a companion compliance-report-lynis
module that provides a report in the CFEngine Enterprises Mission Portal showing a breakdown of all the detected findings. We can find these modules using the command cfbs search lynis
:
cfbs search lynis
compliance-report-lynis - Compliance report with Lynis checks.
lynis - Automates the installation, running, and reporting of CISOfy's lynis system audits.
And install them with cfbs add
:
cfbs add compliance-report-lynis
Added module: compliance-report-imports (Dependency of compliance-report-lynis)
Added module: lynis (Dependency of compliance-report-lynis)
Added module: autorun (Dependency of compliance-report-imports)
Added module: compliance-report-lynis
The default commit message is:
Added 4 modules
- Added module 'lynis'
- Added module 'compliance-report-imports'
- Added module 'compliance-report-lynis'
- Added module 'autorun'
Edit it? [yes/y/NO/n]
Committing using git:
[main 066636b] Added 4 modules
1 file changed, 54 insertions(+)
CFEngine Enterprise users can leverage a subset of features in Mission Portal via the Build app. In Mission Portal, you can set up and switch between multiple projects, each connected to a specific branch in a git repository.
You can search for, install, and upgrade modules from the CFEngine Build Index and remove (but not add) modules added from other sources, with a single click.
Check out our blog post on migrating to cfbs.
Happy Friday! 🎉