defmodule Hush.Provider.AwsSecretsManager do @moduledoc """ Implements a Hush.Provider behaviour to resolve secrets from AWS Secrets Manager at runtime. To configure this provider, ensure you configure ex_aws and hush: config :ex_aws, access_key_id: [{:system, "AWS_ACCESS_KEY_ID"}], secret_access_key: [{:system, "AWS_SECRET_ACCESS_KEY"}] config :hush, providers: [Hush.Provider.AwsSecretsManager] """ alias Hush.Provider.AwsSecretsManager @behaviour Hush.Provider @impl Hush.Provider @spec load(config :: any()) :: :ok | {:error, any()} def load(_config) do with {:ok, _} <- Application.ensure_all_started(:hackney), {:ok, _} <- Application.ensure_all_started(:ex_aws) do :ok end end @impl Hush.Provider @spec fetch(key :: String.t()) :: {:ok, String.t()} | {:error, :not_found} | {:error, any()} def fetch(key) do with {:ok, secret} <- secret(key), {:ok, value} <- json_decode(secret["SecretString"]) do {:ok, value} else error -> parse_error(error, key) end end defp ex_aws() do Application.get_env(:hush_aws_secrets_manager, :ex_aws, AwsSecretsManager.ExAws) end defp secret(key) do key |> ExAws.SecretsManager.get_secret_value() |> ex_aws().request() end defp json_decode(json) do try do defaults = ExAws.Config.Defaults.defaults(:all) codec = defaults[:json_codec] {:ok, codec.decode!(json)} rescue e -> {:error, "Could not parse JSON: #{e.data}"} end end defp parse_error({:error, {:http_error, 400, response}}, key) do if String.match?(response.body, ~r/is not authorized to perform/) do {:ok, error} = json_decode(response.body) msg = """ The supplied account doesn't seem to have access to secret '#{key}', or secret does not exist. Ensure that it is: 1) spelled correctly 2) you have specified the right authentication credentials 3) the account has enough permissions The original error message was: #{error["Message"]} """ {:error, msg} else parse_error(response, key) end end defp parse_error({:error, message}, _key) when is_binary(message) do {:error, message} end defp parse_error(_error, key) do {:error, "An unknown error ocurred while fethching secret for #{key}"} end end