diff options
| author | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2022-02-08 11:51:45 +0100 |
|---|---|---|
| committer | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2022-02-08 11:51:49 +0100 |
| commit | eff99e3e61f4e216b055a805002f5ece8567a915 (patch) | |
| tree | 69771d87bfc3302980625e5524d02d2b455baadb /code/environments/production/modules/ca_extend/plans | |
| parent | 74e2687fa98ea5ba25fbe07c038253d1fc14584e (diff) | |
| download | puppet.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')
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) +} |
