Forge Home

acme

Centralized SSL certificate management using Let's Encrypt™ and the lightweight acme.sh

11,344 downloads

1,165 latest version

5.0 quality score

Version information

  • 3.0.0 (latest)
  • 2.3.0
  • 2.2.0
  • 2.1.0
  • 2.0.0
  • 1.0.5
  • 1.0.4
  • 1.0.3
  • 1.0.2
  • 1.0.1
  • 1.0.0
released Jul 8th 2021
This version is compatible with:
  • Puppet Enterprise 2021.6.x, 2021.5.x, 2021.4.x, 2021.3.x, 2021.2.x, 2021.1.x, 2021.0.x, 2019.8.x, 2019.7.x, 2019.5.x, 2019.4.x, 2019.3.x, 2019.2.x, 2019.1.x, 2019.0.x
  • Puppet >= 6.0.0 < 8.0.0
  • , , , , FreeBSD

Start using this module

  • r10k or Code Manager
  • Bolt
  • Manual installation
  • Direct download

Add this module to your Puppetfile:

mod 'fraenki-acme', '3.0.0'
Learn more about managing modules with a Puppetfile

Add this module to your Bolt project:

bolt module add fraenki-acme
Learn more about using this module with an existing project

Manually install this module globally with Puppet module tool:

puppet module install fraenki-acme --version 3.0.0

Direct download is not typically how you would use a Puppet module to manage your infrastructure, but you may want to download the module in order to inspect the code.

Download

Documentation

fraenki/acme — version 3.0.0 Jul 8th 2021

puppet-acme

Build Status Puppet Forge Puppet Forge - downloads License

Table of Contents

  1. Overview
  2. Requirements
  3. Workflow
  4. Setup
  5. Usage
  6. Examples
  7. Reference
  8. Limitations
  9. Development
  10. Fork
  11. License

Overview

Centralized SSL certificate management using Let's Encrypt™ or other ACME CA's. Keep your private keys safe on the host they belong to and let your Puppet Server sign the CSRs and distribute the certificates.

Requirements

Furthermore it is highly recommended to use hiera-eyaml to protect sensitive information (such as DNS API secrets).

Workflow

For every configured certificate, this module creates a private key and CSR, transfers the CSR to your Puppet Server where it is signed using the popular and lightweight acmesh-official/acme.sh.

Signed certificates are shipped back to the originating host. The private key is never exposed.

You just need to specify the required challenge configuration on your Puppet Server. All DNS-01 hooks that are supported by acme.sh will work immediately.

Setup

Configure your Puppet Server

The whole idea is centralized certificate management, thus you have to add some configuration on your Puppet Server.

First configure the ACME accounts that are available to issue certificates:

    Class { 'acme':
      accounts => ['certmaster@example.com', 'ssl@example.com']
      ...
    }

Next add configuration for the challenge types you want to use, we call each configuration a "profile":

    Class { 'acme':
      accounts => ['certmaster@example.com', 'ssl@example.com'],
      profiles => {
        nsupdate_example => {
          challengetype => 'dns-01',
          hook          => 'nsupdate',
          env           => {
            'NSUPDATE_SERVER' => 'bind.example.com'
          },
          options       => {
            dnssleep      => 15,
            nsupdate_id   => 'example-key',
            nsupdate_type => 'hmac-md5',
            nsupdate_key  => 'abcdefg1234567890',
          }
        },
        route53_example  => {
          challengetype => 'dns-01',
          hook          => 'aws',
          env           => {
            AWS_ACCESS_KEY_ID     => 'foobar',
            AWS_SECRET_ACCESS_KEY => 'secret',
          },
          options       => {
            dnssleep => 15,
          }
        }
      }
    }

In this example we create two "profiles": One is utilizing the "nsupdate" hook to communicate with a BIND DNS server and the other one uses the "aws" hook to communicate with Amazon Route53.

Note that the hook parameter must exactly match the name of the hook that is used by acmesh-official/acme.sh. Some DNS hooks require environment variables that contain usernames or API tokens, simply add them to the env parameter.

All CSRs are collected and signed on your Puppet Server via PuppetDB, and the resulting certificates and CA chain files are shipped back to the originating host via PuppetDB.

Usage

Request a certificate

On the Puppet node where you need the certificate(s):

    class { 'acme':
      certificates => {
        # issue a test certificate
        test.example.com => {
          use_profile => 'route53_example',
          use_account => 'ssl@example.com',
          ca          => 'letsencrypt_test',
        }
        # a cert for the current FQDN is nice too
        ${::fqdn} => {
          use_profile => 'nsupdate_example',
          use_account => 'certmaster@example.com',
          ca          => 'letsencrypt',
        },
      }
    }

Note: The use_profile and use_account parameters must match the profiles and accounts that you've previously configured on your Puppet Server. Otherwise the module will refuse to issue the certificate.

The private key and CSR will be generated on your node and the CSR is shipped to your Puppet Server for signing. The certificate is put on your node as soon as it was signed on your Puppet Server.

Instead of specifying the domains as parameter to the acme class, it is also possible to use the acme::certificate defined type directly:

    acme::certificate { 'test.example.com':
      use_profile => 'route53_example',
      use_account => 'ssl@example.com',
      ca          => 'letsencrypt_test',
    }

Using other ACME CA's

This module uses the Let's Encrypt ACME CA by default. The default ACME CA can be changed on the Puppet Server:

    Class { 'acme':
      default_ca => 'zerossl',
      ca_whitelist => ['letsencrypt', 'letsencrypt_test', 'zerossl'],
      ...
    }

Note that other CA's must always be added to the $ca_whitelist, otherwise issueing certificates will fail for this CA. By default only 'letsencrypt' and 'letsencrypt_test' are whitelisted.

Besides that a different CA can also be specified for individual certificates:

    class { 'acme':
      certificates => {
        test.example.com => {
          use_profile => 'route53_example',
          use_account => 'ssl@example.com',
          ca          => 'zerossl',
        }
      }
    }

SAN certificates

Requesting SAN certificates is easy too. To do so add a space separated list of domain names to the certificates hash. The first domain name in each list is used as the base domain for the request. For example:

    class { 'acme':
      certificates => {
        'test.example.com foo.example.com bar.example.com' => {
          use_profile => 'route53_example',
          use_account => 'ssl@example.com',
          ca          => 'letsencrypt_test',
        }
    }

Or use the defined type directly:

    acme::certificate { 'test.example.com foo.example.com bar.example.com':
      use_profile => 'route53_example',
      use_account => 'ssl@example.com',
      ca          => 'letsencrypt_test',
    }

In both examples "test.example.com" will be used as base domain for the CSR.

DNS alias mode

In order to use DNS alias mode, specify the domain name either in the challenge_alias or domain_alias parameter of your profile:

    Class { 'acme':
      accounts => ['certmaster@example.com', 'ssl@example.com'],
      profiles => {
        route53_example  => {
          challengetype   => 'dns-01',
          challenge_alias => 'alias-example.com',
          hook            => 'aws',
          env             => {
            AWS_ACCESS_KEY_ID     => 'foobar',
            AWS_SECRET_ACCESS_KEY => 'secret',
          },
          options         => {
            challenge_alias => 'alias-example.com',
            dnssleep        => 15,
          }
        }
      }
    }

Testing and Debugging

For testing purposes you should use a test CA, such as letsencrypt_test. Otherwise you will hit rate limits pretty soon. It is possible to set the default CA on your Puppet Server by using the default_ca parameter:

    class { 'acme' :
      default_ca => 'letsencrypt_test'
    }

Or you can use this parameter directly when configuring certificates on your Puppet nodes, as shown in the previous examples.

Note that certificates generated by a test CA cannot be validated and as a result will generate security warnings.

Updating acme.sh

This module automatically updates acme.sh to the latest version, which may not always be desirable. It is possible to change the $acme_revision parameter to install a specific version of acme.sh:

    class { 'acme' :
      acme_revision => '9293bcfb1cd5a56c6cede3f5f46af8529ee99624'
    }

The revision should be taken from the official acme.sh repository. In order to revert to the latest version, the value should be set to 'master'.

Examples

Apache

Using acme in combination with puppetlabs-apache:

    # request a certificate from acme
    acme::certificate { $::fqdn:
      use_profile => 'nsupdate_example',
      use_account => 'certmaster@example.com',
      ca          => 'letsencrypt',
      # restart apache when the certificate is signed (or renewed)
      notify      => Class['apache::service'],
    }

    # get configuration from acme
    include acme
    $base_dir = $acme::base_dir
    $crt_dir  = $acme::crt_dir
    $key_dir  = $acme::key_dir

    # where acme stores our certificate and key files
    $my_key = "${key_dir}/${::fqdn}/private.key"
    $my_cert = "${crt_dir}/${::fqdn}/cert.pem"
    $my_chain = "${crt_dir}/${::fqdn}/chain.pem"

    # configure apache
    include apache
    apache::vhost { $::fqdn:
      port     => '443',
      docroot  => '/var/www/example',
      ssl      => true,
      ssl_cert => "/etc/ssl/${::fqdn}.crt",
      ssl_ca   => "/etc/ssl/${::fqdn}.ca",
      ssl_key  => "/etc/ssl/${::fqdn}.key",
    }

    # copy certificate files
    file { "/etc/ssl/${::fqdn}.crt":
      ensure    => file,
      owner     => 'root',
      group     => 'root',
      mode      => '0644',
      source    => $my_cert,
      subscribe => Acme::Certificate[$::fqdn],
      notify    => Class['apache::service'],
    }
    file { "/etc/ssl/${::fqdn}.key":
      ensure    => file,
      owner     => 'root',
      group     => 'root',
      mode      => '0640',
      source    => $my_key,
      subscribe => Acme::Certificate[$::fqdn],
      notify    => Class['apache::service'],
    }
    file { "/etc/ssl/${::fqdn}.ca":
      ensure    => file,
      owner     => 'root',
      group     => 'root',
      mode      => '0644',
      source    => $my_chain,
      subscribe => Acme::Certificate[$::fqdn],
      notify    => Class['apache::service'],
    }

Reference

Files and directories

Locations of the important certificate files (i.e. "cert.example.com"):

  • /etc/acme.sh/certs/cert.example.com/cert.pem: the certificate
  • /etc/acme.sh/certs/cert.example.com/chain.pem: the CA chain
  • /etc/acme.sh/certs/cert.example.com/fullchain.pem: Certificate and CA chain in one file
  • /etc/acme.sh/certs/cert.example.com/cert.ocsp: OCSP Must-Staple extension data
  • /etc/acme.sh/keys/cert.example.com/private.key: Private key
  • /etc/acme.sh/keys/cert.example.com/fullchain_with_key.pem: All-in-one: certificate, CA chain, private key

Basic directory layout:

  • /etc/acme.sh/accounts: (Puppet Server) Private keys and other files related to ACME accounts
  • /etc/acme.sh/certs: Certificates, CA chains and OCSP files
  • /etc/acme.sh/configs: OpenSSL configuration and other files required for the CSR
  • /etc/acme.sh/csrs: Certificate signing requests (CSR)
  • /etc/acme.sh/home: (Puppet Server) Working directory for acme.sh
  • /etc/acme.sh/keys: Private keys for each certificate
  • /etc/acme.sh/results: (Puppet Server) Working directory, used to export certificates
  • /opt/acme.sh: (Puppet Server) Local copy of acme.sh (GIT repository)
  • /var/log/acme.sh/acme.log: (Puppet Server) acme.sh log file

Classes and parameters

Classes and parameters are documented in REFERENCE.md.

Limitations

Requires multiple Puppet runs

It takes several puppet runs to issue a certificate. Two Puppet runs are required to prepare the CSR on your Puppet node. Two more Puppet runs on your Puppet Server are required to sign the certificate and send it to PuppetDB. Now it's ready to be collected by your Puppet node.

The process is seamless for certificate renewals, but it takes a little time to issue a new certificate.

HTTP-01 challenge type untested

The HTTP-01 challenge type is theoretically supported, but it is untested with this module. Some additional parameters may be missing. Feel free to report issues or suggest enhancements.

OS Compatibility

This module was tested on CentOS/RedHat, Ubuntu/Debian and FreeBSD. Please open a new issue if your operating system is not supported yet, and provide information about problems or missing features.

Development

Please use the GitHub issues functionality to report any bugs or requests for new features. Feel free to fork and submit pull requests for potential contributions.

Fork

This module is a fork of the excellent bzed/bzed-letsencrypt. The fork was necessary in order to use acme.sh instead of dehydrated.

License

Copyright (C) 2017-2021 Frank Wall

Based on bzed/bzed-letsencrypt, Copyright 2017 Bernd Zeimetz.

Let's Encrypt is a trademark of the Internet Security Research Group. All rights reserved.

See the LICENSE file for further information.