Security enabled simple REST service to manage application configuration in AWS SSM Parameter Store.
Related blog post can be found on the Neiman Marcus Medium page.
Cloud applications often require runtime and deployment configuration which must be managed. Following infrastructure-as-code best practices, storing this configuration alongside the application is crucial.
Psm, short for parameter store manager, aims to allow product teams, developers, and cloud architects to easily maintain and deploy configuration in a secure, reliable, and templated manner, inside source control.
Deployed with serverless framework, this application can be easily modified for any AWS environment. If you have an improvement or run into an issue please participate on the github page.
Psm assumes that application configuration is stored in a specific heirarchy in AWS SSM Parameter Store. By default psm deploys and handles configuration under /{application}/{stage}/...
with each application or service having multiple stages for different environments. It is common to have a dev
, staging
and prod
stage of an application.
You can override the default path, by passing a hardcoded string in the x-path-override
header.
You can override the default parser by passing a x-kv-block-parser: true
in the header. This will use parent nodes of the json as keys (see example below).
{
"keyOne": {
"DOB": "02/19/1983",
"Phone": "+1 (214) 999999"
},
"keyTwo": {
"VehicleID": "1XBYUT"
},
"keyThree": {
"API_ENDPOINT": {
"uri": "/dbcy.com/US",
"toggles": ["ORDER_SORT_OPTION_1", "ORDER_SORT_OPTION_2"]
}
}
}
key | value |
---|---|
keyOne |
{"DOB": "02/19/1983","Phone": "+1 (214) 999999" |
keyTwo |
{"VehicleID": "1XBYUT"} |
keyThree |
{"API_ENDPOINT": {"uri": "/dbcy.com/US","toggles": ["ORDER_SORT_OPTION_1","ORDER_SORT_OPTION_2"]}} |
Psm uses the following three lambda functions:
- Encrypt - Encrypt secrets via KMS CMK to securely store in source control.
- Update - Update configuration in AWS SSM Parameter Store
- View - Retrieve and view configuration in AWS SSM Parameter Store
The encrypt
function allows a developer to encrypt a supplied value for storage in source control. The function takes the input and encrypts it against psm's CMK. The returned value is prefixed with cipher:
and is used when pushing configuration to parameter store. It is not necessary to encrypt all values in source control, only secrets.
You have the option to POST
a secret to be encrypted, or to use the GET
method for a encrypted, randomly generated value.
This function requires no API key.
Example API calls and .http
files can be used with the REST VSCode Extension.
POST https://abcdef0123.execute-api.us-west-2.amazonaws.com/prod/encrypt HTTP/1.1
Hello World!
GET https://abcdef0123.execute-api.us-west-2.amazonaws.com/prod/encrypt HTTP/1.1
{
"foo": "cipher:AQICAHjumw2gwZTBa2YnLcaUczMcoU..."
}
The update
function pushes configuration to SSM Parameter Store. It will crawl the json formatted data input, and if configuration is changed, push the new configuration. If the value is encrypted, using the encrypt
function, and prefixed with cipher:
, it will decrypt the value before comparing the parameters.
The update
function uses query string parameters to place the configuration. Psm is looking for the appId
or name of the application, and stage
. See examples below.
This function flattens the json input data. For example, {"foo": {"bar": "buzz"}}
will be transformed to the parameter key, /{application}/{stage}/foo.bar
.
This function requires use of the API key.
You can include tags by including a dedicated metadata.tags
section at the top level of the data input. The application will tag each parameter accordingly. It is advisable to keep tags at a minimum to avoid timeouts, and avoid too many tags which loses much of the value tags provide.
Tags reside within the metadata section, to provide future extensibility within the metadata heirarchy.
To disable maintaining metadata in SSM Parameter Store, supply the --metadataAsParam false
cli option when deploying.
POST https://abcdef0123.execute-api.us-west-2.amazonaws.com/prod/update?appId=psm-test&stage=dev HTTP/1.1
x-api-key: abcdef0123456789ABCDEF0123456789abcdef01
{
"metadata": {
"tags": {
"Application": "Application1",
"Environment": "dev",
"Owner": "admin@foo.io"
}
},
"foo": {
"bar": "buzz"
},
"baz": "cipher:AQICAHjumw2gwZTBa2YnLcaUczMcoU..."
}
curl -X POST 'https://abcdef0123.execute-api.us-west-2.amazonaws.com/prod/update?appId=psm-test&stage=dev' \
-H 'x-api-key: abcdef0123456789ABCDEF0123456789abcdef01' \
-d '@config/dev.json'
with x-path-override
header:
curl -X POST 'https://abcdef0123.execute-api.us-west-2.amazonaws.com/prod/update?appId=psm-test&stage=dev' \
-H 'x-api-key: abcdef0123456789ABCDEF0123456789abcdef01' \
-H 'x-path-override: /org/service/dev5/' \
-d '@config/dev.json'
The view
function will retrieve the configuration from parameter store, for review. SecureString
parameters are encrypted with the CMK during the process. Similar to the update
function, the view
function leverages query string parameters, which are appId
and stage
.
This function requires use of the API key.
GET https://abcdef0123.execute-api.us-west-2.amazonaws.com/prod/view?appId=psm-test&stage=dev HTTP/1.1
x-api-key: abcdef0123456789ABCDEF0123456789abcdef01
- Install the necessary sls plugins with
npm install
. - Install the necessary python requirements
pip install -r requirements.txt
. - Deploy with
serverless deploy --stage prod
Developers may start encrypting their secrets with the encrypt
function and push configuration to source control after deployment.
Next, within deployment pipelines, the build server should look for configuration files to push with each stage. For example, the repository can have a config directory, with json files for each stage. Example: ./config/{stage}.json
, or ./config/dev.json
and /config/prod.json
.
The pipeline can make REST calls to push the parameters within those files, calling the update
function. Use parameters in your pipeline to pass in the appId
and stage
. Neiman Marcus matches the appId
to the repository name.
Serverless Framework is one of the tools that can leverage these parameters from SSM Parameter Store. It is common to push configuration to SSM Parameter Store, and then immediately call it when deploying. Local configuration files can be used with Serverless Framework, but would not leverage secret storage.
- With the above example
provider:
environment:
FOO: ${ssm:/${self:service}/${self:provider.stage}/foo.bar}
SECRET: ${ssm:/${self:service}/${self:provider.stage}/baz~true}
parameter | Description | Required |
---|---|---|
appId | The name of the application or service to prefix parameters with. | yes |
stage | The name of the stage to prefix parameters with. | yes |
header | Description | Required |
---|---|---|
x-path-override | The custom path to override the PSM default. | no |
x-kv-block-parser | An alternate way to parse input json. | no |
- The encrypt function does not require an API key since it is fairly simple and non-impacting.
- The update and view functions require API keys.
- The API key should be stored securely within your build server's credential store and not shared.
- It is suggested that you modify the ApiGatewayRestApi to white list specific IP addresses if possible. Alternatively you can run the Api Gateway as a private endpoint.
- Update the
KMSKey.yml
resource file with the appropriate IAM roles to manange the CMK.
- Suppliying a list will map each item in the list to it's index.
- For example, itme
{"foo": ["bar", "baz"]}
will be flattened to/{application}/{stage}/foo.0
and/{application}/{stage}/foo.1
with the value ofbar
andbaz
respectedly. - The
view
function will return the parameters as{"foo": {"0": bar", "1": baz"}}
- It is best to avoid lists in the mean time.
- For example, itme
- StringLists are not currently supported.
- Optional parameter clean up
- SSM Parameter Store backup and logging of changes
- Better error handling
- String Lists
- Clay Danford - Project creation and development.
- Refer to our contribution guidelines to contribute to this project. See CONTRIBUTING.md.
- All contributions must follow our code of conduct. See CONDUCT.md.
- This project is licensed under the Apache 2.0 license. See LICENSE.