Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Secret Manager support #208

Merged
merged 9 commits into from
Jun 8, 2022
Merged

Conversation

AlexanderWert
Copy link
Member

@AlexanderWert AlexanderWert commented Jun 3, 2022

Closes #105
Closes #206

Description

Added two new configuration options:

  • ELASTIC_APM_SECRETS_MANAGER_SECRET_TOKEN_ID
  • ELASTIC_APM_SECRETS_MANAGER_API_KEY_ID

If a secret manager id is provided through the above config options, the extension retrieves the corresponding secret from the AWS Secret manager. The secret must be in the same AWS region.
If both, a plain text secret is provided (i.e. ELASTIC_APM_SECRET_TOKEN) and the ELASTIC_APM_SECRETS_MANAGER_SECRET_TOKEN_ID, then the value from the Secret Manager takes precedence.

Retrieving the secret from the Secret Manager happens once at the cold start of the extension.

Steps for end to end testing:

Step 1: Create a secret in the AWS Secret Manager.

  • The secret must be in the same AWS region as the target lambda function
  • Create the secret either for the APM secret token OR the APM API key
  • The secret type must be Plain Text
  • Optionally, you can select a custom KMS key for encryption (Note: in this case you will need additional key permissions on your Lambda functions)
    • Otherwise: use the key provided by AWS: aws/secretsmanager (No additional key permissions needed for this)

image

Step 2: Create and setup the Lambda function

  • Follow these docs for general setup of the Lambda function with Elastic APM
  • However, create and use a custom extension layer based on this PR
  • In the environment variables configuration instead of providing the ELASTIC_APM_SECRET_TOKEN or ELASTIC_APM_API_KEY specify the ELASTIC_APM_SECRETS_MANAGER_SECRET_TOKEN_ID or ELASTIC_APM_SECRETS_MANAGER_API_KEY_ID variables. Use the secret-name as the value.

Step 3: Add permissions

  • Under Configuration > Permissions > Execution Role click on the execution role to edit the permissions
  • Add a new inline policy with the following JSON-based permission definition:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:ListSecretVersionIds"
            ],
            "Resource": [
                "HERE_GOES_THE_ARN_OF_THE_SECRET"
            ]
        },
        // The following is only needed if a custom key is used
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "HERE_GOES_THE_ARN_OF_THE_CUSTOM_KEY"
            ]
        }
    ]
}

Step 4: Invoke the lambda function and validate data in Elastic APM UI

@AlexanderWert AlexanderWert mentioned this pull request Jun 3, 2022
4 tasks
@github-actions github-actions bot added the aws-λ-extension AWS Lambda Extension label Jun 3, 2022
@apmmachine
Copy link

apmmachine commented Jun 3, 2022

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2022-06-07T16:36:32.073+0000

  • Duration: 8 min 31 sec

Test stats 🧪

Test Results
Failed 0
Passed 200
Skipped 4
Total 204

🤖 GitHub comments

To re-run your PR in the CI, just comment with:

  • /test : Re-trigger the build.

  • run elasticsearch-ci/docs : Re-trigger the docs validation. (use unformatted text in the comment!)

@simitt simitt requested a review from a team June 3, 2022 09:33
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"encoding/base64"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any sort of linting for import order? other projects separate out stdlib / elastic / 3rd party

@@ -62,6 +66,39 @@ func getIntFromEnv(name string) (int, error) {
return value, nil
}

func getSecret(secretName string) (string, error) {
region := os.Getenv("AWS_REGION")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to check region != "" and return a descriptive error

secretString = *result.SecretString
} else {
decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary)))
len, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

len is a builtin and, while the compiler might let you use it, it'd probably be better to choose a different variable name

if err != nil {
return "", err
}
secretString = string(decodedBinarySecretBytes[:len])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to the docs, [Encoding.Decode] writes at most DecodedLen(len(src)) bytes to dst, so the [:len] shouldn't be necessary -- it should only differ if there's an error and there's a short write, in which case you'll return the error anyway.

result, err := getSecret(apmServerApiKeySMSecretId)
if err != nil {
Log.Fatalf("Failed loading APM Server ApiKey from Secrets Manager: %v", err)
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the else can be dropped, and just executed regularly (the Fatalf will abort the program if there's an error)

result, err := getSecret(apmServerSecretTokenSMSecretId)
if err != nil {
Log.Fatalf("Failed loading APM Server Secret Token from Secrets Manager: %v", err)
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

@simitt
Copy link
Contributor

simitt commented Jun 3, 2022

@elastic/apm-server could you please help pushing this over the finish line; I suggest for this PR rather than only reviewing, commit small code improvements directly to free up @AlexanderWert.
Thanks Alex for this contribution.

Copy link
Contributor

@jlvoiseux jlvoiseux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, aside from the small test correction.
I was also able to get the PR to work end-to-end by following the repro steps.

if config.LogLevel != zapcore.InfoLevel {
t.Log("Log level not set correctly")
t.Fail()
}
}

func TestGetSecretCalled(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the Unit test, when run sequentially together with the whole test suite, fails due to previously set values of the SECRET_TOKEN and the API_KEY.

Suggested change
func TestGetSecretCalled(t *testing.T) {
func TestGetSecretCalled(t *testing.T) {
os.Clearenv()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the test actually to confirm that there's a preference between the managed and unmanaged secrets, since that wasn't being tested

@stuartnelson3 stuartnelson3 merged commit b972778 into elastic:main Jun 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aws-λ-extension AWS Lambda Extension
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Normalize log level values Support for Secrets Managers
5 participants