How to serve policy from a local git server

Posted by Craig Comstock
January 19, 2021

Several months ago I started the practice of using CFEngine Enterprise and its Mission Portal UI on a daily basis to manage the connected devices in my home. To start, I brought up an old desktop machine, cfengine-hub, to use as my hub and downloaded Enterprise, which is free for use up to 25 hosts. The next step in using best practices is to deploy policy from a version control repository. I use a local git server named git-server-zero instead of GitHub or GitLab as I like to be independent of the cloud when possible due to privacy and environmental concerns. raspberry pi zero device plugged in via usb cable to back of
wifi router, low power, local control, private
cloud I will use the Mission Portal Version Control Repository settings section to setup this repo as the source of policy for cfengine-hub.

Configuring the git server

You can choose whether to use a username/password combination for your git URL or a passphrase-less key. I am choosing the latter.

root@cfengine-hub:~# ssh-keygen -o
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): /root/.ssh/hub_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/hub_rsa.
Your public key has been saved in /root/.ssh/
The key fingerprint is:
SHA256:[a-z] root@cfengine-hub

In case the account on the hub gets compromised I want to prevent writes from this user. I created a new account called hubgituser on git-server-zero and ensured that /srv/git on git-server-zero has only read permissions for this user. Afterwards, I added the public key generated on cfengine-hub to the hubgituser’s authorized keys.

root@git-server-zero:/home/hubgituser/.ssh# cat /root/.ssh/ >> authorized_keys

Now check that the connection works between my hub and the git server, while ensuring that I have no saved keys in my ssh-agent.

root@cfengine-hub:~# eval $(ssh-agent -s)
Agent pid 6334
root@cfengine-hub:~# ssh-add -D
All identities removed.
root@cfengine-hub:~# ssh-add .ssh/hub_rsa
Identity added: .ssh/hub_rsa (root@cfengine-hub)
root@cfengine-hub:~# ssh hubgituser@git-server-zero
Linux git-server-zero 4.9.59+ #1047 Sun Oct 29 11:47:10 GMT 2017 armv6l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Jul 15 16:14:33 2020 from
hubgituser@git-server-zero:~ $

Ok! Confirmed!

Configuring Mission Portal Version Control Repository

Now to Mission Portal and entering the details as mentioned in Configuring Upstream VCS. Login and navigate to Settings and then Version control repository: Enter details, select the private key file and click Apply.

Initial Sync of Policy

If you use Mission Portal to configure VCS settings then the initial sync will occur automatically after a little while. To test things out right away or if you configured manually then run the following:

root@cfengine-hub:/opt/cfengine/dc-scripts# cf-agent -KIf --define cfengine_internal_masterfiles_update
info: Executing 'no timeout' ... '/var/cfengine/httpd/htdocs/api/dc-scripts/'
info: Command related to promiser '/var/cfengine/httpd/htdocs/api/dc-scripts/' returned code defined as promise kept 0
info: Completed execution of '/var/cfengine/httpd/htdocs/api/dc-scripts/'

Any troubles? Run the script in debug mode with -D or also with bash -x. For example, once I tried these steps and got a password prompt which I should not:

root@cfengine-hub:~# bash -x /var/cfengine/httpd/htdocs/api/dc-scripts/ -D
Option Deploy Dir: /var/cfengine/masterfiles
Option Params File: /opt/cfengine/dc-scripts/
Cloning into bare repository '/opt/cfengine/git_git_server_zero__srv_git_masterfiles'...
hubgituser@git-server-zero's password:

Test Policy Deployment

Now let’s try making a change to policy and see if it goes through. I push a commit to add a hashed password for a user promise in a file called personal.yaml:

craig@laptop:~/src/masterfiles$ git push
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 1.02 KiB | 1.02 MiB/s, done.
Total 5 (delta 3), reused 0 (delta 0)
To git-server-zero:/srv/git/masterfiles.git
265bb3b..292ee56 master -> master

And then ran the agent asking for updates again:

info: Copied file '/var/cfengine/masterfiles/services/autorun/personal.yaml' to '/var/cfengine/inputs/services/autorun/personal.yaml.cfnew' (mode '600')
info: Backed up '/var/cfengine/inputs/services/autorun/personal.yaml' as 'var/cfengine/inputs/services/autorun/personal.yaml.cfsaved'
info: Moved '/var/cfengine/inputs/services/autorun/personal.yaml.cfnew' to '/var/cfengine/inputs/services/autorun/personal.yaml'
info: Updated '/var/cfengine/inputs/services/autorun/personal.yaml' from source '/var/cfengine/masterfiles/services/autorun/personal.yaml' on 'localhost'
info: Purged '/var/cfengine/inputs/services/autorun/personal.yaml.cfsaved' copy dest directory
info: Purged '/var/cfengine/inputs/cf_promises_release_id.cfsaved' copy dest directory
info: files promise '/var/cfengine/inputs' repaired

The change is present in /var/cfengine/inputs!

# foobarbaz demo password
- hashed_password: $6$hckmJCcp/vOWNjeH$s3FsHKoBuoVpKJW0XhuojyDK5yp1FqixtX5RE.kP8cwWpphmbIpeJ5lvsaKjDPp9I5FBRpBnXOHfrHW2nIM4k1

So let’s see if it takes effect on cfengine-hub by trying to login as agent. I can login with foobarbaz! Success!

$ hostname
$ whoami

Now I can make changes to policy, push the changes to my git server and the change will roll out to all my agents/hosts automatically.