This example shows a simple ssh key distribution implementation.

The policy was designed to work with the services_autorun feature in the Masterfiles Policy Framework. The services_autorun feature can be enabled from the augments_file. If you do not have a def.json in the root of your masterfiles directory simply create it with the following content.

{
  "classes": {
               "services_autorun": [ "any" ]
             }
}

In the following example we will manage the authorized_keys file for bob, frank, and kelly.

For each listed user the ssh_key_distribution bundle is activated if the user exists on the system. Once activated the ssh_key_distribution bundle ensures that proper permissions are set on the users .ssh directory (home is assumed to be in /home/username) and ensures that the users .ssh/authorized_keys is a copy of the users authorized_keys file as found on the server as defined in the ssh_key_info bundle.

Let's assume we collected all users' public keys into a single directory on the server and that users exist on the clients (and have corresponding home directory).

Note: special variable $(sys.policy_hub) contains the hostname of the policy server.

To deploy this policy simply place it in the services/autorun directory of your masterfiles.

body common control
{
    bundlesequence => { "autorun_ssh_key_distribution" };
    inputs => { "$(sys.libdir)/stdlib.cf" };
}

bundle common ssh_key_info
{
  meta:
    "description"
      string => "This bundle defines common ssh key information, like which
                 directory and server keys should be sourced from.";

  vars:
    "key_server" string => "$(sys.policy_hub)";

    # We set the path to the repo in a common bundle so that we can reference
    # the same path when defining access rules and when copying files.
    # This directory is expected to contain one file for each users authorized
    # keys, named for the username. For example: /srv/ssh_authorized_keys/kelly
    "repo_path" string => "/srv/ssh_authorized_keys";
}

bundle agent autorun_ssh_key_distribution
{
  meta:
    # Here we simply tag the bundle for use with the `services_autorun`
    # feature.
    "tags" slist => { "autorun" };

  vars:
    "users" slist => { "bob", "frank", "kelly" };

  methods:
    "Distribute SSH Keys"
      usebundle => ssh_key_distribution( $(users) ),
      if => userexists( $(users) ),
      comment => "It's important that we make sure each of these users
                  ssh_authorized_keys file has the correct content and
                  permissions so that they can successfully log in, if
                  the user exists on the executing agents host.";
}

bundle agent ssh_key_distribution(users)
{
  meta:
    "description"
      string => "Ensure that specified users are able to log in using their ssh
                 keys";
  vars:
    # We get the users UID so that we can set permission appropriately
    "uid[$(users)]" int =>  getuid( $(users) );

  files:
    "/home/$(users)/.ssh/."
      create => "true",
      perms => mo( 700, "$(uid[$(users)])"),
      comment => "It is important to set the proper restrictive permissions and
                  ownership so that the ssh authorized_keys feature works
                  correctly.";

    "/home/$(users)/.ssh/authorized_keys"
      perms => mo( 600, "$(uid[$(users)])" ),
      copy_from => remote_dcp( "$(ssh_key_info.repo_path)/$(users)",
                               $(ssh_key_info.key_server) ),
      comment => "We centrally manage and users authorized keys. We source each
                  users complete authorized_keys file from the central server.";
}


bundle server ssh_key_access_rules
{
  meta:
    "description"
      string => "This bundle handles sharing the directory where ssh keys
                 are distributed from.";

  access:
    # Only hosts with class `policy_server` should share the path to ssh
    # authorized_keys
    policy_server::
      "$(ssh_key_info.repo_path)"
        admit => { @(def.acl) },
        comment => "We share the ssh authorized keys with all authorized
                    hosts.";
}

This policy can be found in /var/cfengine/share/doc/examples/simple_ssh_key_distribution.cf and downloaded directly from github.

Example Run:

First make sure the users exist on your system.

root@host001:~# useradd bob
root@host001:~# useradd frank
root@host001:~# useradd kelly

Then update the policy and run it:

root@host001:~# cf-agent -Kf update.cf; cf-agent -KI
    info: Installing cfe_internal_non_existing_package...
    info: Created directory '/home/bob/.ssh/.'
    info: Owner of '/home/bob/.ssh' was 0, setting to 1002
    info: Object '/home/bob/.ssh' had permission 0755, changed it to 0700
    info: Copying from '192.168.56.2:/srv/ssh_authorized_keys/bob'
    info: Owner of '/home/bob/.ssh/authorized_keys' was 0, setting to 1002
    info: Created directory '/home/frank/.ssh/.'
    info: Owner of '/home/frank/.ssh' was 0, setting to 1003
    info: Object '/home/frank/.ssh' had permission 0755, changed it to 0700
    info: Copying from '192.168.56.2:/srv/ssh_authorized_keys/frank'
    info: Owner of '/home/frank/.ssh/authorized_keys' was 0, setting to 1003
    info: Created directory '/home/kelly/.ssh/.'
    info: Owner of '/home/kelly/.ssh' was 0, setting to 1004
    info: Object '/home/kelly/.ssh' had permission 0755, changed it to 0700
    info: Copying from '192.168.56.2:/srv/ssh_authorized_keys/kelly'
    info: Owner of '/home/kelly/.ssh/authorized_keys' was 0, setting to 1004