Envase Vault Developer's Guide ============================================================================= This guide provides information about how |en| applications and services can integrate |ev| to manage the secrets they need. It provides examples of the API usage, as well as the usage of the |awslb|_ functions exposed by the service. It also provides guidance to developers about when to use the REST API vs. the exposed functions. .. contents:: Index Environments ----------------------------------------------------------------------------- The |ev| service is deployed to three separate environments with different purposes. Client applications using the API will need to register a client in the appropriate environment to access |ev|. Applications and services using the exposed functions can access any environment, but it's recommended that you keep your code separated by environment. This separation comes in two forms. The API has a separate URL for each of the environments. The functions have different prefixes depending on the environment (see below for more information). The following are the different environments where |ev| is deployed: Development/Test ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This environment is used for testing purposes of development teams. It's the environment applications and services should use while testing code that is being developed. Secrets can be created, deleted, and manipulated in all sorts of ways in this environment to insure applications and services functionality works properly. The base URL for this environment is: :url: https://vault-dev.envaseconnect.cloud Functions in this environment use the following prefix: :prefix: vault-dev Staging/Sandbox ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This environment is used to test applications and services when they are ready to get into production. It is used to insure all the functionality that is going to be release is stable before it is released. Applications and services should point to this environment in |ev| to verify they are managing their secrets correctly. The base URL for this environment is: :url: https://vault-stg.envaseconnect.cloud Functions in this environment use the following prefix: :prefix: vault-stg Production ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This environment is used for production applications and services. Applications and services that are released must point to this environment to manage secrets. Secrets stored in this environment should be managed by an approved client application, and there should be no manual interaction with |awssm|_ to change these secrets. The base URL for this environment is: :url: https://vault.envaseconnect.cloud Functions in this environment use the following prefix: :prefix: vault-prd Overview ----------------------------------------------------------------------------- The |ev| service exposes two type of services. The **secrets** service should be used to manage secrets that relate to applications and services to interact with other services. The **organizations** service is used when an application or service needs to manage individual secrets for a specific customer. The API and functions provided are very similar, so they are documented together. You can check the |evspec|_ for more information about the REST API. .. important:: This guide will show production URLs when showing examples of the REST API, and it will refer to functions using their name without the prefix. For example, this guide will refer to the function ``get-secret``, but the function should be prefix with the environment prefix as in ``vault-prd-get-secret`` You should make sure you use the correct URLs or prefixes when working with |ev|. REST API vs. AWS Lambda Function Interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |en| applications and service must decide how they will interact with |ev|. This decision should be based on the type of application or service is being implemented. **Desktop applications** or **services** hosted externally to |en| should use the REST API. The API provides an additional layer of security because |ev| requires |auth|_ from those applications in order to access/manage secrets. The REST API is the preferred method to access |ev|. However, full cloud applications or services **deployed to the Envase AWS account** may decide to use the |awslb| function interface. This interface does not require authentication because security can be achieve by other means within AWS. .. warning:: Applications and services should settle on a single interface whether is the REST API or the |awslb|_ functions. It is not a good idea to mix and match these interfaces. Representation of A Secret ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A secret is represented as a JSON object. There are no structure enforced by |ev| for the secrets stored. An application can decided the structure of the secret that is going to store, and provide it as a JSON object. The only limitation enforce by |ev| is that the attribute names in the JSON object should be specified in ``camelCase``. If they attribute names are stored using other type of capitalization, the names are converted; therefore when retrieving secrets, you will always retrieve ``camelCase``. .. note:: We can write a book about why ``camelCase`` was selected, but the most important reason is that we wanted a single representation of a secret for consistency and ``camelCase`` won. The following are valid secret representations: .. code-block:: json { "user": "username", "password": "the-user-password#1" } .. code-block:: json { "clientId": "the-client-id", "clientSecret": "the-client-secret" } The attribute names of the JSON object do not matter, just make sure they are ``camelCase``. Also, insure that all attributes are at the root of the object. Nested JSON object might not be handled properly in |awssm|_. Creating Secrets ----------------------------------------------------------------------------- The |ev| service allows |en| applications and services to create new secrets to be retrieved later on when needed. These secrets can be standard **secrets** or **organization secrets**. The service provides two sets of APIs and Functions to create secrets. Create Secret API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To create a secret through the API, a **POST** request is required. The payload will require a ``key`` to identify the secret, and the JSON object to be stored as a secret. The payload can also include a description for the secret. This comes handy when trying to identify the purpose of a secret. **Secret API** .. code-block:: shell POST https://vault.envaseconnect.cloud/secrets **Organization Secret API** .. code-block:: shell POST https://vault.envaseconnect.cloud/organizations/{organizationId} The appropriate API should be used based on the secret type. The payload for both APIs is identical: .. code-block:: json { "key": "secret-identifier-key", "secret": { "user": "me@mycompany.com", "pwd": "a-password" }, "description": "Credentials for my service." } The response from the service will be ``200 OK`` if the secret is stored successfully, and it will have the following structure: .. code-block:: json { "id": "secret-identifier-key", "description": "Credentials for my service", "organizationId": "the-organization-id", "ern": "ern:vault:prd:the-organization-id:secret-identifier-key", "arn": "arn:generated-by-aws-secret-manager" } The response will return the specified ``key`` as the ``id`` of the secret. A ARN (Amazon Resource Name) is generated and returned. A ERN (Envase Resource Name) is also generated and return. The description is returned if provided on the request. The ``organizationId`` will also be provided if the secret created is an **organization secret**. The actual **secret value is omitted** to avoid transmitting secrets back and forth. The APIs may return a ``400 BAD REQUEST`` if there is a problem with the request. Usually, this is caused by a duplicate key specified. Regardless, the response will have a ``message`` attribute with information about what went wrong. .. code-block:: json { "message": "Secret key already exists" } .. warning:: Do not rely on specific messages to handle errors. These messages can change at any time, and some of them are not controlled by the service. Instead, use the response status code to make decisions about the error. The message comes handy to use in logs. Create Secret AWS Lambda ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |ev| provides two functions to create secrets: ``create-secret`` and ``create-organization-secret``. The payload for these functions are similar to the payloads for the API. The main difference is that ``create-organization-secret`` will require an additional attribute ``organizationId``, which is specified in the URL when using the API. **Secret Function** .. code-block:: shell create-secret .. code-block:: json { "key": "secret-identifier-key", "secret": { "user": "me@mycompany.com", "pwd": "a-password" }, "description": "Credentials for my service." } **Organization Secret Function** .. code-block:: shell create-organization-secret .. code-block:: json { "key": "secret-identifier-key", "organizationId": "the-organization-id", "secret": { "user": "me@mycompany.com", "pwd": "a-password" }, "description": "Credentials for my service." } The responses from the functions are identical to the API with the addition of the ``statusCode`` provided in the body of the response: .. code-block:: json { "id": "secret-identifier-key", "description": "Credentials for my service", "organizationId": "the-organization-id", "ern": "ern:vault:prd:the-organization-id:secret-identifier-key", "arn": "arn:generated-by-aws-secret-manager", "statusCode": 200 } Notice that the specified key has now become the ``id`` (identifier) of the secret. You'll use the same key/id to access and manage the secret. Retrieving Secrets ----------------------------------------------------------------------------- Once you have secrets stored in |ev|, you can retrieve them using their key/id. The APIs and |awslb|_ functions exposed work the same for **secrets** and **organization secrets**. The following sections show how to use them. Get Secret API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The API to get a secret is exposed in the **secrets** service and uses the key/id of the secret to retrieve it. **Secret API** .. code-block:: shell GET https://vault.envaseconnect.cloud/secrets/{secretId} The ``secretId`` is tokenized so you can specify the key/id of the secret you need to retrieve. The response will have the following structure: .. code-block:: json { "id": "secret-identifier-key", "ern": "ern:vault:prd:secret-identifier-key", "arn": "arn:generated-by-aws-secret-manager", "secret": { "email": "user@company.com", "password": "the-password" } } The API will return a ``404 NOT FOUND`` if the specified key/id is not found. **Organization Secret API** .. code-block:: shell GET https://vault.envaseconnect.cloud/organizations/{organizationId}/{secretId} The ``organizationId`` and ``secretId`` are tokenized. You need to specify them in the URL for the secret you want to retrieve. The response has a similar structure but includes the ``organizationId``: .. code-block:: json { "id": "secret-identifier-key", "organizationId": "the-organization-id", "ern": "ern:vault:prd:the-organization-id:secret-identifier-key", "arn": "arn:generated-by-aws-secret-manager", "secret": { "email": "user@company.com", "password": "the-password" } } The API will return a ``404 NOT FOUND`` if either the organization id or the secret key/id are not found. Get Secret AWS Lambda ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |ev| also exposes |awslb|_ to retrieve the secrets. They work similarly as the API but the parameters are specified through the payload. **Secret Function** .. code-block:: shell get-secret .. code-block:: json { "key": "secret-identifier-key" } The response is identical to the one from the API but includes the status code. .. code-block:: json { "id": "secret-identifier-key", "ern": "ern:vault:prd:secret-identifier-key", "arn": "arn:generated-by-aws-secret-manager", "secret": { "email": "user@company.com", "password": "the-password" }, "statusCode": 200 } The function will return a status of ``404 NOT FOUND`` if the specified secret is not found. It can also return a ``400 BAD REQUEST`` if the key/id is not specified in the payload. **Organization Secret Function** .. code-block:: shell get-organization-secret .. code-block:: json { "key": "secret-identifier-key", "organizationId": "the-organization-id" } The response is identical to the one from the API, and it includes the status code. .. code-block:: json { "id": "secret-identifier-key", "organizationId": "the-organization-id", "ern": "ern:vault:prd:the-organization-id:secret-identifier-key", "arn": "arn:generated-by-aws-secret-manager", "secret": { "email": "user@company.com", "password": "the-password" }, "statusCode": 200 } The function will return a status of ``404 NOT FOUND`` if the specified key/id or organization are not found. It can also return a ``400 BAD REQUEST`` if either those parameters are missing. Updating Secrets ----------------------------------------------------------------------------- |ev| allows you to update the value of an existing secret. It also allows you to update the description when the secret is also updated. Trying to update only the description is an error due to the way that |awssm|_ works. Both APIs and functions have been exposed to update secrets. Update Secret API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can update **secrets** by specifying their key/id and their organization id for organization secrets. **Secret API** .. code-block:: shell POST https://vault.envaseconnect.cloud/secrets/{secretId} The payload must specify the new secret, and you can also change the description. .. code-block:: json { "secret": { "user": "updated@company.com", "pwd": "another-password" }, "description": "Updated description" } **Organization Secret API** .. code-block:: shell POST https://vault.envaseconnect.cloud/organizations/{organizationId}/{secretId} .. code-block:: json { "secret": { "user": "updated@company.com", "pwd": "another-password" }, "description": "Updated description" } The response will have a status of ``200 OK`` and contain the secret metadata as the response when creating a secret. .. code-block:: json { "id": "my-service-credentials", "description": "Credentials for my service", "organizationId": "the-organization-id", "ern": "ern:vault:prd:the-organization-id:my-service-credentials", "arn": "arn:generated-by-aws-secret-manager" } The API can also return ``404 NOT FOUND`` if the secret doesn't exist, or ``400 BAD REQUEST`` if a new secret object is not specified. Update Secret AWS Lambda ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Secrets can also be updated through |awslb|_ functions. It works the same as the API, but the parameters are specified in the event payload. **Secret Function** .. code-block:: shell update-secret .. code-block:: json { "key": "secret-identifier-key", "secret": { "user": "updated@company.com", "pwd": "another-password" }, "description": "Updated description" } **Organization Secret Function** .. code-block:: shell update-organization-secret .. code-block:: json { "key": "secret-identifier-key", "organizationId": "the-organization-id", "secret": { "user": "updated@company.com", "pwd": "another-password" }, "description": "Updated description" } The response will match that of the API but the status code will be specified in the response payload: .. code-block:: json { "id": "my-service-credentials", "description": "Credentials for my service", "organizationId": "the-organization-id", "ern": "ern:vault:prd:the-organization-id:my-service-credentials", "arn": "arn:generated-by-aws-secret-manager", "statusCode": 200 } The function can also return ``404 NOT FOUND`` if the specified secret or organization for organization secrets are not found, and ``400 BAD REQUEST`` if the event payload is missing any required parameters as the key/id, the secrete, and the organization id for organization secrets. Deleting Secrets ----------------------------------------------------------------------------- |ev| allows you to delete stored secrets. The delete functionality is very permissive, and it doesn't cause an error if you try to delete a secret that doesn't exist. Delete Secret API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To delete a secret through the API, you need to specify its key/id. **Secret API** .. code-block:: shell DELETE https://vault.envaseconnect.cloud/secrets/{secretId} **Organization Secret API** .. code-block:: shell DELETE https://vault.envaseconnect.cloud/organizations/{organizationId}/{secretId} The API returns ``204 NO CONTENT`` if the secret is found and deleted or ``205 RESET CONTENT`` if the secret is not found. Both cases indicate that the specified secret doesn't exist any more. Delete Secret AWS Lambda ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The functions to delete secrets work in the same way as the API, but the parameters are specified in the event payload. The parameters must be specified; otherwise, the function will return an error. The responses will contain the status code. **Secret Function** .. code-block:: shell delete-secret .. code-block:: json { "key": "secret-identifier-key" } **Organization Secret Function** .. code-block:: shell delete-organization-secret .. code-block:: json { "key": "secret-identifier-key", "organizationId": "the-organization-id" } The response from the function will contain the status code. .. code-block:: json { "statusCode": 204 } Like the API the functions return ``204 NO CONTENT`` if the secret is found and deleted or ``205 RESET CONTENT`` if the secret is not found. Both cases indicate that the specified secret doesn't exist any more. The functions can return a status of ``400 BAD REQUEST`` if the required parameters are not specified.