How to handle secrets in Bitbucket

secrets

Sensitive data like passwords, API tokens, and DB credentials are essential for project development and testing but should never be stored in plain text within the repository. In this section, we’ll explain how WATA Factory manages these secrets using Bitbucket’s built-in features: repository variables and deployment variables.

To make use of any of these solutions you just need write privileges. However, to be able to set up any kind of variables we must have admin privileges. If you need more technical information you can check this article.

Cypress is one of WATA Factory’s references for automated testing frameworks, so we will use a Cypress project as an example, although this approach can be used in any project with a different technology.

In this project we have a reference to a credentials.ts file within the cypress.config.ts. If we wanted to execute our automated tests in the pipelinewe would need to have the credentials file in the repo or else the pipeline will fail. We already know this is not a good practice, so how do we fix that?

Entering the scene repository variables. With this Bitbucket native feature we can reference defined variables in our pipelines and use them at our convenience.

Our solution will have these two simple steps:

  1. Create the variable in Bitbucket
  2. Create a temporary credentials.ts file
  3. Copy the content of the variable into our temporary file
  4. DONE! Our project already has the file it needed with all our secrets!

Repository variables:

Let’s say our credential.ts file has the following structure:

export const USERS = {
    admin: {
      username: 'admin',
      password: 'adminPassword',
    },
    regularuser: {
      username: 'justAnUser',
      password: 'regularPassword',
    },
  };
   
export const DB_CREDENTIALS = {
    host: 'your-server.com',
    user: 'dbUser',
    password: 'anotherPassword',
    database: 'my_sample_db',
    port: 1234,
  };

And our cypress.config.ts file need to import it to use those credentials:

import { DB_CREDENTIALS } from './cypress/properties/credentials';

//my DB connection
const connections: Record<string, any> = {
  atDashboard: {
    host: DB_CREDENTIALS.host,
    user: DB_CREDENTIALS.user,
    password: DB_CREDENTIALS.password,
    database: DB_CREDENTIALS.database,
    port: DB_CREDENTIALS.port,
  }
};

Variables can be a whole json file if we wanted to, so we are going to copy the content of our credentials.ts file and go to ‘Repository settings > Repository variables’ in our repo.

secrets
secrets

Here we just need to enter a name for our variable and paste the content of our file and click add. In case you want these credentials to be encrypted and hidden in the build logs you can check the ‘Secure’ option but beware you won’t be able to see its content or edit the variable anymore, only give a new value or delete it.

Once we have the variable set up we just need to use ‘$’ to call our variable and redirect the echo output using the ‘>’ command within our pipeline. At your project root path, you must have a bitbucket-pipeline.yml:

image: cypress/base:20.11.0

definitions:
  steps:
    - step: &install-and-check
        name: Installation and Static Analysis
        caches:
          - node
        script:
          - npm install
        artifacts:
          - node_modules/**
    - step: &test
        name: Run tests with coverage
        caches:
          - node
        script:
          - npm run test -- --coverage
          - node_modules/.bin/cypress install --force
          - echo $OUR_CREDENTIALS > ./cypress/properties/credentials.ts
          - node_modules/.bin/cypress run --component --env coverage=true
          - node_modules/.bin/cypress run --env PLUGIN_ENABLED=false
          ...

With this command we create a temporary file with the content of our variable. This way the credential file is gone after the execution of the pipeline:

echo $VARIABLE > ./your/path/filename.ts

Deployment variables:

Here at WATA Factory, to meet our clients’ needs and deliver excellent quality, we require different environments, from dev stage to production without forgetting a much-needed testing environment. Different environments require different credentials or other kinds of flags. To solve this issue Bitbuckets offers deploymeny variables.

These variables work the same way as repository variables, but you need to specify in the pipeline to which environment you are referring to.

To create the deployment variable just:

  1. Go to ‘Repository settings > Deployments’
  2. Create a new environment under your desired section (test, staging or production)
  3. Add a new variable the same way we did in repository variables
  4. Specify in your step pipeline which environment you are referring to:

image: cypress/base:20.11.0

definitions:
  steps:
    - step: &install-and-check
        name: Installation and Static Analysis
        caches:
          - node
        script:
          - npm install
        artifacts:
          - node_modules/**
    - step: &test
        name: Run tests with coverage
        caches:
          - node
        script:
          - npm run test -- --coverage
          - node_modules/.bin/cypress install --force
          - echo $CREDENTIALS > ./cypress/properties/credentials.ts
          - node_modules/.bin/cypress run --component --env coverage=true
          - node_modules/.bin/cypress run --env PLUGIN_ENABLED=false
        artifacts:
          - coverage/**
          - coverage-component/**
          - cypress-image-diff-html-report/**
          - cypress-image-diff-screenshots/**
    - step: &sonar
        name: SonarQube analysis
        image: sonarsource/sonar-scanner-cli:latest
        script:
          - sonar-scanner -Dsonar.projectKey=Gesa-Dashboard---Angular -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_SECRET -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info,coverage-component/lcov.info
    - step: &deploy
        name: Deploy
        script:
          - npm run build -- --base-href /dashboard_ang/ --output-hashing=all --aot --configuration=$DEPLOYMENT_PROFILE
          - ssh $FTP_USERNAME@$FTP_HOST -p222 'mkdir -p ~/public_html/dashboard_ang'
          - scp -P 222 -r dist/new_angular_poc/browser/* $FTP_USERNAME@$FTP_HOST:~/public_html/dashboard_ang/

pipelines:
  branches:
    develop:
      - step:
          <<: *install-and-check
      - step:
          deployment: automatictesting
          <<: *test
      - step:
          <<: *sonar
  custom:
    deploy-company1-dev:
      - step:
          <<: *install-and-check
      - step:
          name: Deploy to Company 1
          deployment: company1
          <<: *deploy
    deploy-at:
      - step:
          <<: *install-and-check
      - step:
          name: Deploy to AT
          deployment: automatictesting
          <<: *deploy

As you can see, under the pipelines section we set the environment and in which step will be used. The name of the step is defined higher above.

As an important warning: Naming a deployment variable with the same name as a repository variable will overwrite the value.