Munki,Open Source January 23, 2015 at 11:58 am

Running Munki with Puppet SSL Client Certificates

Previously, I showed how you can run Munki in a Docker container. Then, I talked about how to build Munki to use Puppet for SSL certificates.

Assuming you’ve got a running Puppetmaster image (which I talked about building here), let’s run the Munki-Puppet image we just built.

Running the Container:

Run a data-only container to keep our data in:
docker run -d --name munki-data --entrypoint /bin/echo macadmins/munki-puppet Data-only container for munki

Run the Munki container by linking it to the Puppetmaster:
docker run -d --name munki --volumes-from munki-data -p 80:80 -p 443:443 -h munki --link puppetmaster:puppet macadmins/munki-puppet

The notable additions in this docker run command:
-p 443:443
Since we’re adding SSL support to the Nginx webserver, we want to make sure that the container is accessible at port 443, the default SSL port.

--link puppetmaster:puppet
The --link argument allows us to tell the Munki container that it can access any exposed ports from the Puppetmaster container by the DNS entry for “puppet”. Since the Puppet agent always tries to access “puppet” to check in, this means that the Munki container will have no trouble with Puppet.

The first step after running the container is to check in with puppet: docker exec munki puppet agent --test Verify that it receives a signed certificate from the Puppetmaster.

Now we’ve got a running Nginx container with our Munki repo, except it’s still only serving content at port 80. We need to tell Nginx to use our SSL configuration.

Using SSL with Munki:

We have an empty Munki repo, so we should populate it first.

Once the repo has some content, we need to add in the Nginx SSL configuration.

You’ll need to edit the provided munki-repo-ssl.conf file so that the name of the .pem certificate files matches what Puppet actually generated. For example, when you ran docker exec puppet agent --test above, you probably got output like this:

Info: Creating a new SSL key for munki.sacredsf.org
Info: Caching certificate for ca
Info: csr_attributes file loading from /etc/puppet/csr_attributes.yaml
Info: Creating a new SSL certificate request for munki.sacredsf.org
Info: Certificate Request fingerprint (SHA256): [snip]
Info: Caching certificate for munki.sacredsf.org
Info: Caching certificate_revocation_list for ca
Info: Caching certificate for ca
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for munki.sacredsf.org
Info: Applying configuration version '1422039029'
Info: Creating state file /var/lib/puppet/state/state.yaml

You can see the name of the certificate from the Puppetmaster:
docker exec puppetmaster puppet cert list -all

+ "munki.sacredsf.org" (SHA256) [snip]
+ "puppet" (SHA256) [snip] (alt names: "DNS:puppet", "DNS:puppet.sacredsf.org")

To be even more thorough, look in the Munki’s Puppet certs directory:
docker exec munki ls -l /var/lib/puppet/ssl/certs/

total 8
-rw-r--r--. 1 puppet puppet 1984 Jan 23 18:18 ca.pem
-rw-r--r--. 1 puppet puppet 2021 Jan 23 18:52 munki.sacredsf.org.pem

That confirms the name of my cert is “munki.sacredsf.org.pem”, so let’s put that into munki-repo-ssl.conf:

server
{
listen 443;
ssl on;
ssl_certificate /var/lib/puppet/ssl/certs/munki.sacredsf.org.pem;
ssl_certificate_key /var/lib/puppet/ssl/private_keys/munki.sacredsf.org.pem;
ssl_client_certificate /var/lib/puppet/ssl/certs/ca.pem;
ssl_crl /var/lib/puppet/ssl/crl.pem;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"
server_name munki;
location /repo/ {
alias /munki_repo/;
autoindex off;
}
}

The three important file paths that must be correct are ssl_certificate, ssl_certificate_key, and ssl_client_certificate. If any of these paths are wrong or can’t be found, Nginx will not start and your Docker container will immediately halt.

For reference, the ssl_protocols and ssl_ciphers are configured for perfect forward secrecy.

Otherwise, the configuration for Nginx for the Munki repo remains the same as the non-SSL version – we’re serving the file path /munki_repo as https://munki/repo/.

To get this new SSL configuration into the Nginx container, we’ll need to edit the existing configuration. Unfortunately, the base Nginx container is extremely minimal and doesn’t have vi or nano or anything. We could either install a text editor into the container, or just use a shell trick:

cat munki-repo-ssl.conf | docker exec -i munki sh -c 'cat > /etc/nginx/sites-enabled/munki-repo.conf'

Since we’ve changed the contents of a configuration file, we’ll need to restart Nginx. Let’s do that gracefully with Docker:
docker stop munki
docker start munki
Stopping the container will send a graceful “shutdown” signal to Nginx, and starting the container will bring it up as it expects.

Configure the clients to use Munki with SSL:

Detailed instructions on configuring Munki with SSL certificates can be found on the official wiki, but I’m going to recreate the steps here.

All of the following steps should be done on your OS X Munki client.

  1. If you haven’t already, run puppet agent --test as root to get a signed certificate.
  2. Copy the certs into /Library/Managed Installs/:
    1. sudo mkdir -p /Library/Managed\ Installs/certs
    2. sudo chmod 0700 /Library/Managed\ Installs/certs
    3. sudo cp /etc/puppet/ssl/certs/mac.local.pem /Library/Managed\ Installs/certs/clientcert.pem
    4. sudo cp /etc/puppet/ssl/private_keys/mac.local.pem /Library/Managed\ Installs/certs/clientkey.pem
    5. sudo cp /etc/puppet/ssl/certs/ca.pem /Library/Managed\ Installs/certs/ca.pem
  3. Change the ManagedInstalls.plist defaults:
    1. sudo defaults write /Library/Preferences/ManagedInstalls SoftwareRepoURL "https://munki/repo"
    2. sudo defaults write /Library/Preferences/ManagedInstalls SoftwareRepoCACertificate "/Library/Managed Installs/certs/ca.pem"
    3. sudo defaults write /Library/Preferences/ManagedInstalls ClientCertificatePath "/Library/Managed Installs/certs/clientcert.pem"
    4. sudo defaults write /Library/Preferences/ManagedInstalls ClientKeyPath "/Library/Managed Installs/certs/clientkey.pem"
    5. sudo defaults write /Library/Preferences/ManagedInstalls UseClientCertificate -bool TRUE
  4. Test out the client: sudo /usr/local/munki/managedsoftwareupdate -vvv --checkonly

Now you’ve got secure Munki communication from clients to server, using Puppet’s client certificates, all in Docker!

Nick McSpadden

I'm Client Systems Manager for Schools of the Sacred Heart, San Francisco. I'm in charge of all OS X and iOS deployments to our faculty, staff, and students.

More Posts

Tags:

Leave a reply

You must be logged in to post a comment.