ExCredstash (ExCredstash v0.1.1)

View Source

Elixir implementation of credstash - a utility for managing credentials in AWS using KMS and DynamoDB.

Usage as a Library

# Store a secret
ExCredstash.put("db_password", "super_secret", region: "us-east-1")

# Retrieve a secret
{:ok, "super_secret"} = ExCredstash.get("db_password", region: "us-east-1")

# List all secrets
{:ok, secrets} = ExCredstash.list(region: "us-east-1")

# Delete a secret
{:ok, count} = ExCredstash.delete("db_password", region: "us-east-1")

Configuration

Options can be passed to each function or configured globally:

config :ex_credstash,
  region: "us-east-1",
  table: "credential-store",
  kms_key: "alias/credstash"

Encryption Scheme

Credstash uses a two-key system:

  • KMS generates a 64-byte data key
  • First 32 bytes are used for AES-256-CTR encryption
  • Last 32 bytes are used for HMAC integrity verification

This implementation is fully compatible with the Python credstash library.

Summary

Functions

Delete all versions of a secret.

Retrieve a secret from credstash.

Retrieve all secrets from credstash.

List unique credential names.

List all credentials in the table.

Store a secret in credstash.

Store multiple secrets at once.

Create the DynamoDB table for storing credentials.

Functions

delete(name, opts \\ [])

@spec delete(
  String.t(),
  keyword()
) :: {:ok, non_neg_integer()} | {:error, term()}

Delete all versions of a secret.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, deleted_count} - Number of versions deleted
  • {:error, reason}

Examples

iex> ExCredstash.delete("old_secret", region: "us-east-1")
{:ok, 3}

get(name, opts \\ [])

@spec get(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Retrieve a secret from credstash.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :version - Specific version to retrieve (default: latest)
  • :context - KMS encryption context map (must match what was used for encryption)
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, secret_string} - The decrypted secret
  • {:error, :not_found} - Secret not found
  • {:error, :integrity_error} - HMAC verification failed
  • {:error, reason} - Other error

Examples

iex> ExCredstash.get("db_password", region: "us-east-1")
{:ok, "super_secret"}

iex> ExCredstash.get("db_password", region: "us-east-1", version: 1)
{:ok, "old_secret"}

get_all(opts \\ [])

@spec get_all(keyword()) :: {:ok, map()} | {:error, term()}

Retrieve all secrets from credstash.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :context - KMS encryption context map
  • :version - :all to get all versions, or :latest (default)
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, %{name => secret_string}} - Map of all secrets (latest version for each)
  • {:error, reason} - Error occurred

Examples

iex> ExCredstash.get_all(region: "us-east-1")
{:ok, %{"db_password" => "secret1", "api_key" => "secret2"}}

keys(opts \\ [])

@spec keys(keyword()) :: {:ok, [String.t()]} | {:error, term()}

List unique credential names.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, [String.t()]} - Sorted list of unique secret names
  • {:error, reason}

Examples

iex> ExCredstash.keys(region: "us-east-1")
{:ok, ["api_key", "db_password"]}

list(opts \\ [])

@spec list(keyword()) :: {:ok, [map()]} | {:error, term()}

List all credentials in the table.

Does NOT decrypt the secrets, only returns metadata.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, [%{name: String.t(), version: String.t(), comment: String.t() | nil}]}

  • {:error, reason}

Examples

iex> ExCredstash.list(region: "us-east-1")
{:ok, [%{name: "db_password", version: "0000000000000000001", comment: nil}]}

put(name, secret, opts \\ [])

@spec put(String.t(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}

Store a secret in credstash.

The secret is encrypted using a KMS data key and stored in DynamoDB.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :key_id or :kms_key - KMS key ID (default: "alias/credstash")
  • :version - Explicit version integer (auto-incremented if not provided)
  • :context - KMS encryption context map
  • :digest - Hash algorithm atom (default: :sha256)
  • :comment - Optional comment to store with the secret
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, version_string} - The version that was stored (zero-padded)
  • {:error, {:already_exists, version}} - Version already exists
  • {:error, reason} - Other error

Examples

iex> ExCredstash.put("db_password", "super_secret", region: "us-east-1")
{:ok, "0000000000000000001"}

iex> ExCredstash.put("api_key", "key123", region: "us-east-1", version: 5)
{:ok, "0000000000000000005"}

put_all(secrets, opts \\ [])

@spec put_all(
  map(),
  keyword()
) :: {:ok, map()} | {:error, map(), map()}

Store multiple secrets at once.

Takes a map of name => value pairs.

Options

Same as put/3, including :access_key_id, :secret_access_key, and :session_token.

Returns

  • {:ok, %{name => version_string}} - All secrets stored successfully
  • {:error, %{name => reason}, %{name => version_string}} - Some failed

Examples

iex> ExCredstash.put_all(%{"key1" => "val1", "key2" => "val2"}, region: "us-east-1")
{:ok, %{"key1" => "0000000000000000001", "key2" => "0000000000000000001"}}

setup(opts \\ [])

@spec setup(keyword()) :: {:ok, :created | :exists} | {:error, term()}

Create the DynamoDB table for storing credentials.

Options

  • :region - AWS region (required unless configured)
  • :table - Table name (default: "credential-store")
  • :tags - Map of tags to apply to the table
  • :access_key_id - AWS access key ID (optional, falls back to :aws_credentials)
  • :secret_access_key - AWS secret access key (optional, falls back to :aws_credentials)
  • :session_token - AWS session token (optional)

Returns

  • {:ok, :created} - Table was created
  • {:ok, :exists} - Table already exists
  • {:error, reason} - Error occurred

Examples

iex> ExCredstash.setup(region: "us-east-1")
{:ok, :created}

iex> ExCredstash.setup(region: "us-east-1", table: "my-secrets")
{:ok, :created}