CFEngine’s trust model is based on the secure exchange of keys. This exchange of keys between client and hub, can either happen manually or automatically. Usually this step is automated as a dead-simple “bootstrap” procedure:
cf-agent --bootstrap $HUB_IP
It is presumed that during this first key exchange, the network is trusted, and no attacker will hijack the connection. After “bootstrapping” is complete, the node can be deployed in the open internet, and all connections are considered secure. However there are cases where initial CFEngine deployment is happening over an insecure network, for example the Internet. In such cases we already have a secure channel to the clients, usually ssh, and we use this channel to manually establish trust from the hub to the clients and vice-versa.
Manual trust establishment
This procedure concerns CFEngine version 3.6 or earlier. While this fully manual procedure should always work, from version 3.7 onwards there is a simpler semi-automatic procedure for establishing trust.
On the policy hub
We must change the policy we’re distributing to fully locked-down
settings. So after we have set-up our hub (using the standard procedure
of cf-agent --bootstrap $HUB_IP
) we take care of the following:
-
Cf-serverd must never accept a connection from a client presenting an untrusted key. So in
body server control
we set:trustkeysfrom => {};
-
Since we will be manually bootstrapping the clients, we need to distribute a proper
failsafe.cf
policy. (NOTE:failsafe.cf
is a file auto-generated in theinputs
directory when we runcf-agent --bootstrap
). In order to do that, we copy hub’sfailsafe.cf
tomasterfiles
:cp /var/cfengine/inputs/failsafe.cf /var/cfengine/masterfiles/
We’ll edit that copy in
masterfiles
in the next step. -
All
copy_from
files promises must never connect to an untrusted server, which means that the following line should not be found anywhere:trustkey => "true"
All occurences of
trustkey
inmasterfiles
directory should be changed to “false”, or be removed (since it defaults to false anyway). It is certain thatfailsafe.cf
that we copied in the previous step will contain such occurences that should be changed. (Those occurences are the reason that automatic bootstrapping requires a trusted network). -
The previous changes in
masterfiles
need to be properly propagated to theinputs
directory. The automated way to do that is to run the update.cf policy:cf-agent -f update.cf
-
Get the hub’s key fingerprint, we’ll need it later:
HUB_KEY=`cf-key -p /var/cfengine/ppkeys/localhost.pub`
On each client we deploy
We should not follow the automatic method, i.e. the cf-agent --bootstrap
command. We will perform a manual bootstrap.
-
Generate a private/public key pair by running
cf-key
. -
Get the client’s key fingerprint, we’ll need it later:
CLIENT_KEY=`cf-key -p /var/cfengine/ppkeys/localhost.pub`
-
Write the policy hub’s IP address to
policy_server.dat
:echo $HUB_IP > /var/cfengine/policy_server.dat
-
Manually copy the modified
failsafe.cf
from hub’smasterfiles
directory into client’sinputs
directory. You should do it in a secure manner, for example usingscp
with properly trusted fingerprint of the remote host. -
NOTE: At this step, you can try running the failsafe policy. Because trust between the two hosts has not been established, you will get a failure, which is totally expected and means everything is correct and secure:
# cf-agent -f failsafe.cf error: TRUST FAILED, server presented untrusted key: MD5=cc27570b8b831192d9f20b54d07dd80b error: No suitable server responded to hail error: TRUST FAILED, server presented untrusted key: MD5=cc27570b8b831192d9f20b54d07dd80b error: No suitable server responded to hail [ ... ]
-
Put the hub’s key into the client’s trusted keys:
scp $HUB_IP:/var/cfengine/ppkeys/localhost.pub /var/cfengine/ppkeys/root-${HUB_KEY}.pub
Final steps
-
Put the client’s key into the hub’s trusted keys. So on the hub, run:
scp $CLIENT_IP:/var/cfengine/ppkeys/localhost.pub /var/cfengine/ppkeys/root-${CLIENT_KEY}.pub
-
If you now run the failsafe policy on each and every client, it should succeed:
cf-agent -f failsafe.cf
Congratulations, you have performed a fully manual bootstrap procedure
for your clients! P.S. In a future blog post, I will present how you
can use the new (as of 3.6) shortcut
and admit_keys
attributes to
easily and securely transfer files or policy, restricting access only to
specific clients.