diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 429c768..6fbd16a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,17 +1,17 @@ --- name: Bug report about: Create a report to help us improve -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** **To Reproduce** Steps to reproduce: -1. + +1. **Expected behavior** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 809b7b0..60f49e9 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,7 @@ --- name: Feature request about: Suggest an idea for this project -title: '' +title: "" labels: enhancement -assignees: '' - +assignees: "" --- - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ab40d21..3bfff59 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,5 @@ -*Issue #, if available:* - -*Description of changes:* +_Issue #, if available:_ +_Description of changes:_ By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b2a560c..23bc671 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,8 +1,9 @@ -name: "Docker Image CI" +name: Docker Image CI on: push: - branches: [main] + branches: main + workflow_dispatch: jobs: build: @@ -11,10 +12,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 + - name: Build uses: docker/build-push-action@v6 with: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f95c317..d46941c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,10 +1,8 @@ name: Rust on: - push: - branches: ["main"] pull_request: - branches: ["main"] + types: [opened, synchronize, ready_for_review] workflow_dispatch: env: @@ -20,36 +18,51 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 + + - name: Prettier + if: runner.os != 'Windows' + uses: rutajdash/prettier-cli-action@v1.0.2 + with: + file_pattern: "**/*.{md,yml}" + - uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true components: rustfmt, clippy, llvm-tools-preview + - name: Install grcov run: cargo install grcov + - name: Install ninja-build tool for aws-lc-fips-sys on Windows if: runner.os == 'Windows' uses: seanmiddleditch/gha-setup-ninja@v6 + - name: Install NASM for aws-lc-fips-sys on Windows if: runner.os == 'Windows' uses: ilammy/setup-nasm@v1 + - name: Install golang for aws-lc-fips-sys on macos if: runner.os == 'MacOS' uses: actions/setup-go@v5 with: - go-version: "stable" - - name: Build - run: cargo build --verbose - - name: Run tests + go-version: stable + + - name: Build and run tests run: cargo test --verbose --all-features --no-fail-fast + - name: Code Coverage run: grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existing -o ./lcov.info + - name: Publish Code Coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} + - name: Lint - run: cargo fmt --all -- --check + run: cargo fmt --all --check + - name: Clippy run: cargo clippy --all-targets --all-features diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5b627cf..ec98f2b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,5 @@ ## Code of Conduct + This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4b6a1c..d05ef95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,6 @@ documentation, we greatly value feedback and contributions from our community. Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. - ## Reporting Bugs/Feature Requests We welcome you to use the GitHub issue tracker to report bugs or suggest features. @@ -14,16 +13,16 @@ We welcome you to use the GitHub issue tracker to report bugs or suggest feature When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment - +- A reproducible test case or series of steps +- The version of our code being used +- Any modifications you've made relevant to the bug +- Anything unusual about your environment or deployment ## Contributing via Pull Requests + Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *main* branch. +1. You are working against the latest source on the _main_ branch. 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. @@ -31,28 +30,34 @@ To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +3. Use rustfmt to ensure consistent formatting for `.rs` files, and Prettier for `.md` and `.yml` files (see [Formatting instructions](#formatting-instructions)). +4. Ensure local tests pass. +5. Commit to your fork using clear commit messages. +6. Send us a pull request, answering any default questions in the pull request interface. +7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). +## Formatting instructions + +- Install [Prettier](https://prettier.io/docs/install). + - If using VS Code, install the VS Code extension for the above. +- Run `cargo fmt --all` and `prettier --write **/*.{md,yml}`, or configure VS Code to format on save using rustfmt and Prettier. ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. ## Code of Conduct + This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. - ## Security issue notifications -If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. ## Licensing diff --git a/README.md b/README.md index 2d4490e..0c6fec8 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,31 @@ # AWS Secrets Manager Agent -The AWS Secrets Manager Agent is a client\-side HTTP service that you can use to standardize consumption of secrets from Secrets Manager across environments such as AWS Lambda, Amazon Elastic Container Service, Amazon Elastic Kubernetes Service, and Amazon Elastic Compute Cloud\. The Secrets Manager Agent can retrieve and cache secrets in memory so that your applications can consume secrets directly from the cache\. That means you can fetch the secrets your application needs from the localhost instead of making calls to Secrets Manager\. The Secrets Manager Agent can only make read requests to Secrets Manager \- it can't modify secrets\. +The AWS Secrets Manager Agent is a client\-side HTTP service that you can use to standardize consumption of secrets from Secrets Manager across environments such as AWS Lambda, Amazon Elastic Container Service, Amazon Elastic Kubernetes Service, and Amazon Elastic Compute Cloud\. The Secrets Manager Agent can retrieve and cache secrets in memory so that your applications can consume secrets directly from the cache\. That means you can fetch the secrets your application needs from the localhost instead of making calls to Secrets Manager\. The Secrets Manager Agent can only make read requests to Secrets Manager \- it can't modify secrets\. -The Secrets Manager Agent uses the AWS credentials you provide in your environment to make calls to Secrets Manager\. The Secrets Manager Agent offers protection against Server Side Request Forgery \(SSRF\) to help improve secret security\. You can configure the Secrets Manager Agent by setting the maximum number of connections, the time to live \(TTL\), the localhost HTTP port, and the cache size\. +The Secrets Manager Agent uses the AWS credentials you provide in your environment to make calls to Secrets Manager\. The Secrets Manager Agent offers protection against Server Side Request Forgery \(SSRF\) to help improve secret security\. You can configure the Secrets Manager Agent by setting the maximum number of connections, the time to live \(TTL\), the localhost HTTP port, and the cache size\. -Because the Secrets Manager Agent uses an in\-memory cache, it resets when the Secrets Manager Agent restarts\. The Secrets Manager Agent periodically refreshes the cached secret value\. The refresh happens when you try to read a secret from the Secrets Manager Agent after the TTL has expired\. The default refresh frequency \(TTL\) is 300 seconds, and you can change it by using a [Configuration file](#secrets-manager-agent-config) which you pass to the Secrets Manager Agent using the `--config` command line argument\. The Secrets Manager Agent does not include cache invalidation\. For example, if a secret rotates before the cache entry expires, the Secrets Manager Agent might return a stale secret value\. +Because the Secrets Manager Agent uses an in\-memory cache, it resets when the Secrets Manager Agent restarts\. The Secrets Manager Agent periodically refreshes the cached secret value\. The refresh happens when you try to read a secret from the Secrets Manager Agent after the TTL has expired\. The default refresh frequency \(TTL\) is 300 seconds, and you can change it by using a [Configuration file](#secrets-manager-agent-config) which you pass to the Secrets Manager Agent using the `--config` command line argument\. The Secrets Manager Agent does not include cache invalidation\. For example, if a secret rotates before the cache entry expires, the Secrets Manager Agent might return a stale secret value\. The Secrets Manager Agent returns secret values in the same format as the response of `GetSecretValue`\. Secret values are not encrypted in the cache\. To download the source code, see [https://github\.com/aws/aws\-secretsmanager\-agent](https://github.com/aws/aws-secretsmanager-agent) on GitHub\. **Topics** + - [AWS Secrets Manager Agent](#aws-secrets-manager-agent) - [Step 1: Build the Secrets Manager Agent binary](#step-1-build-the-secrets-manager-agent-binary) - - [\[ RPM-based systems \]](#-rpm-based-systems-) - - [\[ Debian-based systems \]](#-debian-based-systems-) - - [\[ Windows \]](#-windows-) - - [\[ Cross-compile natively \]](#-cross-compile-natively-) - - [\[ Cross compile with Rust cross \]](#-cross-compile-with-rust-cross-) + - [\[ RPM-based systems \]](#-rpm-based-systems-) + - [\[ Debian-based systems \]](#-debian-based-systems-) + - [\[ Windows \]](#-windows-) + - [\[ Cross-compile natively \]](#-cross-compile-natively-) + - [\[ Cross compile with Rust cross \]](#-cross-compile-with-rust-cross-) - [Step 2: Install the Secrets Manager Agent](#step-2-install-the-secrets-manager-agent) - - [\[ Amazon EC2 \]](#-amazon-ec2-) - - [\[ Running as a Container Sidecar \]](#-running-as-a-container-sidecar-) - - [\[ AWS Lambda \]](#-aws-lambda-) + - [\[ Amazon EC2 \]](#-amazon-ec2-) + - [\[ Running as a Container Sidecar \]](#-running-as-a-container-sidecar-) + - [\[ AWS Lambda \]](#-aws-lambda-) - [Step 3: Retrieve secrets with the Secrets Manager Agent](#step-3-retrieve-secrets-with-the-secrets-manager-agent) - - [\[ curl \]](#-curl-) - - [\[ Python \]](#-python-) + - [\[ curl \]](#-curl-) + - [\[ Python \]](#-python-) - [`refreshNow` parameter behavior](#refreshnow-parameter-behavior) - [Using the refreshNow parameter](#using-the-refreshnow-parameter) - [Example - Secrets Manager Agent GET request with refreshNow parameter](#example---secrets-manager-agent-get-request-with-refreshnow-parameter) @@ -39,7 +40,7 @@ To download the source code, see [https://github\.com/aws/aws\-secretsmanager\-a To build the Secrets Manager Agent binary natively, you need the standard development tools and the Rust tools\. Alternatively, you can cross\-compile for systems that support it, or you can use Rust cross to cross\-compile\. ------- +--- **NOTE:** Building the agent with the `fips` feature enabled on macOS currently requires the following workaround: @@ -53,7 +54,7 @@ To build the Secrets Manager Agent binary natively, you need the standard develo sudo yum -y groupinstall "Development Tools" ``` -1. Follow the instructions at [Install Rust](https://www.rust-lang.org/tools/install) in the *Rust documentation*\. +1. Follow the instructions at [Install Rust](https://www.rust-lang.org/tools/install) in the _Rust documentation_\. ```sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Follow the on-screen instructions @@ -68,7 +69,8 @@ To build the Secrets Manager Agent binary natively, you need the standard develo You will find the executable under `target/release/aws_secretsmanager_agent`\. ------- +--- + #### [ Debian\-based systems ] 1. On Debian\-based systems such as Ubuntu, you can install the developer tools using the build\-essential package\. @@ -77,7 +79,7 @@ To build the Secrets Manager Agent binary natively, you need the standard develo sudo apt install build-essential ``` -1. Follow the instructions at [Install Rust](https://www.rust-lang.org/tools/install) in the *Rust documentation*\. +1. Follow the instructions at [Install Rust](https://www.rust-lang.org/tools/install) in the _Rust documentation_\. ```sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Follow the on-screen instructions @@ -92,10 +94,11 @@ To build the Secrets Manager Agent binary natively, you need the standard develo You will find the executable under `target/release/aws_secretsmanager_agent`\. ------- +--- + #### [ Windows ] -To build on Windows, follow the instructions at [Set up your dev environment on Windows for Rust](https://learn.microsoft.com/en-us/windows/dev-environment/rust/setup) in the *Microsoft Windows documentation*\. +To build on Windows, follow the instructions at [Set up your dev environment on Windows for Rust](https://learn.microsoft.com/en-us/windows/dev-environment/rust/setup) in the _Microsoft Windows documentation_\. 1. Build the agent using the cargo build command: @@ -105,7 +108,8 @@ To build on Windows, follow the instructions at [Set up your dev environment on You will find the executable under `target/release/aws_secretsmanager_agent.exe`\. ------- +--- + #### [ Cross\-compile natively ] On distributions where the mingw\-w64 package is available such as Ubuntu, you can cross compile natively\. @@ -114,22 +118,23 @@ On distributions where the mingw\-w64 package is available such as Ubuntu, you c # Install the cross compile tool chain sudo add-apt-repository universe sudo apt install -y mingw-w64 - + # Install the rust build targets rustup target add x86_64-pc-windows-gnu - + # Cross compile the agent for Windows cargo build --release --target x86_64-pc-windows-gnu ``` You will find the executable at `target/x86_64-pc-windows-gnu/release/aws_secretsmanager_agent.exe`\. ------- +--- + #### [ Cross compile with Rust cross ] If the cross compile tools are not available natively on the system, you can use the Rust cross project\. For more information, see [https://github\.com/cross\-rs/cross](https://github.com/cross-rs/cross)\. -**Important** +**Important** We recommend 32GB disk space for the build environment\. ```sh @@ -137,42 +142,44 @@ We recommend 32GB disk space for the build environment\. sudo yum -y install docker sudo systemctl start docker sudo systemctl enable docker # Make docker start after reboot - + # Give ourselves permission to run the docker images without sudo sudo usermod -aG docker $USER newgrp docker - + # Install cross and cross compile the executable cargo install cross cross build --release --target x86_64-pc-windows-gnu ``` ------- +--- ## Step 2: Install the Secrets Manager Agent Based on the type of compute, you have several options for installing the Secrets Manager Agent\. ------- +--- + #### [ Amazon EC2 ] **To install the Secrets Manager Agent** 1. `cd aws_secretsmanager_agent/configuration` -1. Run the `install` script provided in the repository\. +1. Run the `install` script provided in the repository\. - The script generates a random SSRF token on startup and stores it in the file `/var/run/awssmatoken`\. The token is readable by the `awssmatokenreader` group that the install script creates\. + The script generates a random SSRF token on startup and stores it in the file `/var/run/awssmatoken`\. The token is readable by the `awssmatokenreader` group that the install script creates\. -1. To allow your application to read the token file, you need to add the user account that your application runs under to the `awssmatokenreader` group\. For example, you can grant permissions for your application to read the token file with the following usermod command, where ** is the user ID under which your application runs\. +1. To allow your application to read the token file, you need to add the user account that your application runs under to the `awssmatokenreader` group\. For example, you can grant permissions for your application to read the token file with the following usermod command, where __ is the user ID under which your application runs\. ```sh sudo usermod -aG awssmatokenreader ``` ------- +--- + #### [ Running as a Container Sidecar ] -You can run the Secrets Manager Agent as a sidecar container alongside your application by using Docker\. Then your application can retrieve secrets from the local HTTP server the Secrets Manager Agent provides\. For information about Docker, see the [Docker documentation](https://docs.docker.com)\. +You can run the Secrets Manager Agent as a sidecar container alongside your application by using Docker\. Then your application can retrieve secrets from the local HTTP server the Secrets Manager Agent provides\. For information about Docker, see the [Docker documentation](https://docs.docker.com)\. **To create a sidecar container for the Secrets Manager Agent with Docker** @@ -181,16 +188,16 @@ You can run the Secrets Manager Agent as a sidecar container alongside your appl ```dockerfile # Use the latest Debian image as the base FROM debian:latest - + # Set the working directory inside the container - WORKDIR /app - + WORKDIR /app + # Copy the Secrets Manager Agent binary to the container - COPY secrets-manager-agent . - + COPY secrets-manager-agent . + # Install any necessary dependencies - RUN apt-get update && apt-get install -y ca-certificates - + RUN apt-get update && apt-get install -y ca-certificates + # Set the entry point to run the Secrets Manager Agent binary ENTRYPOINT ["./secrets-manager-agent"] ``` @@ -199,12 +206,12 @@ You can run the Secrets Manager Agent as a sidecar container alongside your appl 1. Create a Docker Compose file to run both containers, being sure that they use the same network interface\. This is necessary because the Secrets Manager Agent does not accept requests from outside the localhost interface\. The following example shows a Docker Compose file where the `network_mode` key attaches the `secrets-manager-agent` container to the network namespace of the `client-application` container, which allows them to share the same network interface\. - **Important** + **Important** - You must load AWS credentials and the SSRF token for the application to be able to use the Secrets Manager Agent\. For EKS and ECS, see the following: - * [Manage access](https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html) in the *Amazon Elastic Kubernetes Service User Guide* - * [Amazon ECS task IAM role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) in the *Amazon Elastic Container Service Developer Guide* + You must load AWS credentials and the SSRF token for the application to be able to use the Secrets Manager Agent\. For EKS and ECS, see the following: + - [Manage access](https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html) in the _Amazon Elastic Kubernetes Service User Guide_ + - [Amazon ECS task IAM role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) in the _Amazon Elastic Container Service Developer Guide_ ```yaml version: '3' @@ -215,8 +222,8 @@ You can run the Secrets Manager Agent as a sidecar container alongside your appl context: . dockerfile: Dockerfile.client command: tail -f /dev/null # Keep the container running - - + + secrets-manager-agent: container_name: secrets-manager-agent build: @@ -237,12 +244,13 @@ You can run the Secrets Manager Agent as a sidecar container alongside your appl 1. In your client container, you can now use the Secrets Manager Agent to retrieve secrets\. For more information, see [Step 3: Retrieve secrets with the Secrets Manager Agent](#secrets-manager-agent-call)\. ------- +--- + #### [ AWS Lambda ] -You can [package the Secrets Manager Agent as an AWS Lambda extension](https://docs.aws.amazon.com/lambda/latest/dg/packaging-layers.html)\. Then you can [add it to your Lambda function as a layer](https://docs.aws.amazon.com/lambda/latest/dg/adding-layers.html) and call the Secrets Manager Agent from your Lambda function to get secrets\. +You can [package the Secrets Manager Agent as an AWS Lambda extension](https://docs.aws.amazon.com/lambda/latest/dg/packaging-layers.html)\. Then you can [add it to your Lambda function as a layer](https://docs.aws.amazon.com/lambda/latest/dg/adding-layers.html) and call the Secrets Manager Agent from your Lambda function to get secrets\. -The following instructions show how to get a secret named *MyTest* by using the example script `secrets-manager-agent-extension.sh` in [https://github\.com/aws/aws\-secretsmanager\-agent](https://github.com/aws/aws-secretsmanager-agent) to install the Secrets Manager Agent as a Lambda extension\. +The following instructions show how to get a secret named _MyTest_ by using the example script `secrets-manager-agent-extension.sh` in [https://github\.com/aws/aws\-secretsmanager\-agent](https://github.com/aws/aws-secretsmanager-agent) to install the Secrets Manager Agent as a Lambda extension\. **To create a Lambda extension that packages the Secrets Manager Agent** @@ -251,21 +259,21 @@ The following instructions show how to get a secret named *MyTest* by using the ```sh AWS_ACCOUNT_ID= LAMBDA_ARN= - - # Build the release binary + + # Build the release binary cargo build --release --target=x86_64-unknown-linux-gnu - + # Copy the release binary into the `bin` folder mkdir -p ./bin cp ./target/x86_64-unknown-linux-gnu/release/aws_secretsmanager_agent ./bin/secrets-manager-agent - + # Copy the `secrets-manager-agent-extension.sh` example script into the `extensions` folder. mkdir -p ./extensions cp aws_secretsmanager_agent/examples/example-lambda-extension/secrets-manager-agent-extension.sh ./extensions - - # Zip the extension shell script and the binary + + # Zip the extension shell script and the binary zip secrets-manager-agent-extension.zip bin/* extensions/* - + # Publish the layer version LAYER_VERSION_ARN=$(aws lambda publish-layer-version \ --layer-name secrets-manager-agent-extension \ @@ -274,8 +282,7 @@ The following instructions show how to get a secret named *MyTest* by using the 2. The default configuration of the agent will automatically set the SSRF token to the value set in the pre-set `AWS_SESSION_TOKEN` or `AWS_CONTAINER_AUTHORIZATION_TOKEN` environment variables (the latter variable for Lambda functions with SnapStart enabled). Alternatively, you can define the `AWS_TOKEN` environment variable with an arbitrary value for your Lambda function instead as this variable takes precedence over the other two. If you choose to use the `AWS_TOKEN` environment variable, you must set that environment variable with a `lambda:UpdateFunctionConfiguration` call\. - -3. Attach the layer version to your Lambda function: +3. Attach the layer version to your Lambda function: ```sh # Attach the layer version to the Lambda function aws lambda update-function-configuration \ @@ -284,10 +291,9 @@ The following instructions show how to get a secret named *MyTest* by using the ``` 4. Update your Lambda function to query `http://localhost:2773/secretsmanager/get?secretId=MyTest` with the `X-Aws-Parameters-Secrets-Token` header value set to the value of the SSRF token sourced from one the environment variables mentioned above to retrieve the secret. Be sure to implement retry logic in your application code to accommodate delays in initialization and registration of the Lambda extension\. +5. Invoke the Lambda function to verify that the secret is being correctly fetched\. -5. Invoke the Lambda function to verify that the secret is being correctly fetched\. - ------- +--- ## Step 3: Retrieve secrets with the Secrets Manager Agent @@ -295,18 +301,20 @@ To use the agent, you call the local Secrets Manager Agent endpoint and include To help protect the Secrets Manager Agent, you must include a SSRF token header as part of each request: `X-Aws-Parameters-Secrets-Token`\. The Secrets Manager Agent denies requests that don't have this header or that have an invalid SSRF token\. You can customize the SSRF header name in the [Configuration file](#secrets-manager-agent-config)\. -The Secrets Manager Agent uses the AWS SDK for Rust, which uses the [https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html)\. The identity of these IAM credentials determines the permissions the Secrets Manager Agent has to retrieve secrets\. +The Secrets Manager Agent uses the AWS SDK for Rust, which uses the [https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html)\. The identity of these IAM credentials determines the permissions the Secrets Manager Agent has to retrieve secrets\. **Required permissions: ** -+ `secretsmanager:DescribeSecret` -+ `secretsmanager:GetSecretValue` + +- `secretsmanager:DescribeSecret` +- `secretsmanager:GetSecretValue` For more information, see [Permissions reference](reference_iam-permissions.md)\. -**Important** +**Important** After the secret value is pulled into the Secrets Manager Agent, any user with access to the compute environment and SSRF token can access the secret from the Secrets Manager Agent cache\. For more information, see [Security considerations](#secrets-manager-agent-security)\. ------- +--- + #### [ curl ] The following curl example shows how to get a secret from the Secrets Manager Agent\. The example relies on the SSRF being present in a file, which is where it is stored by the install script\. @@ -318,7 +326,8 @@ curl -v -H \ echo ``` ------- +--- + #### [ Python ] The following Python example shows how to get a secret from the Secrets Manager Agent\. The example relies on the SSRF being present in a file, which is where it is stored by the install script\. @@ -327,14 +336,14 @@ The following Python example shows how to get a secret from the Secrets Manager import requests import json -# Function that fetches the secret from Secrets Manager Agent for the provided secret id. +# Function that fetches the secret from Secrets Manager Agent for the provided secret id. def get_secret(): # Construct the URL for the GET request url = f"http://localhost:2773/secretsmanager/get?secretId=" # Get the SSRF token from the token file with open('/var/run/awssmatoken') as fp: - token = fp.read() + token = fp.read() headers = { "X-Aws-Parameters-Secrets-Token": token.strip() @@ -356,7 +365,8 @@ def get_secret(): # Handle network errors raise Exception(f"Error: {e}") ``` ------- + +--- **Force-refresh secrets with `RefreshNow`** @@ -367,11 +377,13 @@ Secrets Manager Agent uses an in-memory cache to store secret values, which it r To address this limitation, Secrets Manager Agent supports a parameter called `refreshNow` in the URL. You can use this parameter to force an immediate refresh of a secret's value, bypassing the cache and ensuring you have the most up-to-date information. Default behavior (without `refreshNow`): + - Uses cached values until TTL expires - Refreshes secrets only after TTL (default 300 seconds) - May return stale values if secrets rotate before the cache expires Behavior with `refreshNow=true`: + - Bypasses the cache entirely - Retrieves the latest secret value directly from Secrets Manager - Updates the cache with the fresh value and resets the TTL @@ -382,9 +394,11 @@ By using the `refreshNow` parameter, you can ensure that you're always working w ## `refreshNow` parameter behavior `refreshNow` set to `true`: + - If Secrets Manager Agent can't retrieve the secret from Secrets Manager, it returns an error and does not update the cache. `refreshNow` set to `false` or not specified: + - Secrets Manager Agent follows its default behavior: - If the cached value is fresher than the TTL, Secrets Manager Agent returns the cached value. - If the cached value is older than the TTL, Secrets Manager Agent makes a call to Secrets Manager. @@ -416,14 +430,14 @@ The following Python example shows how to get a secret from the Secrets Manager import requests import json -# Function that fetches the secret from Secrets Manager Agent for the provided secret id. +# Function that fetches the secret from Secrets Manager Agent for the provided secret id. def get_secret(): # Construct the URL for the GET request url = f"http://localhost:2773/secretsmanager/get?secretId=&refreshNow=true" # Get the SSRF token from the token file with open('/var/run/awssmatoken') as fp: - token = fp.read() + token = fp.read() headers = { "X-Aws-Parameters-Secrets-Token": token.strip() @@ -445,39 +459,42 @@ def get_secret(): # Handle network errors raise Exception(f"Error: {e}") ``` ------- + +--- ## Configure the Secrets Manager Agent To change the configuration of the Secrets Manager Agent, create a [TOML](https://toml.io/en/) config file, and then call `./aws_secretsmanager_agent --config config.toml`\. The following list shows the options you can configure for the Secrets Manager Agent\. -+ **log\_level** – The level of detail reported in logs for the Secrets Manager Agent: DEBUG, INFO, WARN, ERROR, or NONE\. The default is INFO\. -+ **log\_to\_file** - Whether to log to a file or stdout/stderr: `true` or `false`. The default is `true`. -+ **http\_port** – The port for the local HTTP server, in the range 1024 to 65535\. The default is 2773\. -+ **region** – The AWS Region to use for requests\. If no Region is specified, the Secrets Manager Agent determines the Region from the SDK\. For more information, see [Specify your credentials and default Region](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html) in the *AWS SDK for Rust Developer Guide*\. -+ **ttl\_seconds** – The TTL in seconds for the cached items, in the range 0 to 3600\. The default is 300\. 0 indicates that there is no caching\. -+ **cache\_size** – The maximum number of secrets that can be stored in the cache, in the range 1 to 1000\. The default is 1000\. -+ **ssrf\_headers** – A list of header names the Secrets Manager Agent checks for the SSRF token\. The default is "X\-Aws\-Parameters\-Secrets\-Token, X\-Vault\-Token"\. -+ **ssrf\_env\_variables** – A list of environment variable names the Secrets Manager Agent checks in sequential order for the SSRF token\. The environment variable can contain the token or a reference to the token file as in: `AWS_TOKEN=file:///var/run/awssmatoken`\. The default is "AWS\_TOKEN, AWS\_SESSION\_TOKEN, AWS\_CONTAINER\_AUTHORIZATION\_TOKEN\". -+ **path\_prefix** – The URI prefix used to determine if the request is a path based request\. The default is "/v1/"\. -+ **max\_conn** – The maximum number of connections from HTTP clients that the Secrets Manager Agent allows, in the range 1 to 1000\. The default is 800\. + +- **log_level** – The level of detail reported in logs for the Secrets Manager Agent: DEBUG, INFO, WARN, ERROR, or NONE\. The default is INFO\. +- **log_to_file** - Whether to log to a file or stdout/stderr: `true` or `false`. The default is `true`. +- **http_port** – The port for the local HTTP server, in the range 1024 to 65535\. The default is 2773\. +- **region** – The AWS Region to use for requests\. If no Region is specified, the Secrets Manager Agent determines the Region from the SDK\. For more information, see [Specify your credentials and default Region](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html) in the _AWS SDK for Rust Developer Guide_\. +- **ttl_seconds** – The TTL in seconds for the cached items, in the range 0 to 3600\. The default is 300\. 0 indicates that there is no caching\. +- **cache_size** – The maximum number of secrets that can be stored in the cache, in the range 1 to 1000\. The default is 1000\. +- **ssrf_headers** – A list of header names the Secrets Manager Agent checks for the SSRF token\. The default is "X\-Aws\-Parameters\-Secrets\-Token, X\-Vault\-Token"\. +- **ssrf_env_variables** – A list of environment variable names the Secrets Manager Agent checks in sequential order for the SSRF token\. The environment variable can contain the token or a reference to the token file as in: `AWS_TOKEN=file:///var/run/awssmatoken`\. The default is "AWS_TOKEN, AWS_SESSION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN\". +- **path_prefix** – The URI prefix used to determine if the request is a path based request\. The default is "/v1/"\. +- **max_conn** – The maximum number of connections from HTTP clients that the Secrets Manager Agent allows, in the range 1 to 1000\. The default is 800\. ## Optional features The Secrets Manager Agent can be built with optional features by passing the `--features` flag to `cargo build`. The available features are: -* `prefer-post-quantum`: makes `X25519MLKEM768` the highest-priority key exchange algorithm. Otherwise, it is available but not highest-priority. `X25519MLKEM768` is a hybrid, post-quantum-secure key exchange algorithm. -* `fips`: restricts the cipher suites used by the agent to only FIPS-approved ciphers + +- `prefer-post-quantum`: makes `X25519MLKEM768` the highest-priority key exchange algorithm. Otherwise, it is available but not highest-priority. `X25519MLKEM768` is a hybrid, post-quantum-secure key exchange algorithm. +- `fips`: restricts the cipher suites used by the agent to only FIPS-approved ciphers ## Logging -The Secrets Manager Agent logs errors locally to the file `logs/secrets_manager_agent.log` or to stdout/stderr depending on the `log_to_file` config variable\. When your application calls the Secrets Manager Agent to get a secret, those calls appear in the local log\. They do not appear in the CloudTrail logs\. +The Secrets Manager Agent logs errors locally to the file `logs/secrets_manager_agent.log` or to stdout/stderr depending on the `log_to_file` config variable\. When your application calls the Secrets Manager Agent to get a secret, those calls appear in the local log\. They do not appear in the CloudTrail logs\. -The Secrets Manager Agent creates a new log file when the file reaches 10 MB, and it stores up to five log files total\. +The Secrets Manager Agent creates a new log file when the file reaches 10 MB, and it stores up to five log files total\. -The log does not go to Secrets Manager, CloudTrail, or CloudWatch\. Requests to get secrets from the Secrets Manager Agent do not appear in those logs\. When the Secrets Manager Agent makes a call to Secrets Manager to get a secret, that call is recorded in CloudTrail with a user agent string containing `aws-secrets-manager-agent`\. +The log does not go to Secrets Manager, CloudTrail, or CloudWatch\. Requests to get secrets from the Secrets Manager Agent do not appear in those logs\. When the Secrets Manager Agent makes a call to Secrets Manager to get a secret, that call is recorded in CloudTrail with a user agent string containing `aws-secrets-manager-agent`\. -You can configure logging in the [Configuration file](#secrets-manager-agent-config)\. +You can configure logging in the [Configuration file](#secrets-manager-agent-config)\. ## Security considerations diff --git a/aws_secretsmanager_agent/src/cache_manager.rs b/aws_secretsmanager_agent/src/cache_manager.rs index 9e5c425..ac53ed2 100644 --- a/aws_secretsmanager_agent/src/cache_manager.rs +++ b/aws_secretsmanager_agent/src/cache_manager.rs @@ -57,7 +57,7 @@ impl CacheManager { /// /// # Example /// - /// ``` + /// ```rust /// let cache_manager = CacheManager::new().await.unwrap(); /// let value = cache_manager.fetch("my-secret", None, None).unwrap(); /// ``` diff --git a/aws_secretsmanager_agent/src/config.rs b/aws_secretsmanager_agent/src/config.rs index 8b7878d..9c6311c 100644 --- a/aws_secretsmanager_agent/src/config.rs +++ b/aws_secretsmanager_agent/src/config.rs @@ -85,7 +85,7 @@ pub struct Config { /// The port for the local HTTP server. http_port: u16, - /// The `time to live` of a secret + /// The time-to-live of a secret ttl: Duration, /// Maximum number secrets that can be stored in the cache. @@ -125,12 +125,11 @@ impl Default for Config { impl Config { /// Initialize the configuation using the optional configuration file. /// - /// If and override file is not provided, default configurations will be - /// used. + /// If and override file is not provided, default configurations will be used. /// /// # Arguments /// - /// * `file_pth` - The configuration file (in toml format) used to override the default, or None to use the defaults. + /// * `file_path` - The configuration file (in toml format) used to override the default, or None to use the defaults. /// /// # Returns /// @@ -167,7 +166,7 @@ impl Config { Config::build(config.build()?.try_deserialize()?) } - /// The level of logging the agent provides ie. debug, info, warn, error or none + /// The level of logging the agent provides: debug, info, warn, error or none /// /// # Returns /// @@ -186,7 +185,7 @@ impl Config { self.log_to_file } - /// The port for the local HTTP server to listen for incomming requests. + /// The port for the local HTTP server to listen for incoming requests. /// /// # Returns /// @@ -195,7 +194,7 @@ impl Config { self.http_port } - /// The `time to live` of a secret in the cache in seconds. + /// The time-to-live of a secret in the cache in seconds. /// /// # Returns /// @@ -213,7 +212,7 @@ impl Config { self.cache_size } - /// A list of request headers which will be checked for the SSRF token (can not be empty). + /// A list of request headers which will be checked for the SSRF token (cannot be empty). /// /// # Returns /// @@ -226,16 +225,16 @@ impl Config { /// /// # Returns /// - /// * `ssrf_env_variables` - The name of the env variable containing the SSRF token value. Defaults to ["AWS_TOKEN", "AWS_SESSION_TOKEN", "AWS_CONTAINER_AUTHORIZATION_TOKEN"]. + /// * `ssrf_env_variables` - The name of the env variable containing the SSRF token value. Defaults to `["AWS_TOKEN", "AWS_SESSION_TOKEN", "AWS_CONTAINER_AUTHORIZATION_TOKEN"]`. pub fn ssrf_env_variables(&self) -> Vec { self.ssrf_env_variables.clone() } - /// The prefix for path based requests (must begin with /). + /// The prefix for path based requests (must begin with `/`). /// /// # Returns /// - /// * `path_prefix` - The path name prefix. Defaults to /v1/. + /// * `path_prefix` - The path name prefix. Defaults to `/v1/`. pub fn path_prefix(&self) -> String { self.path_prefix.clone() } @@ -264,7 +263,7 @@ impl Config { /// /// # Returns /// - /// * `ignore_transient_errors` - Whether the client should serve cached data on transient refresh errors. Defaults to "true" + /// * `ignore_transient_errors` - Whether the client should serve cached data on transient refresh errors. Defaults to `true` pub fn ignore_transient_errors(&self) -> bool { self.ignore_transient_errors } @@ -273,7 +272,7 @@ impl Config { /// /// # Returns /// - /// * `validate_credentials` - Whether the agent should validate AWS credentials at startup. Defaults to "true" + /// * `validate_credentials` - Whether the agent should validate AWS credentials at startup. Defaults to `true` pub fn validate_credentials(&self) -> bool { self.validate_credentials } @@ -361,8 +360,9 @@ impl Config { /// /// # Example /// -/// ``` +/// ```rust /// use std::ops::Range; +/// /// assert_eq!(parse_num::(&String::from("42"), "What is the qustion?", Some(1..100), None).unwrap(), 42); /// ``` #[doc(hidden)] @@ -373,7 +373,7 @@ fn parse_num( neg_range: Option>, ) -> Result> where - T: PartialOrd + Sized + std::str::FromStr, + T: PartialOrd + Sized + FromStr, { let val = match str_val.parse::() { Ok(x) => x, diff --git a/aws_secretsmanager_agent/src/logging.rs b/aws_secretsmanager_agent/src/logging.rs index 3c40218..7910309 100644 --- a/aws_secretsmanager_agent/src/logging.rs +++ b/aws_secretsmanager_agent/src/logging.rs @@ -30,7 +30,7 @@ const MAX_ALLOWED_LOG_SIZE_IN_MB: u64 = 10; #[doc(hidden)] static STARTUP: Once = Once::new(); -/// Initializes file based logging for the daemon. +/// Initializes logging for the daemon. /// /// # Arguments /// diff --git a/aws_secretsmanager_agent/src/main.rs b/aws_secretsmanager_agent/src/main.rs index 61d94a3..48f64be 100644 --- a/aws_secretsmanager_agent/src/main.rs +++ b/aws_secretsmanager_agent/src/main.rs @@ -41,9 +41,9 @@ async fn main() -> Result<(), Box> { /// /// # Example /// -/// ``` +/// ```rust /// use std::net::SocketAddr; -/// report( &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 2773) ); +/// report(&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 2773)); /// ``` #[doc(hidden)] fn report(addr: &SocketAddr) { @@ -63,11 +63,11 @@ fn report(addr: &SocketAddr) { /// /// # Returns /// -/// * bool - Always returns false so the server never shuts down. +/// * `bool` - Always returns false so the server never shuts down. /// /// # Example /// -/// ``` +/// ```rust /// assert_eq!(forever(), false); /// ``` #[doc(hidden)] @@ -82,6 +82,7 @@ fn forever() -> bool { /// * `args` - The command line arguments. /// * `report` - A call back used to report startup and the listener port. /// * `end` - A call back used to signal shut down. +/// /// # Returns /// /// * `Ok(())` - Never retuned when started by the main entry point. @@ -98,7 +99,7 @@ async fn run bool>( report(&addr); // Report the port used. - // Spawn a handler for each incomming request. + // Spawn a handler for each incoming request. loop { // Report errors on accept. if let Err(msg) = svr.serve_request().await { @@ -120,9 +121,7 @@ async fn run bool>( /// /// # Returns /// -/// * (Config, TcpListener) - The configuration info and the TCP listener. -/// -/// ``` +/// * `(Config, TcpListener)` - The configuration info and the TCP listener. #[doc(hidden)] async fn init(args: impl IntoIterator) -> (Config, TcpListener) { // Get the arg iterator and program name from arg 0. diff --git a/aws_secretsmanager_agent/src/server.rs b/aws_secretsmanager_agent/src/server.rs index 5c1826c..2f6a7b2 100644 --- a/aws_secretsmanager_agent/src/server.rs +++ b/aws_secretsmanager_agent/src/server.rs @@ -29,20 +29,20 @@ pub struct Server { /// Handle incoming HTTP requests. /// -/// Implements the HTTP handler. Each incomming request is handled in its own +/// Implements the HTTP handler. Each incoming request is handled in its own /// thread. impl Server { /// Create a server instance. /// /// # Arguments /// - /// * `listener` - The TcpListener to use to accept incomming requests. + /// * `listener` - The TcpListener to use to accept incoming requests. /// * `cfg` - The config object to use for options such header names. /// /// # Returns /// /// * `Ok(Self)` - The server object. - /// * `Box>` - Retruned for errors initializing the agent + /// * `Box>` - Returned for errors initializing the agent pub async fn new( listener: TcpListener, cfg: &Config, @@ -87,11 +87,11 @@ impl Server { Ok(()) } - /// Private helper to process the incomming request body and format a response. + /// Private helper to process the incoming request body and format a response. /// /// # Arguments /// - /// * `req` - The incomming HTTP request. + /// * `req` - The incoming HTTP request. /// * `count` - The number of concurrent requets being handled. /// /// # Returns @@ -118,12 +118,12 @@ impl Server { } } - /// Parse an incomming request and provide the response data. + /// Parse an incoming request and provide the response data. /// /// # Arguments /// - /// * `req` - The incomming HTTP request. - /// * `count` - The number of concurrent requets being handled. + /// * `req` - The incoming HTTP request. + /// * `count` - The number of concurrent requests being handled. /// /// # Returns /// @@ -173,13 +173,13 @@ impl Server { } } - /// Verify the incomming request does not exceed the maximum connection limit. + /// Verify the incoming request does not exceed the maximum connection limit. /// /// The limit is not enforced for ping/health checks. /// /// # Arguments /// - /// * `req` - The incomming HTTP request. + /// * `req` - The incoming HTTP request. /// * `count` - The number of concurrent requets being handled. /// /// # Returns @@ -211,7 +211,7 @@ impl Server { /// /// # Arguments /// - /// * `req` - The incomming HTTP request. + /// * `req` - The incoming HTTP request. /// /// # Returns /// @@ -247,7 +247,7 @@ impl Server { /// /// # Arguments /// - /// * `req` - The incomming HTTP request. + /// * `req` - The incoming HTTP request. /// /// # Returns /// diff --git a/aws_secretsmanager_agent/src/utils.rs b/aws_secretsmanager_agent/src/utils.rs index e8f1a95..b0298c7 100644 --- a/aws_secretsmanager_agent/src/utils.rs +++ b/aws_secretsmanager_agent/src/utils.rs @@ -9,7 +9,8 @@ use std::fs; use std::time::Duration; #[cfg(not(test))] -use std::env::var; // Use the real std::env::var +// Use the real std::env::var +use std::env::var; #[cfg(test)] use tests::var_test as var; @@ -30,7 +31,7 @@ use tests::var_test as var; /// /// # Example /// -/// ``` +/// ```rust /// assert_eq!(err_response("InternalFailure", ""), "{\"__type\":\"InternalFailure\"}"); /// assert_eq!( /// err_response("ResourceNotFoundException", "Secrets Manager can't find the specified secret."), @@ -94,7 +95,7 @@ pub use time_out_test as time_out; /// /// # Returns /// -/// * `Durration` - How long to wait before canceling the operation. +/// * `Duration` - How long to wait before canceling the operation. #[doc(hidden)] pub fn time_out_impl() -> Duration { Duration::from_secs(MAX_REQ_TIME_SEC) @@ -201,7 +202,7 @@ pub mod tests { // Cleanup temp files. if let Some(name) = self.file { - let _ = std::fs::remove_file(name); + let _ = fs::remove_file(name); } } } @@ -273,7 +274,7 @@ pub mod tests { let _cleanup = CleanUp { file: Some(&tmpfile), }; - std::fs::write(&tmpfile, token).expect("could not write"); + fs::write(&tmpfile, token).expect("could not write"); let file = Box::new(format!("file://{tmpfile}")); set_test_var("AWS_TOKEN", Box::leak(file)); let cfg = Config::new(None).expect("config failed"); @@ -318,7 +319,7 @@ pub mod tests { file: Some(&tmpfile), }; set_test_var("", ""); - std::fs::write(&tmpfile, "ssrf_env_variables = [\"NOSUCHENV\"]").expect("could not write"); + fs::write(&tmpfile, "ssrf_env_variables = [\"NOSUCHENV\"]").expect("could not write"); let cfg = Config::new(Some(&tmpfile)).expect("config failed"); assert!(get_token(&cfg) .err() diff --git a/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_config.toml b/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_config.toml index 2edee69..42fe1ac 100644 --- a/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_config.toml +++ b/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_config.toml @@ -1,2 +1,2 @@ log_level = "an invalid log level" -http_port = "9999" \ No newline at end of file +http_port = "9999" diff --git a/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_contents.toml b/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_contents.toml index daa298a..a123375 100644 --- a/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_contents.toml +++ b/aws_secretsmanager_agent/tests/resources/configs/config_file_with_invalid_contents.toml @@ -1 +1 @@ -This is gibberish \ No newline at end of file +This is gibberish diff --git a/aws_secretsmanager_agent/tests/resources/configs/config_file_with_unrecognized_override.toml b/aws_secretsmanager_agent/tests/resources/configs/config_file_with_unrecognized_override.toml index 10f6d9b..509ba55 100644 --- a/aws_secretsmanager_agent/tests/resources/configs/config_file_with_unrecognized_override.toml +++ b/aws_secretsmanager_agent/tests/resources/configs/config_file_with_unrecognized_override.toml @@ -1,2 +1,2 @@ # unrecognized configurations -max_connections = "1001" \ No newline at end of file +max_connections = "1001" diff --git a/aws_secretsmanager_caching/README.md b/aws_secretsmanager_caching/README.md index 5fae15e..fd1b94f 100644 --- a/aws_secretsmanager_caching/README.md +++ b/aws_secretsmanager_caching/README.md @@ -8,10 +8,10 @@ The AWS Secrets Manager Rust Caching Client enables in-process caching of secret To use this client you must have: -* A Rust 2021 development environment. If you do not have one, go to [Rust Getting Started](https://www.rust-lang.org/learn/get-started) on the Rust Programming Language website, then download and install Rust. -* An Amazon Web Services (AWS) account to access secrets stored in AWS Secrets Manager. - * **To create an AWS account**, go to [Sign In or Create an AWS Account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) and then choose **I am a new user.** Follow the instructions to create an AWS account. - * **To create a secret in AWS Secrets Manager**, go to [Creating Secrets](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) and follow the instructions on that page. +- A Rust 2021 development environment. If you do not have one, go to [Rust Getting Started](https://www.rust-lang.org/learn/get-started) on the Rust Programming Language website, then download and install Rust. +- An Amazon Web Services (AWS) account to access secrets stored in AWS Secrets Manager. + - **To create an AWS account**, go to [Sign In or Create an AWS Account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) and then choose **I am a new user.** Follow the instructions to create an AWS account. + - **To create a secret in AWS Secrets Manager**, go to [Creating Secrets](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) and follow the instructions on that page. ### Get Started @@ -50,8 +50,8 @@ let secret_string = match client.get_secret_value("MyTest", None, None).await { ### Cache Configuration -* `max_size: NonZeroUsize`: The maximum number of cached secrets to maintain before evicting secrets that have not been accessed recently. -* `ttl: Duration`: The duration a cached item is considered valid before requiring a refresh of the secret state. +- `max_size: NonZeroUsize`: The maximum number of cached secrets to maintain before evicting secrets that have not been accessed recently. +- `ttl: Duration`: The duration a cached item is considered valid before requiring a refresh of the secret state. ### Instantiating Cache with a custom Config and a custom Client @@ -82,7 +82,7 @@ let client = match SecretsManagerCachingClient::from_builder( let secret_string = client .get_secret_value("MyTest", None, None) - .await + .await { Ok(c) => c.secret_string.unwrap(), Err(_) => panic!("Handle this error"), @@ -95,6 +95,6 @@ let secret_string = client Please use these community resources for getting help: -* Ask a question on [Stack Overflow](https://stackoverflow.com/) and tag it with [aws-secrets-manager](https://stackoverflow.com/questions/tagged/aws-secrets-manager). -* Open a support ticket with [AWS Support](https://console.aws.amazon.com/support/home#/) -* If it turns out that you may have found a bug, or have a feature request, please [open an issue](https://github.com/aws/aws-secretsmanager-agent/issues/new/choose). +- Ask a question on [Stack Overflow](https://stackoverflow.com/) and tag it with [aws-secrets-manager](https://stackoverflow.com/questions/tagged/aws-secrets-manager). +- Open a support ticket with [AWS Support](https://console.aws.amazon.com/support/home#/) +- If it turns out that you may have found a bug, or have a feature request, please [open an issue](https://github.com/aws/aws-secretsmanager-agent/issues/new/choose). diff --git a/aws_secretsmanager_caching/src/lib.rs b/aws_secretsmanager_caching/src/lib.rs index 49c27ac..b28e6c8 100644 --- a/aws_secretsmanager_caching/src/lib.rs +++ b/aws_secretsmanager_caching/src/lib.rs @@ -45,13 +45,16 @@ impl SecretsManagerCachingClient { /// * `max_size` - Maximum size of the store. /// * `ttl` - Time-to-live of the secrets in the store. /// * `ignore_transient_errors` - Whether the client should serve cached data on transient refresh errors + /// + /// # Example + /// /// ```rust /// use aws_sdk_secretsmanager::Client as SecretsManagerClient; /// use aws_sdk_secretsmanager::{config::Region, Config}; /// use aws_secretsmanager_caching::SecretsManagerCachingClient; /// use std::num::NonZeroUsize; /// use std::time::Duration; - + /// /// let asm_client = SecretsManagerClient::from_conf( /// Config::builder() /// .behavior_version_latest() @@ -83,17 +86,20 @@ impl SecretsManagerCachingClient { /// /// * `max_size` - Maximum size of the store. /// * `ttl` - Time-to-live of the secrets in the store. + /// + /// # Example + /// /// ```rust /// tokio_test::block_on(async { - /// use aws_secretsmanager_caching::SecretsManagerCachingClient; - /// use std::num::NonZeroUsize; - /// use std::time::Duration; + /// use aws_secretsmanager_caching::SecretsManagerCachingClient; + /// use std::num::NonZeroUsize; + /// use std::time::Duration; /// - /// let client = SecretsManagerCachingClient::default( - /// NonZeroUsize::new(1000).unwrap(), - /// Duration::from_secs(300), - /// ).await.unwrap(); - /// }) + /// let client = SecretsManagerCachingClient::default( + /// NonZeroUsize::new(1000).unwrap(), + /// Duration::from_secs(300), + /// ).await.unwrap(); + /// }); /// ``` pub async fn default(max_size: NonZeroUsize, ttl: Duration) -> Result { let default_config = &aws_config::load_defaults(BehaviorVersion::latest()).await; @@ -112,28 +118,30 @@ impl SecretsManagerCachingClient { /// * `max_size` - Maximum size of the store. /// * `ttl` - Time-to-live of the secrets in the store. /// + /// # Example + /// /// ```rust /// tokio_test::block_on(async { - /// use aws_secretsmanager_caching::SecretsManagerCachingClient; - /// use std::num::NonZeroUsize; - /// use std::time::Duration; - /// use aws_config::{BehaviorVersion, Region}; - - /// let config = aws_config::load_defaults(BehaviorVersion::latest()) - /// .await - /// .into_builder() - /// .region(Region::from_static("us-west-2")) - /// .build(); - - /// let asm_builder = aws_sdk_secretsmanager::config::Builder::from(&config); - - /// let client = SecretsManagerCachingClient::from_builder( - /// asm_builder, - /// NonZeroUsize::new(1000).unwrap(), - /// Duration::from_secs(300), - /// false, - /// ) - /// .await.unwrap(); + /// use aws_secretsmanager_caching::SecretsManagerCachingClient; + /// use std::num::NonZeroUsize; + /// use std::time::Duration; + /// use aws_config::{BehaviorVersion, Region}; + /// + /// let config = aws_config::load_defaults(BehaviorVersion::latest()) + /// .await + /// .into_builder() + /// .region(Region::from_static("us-west-2")) + /// .build(); + /// + /// let asm_builder = aws_sdk_secretsmanager::config::Builder::from(&config); + /// + /// let client = SecretsManagerCachingClient::from_builder( + /// asm_builder, + /// NonZeroUsize::new(1000).unwrap(), + /// Duration::from_secs(300), + /// false, + /// ) + /// .await.unwrap(); /// }) /// ``` pub async fn from_builder( @@ -192,6 +200,7 @@ impl SecretsManagerCachingClient { /// Refreshes the secret value through a GetSecretValue call to ASM /// /// # Arguments + /// /// * `secret_id` - The ARN or name of the secret to retrieve. /// * `version_id` - The version id of the secret version to retrieve. /// * `version_stage` - The staging label of the version of the secret to retrieve. @@ -254,11 +263,13 @@ impl SecretsManagerCachingClient { /// Check if the value in the cache is still fresh enough to be served again /// /// # Arguments + /// /// * `version_id` - The version id of the secret version to retrieve. /// * `version_stage` - The staging label of the version of the secret to retrieve. Defaults to AWSCURRENT /// * `cached_value` - The value currently in the cache. /// /// # Returns + /// /// * true if value can be reused, false if not async fn is_current( &self, @@ -328,10 +339,7 @@ mod tests { SecretsManagerCachingClient::new( asm_mock::def_fake_client(http_client, endpoint_url), NonZeroUsize::new(1000).unwrap(), - match ttl { - Some(ttl) => ttl, - None => Duration::from_secs(1000), - }, + ttl.unwrap_or_else(|| Duration::from_secs(1000)), ignore_transient_errors, ) .expect("client should create") @@ -585,9 +593,9 @@ mod tests { .await .unwrap(); - if (client + if client .get_secret_value(secret_id, version_id, None, false) - .await) + .await .is_ok() { panic!("Expected failure") @@ -925,16 +933,15 @@ mod tests { .operation_attempt_timeout(Duration::from_millis(100)) .build(), ) - .http_client(match http_client { - Some(custom_client) => custom_client, - None => infallible_client_fn(|_req| { + .http_client(http_client.unwrap_or_else(|| { + infallible_client_fn(|_req| { let (code, rsp) = format_rsp(_req); Response::builder() .status(code) .body(SdkBody::from(rsp)) .unwrap() - }), - }); + }) + })); config_builder = match endpoint_url { Some(endpoint_url) => config_builder.endpoint_url(endpoint_url), None => config_builder, diff --git a/aws_secretsmanager_caching/src/output.rs b/aws_secretsmanager_caching/src/output.rs index fce4f66..c6bb375 100644 --- a/aws_secretsmanager_caching/src/output.rs +++ b/aws_secretsmanager_caching/src/output.rs @@ -10,35 +10,37 @@ use std::time::SystemTime; /// We tried to De/Serialize the remote types using but couldn't as the remote types are non_exhaustive, /// which is a Rust limitation. We can remove this when aws sdk implements De/Serialize trait for the types. #[serde_as] -#[derive(::std::clone::Clone, ::std::cmp::PartialEq, Debug, Serialize, Deserialize, Default)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Default)] #[serde(rename_all = "PascalCase")] pub struct GetSecretValueOutputDef { /// The ARN of the secret. #[serde(rename(serialize = "ARN"))] - pub arn: std::option::Option, + pub arn: Option, /// The friendly name of the secret. - pub name: std::option::Option, + pub name: Option, /// The unique identifier of this version of the secret. - pub version_id: std::option::Option, + pub version_id: Option, - /// The decrypted secret value, if the secret value was originally provided as a string or through the Secrets Manager console. - /// If this secret was created by using the console, then Secrets Manager stores the information as a JSON structure of key/value pairs. + /// The decrypted secret value, if the secret value was originally provided as a string or + /// through the Secrets Manager console. If this secret was created by using the console, then + /// Secrets Manager stores the information as a JSON structure of key/value pairs. #[serde(skip_serializing_if = "Option::is_none")] - pub secret_string: std::option::Option, + pub secret_string: Option, /// Decrypted secret binary, if present. #[serde(skip_serializing_if = "Option::is_none")] - pub secret_binary: std::option::Option, + pub secret_binary: Option, /// A list of all of the staging labels currently attached to this version of the secret. #[serde(skip_serializing_if = "Option::is_none")] - pub version_stages: std::option::Option>, + pub version_stages: Option>, - /// The date and time that this version of the secret was created. If you don't specify which version in VersionId or VersionStage, then Secrets Manager uses the AWSCURRENT version. + /// The date and time that this version of the secret was created. If you don't specify which + /// version in `VersionId` or `VersionStage`, then Secrets Manager uses the `AWSCURRENT` version. #[serde_as(as = "Option>")] - pub created_date: std::option::Option, + pub created_date: Option, } impl GetSecretValueOutputDef { @@ -106,9 +108,9 @@ pub struct DateTimeDef { subsecond_nanos: u32, } -impl SerializeAs<::aws_smithy_types::DateTime> for DateTimeDef { +impl SerializeAs for DateTimeDef { fn serialize_as( - source: &::aws_smithy_types::DateTime, + source: &aws_smithy_types::DateTime, serializer: S, ) -> Result where @@ -118,8 +120,8 @@ impl SerializeAs<::aws_smithy_types::DateTime> for DateTimeDef { } } -impl<'de> DeserializeAs<'de, ::aws_smithy_types::DateTime> for DateTimeDef { - fn deserialize_as(deserializer: D) -> Result<::aws_smithy_types::DateTime, D::Error> +impl<'de> DeserializeAs<'de, aws_smithy_types::DateTime> for DateTimeDef { + fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { @@ -127,8 +129,8 @@ impl<'de> DeserializeAs<'de, ::aws_smithy_types::DateTime> for DateTimeDef { } } -impl From for ::aws_smithy_types::DateTime { - fn from(def: DateTimeDef) -> ::aws_smithy_types::DateTime { - ::aws_smithy_types::DateTime::from_secs_and_nanos(def.seconds, def.subsecond_nanos) +impl From for aws_smithy_types::DateTime { + fn from(def: DateTimeDef) -> aws_smithy_types::DateTime { + aws_smithy_types::DateTime::from_secs_and_nanos(def.seconds, def.subsecond_nanos) } } diff --git a/aws_secretsmanager_caching/src/secret_store/memory_store/cache.rs b/aws_secretsmanager_caching/src/secret_store/memory_store/cache.rs index 68e2227..c20b547 100644 --- a/aws_secretsmanager_caching/src/secret_store/memory_store/cache.rs +++ b/aws_secretsmanager_caching/src/secret_store/memory_store/cache.rs @@ -30,8 +30,8 @@ impl Cache { } /// Inserts a key into the cache. - /// If the key already exists, it overwrites it - /// If the insert results in too many keys in the cache, the oldest updated entry is removed. + /// If the key already exists, it overwrites it + /// If the insert results in too many keys in the cache, the oldest updated entry is removed. pub fn insert(&mut self, key: K, val: V) { self.entries.insert(key, val); if self.len() > self.max_size.get() { @@ -94,12 +94,12 @@ mod tests { cache.insert("test3".to_string(), 3); cache.insert("test4".to_string(), 4); assert_eq!(cache.len(), 4); - let items: Vec = cache.entries.iter().map(|t| (*t.1)).collect(); + let items: Vec = cache.entries.iter().map(|t| *t.1).collect(); assert_eq!(items, [1, 2, 3, 4]); cache.insert("test5".to_string(), 5); assert_eq!(cache.len(), 4); - let items: Vec = cache.entries.iter().map(|t| (*t.1)).collect(); + let items: Vec = cache.entries.iter().map(|t| *t.1).collect(); assert_eq!(items, [2, 3, 4, 5]); } @@ -110,7 +110,7 @@ mod tests { cache.insert("test1".to_string(), 1); cache.insert("test1".to_string(), 2); assert_eq!(cache.len(), 1); - let items: Vec = cache.entries.iter().map(|t| (*t.1)).collect(); + let items: Vec = cache.entries.iter().map(|t| *t.1).collect(); assert_eq!(items, [2]); } }