Showing with 244 additions and 55 deletions.
  1. +12 −0 .travis.yml
  2. +1 −0 CHANGELOG.md
  3. +12 −3 Gemfile
  4. +34 −1 README.md
  5. +37 −19 manifests/certonly.pp
  6. +25 −5 manifests/init.pp
  7. +6 −1 manifests/install.pp
  8. +22 −4 manifests/params.pp
  9. +3 −3 metadata.json
  10. +6 −4 spec/classes/letsencrypt_install_spec.rb
  11. +55 −11 spec/classes/letsencrypt_spec.rb
  12. +31 −4 spec/defines/letsencrypt_certonly_spec.rb
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ rvm:
- 1.9.3
- 2.0.0
- 2.1
- 2.2.5
- 2.3.1
script: bundle exec rake test
env:
- PUPPET_VERSION="~> 3.4.0"
Expand All @@ -17,3 +19,13 @@ env:
- PUPPET_VERSION="~> 4.3.0"
- PUPPET_VERSION="~> 4"
- PUPPET_VERSION="~> 4" STRICT_VARIABLES="yes"
matrix:
exclude:
- rvm: 2.2.5
env: PUPPET_VERSION="~> 3.4"
- rvm: 2.2.5
env: PUPPET_VERSION="~> 3.4.0"
- rvm: 2.3.1
env: PUPPET_VERSION="~> 3.4"
- rvm: 2.3.1
env: PUPPET_VERSION="~> 3.4.0"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased][unreleased]
- Ability to run commands after a successful cronjob-based renewal with the `cron_success_command` parameter.

## [1.0.0] - 2016-02-22
## Added
Expand Down
15 changes: 12 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ source 'https://rubygems.org'
group :test do
gem 'rake'
gem 'puppet', ENV['PUPPET_VERSION'] || ['>= 3.4', '< 5']
gem 'puppet-lint'
gem 'puppet-lint', '~> 1.0'
gem 'rspec-puppet'
gem 'puppet-syntax'
gem 'puppetlabs_spec_helper'
gem 'metadata-json-lint'
unless ENV['PUPPET_VERSION'] == '~> 3.4.0'
gem 'puppet-strings', git: 'git://github.com/puppetlabs/puppetlabs-strings.git'
gem 'puppet-strings'
end
gem 'puppet-lint-absolute_classname-check'
gem 'puppet-lint-alias-check'
Expand All @@ -23,7 +23,16 @@ group :test do
gem 'puppet-lint-undef_in_function-check'
gem 'puppet-lint-unquoted_string-check'
gem 'puppet-lint-variable_contains_upcase'
gem 'rubocop'

if ENV['TRAVIS_RUBY_VERSION'] < '2.2'
gem 'json_pure', '~> 1.8'
end

if ENV['TRAVIS_RUBY_VERSION'] < '2.0'
gem 'rubocop', '~> 0.39.0'
else
gem 'rubocop'
end
end

group :development do
Expand Down
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# **THIS MODULE HAS BEEN MOVED TO VOXPUPULI**

This module has been donated to Vox Pupuli and can be found at https://github.com/voxpupuli/puppet-letsencrypt.

[![Puppet Forge](http://img.shields.io/puppetforge/v/danzilio/letsencrypt.svg?style=flat)](https://forge.puppetlabs.com/danzilio/letsencrypt) [![Build Status](https://travis-ci.org/danzilio/puppet-letsencrypt.svg)](https://travis-ci.org/danzilio/puppet-letsencrypt) [![Documentation Status](http://img.shields.io/badge/docs-puppet--strings-ff69b4.svg?style=flat)](http://danzilio.github.io/puppet-letsencrypt)

This module installs the Let's Encrypt client from source and allows you to request certificates.
Expand All @@ -13,7 +17,7 @@ On EL (Red Hat, CentOS etc.) systems, the EPEL repository needs to be enabled
for the Let's Encrypt client package.

The module can integrate with [stahnma/epel](https://forge.puppetlabs.com/stahnma/epel)
to set up the repo by setting the `configure_epel` parameter to `true` and
to set up the repo by setting the `configure_epel` parameter to `true` (the default for RedHat) and
installing the module.

## Usage
Expand All @@ -36,6 +40,8 @@ class { ::letsencrypt:
}
```

(If you manage epel some other way, disable it with `configure_epel => false`.)

This will install the Let's Encrypt client and its dependencies, agree to the
Terms of Service, initialize the client, and install a configuration file for
the client.
Expand All @@ -50,6 +56,9 @@ class { ::letsencrypt:
}
}
```
During testing, you probably want to direct to the staging server instead with
`server => 'https://acme-staging.api.letsencrypt.org/directory'`


If you don't wish to provide your email address, you can set the
`unsafe_registration` parameter to `true` (this is not recommended):
Expand Down Expand Up @@ -77,6 +86,19 @@ letsencrypt::certonly { 'foo':
}
```

To request a certificate using the `webroot` plugin, the paths to the webroots
for all domains must be given through `webroot_paths`. If `domains` and
`webroot_paths` are not the same length, the last `webroot_paths` element will
be used for all subsequent domains.

```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
plugin => 'webroot',
webroot_paths => ['/var/www/foo', '/var/www/bar'],
}
```

If you need to pass a command line flag to the `letsencrypt-auto` command that
is not supported natively by this module, you can use the `additional_args`
parameter to pass those arguments:
Expand All @@ -89,6 +111,17 @@ letsencrypt::certonly { 'foo':
}
```

To automatically renew a certificate, you can pass the `manage_cron` parameter.
You can optionally add a shell command to be run on success using the `cron_success_command` parameter.

```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
manage_cron => true,
cron_success_command => '/bin/systemctl reload nginx.service',
}
```

## Development

1. Fork it
Expand Down
56 changes: 37 additions & 19 deletions manifests/certonly.pp
Original file line number Diff line number Diff line change
Expand Up @@ -12,61 +12,79 @@
# [*webroot_paths*]
# 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, `webroot_paths`
# will cycle to make up the difference.
# `webroot_paths` are not the same length, the last `webroot_paths`
# element will be used for all subsequent domains.
# [*letsencrypt_command*]
# Command to run letsencrypt
# [*additional_args*]
# An array of additional command line arguments to pass to the
# `letsencrypt-auto` command.
# [*environment*]
# An optional array of environment variables (in addition to VENV_PATH).
# [*manage_cron*]
# Boolean indicating whether or not to schedule cron job for renewal.
# Runs daily but only renews if near expiration, e.g. within 10 days.
# [*cron_success_command*]
# String representation of a command that should be run if the renewal command
# succeeds.
#
define letsencrypt::certonly (
$domains = [$title],
$plugin = 'standalone',
$webroot_paths = undef,
$letsencrypt_command = $letsencrypt::command,
$additional_args = undef,
$manage_cron = false,
$domains = [$title],
$plugin = 'standalone',
$webroot_paths = undef,
$letsencrypt_command = $letsencrypt::command,
$additional_args = undef,
$environment = [],
$manage_cron = false,
$cron_success_command = undef,
) {
validate_array($domains)
validate_re($plugin, ['^apache$', '^standalone$', '^webroot$'])
if $webroot_paths {
validate_array($webroot_paths)
} elsif $plugin == 'webroot' {
fail("The 'webroot_paths' parameter must be specified when using the 'webroot' plugin")
}
validate_string(letsencrypt_path)
validate_string($letsencrypt_command)
if $additional_args {
validate_array($additional_args)
}
validate_array($environment)
validate_bool($manage_cron)

$command_start = "${letsencrypt_command} --agree-tos certonly -a ${plugin} "
$command_domains = $plugin ? {
'webroot' => inline_template('<%= @domains.zip(@webroot_paths.cycle).map { |domain| "--webroot-path #{domain[1]} -d #{domain[0]}"}.join(" ") %>'),
'webroot' => inline_template('<%= @domains.zip(@webroot_paths).map { |domain| "#{"--webroot-path #{domain[1]} " if domain[1]}-d #{domain[0]}"}.join(" ") %>'),
default => inline_template('-d <%= @domains.join(" -d ")%>'),
}
$command_end = inline_template('<% if @additional_args %> <%= @additional_args.join(" ") %><%end%>')
$command = "${command_start}${command_domains}${command_end}"
$live_path = inline_template('/etc/letsencrypt/live/<%= @domains.first %>/cert.pem')

$venv_path_var = "VENV_PATH=${letsencrypt::venv_path}"
exec { "letsencrypt certonly ${title}":
command => $command,
path => $::path,
creates => $live_path,
require => Class['letsencrypt'],
command => $command,
path => $::path,
environment => concat([ $venv_path_var ], $environment),
creates => $live_path,
require => Class['letsencrypt'],
}

if $manage_cron {
$renewcommand = "${command_start}--keep-until-expiring ${command_domains}${command_end}"
$renewcommand = "${command_start}--keep-until-expiring --quiet ${command_domains}${command_end}"
if $cron_success_command {
$cron_cmd = "${renewcommand} && (${cron_success_command})"
} else {
$cron_cmd = $renewcommand
}
$cron_hour = fqdn_rand(24, $title) # 0 - 23, seed is title plus fqdn
$cron_minute = fqdn_rand(60, $title ) # 0 - 59, seed is title plus fqdn
cron { "letsencrypt renew cron ${title}":
command => $renewcommand,
user => root,
hour => $cron_hour,
minute => $cron_minute,
command => $cron_cmd,
environment => concat([ $venv_path_var ], $environment),
user => root,
hour => $cron_hour,
minute => $cron_minute,
}
}
}
30 changes: 25 additions & 5 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# precedence over an 'email' setting defined in $config.
# [*path*]
# The path to the letsencrypt installation.
# [*environment*]
# An optional array of environment variables (in addition to VENV_PATH)
# [*repo*]
# A Git URL to install the Let's encrypt client from.
# [*version*]
Expand All @@ -17,6 +19,12 @@
# [*package_ensure*]
# The value passed to `ensure` when installing the client with the `package`
# method.
# [*package_name*]
# Name of package and command to use when installing the client with the
# `package` method.
# [*package_command*]
# Path or name for letsencrypt executable when installing the client with
# the `package` method.
# [*config_file*]
# The path to the configuration file for the letsencrypt cli.
# [*config*]
Expand All @@ -42,9 +50,13 @@
class letsencrypt (
$email = undef,
$path = $letsencrypt::params::path,
$venv_path = $letsencrypt::params::venv_path,
$environment = [],
$repo = $letsencrypt::params::repo,
$version = $letsencrypt::params::version,
$package_name = $letsencrypt::params::package_name,
$package_ensure = $letsencrypt::params::package_ensure,
$package_command = $letsencrypt::params::package_command,
$config_file = $letsencrypt::params::config_file,
$config = $letsencrypt::params::config,
$manage_config = $letsencrypt::params::manage_config,
Expand All @@ -55,32 +67,40 @@
$agree_tos = $letsencrypt::params::agree_tos,
$unsafe_registration = $letsencrypt::params::unsafe_registration,
) inherits letsencrypt::params {
validate_string($path, $repo, $version, $config_file)
validate_string($path, $repo, $version, $config_file, $package_name, $package_command)
if $email {
validate_string($email)
}
validate_array($environment)
validate_bool($manage_config, $manage_install, $manage_dependencies, $configure_epel, $agree_tos, $unsafe_registration)
validate_hash($config)
validate_re($install_method, ['^package$', '^vcs$'])

if $manage_install {
contain letsencrypt::install
contain letsencrypt::install # lint:ignore:relative_classname_inclusion
Class['letsencrypt::install'] ~> Exec['initialize letsencrypt']
}

$command = $install_method ? {
'package' => 'letsencrypt',
'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
contain letsencrypt::config # lint:ignore:relative_classname_inclusion
Class['letsencrypt::config'] -> Exec['initialize letsencrypt']
}

# TODO: do we need this command when installing from package?
exec { 'initialize letsencrypt':
command => "${command} -h",
command => "${command_init} -h",
path => $::path,
environment => concat([ "VENV_PATH=${venv_path}" ], $environment),
refreshonly => true,
}
}
7 changes: 6 additions & 1 deletion manifests/install.pp
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,24 @@
# [*package_ensure*]
# The value passed to `ensure` when installing the client with the `package`
# method.
# [*package_name*]
# Name of package to use when installing the client with the `package`
# method.
#
class letsencrypt::install (
$manage_install = $letsencrypt::manage_install,
$manage_dependencies = $letsencrypt::manage_dependencies,
$configure_epel = $letsencrypt::configure_epel,
$install_method = $letsencrypt::install_method,
$package_name = $letsencrypt::package_name,
$package_ensure = $letsencrypt::package_ensure,
$path = $letsencrypt::path,
$repo = $letsencrypt::repo,
$version = $letsencrypt::version,
) {
validate_bool($manage_install, $manage_dependencies, $configure_epel)
validate_re($install_method, ['^package$', '^vcs$'])
validate_string($path, $repo, $version)
validate_string($path, $repo, $version, $package_name)

if $install_method == 'vcs' {
if $manage_dependencies {
Expand All @@ -55,6 +59,7 @@
} else {
package { 'letsencrypt':
ensure => $package_ensure,
name => $package_name,
}

if $configure_epel {
Expand Down
26 changes: 22 additions & 4 deletions manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,41 @@
$manage_config = true
$manage_install = true
$manage_dependencies = true
$configure_epel = true
$package_ensure = 'installed'
$config_file = '/etc/letsencrypt/cli.ini'
$path = '/opt/letsencrypt'
$repo = 'git://github.com/letsencrypt/letsencrypt.git'
$version = 'v0.1.0'
$venv_path = '/opt/letsencrypt/.venv' # virtualenv path for vcs-installed letsencrypt
$repo = 'https://github.com/letsencrypt/letsencrypt.git'
$version = 'v0.9.3'
$config = {
'server' => 'https://acme-v01.api.letsencrypt.org/directory',
}

if $::operatingsystem == 'Debian' and versioncmp($::operatingsystemrelease, '9') >= 0 {
$install_method = 'package'
$package_name = 'letsencrypt'
$package_command = 'letsencrypt'
} elsif $::operatingsystem == 'Ubuntu' and versioncmp($::operatingsystemrelease, '16.04') >= 0 {
$install_method = 'package'
} elsif $::osfamily == 'RedHat' {
$package_name = 'letsencrypt'
$package_command = 'letsencrypt'
} elsif $::osfamily == 'RedHat' and versioncmp($::operatingsystemmajrelease, '7') >= 0 {
$install_method = 'package'
$package_name = 'certbot'
$package_command = 'certbot'
} elsif $::osfamily == 'Gentoo' {
$install_method = 'package'
$package_name = 'app-crypt/certbot'
$package_command = 'certbot'
} else {
$install_method = 'vcs'
$package_name = 'letsencrypt'
$package_command = 'letsencrypt'
}

if $::osfamily == 'RedHat' {
$configure_epel = true
} else {
$configure_epel = false
}
}
Loading