Setting ACLs with the community version of Cfengine 3

As you might already know the community version of Cfengine 3 is not able to set ACLs the “Cfengine 3 way”. So when you are using the community edition of Cfengine 3 you have to find another way of doing it.. such as running shell commands out of the Cfengine 3 scripts.

I hope the guys from the Cfengine office won’t be angry with me when I show you a simple example of how setting ACLs with the community edition.

Writing the Cfengine 3 code for setting ACLs on multiple directories

body common control {
        version         => "1.0";
        inputs          => { "cfengine_stdlib.cf" };
        bundlesequence  => { "set_acls" };
}

bundle agent set_acls {
  vars:
        "shared_directories" slist      => { "/home/shared", "/home/archive" };

  files:
        "$(shared_directories)/."
          create        => "true";

  commands:
        "/usr/bin/setfacl -m u:backup:rwx,g:games:rwx ${shared_directories}"
          comment       => "Setting ACLs on the shared directories.";
}

Check the code snippet for errors. If no warning or error message occurs you should be safe running the script (make sure that /home is mounted with ACL support):

/var/cfengine/bin/cf-promises -f /etc/cfengine3/example12.cf
/var/cfengine/bin/cf-agent -f /etc/cfengine3/example12.cf

Now have a look at the two directories:


mintbox / # getfacl /home/shared/
getfacl: Removing leading '/' from absolute path names
# file: home/shared/
# owner: root
# group: root
user::rwx
user:backup:rwx
group::r-x
group:games:rwx
mask::rwx
other::r-x

mintbox / # getfacl /home/archive/
getfacl: Removing leading '/' from absolute path names
# file: home/archive/
# owner: root
# group: root
user::rwx
user:backup:rwx
group::r-x
group:games:rwx
mask::rwx
other::r-x

Wohoo! Cfengine 3 has just set the requested ACLs on the supplied directories.

Analyzing the Cfengine 3 code snippet
I guess you are already familiar with the meaning of the “body common control” section, so let’s jump to the interesting part:

bundle agent set_acls {
  vars:
        "shared_directories" slist      => { "/home/shared", "/home/archive" };

In the code excerpt above a promise of the type “vars” was created. It contains the promiser “shared_directories” which is a variable of the type “slist”. A slist can contain multiple values and we already make use of this feature here: Our varliable “shared_directories” encapsulates two directories which will be used later for our setfacl command.

Which leads us to the next part…

  files:
        "$(shared_directories)/."
          create        => "true";

…where the promise of the type “files” is equipped with the content of the variable “shared_directories” we defined above. As you might have already guessed, Cfengine 3 is iterating through slist until the end of the list is reached. So the statement we perform for the “files” promise will be applied on each item in the slist “shared_directories”. In this case Cfengine 3 makes sure that each directory exists before the ACLs are set.
Please note the “/.” part in this snippet – it is necessary for telling Cfengine 3 that we are dealing with directories and not files.

Now the most important part:

  commands:
        "/usr/bin/setfacl -m u:backup:rwx,g:games:rwx ${shared_directories}"
          comment       => "Setting ACLs on the shared directories.";
}

Here a new promise of the type “commands” is defined. It contains a setfacl command which will be issued on each entry of the “shared_list” variable.

Please note that “shared_directories” string is encapsulated in round brackets at the “files” promise. In the “commands” promise the shell command is enhanced by the “shared_directories” slist with curly braces.

Summary
In the code snippet above we first define a list of directories. Afterwards a file promise iterates through the single entries and makes sure that all directories exist before the command promise sets the requested ACLs.

I think that this is a great example for how variables can be used within multiple promises. Furthermore we are able to set ACLs although we do not have Cfengine 3 Nova installed.

As usual, you can download todays’s Cfengine 3 code snippet here.