SecretMana (secret_mana v0.1.1)
View SourceSecretMana is a module for managing encrypted secrets build to support various backends. Currently only age (https://github.com/FiloSottile/age) is supported.
This module is a wrapper for the SecretMana.Backend:
- Read encrypted secrets with support for nested key access
- Edit secrets using your preferred editor
- Encrypt/decrypt files in supported formats
- Generate keys for storing secrets
- Install backend
Examples
import SecretMana
# Read all secrets
secrets = read!()
# Read a specific nested key
password = read!(["database", "password"])
# Edit secrets in your preferred editor
:ok = SecretMana.edit(config)
# Encrypt a new secrets file
:ok = SecretMana.encrypt(config, "new_secrets.json")
# Generate a new key pair
:ok = SecretMana.gen_key(config)
# Install age binary
:ok = SecretMana.install(config)
Summary
Functions
Release step function that copies secrets from development directories into the release.
Opens the decrypted secrets in your editor for modification, then re-encrypts them when done.
Encrypts a file using the age public key.
Generates a new age key pair in the configured directory.
Sets the private key for age encryption at runtime.
Downloads and installs the age binary for the current platform.
Reads and decrypts secrets from the configured secret file.
Callback implementation for Application.start/2.
Functions
Release step function that copies secrets from development directories into the release.
This function can be used as a release step in mix.exs to automatically copy encrypted secrets and keys from secrets/<env>/ into the release's config/secrets/ directory during the build process. Only the target environment's secrets are copied to avoid including secrets from other environments in the release.
Security Note:
For enhanced security, set embed_private_key? to false and use the runtime configuration
approach with SecretMana.generate_private_key_file/1 in runtime.exs instead of
embedding the private key in the release artifact.
Usage in mix.exs:
# Option 1: Include private key in release (less secure)
def project do
[
# ... other config
releases: [
my_app: [
steps: [:assemble, &SecretMana.copy_secrets_for_release/1]
]
]
]
end
# Option 2: Exclude private key from release (more secure)
def project do
[
# ... other config
releases: [
my_app: [
steps: [:assemble, fn release ->
SecretMana.copy_secrets_for_release(release, false)
end]
]
]
]
endRuntime Configuration (when embed_private_key? is false):
# runtime.exs - Option 1: Using SecretMana module
SecretMana.generate_private_key_file(System.get_env("SECRET_MANA_PRIVATE_KEY"))
# runtime.exs - Option 2: Using AgeBackend directly
SecretMana.generate_private_key_file(System.get_env("SECRET_MANA_PRIVATE_KEY"))Directory Structure:
# Development:
config/secrets/dev/age.key
config/secrets/dev/age.pub
config/secrets/dev/age.enc
# Release (when embed_private_key? is true):
lib/my_app-x.x.x/config/secrets/age.key
lib/my_app-x.x.x/config/secrets/age.pub
lib/my_app-x.x.x/config/secrets/age.enc
# Release (when embed_private_key? is false):
lib/my_app-x.x.x/config/secrets/age.pub
lib/my_app-x.x.x/config/secrets/age.encParameters
release- The Mix.Release structembed_private_key?- Whether to include the private key in the release (defaults to false for security)
Returns
release- The unmodified release struct (following release step convention)
Opens the decrypted secrets in your editor for modification, then re-encrypts them when done.
Uses the EDITOR environment variable to determine which editor to use, falls back to vim if not set.
Parameters
config- The SecretMana configuration struct
Returns
:ok- Successfully edited and re-encrypted secrets
Examples
:ok = SecretMana.edit(config)
Encrypts a file using the age public key.
The file must be in the format specified by the configuration (JSON or YAML).
Parameters
config- The SecretMana configuration structfile- Path to the file to encryptcheck_file_type- Whether to validate the file format matches the configured format, defaults to true
Returns
:ok- Successfully encrypted the file
Examples
:ok = SecretMana.encrypt(config, "secrets.json")
:ok = SecretMana.encrypt(config, "secrets.json", false)
Generates a new age key pair in the configured directory.
Creates both a private key file and a public key file.
Parameters
config- The SecretMana configuration struct
Returns
:ok- Successfully generated key pair
Examples
:ok = SecretMana.gen_key(config)
Sets the private key for age encryption at runtime.
This function allows you to configure the private key dynamically at runtime, typically called from runtime.exs. This is more secure than embedding the private key in the release artifact.
Parameters
private_key- The private key content as a string
Returns
:ok- Successfully configured the private key
Examples
# In runtime.exs
SecretMana.generate_private_key_file(System.get_env("SECRET_MANA_PRIVATE_KEY"))
Downloads and installs the age binary for the current platform.
Automatically detects the correct version based on the current system architecture.
Parameters
config- The SecretMana configuration struct
Returns
:ok- Successfully installed age binary
Examples
:ok = SecretMana.install(config)
Reads and decrypts secrets from the configured secret file.
Parameters
access_path- Optional list of keys to traverse the secret structure, defaults to nil which returns the entire secret
Returns
term()- The decrypted secrets
Examples
import SecretMana
# Read all secrets
secrets = read!()
# Read a specific nested key
password = read!(["database", "password"])
Callback implementation for Application.start/2.