ExCredstash
Elixir implementation of credstash - a utility for managing credentials using AWS KMS and DynamoDB.
Features
- Full compatibility with Python credstash data format
- Both library and CLI interfaces
- AES-256-CTR encryption with HMAC verification
- Automatic version management
- KMS encryption context support
- Multiple digest algorithms (SHA, SHA224, SHA256, SHA384, SHA512, MD5)
Installation
As a Library
Add ex_credstash to your list of dependencies in mix.exs:
def deps do
[
{:ex_credstash, "~> 0.1.0"}
]
end
Then run:
mix deps.get
As a CLI Tool
Install from Hex.pm
mix escript.install hex ex_credstash
Make sure ~/.mix/escripts is in your PATH:
export PATH="$HOME/.mix/escripts:$PATH"
Build from Source
git clone https://github.com/relaypro-open/ex_credstash.git
cd ex_credstash
mix deps.get
mix escript.build
This creates a credstash binary in the project root. You can move it to a directory in your PATH:
sudo mv credstash /usr/local/bin/
Usage
Library Usage
# Set up the DynamoDB table
ExCredstash.setup(region: "us-east-1")
# Store a secret
{:ok, version} = ExCredstash.put("db_password", "super_secret", region: "us-east-1")
# Store with specific version
{:ok, "0000000000000000005"} = ExCredstash.put("api_key", "key123",
region: "us-east-1",
version: 5
)
# Store with encryption context
ExCredstash.put("secret", "value",
region: "us-east-1",
context: %{"environment" => "production"}
)
# Store multiple secrets at once
{:ok, versions} = ExCredstash.put_all(%{
"key1" => "val1",
"key2" => "val2"
}, region: "us-east-1")
# Retrieve a secret (latest version)
{:ok, "super_secret"} = ExCredstash.get("db_password", region: "us-east-1")
# Retrieve a specific version
{:ok, "old_secret"} = ExCredstash.get("db_password", region: "us-east-1", version: 1)
# Retrieve all secrets (decrypted)
{:ok, %{"db_password" => "secret1", "api_key" => "secret2"}} =
ExCredstash.get_all(region: "us-east-1")
# List all credentials (metadata only, not decrypted)
{:ok, [%{name: "db_password", version: "0000000000000000001", comment: nil}]} =
ExCredstash.list(region: "us-east-1")
# List unique credential names
{:ok, ["api_key", "db_password"]} = ExCredstash.keys(region: "us-east-1")
# Delete all versions of a secret
{:ok, 3} = ExCredstash.delete("old_secret", region: "us-east-1")
CLI Usage
# Set up the DynamoDB table
credstash setup -r us-east-1
# Store a secret
credstash put db_password "super_secret" -r us-east-1
# Store with auto-versioning (default behavior)
credstash put api_key "key123" -a -r us-east-1
# Store with explicit version
credstash put api_key "key456" -v 2 -r us-east-1
# Store with custom KMS key
credstash put secret "value" -k alias/my-key -r us-east-1
# Store with encryption context
credstash put secret "value" environment=production app=myapp -r us-east-1
# Store with comment
credstash put api_key "key123" -c "API key for external service" -r us-east-1
# Read secret value from stdin
echo "secret" | credstash put my_secret -r us-east-1
# Read secret value from file
credstash put certificate @/path/to/cert.pem -r us-east-1
# Retrieve a secret
credstash get db_password -r us-east-1
# Retrieve specific version
credstash get db_password -v 1 -r us-east-1
# Retrieve without trailing newline
credstash get db_password -n -r us-east-1
# Retrieve with encryption context
credstash get secret environment=production -r us-east-1
# Retrieve all secrets as JSON
credstash getall -f json -r us-east-1
# Retrieve all secrets as dotenv format
credstash getall -f dotenv -r us-east-1
# List all credentials
credstash list -r us-east-1
# List unique credential names
credstash keys -r us-east-1
# Delete a secret (all versions)
credstash delete old_secret -r us-east-1
# Use a custom table
credstash -t my-secrets list -r us-east-1
Configuration
Application Config
# config/config.exs
config :ex_credstash,
region: "us-east-1",
table: "credential-store",
kms_key: "alias/credstash"
Environment Variables
AWS_REGIONorAWS_DEFAULT_REGION- AWS regionCREDSTASH_TABLE- DynamoDB table name (default: "credential-store")CREDSTASH_KEY_ID- KMS key ID or alias (default: "alias/credstash")
AWS Credentials
ExCredstash uses the standard AWS credential chain:
- Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - Shared credentials file (
~/.aws/credentials) - IAM role (for EC2 instances, ECS tasks, Lambda functions)
AWS Setup
KMS Key
Create a KMS key for credstash:
aws kms create-key --description "Credstash key"
aws kms create-alias --alias-name alias/credstash --target-key-id <key-id>
Or use an existing key by setting the kms_key configuration.
IAM Permissions
The following IAM permissions are required:
For reading secrets:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["kms:Decrypt"],
"Resource": "arn:aws:kms:REGION:ACCOUNT:key/KEY-ID"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:REGION:ACCOUNT:table/credential-store"
}
]
}
For writing secrets:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["kms:GenerateDataKey"],
"Resource": "arn:aws:kms:REGION:ACCOUNT:key/KEY-ID"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:Query"
],
"Resource": "arn:aws:dynamodb:REGION:ACCOUNT:table/credential-store"
}
]
}
For setup (creating table):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:CreateTable",
"dynamodb:DescribeTable",
"dynamodb:TagResource"
],
"Resource": "arn:aws:dynamodb:REGION:ACCOUNT:table/credential-store"
}
]
}
For deleting secrets:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:Query",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:REGION:ACCOUNT:table/credential-store"
}
]
}
Running Tests
Unit Tests
Unit tests run without AWS credentials and mock AWS SDK responses:
mix test
Integration Tests
Integration tests verify real AWS operations and require:
AWS Credentials: Set up via environment variables or
~/.aws/credentials:export AWS_ACCESS_KEY_ID="your-access-key"export AWS_SECRET_ACCESS_KEY="your-secret-key"export AWS_REGION="us-east-1"KMS Key: A KMS key with alias
alias/credstash(or configure a different key)Run integration tests:
mix test --include integration
Development
# Run tests
mix test
# Run formatter
mix format
# Check formatting
mix format --check-formatted
# Run type checker
mix dialyzer
# Run linter
mix credo
# Build escript
mix escript.build
# Compile with warnings as errors
mix compile --warnings-as-errors
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature) - Run tests and ensure they pass (
mix test) - Run formatter (
mix format) - Commit your changes (
git commit -am 'Add new feature') - Push to the branch (
git push origin feature/my-feature) - Create a Pull Request
Acknowledgments
- credstash - The original Python implementation
- AWS Erlang SDK - AWS client library