# frozen_string_literal: true require 'safe_yaml' SafeYAML::OPTIONS[:default_mode] = :safe module NoradBeacon class ContainerOptions CONTAINERS_FILE_URI = '/containers.yml.enc' AES_KEY_SIZE = 128 attr_reader :values def initialize @values = File.exist?(CONTAINERS_FILE_URI) ? decrypt_and_load : {} end private # Reads encrypted credentials from disk, decrypts the # credentials, and loads and returns the YAML. # @return [Hash] def decrypt_and_load aes_key = load_key_from_env symbolize YAML.safe_load(decrypt_containers_file(aes_key)) rescue Psych::SyntaxError raise NoradBeaconError, e.message end # Load the Base64 encoded key from the environment # @return [String] AES Key def load_key_from_env Base64.strict_decode64(ENV.fetch('NORAD_CONTAINERS_FILE_KEY')) rescue ArgumentError => e raise NoradBeaconError, e.message end # Decrypt the contents of the containers options file # @param aes_key [String] # @return [String] decrypted contents of the file def decrypt_containers_file(aes_key) decipher = OpenSSL::Cipher::AES.new(AES_KEY_SIZE, :CBC) decipher.decrypt decipher.key = aes_key encrypted_data = File.read(CONTAINERS_FILE_URI, mode: 'rb') decipher.iv = encrypted_data[0..16] # First 16 bytes of this file is the IV decipher.update(encrypted_data[16..-1]) + decipher.final rescue Errno::ENOENT, OpenSSL::Cipher::CipherError => e raise NoradBeaconError, e.message end # Symbolize the keys for a deeply nested array # Adapted from ActiveSupport's implementation # # @param object [Object] some value we wish to symbolize # @return [Hash] a hash with symbolized keys def symbolize(object) case object when Hash object.each_with_object({}) do |(k, v), result| result[k.respond_to?(:to_sym) ? k.to_sym : k] = symbolize(v) end when Array object.map { |el| symbolize(el) } else object end end end end