Merge pull request #1141 from justindell/feat-add-aws-secrets-manager-adapter

feat: add secrets adapter for aws secrets manager
This commit is contained in:
Donal McBreen
2024-11-21 15:03:54 +00:00
committed by GitHub
2 changed files with 132 additions and 0 deletions

View File

@@ -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

View File

@@ -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