Skip to content

Commit

Permalink
Merge pull request #1141 from justindell/feat-add-aws-secrets-manager…
Browse files Browse the repository at this point in the history
…-adapter

feat: add secrets adapter for aws secrets manager
  • Loading branch information
djmb authored Nov 21, 2024
2 parents 4acb78f + b4d395c commit 57e48a3
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
34 changes: 34 additions & 0 deletions lib/kamal/secrets/adapters/aws_secrets_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Kamal::Secrets::Adapters::AwsSecretsManager < Kamal::Secrets::Adapters::Base
private
def login(_account)
nil
end

def fetch_secrets(secrets, account:, session:)
{}.tap do |results|
JSON.parse(get_from_secrets_manager(secrets, account: account))["SecretValues"].each do |secret|
secret_name = secret["Name"]
secret_string = JSON.parse(secret["SecretString"])

secret_string.each do |key, value|
results["#{secret_name}/#{key}"] = value
end
end
end
end

def get_from_secrets_manager(secrets, account:)
`aws secretsmanager batch-get-secret-value --secret-id-list #{secrets.map(&:shellescape).join(" ")} --profile #{account.shellescape}`.tap do
raise RuntimeError, "Could not read #{secret} from AWS Secrets Manager" unless $?.success?
end
end

def check_dependencies!
raise RuntimeError, "AWS CLI is not installed" unless cli_installed?
end

def cli_installed?
`aws --version 2> /dev/null`
$?.success?
end
end
98 changes: 98 additions & 0 deletions test/secrets/aws_secrets_manager_adapter_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
require "test_helper"

class AwsSecretsManagerAdapterTest < SecretAdapterTestCase
test "fetch" do
stub_ticks.with("aws --version 2> /dev/null")
stub_ticks
.with("aws secretsmanager batch-get-secret-value --secret-id-list secret/KEY1 secret/KEY2 secret2/KEY3 --profile default")
.returns(<<~JSON)
{
"SecretValues": [
{
"ARN": "arn:aws:secretsmanager:us-east-1:aaaaaaaaaaaa:secret:secret",
"Name": "secret",
"VersionId": "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv",
"SecretString": "{\\"KEY1\\":\\"VALUE1\\", \\"KEY2\\":\\"VALUE2\\"}",
"VersionStages": [
"AWSCURRENT"
],
"CreatedDate": "2024-01-01T00:00:00.000000"
},
{
"ARN": "arn:aws:secretsmanager:us-east-1:aaaaaaaaaaaa:secret:secret2",
"Name": "secret2",
"VersionId": "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv",
"SecretString": "{\\"KEY3\\":\\"VALUE3\\"}",
"VersionStages": [
"AWSCURRENT"
],
"CreatedDate": "2024-01-01T00:00:00.000000"
}
],
"Errors": []
}
JSON

json = JSON.parse(shellunescape(run_command("fetch", "secret/KEY1", "secret/KEY2", "secret2/KEY3")))

expected_json = {
"secret/KEY1"=>"VALUE1",
"secret/KEY2"=>"VALUE2",
"secret2/KEY3"=>"VALUE3"
}

assert_equal expected_json, json
end

test "fetch with secret names" do
stub_ticks.with("aws --version 2> /dev/null")
stub_ticks
.with("aws secretsmanager batch-get-secret-value --secret-id-list secret/KEY1 secret/KEY2 --profile default")
.returns(<<~JSON)
{
"SecretValues": [
{
"ARN": "arn:aws:secretsmanager:us-east-1:aaaaaaaaaaaa:secret:secret",
"Name": "secret",
"VersionId": "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv",
"SecretString": "{\\"KEY1\\":\\"VALUE1\\", \\"KEY2\\":\\"VALUE2\\"}",
"VersionStages": [
"AWSCURRENT"
],
"CreatedDate": "2024-01-01T00:00:00.000000"
}
],
"Errors": []
}
JSON

json = JSON.parse(shellunescape(run_command("fetch", "--from", "secret", "KEY1", "KEY2")))

expected_json = {
"secret/KEY1"=>"VALUE1",
"secret/KEY2"=>"VALUE2"
}

assert_equal expected_json, json
end

test "fetch without CLI installed" do
stub_ticks_with("aws --version 2> /dev/null", succeed: false)

error = assert_raises RuntimeError do
JSON.parse(shellunescape(run_command("fetch", "SECRET1")))
end
assert_equal "AWS CLI is not installed", error.message
end

private
def run_command(*command)
stdouted do
Kamal::Cli::Secrets.start \
[ *command,
"-c", "test/fixtures/deploy_with_accessories.yml",
"--adapter", "aws_secrets_manager",
"--account", "default" ]
end
end
end

0 comments on commit 57e48a3

Please sign in to comment.