Skip to content

Latest commit

 

History

History
 
 

vep-1493-vault-integration

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

VEP-1493: Vault Integration For Secrets Storage

To get started with this template:

  • Fill out this file as best you can. There are instructions as HTML comments. At minimum, you should fill in the "Summary" and "Motivation" sections.
  • Create a PR for this VEP.
  • Merge early and iterate. Avoid getting hung up on specific details and instead aim to get the goals of the VEP clarified and merged quickly. The best way to do this is to just start with the high-level sections and fill out details incrementally in subsequent PRs.

Summary

Provide integration with Hashicorp Vault for secure storage of secrets. Control Service operators will be able to optionally configure a vault instance to be used for storing data job properties which are marked as secret.

Glossary

CS or VDK-CS - The Versatile Data Kit Control Service Hashicorp Vault or Vault - A tool for secrets management, encryption as a service, and privileged access management Secret/Secrets - sensitive data which needs to be available to data jobs - including but no limited to: secrets/ passwords/credentials/tokens/data

Motivation

  • Some data jobs need to use secrets in order to connect to third party systems by providing user credentials or tokens - currently these secrets are stored in data jobs properties, which in turn are stored in plain text in the configured Control Service Database. This enhancement will allow users to securely store secrets/passwords/credentials/tokens/data into a vault.
  • Compliance: Storing sensitive information such as credentials, tokens, or API keys in plaintext may lead to non-compliance with certain industry standards or regulations, such as GDPR, HIPAA. Without it, VDK would be hard to be certified to store or process restricted type of data.
  • Risk reduction: Storing secrets in plaintext increases the risk of unauthorized access and potential data breaches. This is severely mitigated by services like Vault .

Requirements and goals

Goals:

  • provide the ability to optionally configure a Vault instance in the Control Service for storing secrets
  • change the APIs and SDK so that users can set and retrieve secrets and data jobs can use them during runtime

Non-Goals:

  • automatic provisioning of a Vault instance as part of the VDK-CS installation
  • automatic migration of existing secrets to Vault

High-level design

In order to implement the proposed change, we should make the following changes - highlighted in the diagram below:

  • provide the ability to optionally configure a Vault instance in the Control Service for storing secrets
  • change the CS to store properties marked as secret into the configured Vault
  • enhance vdk cli to allow for users to store secrets for their data jobs
  • during runtime, data jobs can retrieve/store/update the secrets

high_level_design.png

API design

Currently, data job properties are stored and read in bulk. We are going to introduce similar APIs for storing and reading secrets.

Storing/retrieving secrets Control Service API

Introduce new API methods for storing and retrieving secrets under: /data-jobs/for-team/{team_name}/jobs/{job_name}/deployments/{deployment_id}/secrets

...
    Secrets:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataJobSecrets'
      description: Data Job properties


...
    DataJobSecrets:
      description: Secrets of a Data Job
      type: object
      additionalProperties:
        type: object
      example:
        redshift-user: foo
        redshift-password: bar
...

  '/data-jobs/for-team/{team_name}/jobs/{job_name}/deployments/{deployment_id}/secrets':
    summary: |
      Data Job Secrets API.
    get:
      tags:
        - Data Jobs Secrets
      responses:
        '200':
          $ref: '#/components/responses/Secrets'
      operationId: dataJobSecretsRead
      summary: Get Data Job secrets.
      description: <i>(Introduced in v1.0)</i>
    put:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DataJobSecrets'
        required: true
      tags:
        - Data Jobs Secrets
      responses:
        '201':
          description: Created successfully.
        '204':
          description: Updated successfully.
      operationId: dataJobSecretsUpdate
      summary: Update Data Job secrets.
      description: <i>(Introduced in v1.0)</i>
    parameters:
      -  name: team_name
         description: Team Name
         schema:
           type: string
         in: path
         required: true
      - name: job_name
        description: Data Job Name.
        schema:
          type: string
        in: path
        required: true
      - name: deployment_id
        description: Data job deployment id.
        schema:
          $ref: '#/components/schemas/DataJobDeploymentId'
        in: path
        required: true

When storing secrets, the request body is expected to provide a JSON object, containing all secrets:

{
  "secret1": "secret_value1",
  "secret2": "secret_value2",
  "secret3": "secret_value3"
}

Storing/retrieving secrets via the SDK during data job runtime

We are going to introduce Secrets API similar to the existing Properties API in the JobInput class which allow data jobs to get and set secrets:

class ISecrets:
    """
    Allows for Data Job to store and retrieve secrets.
    Secrets are a solution for securely storing API keys, tokens and passwords necessary to connect to different systems
    """

    @abstractmethod
    def get_secret(self, name: str, default_value: Any = None) -> str:
        pass

    @abstractmethod
    def get_all_secrets(self) -> dict:
        pass

    @abstractmethod
    def set_all_secrets(self, properties: dict):
        pass

And here are examples of the usage:

Get a single secret:

def run(job_input):
   my_secret_token = job_input.get_secret("my_secret_key")

Get all secrets:

def run(job_input):
   secrets = job_input.get_all_secrets()
   aws_token = secrets['MY_AWS_TOKEN']
   authenticate(aws_token)

Set a secrets:

def run(job_input):
   secrets = job_input.get_all_secrets()
   secrets['MY_TOKEN'] = get_new_token()
   job_input.set_all_secrets(secrets)

Storing/retrieving secrets via the CLI

We are going to introduce a new set of commands for secrets, similar to "vdk properties" where users will be prompted to set to enter each password.

  • Will prompt for the value so it's not printed on the screen vdk secrets --set "my-password"

  • Return the secret value associated with the given key "my-password" vdk secrets --get "my-password"

  • List all secret keys vdk secrets --list

  • List all secret keys and values vdk secrets --list --show-secrets

  • Delete a secret vdk secrets --delete "my-password"

  • Delete all job secrets vdk secrets --delete-all-job-properties

  • Overwrite all job secrets by passing a JSON file vdk secrets --overwrite-all-job-secrets

Changes to the properties cli command:

  • remove the --set-secret option
  • change the explanation of the command from "store credentials securely" to "store credentials"

Detailed design

Configuration changes

We are going to enhance the VDK-CS configuration with an optional Spring Vault Configuration.

# Hashicorp Vault Integration settings
# When disabled/not configured the Secrets functionality won't work
featureflag.vault.integration.enabled=true
vdk.vault.uri=https://localhost:8200/v1/
vdk.vault.approle.roleid=z2d59142-9b53-d163-1ae9-d6286d3dfe22
vdk.vault.approle.secretid=c336f9e9-1555-8c29-9a7f-283608d06fbd
datajobs.vault.size.limit.bytes=1048576

The feature flag allows users who are not interested in using a secret storage, to simply disable this feature.

The VDK Control Service is going to authenticate using Vault's AppRole mechanism. THe Service Operator should setup the App Role authentication based on their needs in Vault, and provide the Vault URI the App Role's Role ID and Secret ID.

Secrets service

We are going to introduce a new Secrets Service class, similar to the existing Properties Service which handles requests for the Secrets functionality. We are going to implement changes in VDK-CS API as outlined API Design section.

Storing secrets in Vault

The secrets for a given data job will be stored under "secret//" entry in Vault in JSON object format. So the secrets for a data job "my-ingestion-job" belonging to the "ingestion" team will be stored under "secret/ingestion/my-ingestion-job".

The maximum size of a secret entry in Vault is 1MiB. Upon calling the store secret method, the VDK-CS will check the size of the secret object and will return an error if the secret is above the limit. The VDK SDK will interpret this error as a user error.

VDK SDK changes

Once the changes in the API have been introduced, we are going to implement the changes in the VDK SDK, e.g. introduce the secrets related methods in classes, similar to the existing job_properties built-in plugin as outlined API Design section.

VDK CLI changes

As soon as the changes in the VDK SDK are completed, we can introduce the secrets related commands in the VDK SDK.

Performance

To improve data job performance, the secrets will be loaded and cached at the start of the SDK run job - the same functionality we currently have for properties.

Alternatives

"Separate secrets and properties API" vs "Secrets as part of the existing properties API"

During the initial architecture of this feature we had a discussion on whether the secrets should be part of the existing properties api/interface. We also reached out to several people whom we consider power users of VDK and everyone agreed that the secrets should be part of a separate interface as this would eliminate ambiguity and provide clear separation between the two types of data.

Also storing secrets separately would overall decrease the number of updates of secrets which is generally undesirable due to Vault's characteristics where frequent updates of secrets could lead to high latency and reduced performance.

Storing all properties in Vault

We are not going to start storing all properties in vault as secrets as Vault is not designed to handle frequent updates of the entries stored within.

Our general guidance for properties is that they should be used to store state, e.g. last time stamp or last record of processed data which could change frequently. We have observed data jobs where users update the properties quite often, multiple times per single data job run, which is not suitable for secrets stored in Vault.

To that end we are going to keep the two functionalities separate.