We here at CFEngine have seen the collaboration possibilities with Ansible for a long time. See our many ansible related blog posts including previously where I discussed our promise-type-ansible module which enables you to run ansible playbooks from CFEngine policy.
You might ask why you would want to do such a thing?
We came up with one possible answer: what happens if you block ssh access to a host?
Now you can certainly setup ansible-pull but that requires configuring credentials and access to a repository.
If you are already or interested in using both CFEngine and Ansible together then this episode will interest you.
Notes
Previously we used the promise-type-ansible to patch our systems. This time we will leverage CFEngine’s Host specific data to easily manage whether to “lockdown” certain hosts or not.
Copy/Paste/Modify
Let’s take the previous blogs framework of a main.cf and site.yml and adjust it to our purpose: locking down sshd with the firewalld ansible module.
Previously I had worked with a Debian system but this time I will use Rocky Linux so we must adjust the packages needed on each client:
vars:
redhat::
"ansible_package" string => "ansible-core";
debian::
"ansible_package" string => "ansible";
packages:
"${ansible_package}"
classes => if_ok("ansible_package_installed");I wanted to drive the lock or not lock state from host specific data.
By default the namespace for classes entered into the UI is data so a bit of mangling is needed to convert the data:lockdown class into something simple to use in policy:
vars:
# here we have to do some mangling to support CMDB since those classes are prefixed with the namespace "data:"
"ansible_role_classes" slist => classesmatching(".*", "ansible_role");
"ansible_roles_normalized[${ansible_role_classes}]" string => string_replace("${ansible_role_classes}", "data:", "");
"ansible_roles" slist => getvalues("ansible_roles_normalized");When paired with some policy to render the inventory:
files:
"$(localhost_inventory)"
create => "true",
content => string_mustache("[all:vars]
ansible_connection=local
{{#-top-}}[{{.}}]
localhost
{{/-top-}}",
"ansible_roles"
),
classes => if_ok("localhost_inventory_created");We will arrive at inventory something like this for our rocky VM:
[root@localhost vagrant]# cat /var/cfengine/state/localhost_inventory.ini
[all:vars]
ansible_connection=local
[managed]
localhost
[lockdown]
localhostFinally, lets craft our site.yml to implement our lockdown (or not).
- hosts: lockdown
roles:
- lockdown
- hosts: all:!lockdown
roles:
- notlockdownSo here I am leveraging the default role of all and the expression !lockdown to implement enabling sshd when the lockdown class is NOT defined.
And for our roles we have:
# roles/lockdown/tasks/main.yml
---
- name: lockdown
firewalld:
service: 'ssh'
state: disabled
permanent: true
immediate: true
offline: trueand
# roles/notlockdown/tasks/main.yml
---
- name: notlockdown
firewalld:
service: 'ssh'
state: enabled
permanent: true
immediate: true
offline: trueNotice the difference is state: enabled or state: disabled.
I certainly could have used various other ways to set the state either way but this way seemed a nice balance of simple and working.
To Lock or not to Lock
So now we configure our hub to use this build project, bootstrap our rocky-10 VM and sit back and enjoy the show.
watch -n 5 'ssh vagrant@192.168.1.117 date'At first all is well and we can SSH:
Mon Jan 26 05:51:34 PM UTC 2026But then… we set the class in host info page to lockdown
After a few minutes (CFEngineers can take their time usually) we see our rocky VM is unavailable.
ssh: connect to host 192.168.1.117 port 22: Host is unreachableAnd because we are using CFEngine which defaults to a “pull” architecture for updating policy even if sshd is locked down we can update the host info page and open up sshd again. :)
To achieve that we can either remove the lockdown class or rename it to something easy to remember like lockdownnope.
QED
That’s it!
We showed how you can leverage the benefits of both CFEngine (default pull architecture) and Ansible (lots of modules supporting many things to manage).
Thanks for your time.
Be well!
At the end of every webinar, we stop the recording for a nice and relaxed, off-the-record chat with attendees. Join the next webinar to not miss this discussion.
Links
- Connect w/ Cody, Craig, Herman, or Nick
- All Episodes