summaryrefslogtreecommitdiff
path: root/code/environments/production/modules/ca_extend/plans
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2022-02-08 11:51:45 +0100
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2022-02-08 11:51:49 +0100
commiteff99e3e61f4e216b055a805002f5ece8567a915 (patch)
tree69771d87bfc3302980625e5524d02d2b455baadb /code/environments/production/modules/ca_extend/plans
parent74e2687fa98ea5ba25fbe07c038253d1fc14584e (diff)
downloadpuppet.KATH-eff99e3e61f4e216b055a805002f5ece8567a915.tar.gz
puppet.KATH-eff99e3e61f4e216b055a805002f5ece8567a915.tar.bz2
puppet.KATH-eff99e3e61f4e216b055a805002f5ece8567a915.zip
code/environments/production/modules: Add ca_extend module instead.
Diffstat (limited to 'code/environments/production/modules/ca_extend/plans')
-rw-r--r--code/environments/production/modules/ca_extend/plans/extend_ca_cert.pp84
-rw-r--r--code/environments/production/modules/ca_extend/plans/get_agent_facts.pp6
-rw-r--r--code/environments/production/modules/ca_extend/plans/upload_ca_cert.pp84
3 files changed, 174 insertions, 0 deletions
diff --git a/code/environments/production/modules/ca_extend/plans/extend_ca_cert.pp b/code/environments/production/modules/ca_extend/plans/extend_ca_cert.pp
new file mode 100644
index 0000000..84e4d42
--- /dev/null
+++ b/code/environments/production/modules/ca_extend/plans/extend_ca_cert.pp
@@ -0,0 +1,84 @@
+# @summary
+# Plan that extends the Puppet CA certificate and configures the primary Puppet server
+# and Compilers to use the extended certificate.
+# @param targets The target node on which to run the plan. Should be the primary Puppet server
+# @param compilers Optional comma separated list of compilers to upload the certificate to
+# @param ssldir Location of the ssldir on disk
+# @param regen_primary_cert Whether to also regenerate the agent certificate of the primary Puppet server
+# @example Extend the CA cert and regenerate the primary agent cert locally on the primary Puppet server
+# bolt plan run ca_extend::extend_ca_cert regen_primary_cert=true --targets local://$(hostname -f) --run-as root
+# @example Extend the CA cert by running the plan remotely
+# bolt plan run ca_extend::extend_ca_cert --targets <primary_fqdn> --run-as root
+plan ca_extend::extend_ca_cert(
+ TargetSpec $targets,
+ Optional[TargetSpec] $compilers = undef,
+ $ssldir = '/etc/puppetlabs/puppet/ssl',
+ $regen_primary_cert = false,
+) {
+ $targets.apply_prep
+ $primary_facts = run_task('facts', $targets, '_catch_errors' => true).first
+
+ if $primary_facts['pe_build'] {
+ $is_pe = true
+ $services = ['puppet', 'pe-puppetserver', 'pe-postgresql']
+ }
+ elsif $primary_facts['puppetversion'] {
+ $is_pe = false
+ $services = ['puppet', 'puppetserver']
+ }
+ else {
+ fail_plan("Puppet not detected on ${targets}")
+ }
+
+ if $is_pe and ! $regen_primary_cert{
+ $out = run_task('ca_extend::check_primary_cert', $targets, '_catch_errors' => true).first
+ unless $out.ok {
+ fail_plan($out.value['message'])
+ }
+ if $out.value['status'] == 'warn' {
+ warning($out.value['message'])
+ }
+ }
+
+ out::message("INFO: Stopping Puppet services on ${targets}")
+ $services.each |$service| {
+ run_task('service::linux', $targets, 'action' => 'stop', 'name' => $service)
+ }
+
+ out::message("INFO: Extending CA certificate on ${targets}")
+ $regen_results = run_task('ca_extend::extend_ca_cert', $targets)
+ $new_cert = $regen_results.first.value
+ $cert_contents = base64('decode', $new_cert['contents'])
+
+ out::message("INFO: Configuring ${targets} to use the extended CA certificate")
+ if $is_pe {
+ run_task('ca_extend::configure_primary', $targets,
+ 'new_cert' => $new_cert['new_cert'], 'regen_primary_cert' => $regen_primary_cert
+ )
+ }
+ else {
+ run_command("/bin/cp ${new_cert['new_cert']} ${ssldir}/certs/ca.pem", $targets)
+ run_command("/bin/cp ${new_cert['new_cert']} ${ssldir}/ca/ca_crt.pem", $targets)
+ run_task('service::linux', $targets, 'action' => 'start', 'name' => 'puppetserver')
+ }
+ run_task('service::linux', $targets, 'action' => 'start', 'name' => 'puppet')
+
+ $tmp = run_command('mktemp', 'localhost', '_run_as' => system::env('USER'))
+ $tmp_file = $tmp.first.value['stdout'].chomp
+ file::write($tmp_file, $cert_contents)
+
+ if $compilers {
+ out::message("INFO: Stopping Puppet services on compilers (${compilers})")
+ run_task('service::linux', $compilers, 'action' => 'stop', 'name' => 'puppet')
+
+ out::message("INFO: Configuring compilers (${compilers}) to use the extended CA certificate")
+ upload_file($tmp_file, '/etc/puppetlabs/puppet/ssl/certs/ca.pem', $compilers)
+
+ # Just running Puppet with the new CA certificate in place should be enough.
+ run_command('/opt/puppetlabs/bin/puppet agent --no-daemonize --no-noop --onetime', $compilers)
+ run_task('service::linux', $compilers, 'action' => 'start', 'name' => 'puppet')
+ }
+
+ out::message("INFO: Extended CA certificate decoded and stored at ${tmp_file}")
+ out::message("INFO: Run the 'ca_extend::upload_ca_cert' plan to distribute the extended CA certificate to agents")
+}
diff --git a/code/environments/production/modules/ca_extend/plans/get_agent_facts.pp b/code/environments/production/modules/ca_extend/plans/get_agent_facts.pp
new file mode 100644
index 0000000..6be14f4
--- /dev/null
+++ b/code/environments/production/modules/ca_extend/plans/get_agent_facts.pp
@@ -0,0 +1,6 @@
+# @summary
+# A plan to work around BOLT-1168 so that one agent failing in apply_prep won't cause the whole plan to fail.
+# @param nodes The targets to run apply_prep on
+plan ca_extend::get_agent_facts(TargetSpec $nodes) {
+ $nodes.apply_prep
+}
diff --git a/code/environments/production/modules/ca_extend/plans/upload_ca_cert.pp b/code/environments/production/modules/ca_extend/plans/upload_ca_cert.pp
new file mode 100644
index 0000000..3a1af75
--- /dev/null
+++ b/code/environments/production/modules/ca_extend/plans/upload_ca_cert.pp
@@ -0,0 +1,84 @@
+# @summary
+# A plan to upload a given CA certificate to a number of Puppet agent nodes
+# @param nodes The targets to upload the certificate to
+# @param cert The location of the CA certificate on disk of the local machine
+# @return JSON object with two keys: success and failure. Each key contains any number of objects consisting of the agent certname and the output of the upload_file command
+plan ca_extend::upload_ca_cert(
+ TargetSpec $nodes,
+ String $cert
+) {
+ # Work around BOLT-1168
+ run_plan('ca_extend::get_agent_facts', 'nodes' => $nodes, '_catch_errors' => true)
+ $tmp = run_plan('facts', 'targets' => $nodes, '_catch_errors' => true)
+
+ # Extract the ResultSet from an error object
+ case $tmp {
+ Error['bolt/run-failure']: {
+ $results = $tmp.details['result_set']
+ $not_ok = $results.error_set
+ }
+ default: {
+ $results = $tmp
+ $not_ok = undef
+ }
+ }
+
+ # The os.family fact should consistantly be "windows" on, well, Windows.
+ $windows_targets = $results.ok_set.filter |$n| { "${n.value['os']['family']}" == 'windows' }
+ $linux_targets = $results.ok_set.filter |$n| { ! ("${n.value['os']['family']}" == 'windows') }
+
+ $windows_results = upload_file(
+ $cert,
+ 'C:\ProgramData\PuppetLabs\puppet\etc\ssl\certs\ca.pem',
+ $windows_targets.map |$item| { $item.target.name },
+ '_catch_errors' => true
+ )
+
+ $linux_results = upload_file(
+ $cert,
+ '/etc/puppetlabs/puppet/ssl/certs/ca.pem',
+ $linux_targets.map |$item| { $item.target.name },
+ '_catch_errors' => true
+ )
+
+ # Create a hash for *nix and Windows successful and failed uploads and merge them together
+ # filter will return nil if anything doesn't match the lambda, and deep merge will
+ # crunch the left hashes if the rightmost value isn't a hash, so check for that
+ $good = deep_merge(
+ if $linux_results.any |$r| { $r.ok } {
+ { 'success' => $linux_results.filter |$result| { $result.ok }.map |$result| {
+ { $result.target.name => $result.value }
+ }.reduce |$memo, $value| { $memo + $value }
+ }
+ },
+ if $windows_results.any |$r| { $r.ok } {
+ { 'success' => $windows_results.filter |$result| { $result.ok }.map |$result| {
+ { $result.target.name => $result.value }
+ }.reduce |$memo, $value| { $memo + $value }
+ }
+ }
+ )
+
+ $bad = deep_merge(
+ if ! $windows_results.ok {
+ { 'failure' => $windows_results.filter |$result| { ! $result.ok }.map |$result| {
+ { $result.target.name => $result.value }
+ }.reduce |$memo, $value| { $memo + $value }
+ }
+ },
+ if ! $linux_results.ok {
+ { 'failure' => $linux_results.filter |$result| { ! $result.ok }.map |$result| {
+ { $result.target.name => $result.value }
+ }.reduce |$memo, $value| { $memo + $value }
+ }
+ },
+ if $not_ok {
+ { 'failure' => $not_ok.map |$result| {
+ { $result.target.name => $result.value }
+ }.reduce |$memo, $value| { $memo + $value }
+ }
+ }
+ )
+
+ return deep_merge($good, $bad)
+}