diff --git a/REFERENCE.md b/REFERENCE.md index a6e5079..28107b8 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,810 +1,817 @@ # Reference ## Table of Contents **Classes** _Public Classes_ * [`letsencrypt`](#letsencrypt): Install and configure Certbot, the LetsEncrypt client * [`letsencrypt::install`](#letsencryptinstall): Installs the Let's Encrypt client. * [`letsencrypt::plugin::dns_rfc2136`](#letsencryptplugindns_rfc2136): Installs and configures the dns-rfc2136 plugin * [`letsencrypt::renew`](#letsencryptrenew): Configures renewal of Let's Encrypt certificates using Certbot _Private Classes_ * `letsencrypt::config`: Configures the Let's Encrypt client. -* `letsencrypt::params`: Default parameters **Defined types** * [`letsencrypt::certonly`](#letsencryptcertonly): Request a certificate using the `certonly` installer * [`letsencrypt::hook`](#letsencrypthook): Creates hook scripts. **Functions** * [`letsencrypt::letsencrypt_lookup`](#letsencryptletsencrypt_lookup): **Data types** * [`Letsencrypt::Cron::Hour`](#letsencryptcronhour): mimic hour setting in cron as defined in man 5 crontab * [`Letsencrypt::Cron::Minute`](#letsencryptcronminute): mimic minute setting in cron as defined in man 5 crontab * [`Letsencrypt::Cron::Monthday`](#letsencryptcronmonthday): mimic monthday setting in cron as defined in man 5 crontab * [`Letsencrypt::Plugin`](#letsencryptplugin): List of accepted plugins ## Classes ### letsencrypt Install and configure Certbot, the LetsEncrypt client #### Examples ##### ```puppet class { 'letsencrypt' : email => 'letsregister@example.com', config => { 'server' => 'https://acme-staging.api.letsencrypt.org/directory', }, } ``` #### Parameters The following parameters are available in the `letsencrypt` class. ##### `email` Data type: `Optional[String]` The email address to use to register with Let's Encrypt. This takes precedence over an 'email' setting defined in $config. Default value: `undef` ##### `path` Data type: `String` The path to the letsencrypt installation. -Default value: $letsencrypt::params::path +Default value: '/opt/letsencrypt' ##### `venv_path` Data type: `Any` virtualenv path for vcs-installed Certbot -Default value: $letsencrypt::params::venv_path +Default value: '/opt/letsencrypt/.venv' ##### `environment` Data type: `Array` An optional array of environment variables (in addition to VENV_PATH) Default value: [] ##### `repo` Data type: `String` A Git URL to install the Let's encrypt client from. -Default value: $letsencrypt::params::repo +Default value: 'https://github.com/certbot/certbot.git' ##### `version` Data type: `String` The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method. -Default value: $letsencrypt::params::version +Default value: 'v0.30.2' ##### `package_name` Data type: `String` Name of package and command to use when installing the client with the `package` method. -Default value: $letsencrypt::params::package_name +Default value: 'certbot' ##### `package_ensure` Data type: `Any` The value passed to `ensure` when installing the client with the `package` method. -Default value: $letsencrypt::params::package_ensure +Default value: 'installed' ##### `package_command` Data type: `String` Path or name for letsencrypt executable when installing the client with the `package` method. -Default value: $letsencrypt::params::package_command +Default value: 'certbot' ##### `config_file` Data type: `String` The path to the configuration file for the letsencrypt cli. -Default value: $letsencrypt::params::config_file +Default value: "${config_dir}/cli.ini" ##### `config` Data type: `Hash` A hash representation of the letsencrypt configuration file. -Default value: $letsencrypt::params::config +Default value: {'server' => 'https://acme-v01.api.letsencrypt.org/directory'} ##### `cron_scripts_path` Data type: `String` -The path to put the script we'll call with cron. Defaults to $puppet_vardir/letsencrypt. +The path for renewal scripts called by cron -Default value: $letsencrypt::params::cron_scripts_path +Default value: "${facts['puppet_vardir']}/letsencrypt" + +##### `cron_owner_group` + +Data type: `String` + +Group owner of cron renew scripts. + +Default value: 'root' ##### `manage_config` Data type: `Boolean` A feature flag to toggle the management of the letsencrypt configuration file. -Default value: $letsencrypt::params::manage_config +Default value: `true` ##### `manage_install` Data type: `Boolean` A feature flag to toggle the management of the letsencrypt client installation. -Default value: $letsencrypt::params::manage_install +Default value: `true` ##### `manage_dependencies` Data type: `Boolean` A feature flag to toggle the management of the letsencrypt dependencies. -Default value: $letsencrypt::params::manage_dependencies +Default value: `true` ##### `configure_epel` Data type: `Boolean` A feature flag to include the 'epel' class and depend on it for package installation. -Default value: $letsencrypt::params::configure_epel +Default value: `undef` ##### `install_method` Data type: `Enum['package', 'vcs']` Method to install the letsencrypt client, either package or vcs. -Default value: $letsencrypt::params::install_method +Default value: 'package' ##### `agree_tos` Data type: `Boolean` A flag to agree to the Let's Encrypt Terms of Service. -Default value: $letsencrypt::params::agree_tos +Default value: `true` ##### `unsafe_registration` Data type: `Boolean` A flag to allow using the 'register-unsafely-without-email' flag. -Default value: $letsencrypt::params::unsafe_registration +Default value: `false` ##### `config_dir` Data type: `Stdlib::Unixpath` The path to the configuration directory. -Default value: $letsencrypt::params::config_dir +Default value: '/etc/letsencrypt' ##### `key_size` Data type: `Integer[2048]` Size for the RSA public key Default value: 4096 ##### `renew_pre_hook_commands` Data type: `Any` Array of commands to run in a shell before obtaining/renewing any certificates. -Default value: $letsencrypt::params::renew_pre_hook_commands +Default value: [] ##### `renew_post_hook_commands` Data type: `Any` Array of commands to run in a shell after attempting to obtain/renew certificates. -Default value: $letsencrypt::params::renew_post_hook_commands +Default value: [] ##### `renew_deploy_hook_commands` Data type: `Any` Array of commands to run in a shell once for each successfully issued/renewed certificate. Two environmental variables are supplied by certbot: - $RENEWED_LINEAGE: Points to the live directory with the cert files and key. Example: /etc/letsencrypt/live/example.com - $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains. Example: "example.com www.example.com" -Default value: $letsencrypt::params::renew_deploy_hook_commands +Default value: [] ##### `renew_additional_args` Data type: `Any` Array of additional command line arguments to pass to 'certbot renew'. -Default value: $letsencrypt::params::renew_additional_args +Default value: [] ##### `renew_cron_ensure` Data type: `Any` Intended state of the cron resource running certbot renew. -Default value: $letsencrypt::params::renew_cron_ensure +Default value: 'absent' ##### `renew_cron_hour` Data type: `Any` Optional string, integer or array of hour(s) the renewal command should run. -E.g. '[0,12]' to execute at midnight and midday. Default: fqdn-seeded random +E.g. '[0,12]' to execute at midnight and midday. hour. -Default value: $letsencrypt::params::renew_cron_hour +Default value: fqdn_rand(24) ##### `renew_cron_minute` Data type: `Any` Optional string, integer or array of minute(s) the renewal command should -run. E.g. 0 or '00' or [0,30]. Default: fqdn-seeded random minute. +run. E.g. 0 or '00' or [0,30]. -Default value: $letsencrypt::params::renew_cron_minute +Default value: fqdn_rand(60, fqdn_rand_string(10)) ##### `renew_cron_monthday` Data type: `Any` Optional string, integer or array of monthday(s) the renewal command should -run. E.g. '2-30/2' to run on even days. Default: Every day. +run. E.g. '2-30/2' to run on even days. -Default value: $letsencrypt::params::renew_cron_monthday +Default value: '*' ### letsencrypt::install Installs the Let's Encrypt client. #### Parameters The following parameters are available in the `letsencrypt::install` class. ##### `manage_install` Data type: `Boolean` A feature flag to toggle the management of the letsencrypt client installation. Default value: $letsencrypt::manage_install ##### `manage_dependencies` Data type: `Boolean` A feature flag to toggle the management of the letsencrypt dependencies. Default value: $letsencrypt::manage_dependencies ##### `configure_epel` Data type: `Boolean` A feature flag to include the 'epel' class and depend on it for package installation. Default value: $letsencrypt::configure_epel ##### `install_method` Data type: `Enum['package', 'vcs']` Method to install the letsencrypt client Default value: $letsencrypt::install_method ##### `path` Data type: `String` The path to the letsencrypt installation. Default value: $letsencrypt::path ##### `repo` Data type: `String` A Git URL to install the Let's encrypt client from. Default value: $letsencrypt::repo ##### `version` Data type: `String` The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method. Default value: $letsencrypt::version ##### `package_ensure` Data type: `String` The value passed to `ensure` when installing the client with the `package` method. Default value: $letsencrypt::package_ensure ##### `package_name` Data type: `String` Name of package to use when installing the client with the `package` method. Default value: $letsencrypt::package_name ### letsencrypt::plugin::dns_rfc2136 This class installs and configures the Let's Encrypt dns-rfc2136 plugin. https://certbot-dns-rfc2136.readthedocs.io #### Parameters The following parameters are available in the `letsencrypt::plugin::dns_rfc2136` class. ##### `server` Data type: `Stdlib::Host` Target DNS server. ##### `key_name` Data type: `String[1]` TSIG key name. ##### `key_secret` Data type: `String[1]` TSIG key secret. ##### `key_algorithm` Data type: `String[1]` TSIG key algorithm. -Default value: $letsencrypt::dns_rfc2136_algorithm +Default value: 'HMAC-SHA512' ##### `port` Data type: `Stdlib::Port` Target DNS port. -Default value: $letsencrypt::dns_rfc2136_port +Default value: 53 ##### `propagation_seconds` Data type: `Integer` Number of seconds to wait for the DNS server to propagate the DNS-01 challenge. -Default value: $letsencrypt::dns_rfc2136_propagation_seconds +Default value: 10 ##### `manage_package` Data type: `Boolean` Manage the plugin package. -Default value: $letsencrypt::dns_rfc2136_manage_package +Default value: `true` ##### `package_name` Data type: `String` The name of the package to install when $manage_package is true. -Default value: $letsencrypt::dns_rfc2136_package_name +Default value: `undef` ##### `config_dir` Data type: `Stdlib::Absolutepath` The path to the configuration directory. Default value: $letsencrypt::config_dir ### letsencrypt::renew Configures renewal of Let's Encrypt certificates using the certbot renew command. Note: Hooks set here will run before/after/for ALL certificates, including any not managed by Puppet. If you want to create hooks for specific certificates only, create them using letsencrypt::certonly. #### Parameters The following parameters are available in the `letsencrypt::renew` class. ##### `pre_hook_commands` Data type: `Variant[String[1], Array[String[1]]]` Array of commands to run in a shell before obtaining/renewing any certificates. Default value: $letsencrypt::renew_pre_hook_commands ##### `post_hook_commands` Data type: `Variant[String[1], Array[String[1]]]` Array of commands to run in a shell after attempting to obtain/renew certificates. Default value: $letsencrypt::renew_post_hook_commands ##### `deploy_hook_commands` Data type: `Variant[String[1], Array[String[1]]]` Array of commands to run in a shell once for each successfully issued/renewed certificate. Two environmental variables are supplied by certbot: - $RENEWED_LINEAGE: Points to the live directory with the cert files and key. Example: /etc/letsencrypt/live/example.com - $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains. Example: "example.com www.example.com" Default value: $letsencrypt::renew_deploy_hook_commands ##### `additional_args` Data type: `Array[String[1]]` Array of additional command line arguments to pass to 'certbot renew'. Default value: $letsencrypt::renew_additional_args ##### `cron_ensure` Data type: `Enum['present', 'absent']` Intended state of the cron resource running certbot renew Default value: $letsencrypt::renew_cron_ensure ##### `cron_hour` Data type: `Letsencrypt::Cron::Hour` Optional string, integer or array of hour(s) the renewal command should run. E.g. '[0,12]' to execute at midnight and midday. Default: fqdn-seeded random hour. Default value: $letsencrypt::renew_cron_hour ##### `cron_minute` Data type: `Letsencrypt::Cron::Minute` Optional string, integer or array of minute(s) the renewal command should run. E.g. 0 or '00' or [0,30]. Default: fqdn-seeded random minute. Default value: $letsencrypt::renew_cron_minute ##### `cron_monthday` Data type: `Letsencrypt::Cron::Monthday` Optional string, integer or array of monthday(s) the renewal command should run. E.g. '2-30/2' to run on even days. Default: Every day. Default value: $letsencrypt::renew_cron_monthday ## Defined types ### letsencrypt::certonly This type can be used to request a certificate using the `certonly` installer. #### Parameters The following parameters are available in the `letsencrypt::certonly` defined type. ##### `ensure` Data type: `Enum['present','absent']` Intended state of the resource Will remove certificates for specified domains if set to 'absent'. Will also remove cronjobs and renewal scripts if `manage_cron` is set to 'true'. Default value: 'present' ##### `domains` Data type: `Array[String[1]]` An array of domains to include in the CSR. Default value: [$title] ##### `custom_plugin` Data type: `Boolean` Whether to use a custom plugin in additional_args and disable -a flag. Default value: `false` ##### `plugin` Data type: `Letsencrypt::Plugin` The authenticator plugin to use when requesting the certificate. Default value: 'standalone' ##### `webroot_paths` Data type: `Array[Stdlib::Unixpath]` An array of webroot paths for the domains in `domains`. Required if using `plugin => 'webroot'`. If `domains` and `webroot_paths` are not the same length, the last `webroot_paths` element will be used for all subsequent domains. Default value: [] ##### `letsencrypt_command` Data type: `String[1]` Command to run letsencrypt Default value: $letsencrypt::command ##### `additional_args` Data type: `Array[String[1]]` An array of additional command line arguments to pass to the `letsencrypt-auto` command. Default value: [] ##### `environment` Data type: `Array[String[1]]` An optional array of environment variables (in addition to VENV_PATH). Default value: [] ##### `key_size` Data type: `Integer[2048]` Size for the RSA public key Default value: $letsencrypt::key_size ##### `manage_cron` Data type: `Boolean` Indicating whether or not to schedule cron job for renewal. Runs daily but only renews if near expiration, e.g. within 10 days. Default value: `false` ##### `suppress_cron_output` Data type: `Boolean` Redirect cron output to devnull Default value: `false` ##### `cron_before_command` Data type: `Optional[String[1]]` Representation of a command that should be run before renewal command Default value: `undef` ##### `cron_success_command` Data type: `Optional[String[1]]` Representation of a command that should be run if the renewal command succeeds. Default value: `undef` ##### `cron_hour` Data type: `Variant[Integer[0,23], String, Array]` Optional hour(s) that the renewal command should execute. e.g. '[0,12]' execute at midnight and midday. Default - seeded random hour. Default value: fqdn_rand(24, $title) ##### `cron_minute` Data type: `Variant[Integer[0,59], String, Array]` Optional minute(s) that the renewal command should execute. e.g. 0 or '00' or [0,30]. Default - seeded random minute. Default value: fqdn_rand(60, fqdn_rand_string(10, $title)) ##### `cron_monthday` Data type: `Array[Variant[Integer[0, 59], String[1]]]` Optional string, integer or array of monthday(s) the renewal command should run. E.g. '2-30/2' to run on even days. Default: Every day. Default value: ['*'] ##### `config_dir` Data type: `Stdlib::Unixpath` The path to the configuration directory. Default value: $letsencrypt::config_dir ##### `pre_hook_commands` Data type: `Variant[String[1], Array[String[1]]]` Array of commands to run in a shell before attempting to obtain/renew the certificate. Default value: [] ##### `post_hook_commands` Data type: `Variant[String[1], Array[String[1]]]` Array of command(s) to run in a shell after attempting to obtain/renew the certificate. Default value: [] ##### `deploy_hook_commands` Data type: `Variant[String[1], Array[String[1]]]` Array of command(s) to run in a shell once if the certificate is successfully issued. Two environmental variables are supplied by certbot: - $RENEWED_LINEAGE: Points to the live directory with the cert files and key. Example: /etc/letsencrypt/live/example.com - $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains. Example: "example.com www.example.com" Default value: [] ### letsencrypt::hook This type is used by letsencrypt::renew and letsencrypt::certonly to create hook scripts. #### Parameters The following parameters are available in the `letsencrypt::hook` defined type. ##### `type` Data type: `Enum['pre', 'post', 'deploy']` Hook type. ##### `hook_file` Data type: `String[1]` Path to deploy hook script. ##### `commands` Data type: `Variant[String[1],Array[String[1]]]` Bash commands to execute when the hook is run by certbot. ## Functions ### letsencrypt::letsencrypt_lookup Type: Ruby 4.x API The letsencrypt::letsencrypt_lookup function. #### `letsencrypt::letsencrypt_lookup(Any $cn)` The letsencrypt::letsencrypt_lookup function. Returns: `Any` ##### `cn` Data type: `Any` ## Data types ### Letsencrypt::Cron::Hour mimic hour setting in cron as defined in man 5 crontab Alias of `Variant[Integer[0,23], String[1], Array[ Variant[ Integer[0,23], String[1], ] ]]` ### Letsencrypt::Cron::Minute mimic minute setting in cron as defined in man 5 crontab Alias of `Variant[Integer[0,59], String[1], Array[ Variant[ Integer[0,59], String[1], ] ]]` ### Letsencrypt::Cron::Monthday mimic monthday setting in cron as defined in man 5 crontab Alias of `Variant[Integer[0,31], String[1], Array[ Variant[ Integer[0,31], String[1], ] ]]` ### Letsencrypt::Plugin List of accepted plugins Alias of `Enum['apache', 'standalone', 'webroot', 'nginx', 'dns-route53', 'dns-google', 'dns-cloudflare', 'dns-rfc2136']` diff --git a/data/Debian.yaml b/data/Debian.yaml new file mode 100644 index 0000000..23925b4 --- /dev/null +++ b/data/Debian.yaml @@ -0,0 +1,2 @@ +--- +letsencrypt::configure_epel: false diff --git a/data/Debian/10.yaml b/data/Debian/10.yaml new file mode 100644 index 0000000..f0f7bfd --- /dev/null +++ b/data/Debian/10.yaml @@ -0,0 +1,5 @@ +--- +letsencrypt::package_name: 'certbot' +letsencrypt::package_command: 'certbot' +letsencrypt::configure_epel: false +letsencrypt::plugin::dns_rfc2136::package_name: 'python3-certbot-dns-rfc2136' diff --git a/data/Debian/16.04.yaml b/data/Debian/16.04.yaml new file mode 100644 index 0000000..23925b4 --- /dev/null +++ b/data/Debian/16.04.yaml @@ -0,0 +1,2 @@ +--- +letsencrypt::configure_epel: false diff --git a/data/Debian/18.04.yaml b/data/Debian/18.04.yaml new file mode 100644 index 0000000..f0f7bfd --- /dev/null +++ b/data/Debian/18.04.yaml @@ -0,0 +1,5 @@ +--- +letsencrypt::package_name: 'certbot' +letsencrypt::package_command: 'certbot' +letsencrypt::configure_epel: false +letsencrypt::plugin::dns_rfc2136::package_name: 'python3-certbot-dns-rfc2136' diff --git a/data/FreeBSD.yaml b/data/FreeBSD.yaml new file mode 100644 index 0000000..baa55b0 --- /dev/null +++ b/data/FreeBSD.yaml @@ -0,0 +1,5 @@ +--- +letsencrypt::package_name: 'py27-certbot' +letsencrypt::config_dir: '/usr/local/etc/letsencrypt' +letsencrypt::cron_owner_group: 'wheel' +letsencrypt::configure_epel: false diff --git a/data/OpenBSD.yaml b/data/OpenBSD.yaml new file mode 100644 index 0000000..f0dab5c --- /dev/null +++ b/data/OpenBSD.yaml @@ -0,0 +1,3 @@ +--- +letsencrypt::cron_owner_group: 'wheel' +letsencrypt::configure_epel: false diff --git a/data/RedHat.yaml b/data/RedHat.yaml new file mode 100644 index 0000000..ab6b13a --- /dev/null +++ b/data/RedHat.yaml @@ -0,0 +1,3 @@ +--- +letsencrypt::configure_epel: true +letsencrypt::plugin::dns_rfc2136::package_name: 'python2-certbot-dns-rfc2136' diff --git a/data/RedHat/29.yaml b/data/RedHat/29.yaml new file mode 100644 index 0000000..b2bc628 --- /dev/null +++ b/data/RedHat/29.yaml @@ -0,0 +1,3 @@ +--- +letsencrypt::configure_epel: false +letsencrypt::plugin::dns_rfc2136::package_name: 'python3-certbot-dns-rfc2136' diff --git a/data/RedHat/30.yaml b/data/RedHat/30.yaml new file mode 100644 index 0000000..b2bc628 --- /dev/null +++ b/data/RedHat/30.yaml @@ -0,0 +1,3 @@ +--- +letsencrypt::configure_epel: false +letsencrypt::plugin::dns_rfc2136::package_name: 'python3-certbot-dns-rfc2136' diff --git a/hiera.yaml b/hiera.yaml new file mode 100644 index 0000000..d1bdc64 --- /dev/null +++ b/hiera.yaml @@ -0,0 +1,16 @@ +--- +version: 5 + +defaults: + datadir: 'data' + data_hash: 'yaml_data' + +hierarchy: + - name: 'Operating System Major Release' + path: '%{facts.os.family}/%{facts.os.release.major}.yaml' + + - name: 'Operating System Family' + path: '%{facts.os.family}.yaml' + + - name: 'common' + path: 'common.yaml' diff --git a/manifests/init.pp b/manifests/init.pp index 9d72f7c..cff5c6c 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,130 +1,132 @@ # @summary Install and configure Certbot, the LetsEncrypt client # # Install and configure Certbot, the LetsEncrypt client # # @example # class { 'letsencrypt' : # email => 'letsregister@example.com', # config => { # 'server' => 'https://acme-staging.api.letsencrypt.org/directory', # }, # } # # @param email # The email address to use to register with Let's Encrypt. This takes # precedence over an 'email' setting defined in $config. # @param path The path to the letsencrypt installation. # @param venv_path virtualenv path for vcs-installed Certbot # @param environment An optional array of environment variables (in addition to VENV_PATH) # @param repo A Git URL to install the Let's encrypt client from. # @param version The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method. # @param package_name Name of package and command to use when installing the client with the `package` method. # @param package_ensure The value passed to `ensure` when installing the client with the `package` method. # @param package_command Path or name for letsencrypt executable when installing the client with the `package` method. # @param config_file The path to the configuration file for the letsencrypt cli. # @param config A hash representation of the letsencrypt configuration file. -# @param cron_scripts_path The path to put the script we'll call with cron. Defaults to $puppet_vardir/letsencrypt. +# @param cron_scripts_path The path for renewal scripts called by cron +# @param cron_owner_group Group owner of cron renew scripts. # @param manage_config A feature flag to toggle the management of the letsencrypt configuration file. # @param manage_install A feature flag to toggle the management of the letsencrypt client installation. # @param manage_dependencies A feature flag to toggle the management of the letsencrypt dependencies. # @param configure_epel A feature flag to include the 'epel' class and depend on it for package installation. # @param install_method Method to install the letsencrypt client, either package or vcs. # @param agree_tos A flag to agree to the Let's Encrypt Terms of Service. # @param unsafe_registration A flag to allow using the 'register-unsafely-without-email' flag. # @param config_dir The path to the configuration directory. # @param key_size Size for the RSA public key # @param renew_pre_hook_commands Array of commands to run in a shell before obtaining/renewing any certificates. # @param renew_post_hook_commands Array of commands to run in a shell after attempting to obtain/renew certificates. # @param renew_deploy_hook_commands # Array of commands to run in a shell once for each successfully issued/renewed # certificate. Two environmental variables are supplied by certbot: # - $RENEWED_LINEAGE: Points to the live directory with the cert files and key. # Example: /etc/letsencrypt/live/example.com # - $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains. # Example: "example.com www.example.com" # @param renew_additional_args Array of additional command line arguments to pass to 'certbot renew'. # @param renew_cron_ensure Intended state of the cron resource running certbot renew. # @param renew_cron_hour # Optional string, integer or array of hour(s) the renewal command should run. -# E.g. '[0,12]' to execute at midnight and midday. Default: fqdn-seeded random +# E.g. '[0,12]' to execute at midnight and midday. # hour. # @param renew_cron_minute # Optional string, integer or array of minute(s) the renewal command should -# run. E.g. 0 or '00' or [0,30]. Default: fqdn-seeded random minute. +# run. E.g. 0 or '00' or [0,30]. # @param renew_cron_monthday # Optional string, integer or array of monthday(s) the renewal command should -# run. E.g. '2-30/2' to run on even days. Default: Every day. +# run. E.g. '2-30/2' to run on even days. # class letsencrypt ( Optional[String] $email = undef, - String $path = $letsencrypt::params::path, - $venv_path = $letsencrypt::params::venv_path, + String $path = '/opt/letsencrypt', + $venv_path = '/opt/letsencrypt/.venv', Array $environment = [], - String $repo = $letsencrypt::params::repo, - String $version = $letsencrypt::params::version, - String $package_name = $letsencrypt::params::package_name, - $package_ensure = $letsencrypt::params::package_ensure, - String $package_command = $letsencrypt::params::package_command, - String $config_file = $letsencrypt::params::config_file, - Hash $config = $letsencrypt::params::config, - String $cron_scripts_path = $letsencrypt::params::cron_scripts_path, - Boolean $manage_config = $letsencrypt::params::manage_config, - Boolean $manage_install = $letsencrypt::params::manage_install, - Boolean $manage_dependencies = $letsencrypt::params::manage_dependencies, - Boolean $configure_epel = $letsencrypt::params::configure_epel, - Enum['package', 'vcs'] $install_method = $letsencrypt::params::install_method, - Boolean $agree_tos = $letsencrypt::params::agree_tos, - Boolean $unsafe_registration = $letsencrypt::params::unsafe_registration, - Stdlib::Unixpath $config_dir = $letsencrypt::params::config_dir, + String $repo = 'https://github.com/certbot/certbot.git', + String $version = 'v0.30.2', + String $package_name = 'certbot', + $package_ensure = 'installed', + String $package_command = 'certbot', + Stdlib::Unixpath $config_dir = '/etc/letsencrypt', + String $config_file = "${config_dir}/cli.ini", + Hash $config = {'server' => 'https://acme-v01.api.letsencrypt.org/directory'}, + String $cron_scripts_path = "${facts['puppet_vardir']}/letsencrypt", + String $cron_owner_group = 'root', + Boolean $manage_config = true, + Boolean $manage_install = true, + Boolean $manage_dependencies = true, + Boolean $configure_epel = undef, + Enum['package', 'vcs'] $install_method = 'package', + Boolean $agree_tos = true, + Boolean $unsafe_registration = false, Integer[2048] $key_size = 4096, # $renew_* should only be used in letsencrypt::renew (blame rspec) - $renew_pre_hook_commands = $letsencrypt::params::renew_pre_hook_commands, - $renew_post_hook_commands = $letsencrypt::params::renew_post_hook_commands, - $renew_deploy_hook_commands = $letsencrypt::params::renew_deploy_hook_commands, - $renew_additional_args = $letsencrypt::params::renew_additional_args, - $renew_cron_ensure = $letsencrypt::params::renew_cron_ensure, - $renew_cron_hour = $letsencrypt::params::renew_cron_hour, - $renew_cron_minute = $letsencrypt::params::renew_cron_minute, - $renew_cron_monthday = $letsencrypt::params::renew_cron_monthday, -) inherits letsencrypt::params { + $renew_pre_hook_commands = [], + $renew_post_hook_commands = [], + $renew_deploy_hook_commands = [], + $renew_additional_args = [], + $renew_cron_ensure = 'absent', + $renew_cron_hour = fqdn_rand(24), + $renew_cron_minute = fqdn_rand(60, fqdn_rand_string(10)), + $renew_cron_monthday = '*', +) { if $manage_install { contain letsencrypt::install # lint:ignore:relative_classname_inclusion Class['letsencrypt::install'] ~> Exec['initialize letsencrypt'] Class['letsencrypt::install'] -> Class['letsencrypt::renew'] } $command = $install_method ? { 'package' => $package_command, 'vcs' => "${venv_path}/bin/letsencrypt", } $command_init = $install_method ? { 'package' => $package_command, 'vcs' => "${path}/letsencrypt-auto", } if $manage_config { contain letsencrypt::config # lint:ignore:relative_classname_inclusion Class['letsencrypt::config'] -> Exec['initialize letsencrypt'] } contain letsencrypt::renew # TODO: do we need this command when installing from package? exec { 'initialize letsencrypt': command => "${command_init} -h", path => $facts['path'], environment => concat([ "VENV_PATH=${venv_path}" ], $environment), refreshonly => true, } # Used in letsencrypt::certonly Exec["letsencrypt certonly ${title}"] file { '/usr/local/sbin/letsencrypt-domain-validation': ensure => file, owner => 'root', group => 'root', mode => '0500', source => "puppet:///modules/${module_name}/domain-validation.sh", } } diff --git a/manifests/params.pp b/manifests/params.pp deleted file mode 100644 index cbb1da5..0000000 --- a/manifests/params.pp +++ /dev/null @@ -1,85 +0,0 @@ -# @summary Default parameters -# @api private -class letsencrypt::params { - $agree_tos = true - $unsafe_registration = false - $manage_config = true - $manage_install = true - $manage_dependencies = true - $package_ensure = 'installed' - $path = '/opt/letsencrypt' - $venv_path = '/opt/letsencrypt/.venv' # virtualenv path for vcs-installed letsencrypt - $repo = 'https://github.com/certbot/certbot.git' - $cron_scripts_path = "${facts['puppet_vardir']}/letsencrypt" # path for renewal scripts called by cron - $version = 'v0.30.2' - $config = { - 'server' => 'https://acme-v02.api.letsencrypt.org/directory', - } - - if $facts['osfamily'] == 'Debian' { - $install_method = 'package' - $package_name = 'certbot' - $package_command = 'certbot' - $config_dir = '/etc/letsencrypt' - $dns_rfc2136_package_name = 'python3-certbot-dns-rfc2136' - } elsif $facts['osfamily'] == 'RedHat' { - $install_method = 'package' - $package_name = 'certbot' - $package_command = 'certbot' - $config_dir = '/etc/letsencrypt' - if $facts['operatingsystemmajrelease'] == '7' { - $dns_rfc2136_package_name = 'python2-certbot-dns-rfc2136' - } else { - $dns_rfc2136_package_name = 'python3-certbot-dns-rfc2136' - } - } elsif $facts['osfamily'] == 'Gentoo' { - $install_method = 'package' - $package_name = 'app-crypt/certbot' - $package_command = 'certbot' - $config_dir = '/etc/letsencrypt' - $dns_rfc2136_package_name = undef - } elsif $facts['osfamily'] == 'OpenBSD' { - $install_method = 'package' - $package_name = 'certbot' - $package_command = 'certbot' - $config_dir = '/etc/letsencrypt' - $dns_rfc2136_package_name = undef - } elsif $facts['osfamily'] == 'FreeBSD' { - $install_method = 'package' - $package_name = 'py27-certbot' - $package_command = 'certbot' - $config_dir = '/usr/local/etc/letsencrypt' - $dns_rfc2136_package_name = undef - } else { - $install_method = 'vcs' - $package_name = 'letsencrypt' - $package_command = 'letsencrypt' - $config_dir = '/etc/letsencrypt' - $dns_rfc2136_package_name = undef - } - - $config_file = "${config_dir}/cli.ini" - - $configure_epel = $facts['osfamily'] == 'RedHat' and $facts['os']['name'] != 'Fedora' - - $cron_owner_group = $facts['osfamily'] ? { - 'OpenBSD' => 'wheel', - 'FreeBSD' => 'wheel', - default => 'root', - } - - $renew_pre_hook_commands = [] - $renew_post_hook_commands = [] - $renew_deploy_hook_commands = [] - $renew_additional_args = [] - $renew_cron_ensure = 'absent' - $renew_cron_hour = fqdn_rand(24) - $renew_cron_minute = fqdn_rand(60, fqdn_rand_string(10)) - $renew_cron_monthday = '*' - - $dns_rfc2136_manage_package = true - $dns_rfc2136_port = 53 - $dns_rfc2136_algorithm = 'HMAC-SHA512' - $dns_rfc2136_propagation_seconds = 10 - -} diff --git a/manifests/plugin/dns_rfc2136.pp b/manifests/plugin/dns_rfc2136.pp index 9a790ba..2513565 100644 --- a/manifests/plugin/dns_rfc2136.pp +++ b/manifests/plugin/dns_rfc2136.pp @@ -1,53 +1,53 @@ # @summary Installs and configures the dns-rfc2136 plugin # # This class installs and configures the Let's Encrypt dns-rfc2136 plugin. # https://certbot-dns-rfc2136.readthedocs.io # # @param server Target DNS server. # @param key_name TSIG key name. # @param key_secret TSIG key secret. # @param key_algorithm TSIG key algorithm. # @param port Target DNS port. # @param propagation_seconds Number of seconds to wait for the DNS server to propagate the DNS-01 challenge. # @param manage_package Manage the plugin package. # @param package_name The name of the package to install when $manage_package is true. # @param config_dir The path to the configuration directory. # class letsencrypt::plugin::dns_rfc2136 ( Stdlib::Host $server, String[1] $key_name, String[1] $key_secret, - String[1] $key_algorithm = $letsencrypt::dns_rfc2136_algorithm, - Stdlib::Port $port = $letsencrypt::dns_rfc2136_port, - Integer $propagation_seconds = $letsencrypt::dns_rfc2136_propagation_seconds, + String[1] $key_algorithm = 'HMAC-SHA512', + Stdlib::Port $port = 53, + Integer $propagation_seconds = 10, Stdlib::Absolutepath $config_dir = $letsencrypt::config_dir, - Boolean $manage_package = $letsencrypt::dns_rfc2136_manage_package, - String $package_name = $letsencrypt::dns_rfc2136_package_name, + Boolean $manage_package = true, + String $package_name = undef, ) { if $manage_package { package { $package_name: ensure => installed, } } $ini_vars = { dns_rfc2136_server => $server, dns_rfc2136_port => $port, dns_rfc2136_name => $key_name, dns_rfc2136_secret => $key_secret, dns_rfc2136_algorithm => $key_algorithm, } file { "${config_dir}/dns-rfc2136.ini": ensure => file, owner => 'root', group => 'root', mode => '0400', content => epp('letsencrypt/ini.epp', { vars => { '' => $ini_vars }, }), require => Class['letsencrypt'], } } diff --git a/spec/acceptance/letsencrypt_spec.rb b/spec/acceptance/letsencrypt_spec.rb index 97099bf..7c30e7b 100644 --- a/spec/acceptance/letsencrypt_spec.rb +++ b/spec/acceptance/letsencrypt_spec.rb @@ -1,65 +1,65 @@ require 'spec_helper_acceptance' describe 'letsencrypt' do context 'with defaults values' do pp = %( class { 'letsencrypt' : email => 'letsregister@example.com', config => { 'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory', }, } ) it 'installs letsencrypt without error' do apply_manifest(pp, catch_failures: true) end it 'installs letsencrypt idempotently' do apply_manifest(pp, catch_changes: true) end describe file('/etc/letsencrypt/cli.ini') do it { is_expected.to be_file } it { is_expected.to be_owned_by 'root' } it { is_expected.to be_grouped_into 'root' } it { is_expected.to be_mode 644 } its(:content) { is_expected.to match %r{server = https://acme-staging-v02.api.letsencrypt.org/directory} } its(:content) { is_expected.to match %r{email = letsregister@example.com} } end end context 'with install_method => vcs' do pp = %( class { 'letsencrypt' : install_method => 'vcs', email => 'letsregister@example.com', config => { 'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory', }, } ) it 'installs letsencrypt without error' do apply_manifest(pp, catch_failures: true) end it 'installs letsencrypt idempotently' do apply_manifest(pp, catch_changes: true) end describe file('/etc/letsencrypt/cli.ini') do it { is_expected.to be_file } it { is_expected.to be_owned_by 'root' } it { is_expected.to be_grouped_into 'root' } it { is_expected.to be_mode 644 } its(:content) { is_expected.to match %r{server = https://acme-staging-v02.api.letsencrypt.org/directory} } its(:content) { is_expected.to match %r{email = letsregister@example.com} } end - describe file('/opt/letsencrypt/.venv/bin/letsencrypt') do + describe file('/opt/letsencrypt/.venv/bin/certbot') do it { is_expected.to be_file } it { is_expected.to be_owned_by 'root' } it { is_expected.to be_grouped_into 'root' } it { is_expected.to be_mode 755 } end end end diff --git a/spec/defines/letsencrypt_certonly_spec.rb b/spec/defines/letsencrypt_certonly_spec.rb index f5c7ee6..ea32bbb 100644 --- a/spec/defines/letsencrypt_certonly_spec.rb +++ b/spec/defines/letsencrypt_certonly_spec.rb @@ -1,434 +1,433 @@ require 'spec_helper' describe 'letsencrypt::certonly' do on_supported_os.each do |os, facts| context "on #{os} based operating systems" do let :facts do facts end let(:pre_condition) { "class { letsencrypt: email => 'foo@example.com', package_command => 'letsencrypt' }" } # FreeBSD uses a different filesystem path pathprefix = facts[:kernel] == 'FreeBSD' ? '/usr/local' : '' context 'with a single domain' do let(:title) { 'foo.example.com' } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('Letsencrypt::Install') } it { is_expected.to contain_class('Letsencrypt::Config') } - it { is_expected.to contain_class('Letsencrypt::Params') } if facts[:osfamily] == 'FreeBSD' it { is_expected.to contain_file('/usr/local/etc/letsencrypt') } it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com') } it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory') } else it { is_expected.to contain_file('/etc/letsencrypt') } it { is_expected.to contain_package('letsencrypt') } unless facts[:os]['release']['full'] == '14.04' it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini email foo@example.com') } it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory') } end it { is_expected.to contain_exec('initialize letsencrypt') } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com') } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless "/usr/local/sbin/letsencrypt-domain-validation #{pathprefix}/etc/letsencrypt/live/foo.example.com/cert.pem 'foo.example.com'" } end context 'with ensure absent' do let(:title) { 'foo.example.com' } let(:params) { { ensure: 'absent' } } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com') } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive delete --cert-name 'foo.example.com'" } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_onlyif "/usr/local/sbin/letsencrypt-domain-validation #{pathprefix}/etc/letsencrypt/live/foo.example.com/cert.pem 'foo.example.com'" } end context 'with multiple domains' do let(:title) { 'foo' } let(:params) { { domains: ['foo.example.com', 'bar.example.com', '*.example.com'] } } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo' -d 'foo.example.com' -d 'bar.example.com' -d '*.example.com'" } end context 'with custom command' do let(:title) { 'foo.example.com' } let(:params) { { letsencrypt_command: '/usr/lib/letsencrypt/letsencrypt-auto' } } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command '/usr/lib/letsencrypt/letsencrypt-auto --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name \'foo.example.com\' -d \'foo.example.com\'' } end context 'with webroot plugin' do let(:title) { 'foo.example.com' } let(:params) do { plugin: 'webroot', webroot_paths: ['/var/www/foo'] } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a webroot --cert-name 'foo.example.com' --webroot-path /var/www/foo -d 'foo.example.com'" } end context 'with webroot plugin and multiple domains' do let(:title) { 'foo' } let(:params) do { domains: ['foo.example.com', 'bar.example.com'], plugin: 'webroot', webroot_paths: ['/var/www/foo', '/var/www/bar'] } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a webroot --cert-name 'foo' --webroot-path /var/www/foo -d 'foo.example.com' --webroot-path /var/www/bar -d 'bar.example.com'" } end context 'with webroot plugin, one webroot, and multiple domains' do let(:title) { 'foo' } let(:params) do { domains: ['foo.example.com', 'bar.example.com'], plugin: 'webroot', webroot_paths: ['/var/www/foo'] } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a webroot --cert-name 'foo' --webroot-path /var/www/foo -d 'foo.example.com' -d 'bar.example.com'" } end context 'with webroot plugin and no webroot_paths' do let(:title) { 'foo.example.com' } let(:params) { { plugin: 'webroot' } } it { is_expected.not_to compile.with_all_deps } it { is_expected.to raise_error Puppet::Error, %r{'webroot_paths' parameter must be specified} } end context 'with dns-rfc2136 plugin' do let(:title) { 'foo.example.com' } let(:params) { { plugin: 'dns-rfc2136', letsencrypt_command: 'letsencrypt' } } let(:pre_condition) do <<-PUPPET class { 'letsencrypt': email => 'foo@example.com', config_dir => '/etc/letsencrypt', } class { 'letsencrypt::plugin::dns_rfc2136': server => '192.0.2.1', key_name => 'certbot', key_secret => 'secret', package_name => 'irrelevant', } PUPPET end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('letsencrypt::plugin::dns_rfc2136') } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a dns-rfc2136 --cert-name 'foo.example.com' -d 'foo.example.com' --dns-rfc2136-credentials /etc/letsencrypt/dns-rfc2136.ini --dns-rfc2136-propagation-seconds 10" } end context 'with custom plugin' do let(:title) { 'foo.example.com' } let(:params) { { plugin: 'apache' } } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com'" } end context 'with custom plugin and manage_cron' do let(:title) { 'foo.example.com' } let(:params) do { plugin: 'apache', manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command('"/var/lib/puppet/letsencrypt/renew-foo.example.com.sh"').with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with hook' do context 'pre' do let(:title) { 'foo.example.com' } let(:params) { { config_dir: '/etc/letsencrypt', pre_hook_commands: ['FooBar'] } } it do is_expected.to compile.with_all_deps is_expected.to contain_letsencrypt__hook('foo.example.com-pre').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/foo.example.com-pre.sh') end end context 'pre with wildcard domain' do let(:title) { '*.example.com' } let(:params) { { config_dir: '/etc/letsencrypt', pre_hook_commands: ['FooBar'] } } it do is_expected.to compile.with_all_deps is_expected.to contain_letsencrypt__hook('*.example.com-pre').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/example.com-pre.sh') end end context 'post' do let(:title) { 'foo.example.com' } let(:params) { { config_dir: '/etc/letsencrypt', post_hook_commands: ['FooBar'] } } it do is_expected.to compile.with_all_deps is_expected.to contain_letsencrypt__hook('foo.example.com-post').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/foo.example.com-post.sh') end end context 'deploy' do let(:title) { 'foo.example.com' } let(:params) { { config_dir: '/etc/letsencrypt', deploy_hook_commands: ['FooBar'] } } it do is_expected.to compile.with_all_deps is_expected.to contain_letsencrypt__hook('foo.example.com-deploy').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/foo.example.com-deploy.sh') end end end # context 'with hook' context 'with manage_cron and defined cron_hour (integer)' do let(:title) { 'foo.example.com' } let(:params) do { cron_hour: 13, manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_hour(13).with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with manage_cron and out of range defined cron_hour (integer)' do let(:title) { 'foo.example.com' } let(:params) do { cron_hour: 24, manage_cron: true } end it { is_expected.not_to compile.with_all_deps } it { is_expected.to raise_error Puppet::Error } end context 'with manage_cron and defined cron_hour (string)' do let(:title) { 'foo.example.com' } let(:params) do { cron_hour: '00', manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_hour('00').with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with manage_cron and defined cron_hour (array)' do let(:title) { 'foo.example.com' } let(:params) do { cron_hour: [1, 13], manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_hour([1, 13]).with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with manage_cron and defined cron_minute (integer)' do let(:title) { 'foo.example.com' } let(:params) do { cron_minute: 15, manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_minute(15).with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with manage_cron and out of range defined cron_hour (integer)' do let(:title) { 'foo.example.com' } let(:params) do { cron_hour: 66, manage_cron: true } end it { is_expected.not_to compile.with_all_deps } it { is_expected.to raise_error Puppet::Error } end context 'with manage_cron and defined cron_minute (string)' do let(:title) { 'foo.example.com' } let(:params) do { cron_minute: '15', manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_minute('15').with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with manage_cron and defined cron_minute (array)' do let(:title) { 'foo.example.com' } let(:params) do { cron_minute: [0, 30], manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_minute([0, 30]).with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with manage_cron and ensure absent' do let(:title) { 'foo.example.com' } let(:params) do { ensure: 'absent', manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_ensure('absent') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('absent') } end context 'with custom puppet_vardir path and manage_cron' do let :facts do super().merge(puppet_vardir: '/tmp/custom_vardir') end let(:title) { 'foo.example.com' } let(:params) do { plugin: 'apache', manage_cron: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_file('/tmp/custom_vardir/letsencrypt').with_ensure('directory') } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command '"/tmp/custom_vardir/letsencrypt/renew-foo.example.com.sh"' } it { is_expected.to contain_file('/tmp/custom_vardir/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with custom plugin and manage cron and cron_success_command' do let(:title) { 'foo.example.com' } let(:params) do { plugin: 'apache', manage_cron: true, cron_before_command: 'echo before', cron_success_command: 'echo success' } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command '"/var/lib/puppet/letsencrypt/renew-foo.example.com.sh"' } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\n(echo before) && letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring && (echo success)\n") } end context 'without plugin' do let(:title) { 'foo.example.com' } let(:params) { { custom_plugin: true } } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 --cert-name 'foo.example.com' -d 'foo.example.com'" } end context 'with invalid plugin' do let(:title) { 'foo.example.com' } let(:params) { { plugin: 'bad' } } it { is_expected.not_to compile.with_all_deps } it { is_expected.to raise_error Puppet::Error } end context 'when specifying additional arguments' do let(:title) { 'foo.example.com' } let(:params) { { additional_args: ['--foo bar', '--baz quux'] } } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --foo bar --baz quux" } end describe 'when specifying custom environment variables' do let(:title) { 'foo.example.com' } let(:params) { { environment: ['FOO=bar', 'FIZZ=buzz'] } } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_environment(['VENV_PATH=/opt/letsencrypt/.venv', 'FOO=bar', 'FIZZ=buzz']) } end context 'with custom environment variables and manage_cron' do let(:title) { 'foo.example.com' } let(:params) { { environment: ['FOO=bar', 'FIZZ=buzz'], manage_cron: true } } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_content "#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nexport FOO=bar\nexport FIZZ=buzz\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n" } end context 'with manage cron and suppress_cron_output' do\ let(:title) { 'foo.example.com' } let(:params) do { manage_cron: true, suppress_cron_output: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command('"/var/lib/puppet/letsencrypt/renew-foo.example.com.sh"').with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring > /dev/null 2>&1\n") } end context 'with manage cron and custom day of month' do let(:title) { 'foo.example.com' } let(:params) do { manage_cron: true, cron_monthday: [1, 15] } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with(monthday: [1, 15]).with_ensure('present') } it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --keep-until-expiring\n") } end context 'with custom config_dir' do let(:title) { 'foo.example.com' } let(:pre_condition) { "class { letsencrypt: email => 'foo@example.com', config_dir => '/foo/bar/baz', package_command => 'letsencrypt'}" } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_file('/foo/bar/baz').with_ensure('directory') } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless '/usr/local/sbin/letsencrypt-domain-validation /foo/bar/baz/live/foo.example.com/cert.pem \'foo.example.com\'' } end context 'on FreeBSD', if: facts[:os]['name'] == 'FreeBSD' do let(:title) { 'foo.example.com' } let(:pre_condition) { "class { letsencrypt: email => 'foo@example.com'}" } it { is_expected.to compile.with_all_deps } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command %r{^certbot} } it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com') } it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory') } it { is_expected.to contain_file('/usr/local/etc/letsencrypt').with_ensure('directory') } it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless '/usr/local/sbin/letsencrypt-domain-validation /usr/local/etc/letsencrypt/live/foo.example.com/cert.pem \'foo.example.com\'' } end end end end