Skip to content

Commit

Permalink
profile::certbot_incommon
Browse files Browse the repository at this point in the history
  • Loading branch information
rrotter committed Jan 30, 2025
1 parent 1109dc0 commit 727343a
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 0 deletions.
78 changes: 78 additions & 0 deletions manifests/profile/certbot_incommon.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# @param certs domains/certificates with haproxy services; wildcard implicitly added
# example: "quod": { "somejournal.org": ["san-for-journal.org"] }
# @param simple_certs domains/certificates for standalone hosts; no implicit wildcard
# example: "somedomain.org": ["san-for-domain.org","*.somedomain.org"]
class nebula::profile::certbot_incommon (
Hash[String, Hash[String, Array[String]]] $certs = {},
Hash[String, Array[String]] $simple_certs = {},
String $cert_dir = '/var/local/cert_dir',
String $haproxy_cert_dir = '/var/local/haproxy_cert_dir',
String $letsencrypt_email = '[email protected]',
String $server = 'https://acme.sectigo.com/v2/InCommonECCOV'
) {
ensure_packages([
'certbot',
])

file { '/tmp/all_cert_commands_incommon':
content => template('nebula/profile/certbot_incommon/commands.erb')
}

$certs.each |$service, $domains| {
$domains.each |$main_domain, $alt_domains| {
concat { "${cert_dir}/${main_domain}.crt":
group => 'puppet',
}

concat { "${cert_dir}/${main_domain}.key":
group => 'puppet',
}

concat { "${haproxy_cert_dir}/${service}/${main_domain}.pem":
group => 'puppet',
}

concat_fragment { "${main_domain}.crt cert":
target => "${cert_dir}/${main_domain}.crt",
source => "/etc/letsencrypt/live/${main_domain}/fullchain.pem"
}

concat_fragment { "${main_domain}.key key":
target => "${cert_dir}/${main_domain}.key",
source => "/etc/letsencrypt/live/${main_domain}/privkey.pem"
}

concat_fragment { "${main_domain}.pem cert":
order => '01',
target => "${haproxy_cert_dir}/${service}/${main_domain}.pem",
source => "/etc/letsencrypt/live/${main_domain}/fullchain.pem"
}

concat_fragment { "${main_domain}.pem key":
order => '02',
target => "${haproxy_cert_dir}/${service}/${main_domain}.pem",
source => "/etc/letsencrypt/live/${main_domain}/privkey.pem"
}
}
}

$simple_certs.each |$domain, $sans| {
concat { "${cert_dir}/${domain}.crt":
group => 'puppet',
}

concat { "${cert_dir}/${domain}.key":
group => 'puppet',
}

concat_fragment { "${cert_dir}/${domain}.crt cert":
target => "${cert_dir}/${domain}.crt",
source => "/etc/letsencrypt/live/${domain}/fullchain.pem",
}

concat_fragment { "${cert_dir}/${domain}.key key":
target => "${cert_dir}/${domain}.key",
source => "/etc/letsencrypt/live/${domain}/privkey.pem",
}
}
}
1 change: 1 addition & 0 deletions manifests/role/puppet/master.pp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
include nebula::profile::puppet::master
include nebula::profile::certbot_route53
include nebula::profile::certbot_cloudflare
include nebula::profile::certbot_incommon

# FIXME because there's also a git repo
include nebula::profile::krb5
Expand Down
207 changes: 207 additions & 0 deletions spec/classes/profile/certbot_incommon_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# frozen_string_literal: true

# Copyright (c) 2022 The Regents of the University of Michigan.
# All Rights Reserved. Licensed according to the terms of the Revised
# BSD License. See LICENSE.txt for details.
require "spec_helper"

describe "nebula::profile::certbot_incommon" do
on_supported_os.each do |os, os_facts|
context "on #{os}" do
let(:facts) { os_facts }

it { is_expected.to compile }

it { is_expected.to contain_package("certbot") }

context "with a single cert" do
let(:params) do
{
certs: {"onlyservice" => {"example.invalid" => []}},
cert_dir: "/certs",
haproxy_cert_dir: "/haproxy",
letsencrypt_email: "[email protected]"
}
end

it { is_expected.to compile }

it do
expect(subject).to contain_file("/tmp/all_cert_commands_incommon")
.with_content(
<<~CREDENTIALS
certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "example.invalid,*.example.invalid"
CREDENTIALS
)
end

it { is_expected.to contain_concat("/certs/example.invalid.crt").with_group("puppet") }
it { is_expected.to contain_concat("/certs/example.invalid.key").with_group("puppet") }
it { is_expected.to contain_concat("/haproxy/onlyservice/example.invalid.pem").with_group("puppet") }

it do
expect(subject).to contain_concat_fragment("example.invalid.crt cert")
.with_target("/certs/example.invalid.crt")
.with_source("/etc/letsencrypt/live/example.invalid/fullchain.pem")
end

it do
expect(subject).to contain_concat_fragment("example.invalid.key key")
.with_target("/certs/example.invalid.key")
.with_source("/etc/letsencrypt/live/example.invalid/privkey.pem")
end

it do
expect(subject).to contain_concat_fragment("example.invalid.pem cert")
.with_order("01")
.with_target("/haproxy/onlyservice/example.invalid.pem")
.with_source("/etc/letsencrypt/live/example.invalid/fullchain.pem")
end

it do
expect(subject).to contain_concat_fragment("example.invalid.pem key")
.with_order("02")
.with_target("/haproxy/onlyservice/example.invalid.pem")
.with_source("/etc/letsencrypt/live/example.invalid/privkey.pem")
end
end

context "with a multiple certs and services" do
let(:params) do
{
certs: {
"a" => {"abc.invalid" => %w[abc.example], "abc.com" => []},
"z" => {"zyx.invalid" => []}
}
}
end

it do
expect(subject).to contain_file("/tmp/all_cert_commands_incommon")
.with_content(
<<~CREDENTIALS
certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "abc.invalid,*.abc.invalid,abc.example,*.abc.example"
certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "abc.com,*.abc.com"
certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "zyx.invalid,*.zyx.invalid"
CREDENTIALS
)
end

it { is_expected.to compile }

it { is_expected.to contain_concat("/var/local/cert_dir/abc.invalid.crt") }
it { is_expected.to contain_concat("/var/local/cert_dir/abc.invalid.key") }
it { is_expected.to contain_concat("/var/local/haproxy_cert_dir/a/abc.invalid.pem") }
it { is_expected.to contain_concat_fragment("abc.invalid.crt cert") }
it { is_expected.to contain_concat_fragment("abc.invalid.key key") }
it { is_expected.to contain_concat_fragment("abc.invalid.pem cert") }
it { is_expected.to contain_concat_fragment("abc.invalid.pem key") }

it { is_expected.to contain_concat("/var/local/cert_dir/abc.com.crt") }
it { is_expected.to contain_concat("/var/local/cert_dir/abc.com.key") }
it { is_expected.to contain_concat("/var/local/haproxy_cert_dir/a/abc.com.pem") }
it { is_expected.to contain_concat_fragment("abc.com.crt cert") }
it { is_expected.to contain_concat_fragment("abc.com.key key") }
it { is_expected.to contain_concat_fragment("abc.com.pem cert") }
it { is_expected.to contain_concat_fragment("abc.com.pem key") }

it { is_expected.to contain_concat("/var/local/cert_dir/zyx.invalid.crt") }
it { is_expected.to contain_concat("/var/local/cert_dir/zyx.invalid.key") }
it { is_expected.to contain_concat("/var/local/haproxy_cert_dir/z/zyx.invalid.pem") }
it { is_expected.to contain_concat_fragment("zyx.invalid.crt cert") }
it { is_expected.to contain_concat_fragment("zyx.invalid.key key") }
it { is_expected.to contain_concat_fragment("zyx.invalid.pem cert") }
it { is_expected.to contain_concat_fragment("zyx.invalid.pem key") }

it { is_expected.not_to contain_concat("/var/local/cert_dir/abc.example.crt") }
it { is_expected.not_to contain_concat("/var/local/cert_dir/abc.example.key") }
it { is_expected.not_to contain_concat("/var/local/haproxy_cert_dir/a/abc.example.pem") }
it { is_expected.not_to contain_concat_fragment("abc.example.crt cert") }
it { is_expected.not_to contain_concat_fragment("abc.example.key key") }
it { is_expected.not_to contain_concat_fragment("abc.example.pem cert") }
it { is_expected.not_to contain_concat_fragment("abc.example.pem key") }
end

context "with one simple cert, no SANs" do
let(:params) do
{
simple_certs: {
"abc.example": []
}
}
end

it { is_expected.to compile }

it do
expect(subject).to contain_file("/tmp/all_cert_commands_incommon")
.with_content(%r{certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "abc.example"})
end

it do
expect(subject).to contain_concat_fragment("/var/local/cert_dir/abc.example.crt cert")
.with_target("/var/local/cert_dir/abc.example.crt")
.with_source("/etc/letsencrypt/live/abc.example/fullchain.pem")
end

it do
expect(subject).to contain_concat_fragment("/var/local/cert_dir/abc.example.key key")
.with_target("/var/local/cert_dir/abc.example.key")
.with_source("/etc/letsencrypt/live/abc.example/privkey.pem")
end
end

context "with one simple cert, two SANs" do
let(:params) do
{
simple_certs: {
"abc.example": ["san.local", "xyz.local"]
}
}
end

it do
expect(subject).to contain_file("/tmp/all_cert_commands_incommon")
.with_content(
<<~CREDENTIALS
certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "abc.example,san.local,xyz.local"
CREDENTIALS
)
end
end

context "with two simple certs" do
let(:params) do
{
simple_certs: {
"abc.example": [],
"xyz.example": ["alt.example", "*.alt.example"]
}
}
end

it do
expect(subject).to contain_file("/tmp/all_cert_commands_incommon")
.with_content(%r{certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "abc.example"})
end

it do
expect(subject).to contain_file("/tmp/all_cert_commands_incommon")
.with_content(%r{certbot certonly --standalone --server "https://acme.sectigo.com/v2/InCommonECCOV" -m "[email protected]" -d "xyz.example,alt.example,\*.alt.example"})
end

it do
expect(subject).to contain_concat_fragment("/var/local/cert_dir/abc.example.crt cert")
.with_target("/var/local/cert_dir/abc.example.crt")
.with_source("/etc/letsencrypt/live/abc.example/fullchain.pem")
end

it do
expect(subject).to contain_concat_fragment("/var/local/cert_dir/xyz.example.crt cert")
.with_target("/var/local/cert_dir/xyz.example.crt")
.with_source("/etc/letsencrypt/live/xyz.example/fullchain.pem")
end
end
end
end
end
10 changes: 10 additions & 0 deletions templates/profile/certbot_incommon/commands.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<% @certs.each do |service, domains| -%>
<% domains.each do |main_domain, alt_domains| -%>
<% full_san = [main_domain, "*.#{main_domain}"] -%>
<% alt_domains.each { |x| full_san += [x, "*.#{x}"] } -%>
certbot certonly --standalone --server "<%= @server %>" -m "<%= @letsencrypt_email %>" -d "<%= full_san.join(",") %>"
<% end -%>
<% end -%>
<% @simple_certs.each do |domain, sans| -%>
certbot certonly --standalone --server "<%= @server %>" -m "<%= @letsencrypt_email %>" -d "<%= [domain, sans].flatten.join(",") %>"
<% end -%>

0 comments on commit 727343a

Please sign in to comment.