Last active
February 19, 2020 11:22
-
-
Save peterellisjones/83cd7448be09c52aa137ec455eab4231 to your computer and use it in GitHub Desktop.
BOSH-generated certificate rotation script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'yaml' | |
require 'json' | |
# | |
# Simple Ruby script to rotate bosh-generated certificates | |
# Requires a BOSH deployment manifest that uses certificate variables and the original credentials file | |
# Outputs 4 credentials files that should be deployed one at a time, in order | |
# | |
# Allows for zero-downtime certificate rotation by creating an intermediate state where TLS clients trust | |
# both the old and new CA certificates | |
# | |
if ARGV.length != 2 | |
puts "Usage:\n\trotate_certs.rb path_to_manifest.yml path_to_existing_credentials_file.yml" | |
puts "Requires BOSH CLI 6.1+" | |
exit 1 | |
end | |
puts "Loading manifest" | |
manifest_path = ARGV[0] | |
manifest = YAML.load_file(manifest_path) | |
original_creds_path = ARGV[1] | |
original_creds = YAML.load_file(original_creds_path) | |
variables = manifest["variables"] | |
ca_certs = variables.select do |var| | |
var['type'] == 'certificate' && var['options']['is_ca'] | |
end | |
leaf_certs = variables.select do |var| | |
var['type'] == 'certificate' && !var['options']['is_ca'] | |
end | |
puts "Found CA certs:" | |
puts ca_certs.map { |cert| "\t#{cert.fetch('name')}" } | |
puts "Found leaf certs:" | |
puts leaf_certs.map { |cert| "\t#{cert.fetch('name')}" } | |
creds_without_certs = original_creds.reject do |key, val| | |
ca_certs.map { |cert| cert.fetch('name') }.include?(key) || leaf_certs.map { |cert| cert.fetch('name') }.include?(key) | |
end | |
puts "Writing old creds file => creds_step_0_old_ca_old_leaf.yml" | |
File.write("creds_step_0_old_ca_old_leaf.yml", original_creds.to_yaml) | |
puts "Generating new certs" | |
new_creds_path = "creds_step_3_new_ca_new_leaf.yml" | |
File.write(new_creds_path, creds_without_certs.to_yaml) | |
puts "Writing new creds file => #{new_creds_path}.yml" | |
interpolate_command = "bosh int --var-errs #{manifest_path} --vars-store #{new_creds_path}" | |
puts "Running command `#{interpolate_command}`" | |
system(interpolate_command) | |
# Load and write it so that we use Ruby YAML library for final version | |
# this makes it easier to diff | |
new_creds = YAML.load_file(new_creds_path) | |
File.write(new_creds_path, new_creds.to_yaml) | |
# Generate first intermediate creds file with old & new CA certs but old leaf certs | |
intermediate_creds_1 = YAML.load_file("creds_step_0_old_ca_old_leaf.yml") | |
# 1. Ensure that all CA certs are replaced | |
ca_certs.each do |var| | |
name = var.fetch('name') | |
intermediate_creds_1.fetch(name)['ca'] += "\n" + new_creds.fetch(name).fetch('ca') | |
end | |
# 2. Ensure that "CA" component of all leaf certs is replaced | |
leaf_certs.each do |var| | |
name = var.fetch('name') | |
intermediate_creds_1.fetch(name)['ca'] += "\n" + new_creds.fetch(name).fetch('ca') | |
end | |
# 3. Write file | |
puts "Writing intermediate creds file => creds_step_1_new_and_old_ca_old_leaf.yml" | |
File.write('creds_step_1_new_and_old_ca_old_leaf.yml', intermediate_creds_1.to_yaml) | |
# Generate second intermediate creds file with old & new CA certs but new leaf certs | |
intermediate_creds_2 = YAML.load_file("creds_step_3_new_ca_new_leaf.yml") | |
# 1. Ensure that all CA certs are replaced | |
ca_certs.each do |var| | |
name = var.fetch('name') | |
intermediate_creds_2.fetch(name)['ca'] = original_creds.fetch(name).fetch('ca') + "\n" + intermediate_creds_2.fetch(name)['ca'] | |
end | |
# 2. Ensure that "CA" component of all leaf certs is replaced | |
leaf_certs.each do |var| | |
name = var.fetch('name') | |
intermediate_creds_2.fetch(name)['ca'] = original_creds.fetch(name).fetch('ca') + "\n" + intermediate_creds_2.fetch(name)['ca'] | |
end | |
puts "Writing intermediate creds file => creds_step_2_new_and_old_ca_new_leaf.yml" | |
File.write('creds_step_2_new_and_old_ca_new_leaf.yml', intermediate_creds_2.to_yaml) | |
puts "DEPLOYMENT INSTRUCTIONS:" | |
puts "Deploy #{manifest_path} using these 4 creds files, one at a time" | |
puts "\tcreds_step_0_old_ca_old_leaf.yml\n\tcreds_step_1_new_and_old_ca_old_leaf.yml\n\tcreds_step_2_new_and_old_ca_new_leaf.yml\n\tcreds_step_3_new_ca_new_leaf.yml" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment