class Seira::Secrets
Constants
- PGBOUNCER_SECRETS_NAME
- SUMMARY
- VALID_ACTIONS
Attributes
action[R]
app[R]
args[R]
context[R]
Public Class Methods
new(app:, action:, args:, context:)
click to toggle source
# File lib/seira/secrets.rb, line 19 def initialize(app:, action:, args:, context:) @app = app @action = action @args = args @context = context end
Public Instance Methods
copy_secret_across_namespace(key:, to:, from:)
click to toggle source
# File lib/seira/secrets.rb, line 50 def copy_secret_across_namespace(key:, to:, from:) puts "Copying the #{key} secret from namespace #{from} to #{to}." json_string = kubectl("get secret #{key} -o json -n #{from}", context: :none, return_output: true) secrets = JSON.parse(json_string) # At this point we would preferably simply do a write_secrets call, but the metadata is highly coupled to old # namespace so we need to clear out the old metadata new_secrets = Marshal.load(Marshal.dump(secrets)) new_secrets.delete('metadata') new_secrets['metadata'] = { 'name' => key, 'namespace' => to } write_secrets(secrets: new_secrets, secret_name: key) end
get(key)
click to toggle source
# File lib/seira/secrets.rb, line 70 def get(key) secrets = fetch_current_secrets encoded_value = secrets.dig('data', key) encoded_value.nil? ? nil : Base64.decode64(encoded_value) end
main_secret_name()
click to toggle source
# File lib/seira/secrets.rb, line 66 def main_secret_name "#{app}-secrets" end
run()
click to toggle source
# File lib/seira/secrets.rb, line 26 def run case action when 'help' run_help when 'get' validate_single_key run_get when 'set' validate_keys_and_values run_set when 'unset' validate_single_key run_unset when 'list' run_list when 'list-decoded' run_list_decoded when 'create-secret-container' run_create_secret_container else fail "Unknown command encountered" end end
Private Instance Methods
fetch_current_secrets()
click to toggle source
Returns the still-base64encoded secrets hashmap
# File lib/seira/secrets.rb, line 175 def fetch_current_secrets json_string = kubectl("get secret #{secret_container_from_args} -o json", context: context, return_output: true) json = JSON.parse(json_string) json['data'] ||= {} # For secret that has no key/values yet, this ensures a consistent experience fail "Unexpected Kind" unless json['kind'] == 'Secret' json end
key()
click to toggle source
# File lib/seira/secrets.rb, line 183 def key args[0] end
key_value_map()
click to toggle source
Filter out parameters which start with –
# File lib/seira/secrets.rb, line 198 def key_value_map args.reject { |arg| arg.start_with?("--") }.map do |arg| equals_index = arg.index('=') [arg[0..equals_index - 1], arg[equals_index + 1..-1]] end.to_h end
run_create_secret_container()
click to toggle source
# File lib/seira/secrets.rb, line 145 def run_create_secret_container secret_name = key puts "Creating Kubernetes Secret with name '#{secret_name}'..." kubectl("create secret generic #{secret_name}", context: context) puts "Secret Object '#{secret_name}' created. You can now set, unset, list secrets in this container Secret object." end
run_get()
click to toggle source
# File lib/seira/secrets.rb, line 108 def run_get value = get(key) if value.nil? puts "Secret '#{key}' not found" else puts "#{key.green}: #{value}" end end
run_help()
click to toggle source
# File lib/seira/secrets.rb, line 78 def run_help puts SUMMARY puts "\n\n" puts "Possible actions to operate on secret contaiers. Default" puts "container will be used unless --container=<name> specified:\n\n" puts "get: fetch the value of a secret: `secrets get PASSWORD`" puts "set: set one or more secret values: `secrets set USERNAME=admin PASSWORD=asdf`" puts " to specify a value with spaces: `secrets set LIPSUM=\"Lorem ipsum\"`" puts " to specify a value with newlines: `secrets set RSA_KEY=\"$(cat key.pem)\"`" puts "unset: remove a secret: `secrets unset PASSWORD`" puts "list: list all secret keys and values" puts "list-decoded: list all secret keys and values, and decode from base64" puts "\n\n" puts "create-secret-container: takes one argument, the name, and creates a new container of secrets (Secret object) with that name" end
run_list()
click to toggle source
# File lib/seira/secrets.rb, line 129 def run_list secrets = fetch_current_secrets puts "Base64 encoded keys for #{app}:" secrets['data'].each do |k, v| puts "#{k.green}: #{v}" end end
run_list_decoded()
click to toggle source
# File lib/seira/secrets.rb, line 137 def run_list_decoded secrets = fetch_current_secrets puts "Decoded (raw) keys for #{app}:" secrets['data'].each do |k, v| puts "#{k.green}: #{Base64.decode64(v)}" end end
run_set()
click to toggle source
# File lib/seira/secrets.rb, line 117 def run_set secrets = fetch_current_secrets secrets['data'].merge!(key_value_map.transform_values { |value| Base64.strict_encode64(value) }) write_secrets(secrets: secrets) end
run_unset()
click to toggle source
# File lib/seira/secrets.rb, line 123 def run_unset secrets = fetch_current_secrets secrets['data'].delete(key) write_secrets(secrets: secrets) end
secret_container_from_args()
click to toggle source
# File lib/seira/secrets.rb, line 187 def secret_container_from_args relevant_arg = args.find { |arg| arg.start_with? '--container=' } if relevant_arg relevant_arg.split("=")[1] else main_secret_name end end
validate_keys_and_values()
click to toggle source
# File lib/seira/secrets.rb, line 101 def validate_keys_and_values if args.empty? || !args.all? { |arg| /^[^=]+=.+$/ =~ arg } puts "Please list keys and values to set like KEY_ONE=value_one KEY_TWO=value_two" exit(1) end end
validate_single_key()
click to toggle source
# File lib/seira/secrets.rb, line 94 def validate_single_key if key.nil? || key.strip == "" puts "Please specify a key in all caps and with underscores" exit(1) end end
write_secrets(secrets:, secret_name: secret_container_from_args)
click to toggle source
In the normal case the secret we are updating is just main_secret_name
, but in special cases we may be doing an operation on a different secret such as use passing –container arg
# File lib/seira/secrets.rb, line 155 def write_secrets(secrets:, secret_name: secret_container_from_args) Dir.mktmpdir do |dir| file_name = "#{dir}/temp-secrets-#{Seira::Cluster.current_cluster}-#{secret_name}.json" File.open(file_name, "w") do |f| f.write(secrets.to_json) end # The command we use depends on if it already exists or not secret_exists = kubectl("get secret #{secret_name}", context: context) # TODO: Do not log, pipe output to dev/null command = secret_exists ? "replace" : "create" if kubectl("#{command} -f #{file_name}", context: context) puts "Successfully created/replaced #{secret_name} secret #{key} in cluster #{Seira::Cluster.current_cluster}" else puts "Failed to update secret" end end end