How to securely deploy to Kubernetes from Bitbucket Pipelines

Dane Stevens
We’ve moved to freeCodeCamp.org/news
3 min readApr 18, 2019

Over 100,000 GitHub repos have leaked API or cryptographic keys — ZDNet

Hands up if this has happened to you. Say you’re reading a well-written article on one of countless topics, and you get to the line that goes something like this:

// DO NOT DO THIS IN A PRODUCTION APP
const API_KEY = '<api-key-displayed-in-plain-text>'

Ok, so how should you be doing this? Unfortunately, there isn’t a one-size-fits-all approach to securing your secrets. Different programming languages deployed in different environments all handle secrets in their own way.

Suffice it to say that you should never store secrets in your code or repository. Secrets should be passed into your app through environment variables at the last possible moment.

Bitbucket Pipelines — Continuous Delivery

I have been using Bitbucket Pipelines since it was in Alpha and I have to say, it’s fantastic. It has to be the quickest and easiest way to setup continuous delivery right from your repo.

Pipelines are configured with YAML files and can be very simple or extremely complex depending on your needs.

Pipelines Configuration

I like to break up my build jobs into steps for a couple of reasons:

  • If a step fails, you can re-run individual steps.
  • Each step is isolated from the others. Only your base repo and any “artifacts” you declare will be passed to the next step.

Here is a 3-step bitbucket-pipelines.yml file that takes a create-react-app site, packages it as a Docker image and deploys it to a Kubernetes cluster:

Bitbucket Pipelines Configuration
Dockerfile

Here’s the important part of all that:

- echo $KUBECONFIG | base64 -d > kubeconfig.yml

Our kubeconfig file is stored as a Base64 encoded string in a Bitbucket secret named $KUBECONFIG.

Bitbucket secrets are stored encrypted and are decrypted when you call the variable in pipelines.

We decode the $KUBECONFIG variable and store it in a temporary file called kubeconfig.yml which is automatically deleted as soon as this step completes.

Breaking it Down

Step 1

  1. Install dependencies
  2. Run tests
  3. Build
  4. Pass build directory to Step 2

Step 2

  1. Name Docker image
  2. Build Docker image
  3. Push image to Docker Hub

Step 3

  1. Decode kubeconfig
  2. Set deployment image

Build Performance

This entire build takes less than 1 minute 40 seconds and using Alpine Node the Docker image is just 29 MB.

Conclusion

Securing your secrets isn’t hard, but it starts with knowing where to look.

Some tips for securing secrets in different Node.js environments:

  • Node.js (Development): use .env files and .gitignore to keep .env files out of your repository.
  • Node.js (Production): use Kubernetes Secrets, Docker Secrets and pass as environment variables into the container.

Remember this one rule:

  • Don’t store secrets in your code, your repository, or your docker image.

Happy coding!

--

--