Skip to content

Commit

Permalink
Merge pull request voxpupuli#279 from bkuebler/feature/plugins/dns-cl…
Browse files Browse the repository at this point in the history
…oudflare

Add Cloudflare DNS plugin support
  • Loading branch information
ekohl authored Apr 22, 2022
2 parents 291f4b2 + fcb715d commit 841f5c7
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 4 deletions.
79 changes: 79 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

* [`letsencrypt`](#letsencrypt): Install and configure Certbot, the LetsEncrypt client
* [`letsencrypt::install`](#letsencryptinstall): Installs the Let's Encrypt client.
* [`letsencrypt::plugin::dns_cloudflare`](#letsencryptplugindns_cloudflare): Installs and configures the dns-cloudflare plugin
* [`letsencrypt::plugin::dns_rfc2136`](#letsencryptplugindns_rfc2136): Installs and configures the dns-rfc2136 plugin
* [`letsencrypt::plugin::dns_route53`](#letsencryptplugindns_route53): Installs and configures the dns-route53 plugin
* [`letsencrypt::plugin::nginx`](#letsencryptpluginnginx): install and configure the Let's Encrypt nginx plugin
Expand Down Expand Up @@ -329,6 +330,84 @@ Name of package to use when installing the client package.

Default value: `$letsencrypt::package_name`

### <a name="letsencryptplugindns_cloudflare"></a>`letsencrypt::plugin::dns_cloudflare`

This class installs and configures the Let's Encrypt dns-cloudflare plugin.
https://certbot-dns-cloudflare.readthedocs.io

#### Parameters

The following parameters are available in the `letsencrypt::plugin::dns_cloudflare` class:

* [`package_name`](#package_name)
* [`api_key`](#api_key)
* [`api_token`](#api_token)
* [`email`](#email)
* [`config_dir`](#config_dir)
* [`manage_package`](#manage_package)
* [`propagation_seconds`](#propagation_seconds)
* [`config_path`](#config_path)

##### <a name="package_name"></a>`package_name`

Data type: `Optional[String[1]]`

The name of the package to install when $manage_package is true.

Default value: ``undef``

##### <a name="api_key"></a>`api_key`

Data type: `Optional[String[1]]`

Optional string, cloudflare api key value for authentication.

Default value: ``undef``

##### <a name="api_token"></a>`api_token`

Data type: `Optional[String[1]]`

Optional string, cloudflare api token value for authentication.

Default value: ``undef``

##### <a name="email"></a>`email`

Data type: `Optional[String[1]]`

Optional string, cloudflare account email address, used in conjunction with api_key.

Default value: ``undef``

##### <a name="config_dir"></a>`config_dir`

The path to the configuration directory.

##### <a name="manage_package"></a>`manage_package`

Data type: `Boolean`

Manage the plugin package.

Default value: ``true``

##### <a name="propagation_seconds"></a>`propagation_seconds`

Data type: `Integer`

Number of seconds to wait for the DNS server to propagate the DNS-01 challenge.

Default value: `10`

##### <a name="config_path"></a>`config_path`

Data type: `Stdlib::Absolutepath`



Default value: `"${letsencrypt::config_dir}/dns-cloudflare.ini"`

### <a name="letsencryptplugindns_rfc2136"></a>`letsencrypt::plugin::dns_rfc2136`

This class installs and configures the Let's Encrypt dns-rfc2136 plugin.
Expand Down
1 change: 1 addition & 0 deletions data/Debian-family.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
letsencrypt::plugin::dns_rfc2136::package_name: 'python3-certbot-dns-rfc2136'
letsencrypt::plugin::dns_route53::package_name: 'python3-certbot-dns-route53'
letsencrypt::plugin::dns_cloudflare::package_name: 'python3-certbot-dns-cloudflare'
1 change: 1 addition & 0 deletions data/FreeBSD-family.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ letsencrypt::config_dir: '/usr/local/etc/letsencrypt'
letsencrypt::cron_owner_group: 'wheel'
letsencrypt::plugin::dns_rfc2136::package_name: 'py38-certbot-dns-rfc2136'
letsencrypt::plugin::dns_route53::package_name: 'py38-certbot-dns-route53'
letsencrypt::plugin::dns_cloudflare::package_name: 'py38-certbot-dns-cloudflare'
1 change: 1 addition & 0 deletions data/RedHat-family.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
letsencrypt::configure_epel: true
letsencrypt::plugin::dns_rfc2136::package_name: 'python3-certbot-dns-rfc2136'
letsencrypt::plugin::dns_route53::package_name: 'python3-certbot-dns-route53'
letsencrypt::plugin::dns_cloudflare::package_name: 'python3-certbot-dns-cloudflare'
1 change: 1 addition & 0 deletions data/os/CentOS/7.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
letsencrypt::plugin::dns_rfc2136::package_name: 'python2-certbot-dns-rfc2136'
letsencrypt::plugin::dns_route53::package_name: 'python2-certbot-dns-route53'
letsencrypt::plugin::dns_cloudflare::package_name: 'python2-certbot-dns-cloudflare'
letsencrypt::plugin::nginx::package_name: 'python2-certbot-nginx'
1 change: 1 addition & 0 deletions data/os/RedHat/7.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
letsencrypt::plugin::dns_rfc2136::package_name: 'python2-certbot-dns-rfc2136'
letsencrypt::plugin::dns_route53::package_name: 'python2-certbot-dns-route53'
letsencrypt::plugin::dns_cloudflare::package_name: 'python2-certbot-dns-cloudflare'
letsencrypt::plugin::nginx::package_name: 'python2-certbot-nginx'
14 changes: 13 additions & 1 deletion manifests/certonly.pp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@
$plugin_args = ["--cert-name '${cert_name}'"] + $_plugin_args
}

'dns-cloudflare': {
require letsencrypt::plugin::dns_cloudflare
$_domains = join($domains, '\' -d \'')
$plugin_args = [
"--cert-name '${cert_name}' -d '${_domains}'",
'--dns-cloudflare',
"--dns-cloudflare-credentials ${letsencrypt::plugin::dns_cloudflare::config_path}",
"--dns-cloudflare-propagation-seconds ${letsencrypt::plugin::dns_cloudflare::propagation_seconds}",
]
}

'dns-rfc2136': {
require letsencrypt::plugin::dns_rfc2136
$_domains = join($domains, '\' -d \'')
Expand Down Expand Up @@ -242,7 +253,8 @@
$verify_domains = join(unique($domains), '\' \'')

if $ensure == 'present' {
$exec_ensure = { 'unless' => "/usr/local/sbin/letsencrypt-domain-validation ${live_path} '${verify_domains}'" }
$exec_ensure = { 'unless' => ['test ! -f /usr/local/sbin/letsencrypt-domain-validation',
"/usr/local/sbin/letsencrypt-domain-validation ${live_path} '${verify_domains}'"] }
} else {
$exec_ensure = { 'onlyif' => "/usr/local/sbin/letsencrypt-domain-validation ${live_path} '${verify_domains}'" }
}
Expand Down
67 changes: 67 additions & 0 deletions manifests/plugin/dns_cloudflare.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# @summary Installs and configures the dns-cloudflare plugin
#
# This class installs and configures the Let's Encrypt dns-cloudflare plugin.
# https://certbot-dns-cloudflare.readthedocs.io
#
# @param package_name The name of the package to install when $manage_package is true.
# @param api_key
# Optional string, cloudflare api key value for authentication.
# @param api_token
# Optional string, cloudflare api token value for authentication.
# @param email
# Optional string, cloudflare account email address, used in conjunction with api_key.
# @param config_dir The path to the configuration directory.
# @param manage_package Manage the plugin package.
# @param propagation_seconds Number of seconds to wait for the DNS server to propagate the DNS-01 challenge.
#
class letsencrypt::plugin::dns_cloudflare (
Optional[String[1]] $package_name = undef,
Optional[String[1]] $api_key = undef,
Optional[String[1]] $api_token = undef,
Optional[String[1]] $email = undef,
Stdlib::Absolutepath $config_path = "${letsencrypt::config_dir}/dns-cloudflare.ini",
Boolean $manage_package = true,
Integer $propagation_seconds = 10,
) {
require letsencrypt::install

if ! $api_key and ! $api_token {
fail('No authentication method provided, please specify either api_token or api_key and api_email.')
}

if $manage_package {
if ! $package_name {
fail('No package name provided for certbot dns cloudflare plugin.')
}

package { $package_name:
ensure => installed,
}
}

if $api_token {
$ini_vars = {
dns_cloudflare_api_token => $api_token,
}
}
else {
if ! $email {
fail('Cloudflare email not provided for specified api_key.')
}

$ini_vars = {
dns_cloudflare_api_key => $api_key,
dns_cloudflare_email => $email,
}
}

file { $config_path:
ensure => file,
owner => 'root',
group => 'root',
mode => '0400',
content => epp('letsencrypt/ini.epp', {
vars => { '' => $ini_vars },
}),
}
}
28 changes: 28 additions & 0 deletions spec/acceptance/letsencrypt_plugin_dns_cloudflare_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

require 'spec_helper_acceptance'

describe 'letsencrypt::plugin::dns_cloudflare' do
it_behaves_like 'an idempotent resource' do
let(:manifest) do
<<-PUPPET
class { 'letsencrypt' :
email => 'letsregister@example.com',
config => {
'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory',
},
}
class { 'letsencrypt::plugin::dns_cloudflare':
api_token => 'dummy-cloudflare-api-token',
}
PUPPET
end
end

describe file('/etc/letsencrypt/dns-cloudflare.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 400 }
end
end
60 changes: 60 additions & 0 deletions spec/classes/plugin/dns_cloudflare_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

require 'spec_helper'

describe 'letsencrypt::plugin::dns_cloudflare' do
on_supported_os.each do |os, os_facts|
context "on #{os} based operating systems" do
let(:facts) { os_facts }
let(:params) { { 'api_token' => 'dummy-cloudflare-api-token' } }
let(:pre_condition) do
<<-PUPPET
class { 'letsencrypt':
email => 'foo@example.com',
}
PUPPET
end
let(:package_name) do
osname = facts[:os]['name']
osrelease = facts[:os]['release']['major']
osfull = "#{osname}-#{osrelease}"
if %w[RedHat-7 CentOS-7].include?(osfull)
'python2-certbot-dns-cloudflare'
elsif %w[Debian RedHat].include?(facts[:os]['family'])
'python3-certbot-dns-cloudflare'
elsif %w[FreeBSD].include?(facts[:os]['family'])
'py38-certbot-dns-cloudflare'
end
end

context 'with required parameters' do
it do
if package_name.nil?
is_expected.not_to compile
else
is_expected.to compile.with_all_deps
end
end

describe 'with manage_package => true' do
let(:params) { super().merge(manage_package: true) }

it do
if package_name.nil?
is_expected.not_to compile
else
is_expected.to contain_class('letsencrypt::plugin::dns_cloudflare').with_package_name(package_name)
is_expected.to contain_package(package_name).with_ensure('installed')
end
end
end

describe 'with manage_package => false' do
let(:params) { super().merge(manage_package: false, package_name: 'dns-cloudflare-package') }

it { is_expected.not_to contain_package('dns-cloudflare-package') }
end
end
end
end
end
27 changes: 24 additions & 3 deletions spec/defines/letsencrypt_certonly_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
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'" }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless(['test ! -f /usr/local/sbin/letsencrypt-domain-validation', "/usr/local/sbin/letsencrypt-domain-validation #{pathprefix}/etc/letsencrypt/live/foo.example.com/cert.pem 'foo.example.com'"]) }
end

context 'with ensure absent' do
Expand Down Expand Up @@ -186,6 +186,27 @@ class { 'letsencrypt::plugin::nginx':
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 nginx --cert-name 'foo.example.com' -d 'foo.example.com'" }
end

context 'with dns-cloudflare plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'dns-cloudflare', letsencrypt_command: 'letsencrypt' } }
let(:pre_condition) do
<<-PUPPET
class { 'letsencrypt':
email => 'foo@example.com',
config_dir => '/etc/letsencrypt',
}
class { 'letsencrypt::plugin::dns_cloudflare':
package_name => 'irrelevant',
api_token => 'dummy-cloudflare-api-token',
}
PUPPET
end

it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('letsencrypt::plugin::dns_cloudflare') }
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-cloudflare --cert-name 'foo.example.com' -d 'foo.example.com' --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/dns-cloudflare.ini --dns-cloudflare-propagation-seconds 10" }
end

context 'with custom plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'apache' } }
Expand Down Expand Up @@ -462,7 +483,7 @@ class { 'letsencrypt::plugin::nginx':

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\'' }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless(['test ! -f /usr/local/sbin/letsencrypt-domain-validation', '/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
Expand All @@ -474,7 +495,7 @@ class { 'letsencrypt::plugin::nginx':
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\'' }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless(['test ! -f /usr/local/sbin/letsencrypt-domain-validation', '/usr/local/sbin/letsencrypt-domain-validation /usr/local/etc/letsencrypt/live/foo.example.com/cert.pem \'foo.example.com\'']) }
end
end
end
Expand Down

0 comments on commit 841f5c7

Please sign in to comment.