DEV Community

Montana Mendy for Travis CI

Posted on • Updated on

Encrypting Sensitive Data Using the Travis CLI Tool

CLI Encryption

Encrypting Sensitive Data Using the Travis CLI Tool

Protecting sensitive data is an essential aspect of the continuous integration and continuous delivery (CI/CD) process. Leaving security tokens, passwords and other types of secret information exposed to malicious parties is always a security risk.

Fortunately, Travis CI has a command-line interface (CLI) tool that makes encrypting sensitive data for use in the CI/CD process reasonably straightforward.

Encryption under Travis CI can be applied to all sorts of information, from a username-password pair to text files. Let’s look at how to apply Travis CI encryption to an environment variable.

I’ve created a small Node.js project that you can find on GitHub here. The project, SimpleTravis, is bound to Travis CI’s CI/CD build process. Travis CI will execute the instructions in the project’s .travis.yml file whenever a code update is committed to GitHub. The aim of SimpleTravis is to demonstrate the result of using the Travis CI encryption feature.

The first thing you can do is run:

travis whatsup
Enter fullscreen mode Exit fullscreen mode

Make sure you're logged into Travis, you can do this by:

travis login
Enter fullscreen mode Exit fullscreen mode

You can make sure you're logged in by getting your token from GitHub:

--github-token or --auto 
Enter fullscreen mode Exit fullscreen mode

If you don't want to enter your password, this is the best alternative. To get an idea of what foundation you need to lay down first.

Make sure you have nothing in stash:

git stash pop
Enter fullscreen mode Exit fullscreen mode

This throws away the (topmost file, this is by default) stash after applying git stash pop.

Working with the example project

The SimpleTravis project is a simple HTTP server that supports a GET method call against the root endpoint. Listing 1 below shows the function handleRequest(). The function is an excerpt from the index.js file that creates and runs the HTTP server. The request processing and response reply of the HTTP interaction take place in handleRequest():

/** A function which handles requests and send response. */
function handleRequest(request, response) {
    const message = process.env.TRAVIS_MESSAGE || `Hello at ${new Date()}`
    response.setHeader("Content-Type", "application/json");
    response.writeHead(200);
    response.end(JSON.stringify({message}));
}
Enter fullscreen mode Exit fullscreen mode

Listing 1: The code that handles a request to and a response from the SimpleTravis HTTP server

The HTTP server has only once piece of functional logic, which is shown at line 3 in listing 1 above. The code will check to see if the environment variable, TRAVIS_MESSAGE, exists, and that it has a value. If TRAVIS_MESSAGE does have value, that value will be assigned to the local variable, message. Otherwise, the current date is appended to the string, Hello at. Then the concatenated string is assigned to the variable message.

SimpleTravis has a unit test that verifies that the HTTP server is working to expectations.The unit test is shown below in listing 2.

describe('API Tests: ', () => {
    after(function () {
        shutdown();
    });
    it('Can access GET at /', function(done){
        //Call the API
        supertest(server)
            .get('/')
            .set('Accept', 'application/json')
            .then((res) => {
                expect(res.body).to.be.an('object');
                console.log(res.body);
                done();
            })
            .catch(done);
    });
});
Enter fullscreen mode Exit fullscreen mode

Listing 2: The unit test that makes a call to the SimpleTravis HTTP server

The unit test is part of the Travis CI build process for SimpleTravis. Listing 3 below shows the content of the travis.yml file for the SimpleTravis project. Notice that the unit test is run at line 12.

language: node_js
node_js:
- 14
os: ubuntu
branches:
  only:
  - master
before_install:
- pwd
script:
- npm install
- npm test
Enter fullscreen mode Exit fullscreen mode

Listing 3: The travis.yml file without an environment variable

Listing 4 below shows the output of the unit test. Notice that because there is no TRAVIS_MESSAGE environment variable in play, the output of the call to the server is the default string that has the current date, as shown at line 4.

Node HTTP listening on 3000
  API Tests: 
{
  message: 'Hello at Sun Sep 27 2020 05:04:59 GMT+0000 (Coordinated Universal Time)'
}
     Can access GET at /
API Server shutting down at Sun Sep 27 2020 05:04:59 GMT+0000 (Coordinated Universal Time)
Enter fullscreen mode Exit fullscreen mode

Listing 4: The result of a unit test run without the environment variable TRAVIS_MESSAGE

TRAVIS_MESSAGE is the environment variable I’m going to add to the build process. First, I’ll show a build execution that has TRAVIS_MESSAGE unencrypted. Then I’ll use the Travis CI CLI tool to encrypt TRAVIS_MESSAGE and use it in the run script.

Installing the Travis CI CLI tool

In order to perform encryption under Travis CI, you need to have both the Ruby programming language and the Travis CI CLI tool installed on your machine. You can read the installation instructions for the Travis CI tool on GitHub here.

The first time you use the tool, Travis CI CLI will ask for your access credentials. However, if your machine already has your GitHub credentials installed, which is typical if you’re accessing remote repositories using git from the command line, this step will be skipped.

Once the Travis CI CLI tool is installed and operational, you’ll be able to encrypt one or more environment variables. But, before we do encryption, let’s just add an environment variable in plain text to the build. Then, we’ll encrypt it.

Using a standard environment variable

Listing 5 below shows a travis.yml file that declares the environment variable TRAVIS_MESSAGE. The environment variable is assigned the value “All Good Things Take Time”.

language: node_js
node_js:
- 14
os: ubuntu
branches:
  only:
  - master
before_install:
- pwd
script:
- npm install
- npm test
env:
  - TRAVIS_MESSAGE="All Good Things Take Time"
Enter fullscreen mode Exit fullscreen mode

Listing 5: The travis.yml file with the unencrypted environment variable TRAVIS_MESSAGE

Listing 6 that follows below shows the HTTP output from the project’s unit tests. Notice that the string All Good Things Take Time is part of the response, as shown at line 3 below. This output is expected. Remember, the code shown above in listing 1 will report the value of the environment variable TRAVIS_MESSAGE, should it exist. The environment variable does indeed exist, and it has a value. Thus, that value is returned in the HTTP response here.

Node HTTP listening on 3000
  API Tests: 
{ message: 'All Good Things Take Time' }
     Can access GET at /
API Server shutting down at Sun Sep 27 2020 05:13:11 GMT+0000 (Coordinated Universal Time)
The command "npm test" exited with 0.
Enter fullscreen mode Exit fullscreen mode

Listing 6: The result of a unit test run with the unencrypted environment variable TRAVIS_MESSAGE

Now that we have the environment variable wired into the Travis CI build, let’s encrypt TRAVIS_MESSAGE.

Using an encrypted environment variable

We’ll encrypt the environment variable TRAVIS_MESSAGE, along with the value assigned to it, using the Travis CI CLI tool. The following is the command used to perform the encryption:

travis encrypt TRAVIS_MESSAGE="All Good Things Take Time" 
Enter fullscreen mode Exit fullscreen mode

WHERE

travis is the command

encrypt is the subcommand

TRAVIS_MESSAGE="All Good Things Take Time" is the environment variable along with the assigned value to encrypt

The result of executing the encryption command is shown below in listing 7.

secure: "RWjsrxlhbU6P8t6F/YuXhc4wZstESJwFR3UGME7Lf5rlNzvYROQRsEpQ4JbIM0iyFtgAZzCe0zt0gtnOfhUzwYXaVNyUFrtrmXk6/+oJVLrLOdiAUd9W8iVczzWXpI0PrT7ZNltSef4j+BQEVsHYPKOLK9Q1jMsEc2fWNwyfYUL/kEqBxLGnMql5DJwdTMHXTMJpFjul07Rv8HLAkpBl4+N6O1ctSFoJJwXl6jVFIKuzvM9gK9LkMHwp22vNvAL+QUqI679NepB4+L8O/5zL5vM0FcYlIbe+SAjKu/53CQxJ4UTlO822R99+E/2aqN33u9AE8MdDfrDoMC2sGYGO0cwfkjzyTIIPHylysOQrHU7AjuG54JGoHfQcjLtjSATriL98WQevmYSH7GVrOWE3Q/LQsKywppRacmeMcW2nfcqMAT42S7ENaGv3JFm29ACkhC8tIoHXtCBWeFAkEXhZ2bR3C9cHteCn6ZWFiIcv20oPU0H2kwEXSTzcsAWHdcmpJWyTqDiPL/06aF4KZSHVZbS71qvmMZB3pgsHr4tlHCs+g2zcndDdHv7MBsW1FECYH43uWnfsvcyuccSX374bZIGBYHhcCfGIG4L7NH9fClhGzwqm2NfeBihWb4t45NHQqzGpAfGTCz5qwU1uUnMOXgUlZkF4t0iZXEabH7cK2hE="
Enter fullscreen mode Exit fullscreen mode

Pro Tip: You can add it automatically by running with --add.

Listing 7: The result of using the Travis CI CLI tool to encrypt an environment variable

Once I’ve done the encryption, I apply it to the .travis.yml file. Notice that I use the attribute secure. Using this attribute tells Travis CI that the value is encrypted and to use its private key to decrypt the value. If I were to take Travis CI’s suggestion and use the --add option when executing the encryption command, the Travis CI CLI tools would add the encryption as a global environment variable, like so:

env:
  global:
    secure: <string-of-encrypted-data>
Enter fullscreen mode Exit fullscreen mode

But for now I am going to keep things simple and skip making the environment variable global. Listing 8 below shows the .travis.yml file with the encrypted environment variable added.

language: node_js
node_js:
- 14
os: ubuntu
branches:
  only:
  - master
before_install:
- pwd
script:
- npm install
- npm test
env:
secure: "RWjsrxlhbU6P8t6F/YuXhc4wZstESJwFR3UGME7Lf5rlNzvYROQRsEpQ4JbIM0iyFtgAZzCe0zt0gtnOfhUzwYXaVNyUFrtrmXk6/+oJVLrLOdiAUd9W8iVczzWXpI0PrT7ZNltSef4j+BQEVsHYPKOLK9Q1jMsEc2fWNwyfYUL/kEqBxLGnMql5DJwdTMHXTMJpFjul07Rv8HLAkpBl4+N6O1ctSFoJJwXl6jVFIKuzvM9gK9LkMHwp22vNvAL+QUqI679NepB4+L8O/5zL5vM0FcYlIbe+SAjKu/53CQxJ4UTlO822R99+E/2aqN33u9AE8MdDfrDoMC2sGYGO0cwfkjzyTIIPHylysOQrHU7AjuG54JGoHfQcjLtjSATriL98WQevmYSH7GVrOWE3Q/LQsKywppRacmeMcW2nfcqMAT42S7ENaGv3JFm29ACkhC8tIoHXtCBWeFAkEXhZ2bR3C9cHteCn6ZWFiIcv20oPU0H2kwEXSTzcsAWHdcmpJWyTqDiPL/06aF4KZSHVZbS71qvmMZB3pgsHr4tlHCs+g2zcoccurW1FECYH43uWnfsvcyuccSX374bZIGBYHhcCfGIG4L7NH9fClhGzwqm2NfeBihWb4t45NHQqzGpAfGTCz5qwU1uUnMOXgUlZkF4t0iZXEabH7cK2hE="
Enter fullscreen mode Exit fullscreen mode

Listing 8: The travis.yml file with the encrypted environment variable TRAVIS_MESSAGE

Understanding Travis CI encryption

Travis CI uses a public-key/private-key security system, also known as asymmetric cryptography, to encode and decode sensitive data. When I encrypted the environment variable, under the covers, the Travis CI CLI tool used the public key to do the encryption. However, decryption can only take place within the Travis CI runtime environment, because only Travis CI has access to the private key.

Using asymmetric cryptography is a time-tested technique for securing data. You can read up on the details on the Travis CI website here.
Listing 9 below shows the output of the web server when encrypting the environment variable. Notice that at line 3, the value of the environment variable TRAVIS_MESSAGE is reported as [secure].

Node HTTP listening on 3000
  API Tests: 
{ message: '[secure]' }
     Can access GET at /
API Server shutting down at Sun Sep 27 2020 05:00:25 GMT+0000 (Coordinated Universal Time)
The command "npm test" exited with 0.
Enter fullscreen mode Exit fullscreen mode

Listing 9: The output of the web server with the encrypted environment variable TRAVIS_MESSAGE

You might be wondering why the value of environment variable TRAVIS_MESSAGE is not shown and all that’s displayed is “[secure].”

Obfuscating the environment variable-value pair makes sense. Remember, because of asymmetric cryptography, the Travis CI private key is required in order to decrypt data that’s been encrypted using the Travis CI CLI tool. The private key is available when Travis CI is running the build on travis.yml. Travis CI will sense the data is encrypted and decrypt it automatically at the build level. However, the code inside the Node.js SimpleTravis HTTP server has no direct access to the private key. All the application can do is report that the data is secure.

There are ways in which to use encrypted data within source code, but for now, the important thing to understand is that Travis CI provides the capability to encrypt data in a secure manner in a way that requires minimal effort.

Putting it all together

The ability to encrypt sensitive data is a fundamental requirement for any modern CI/CD platform. There’s a legion of malicious hackers out there who spend their days writing scripts that watch every commit made on all the public repos of the cloud-based major source code management providers. Publishing a password as clear text by mistake will make its way through the dark web in no time at all. Fortunately, using the environment variable encryption technique described in this article will go a long way toward thwarting the efforts of those with nefarious intentions.

Encrypting sensitive data in CircleCI is easy and necessary. Using the data encryption capabilities that Travis CI provides will make your code safe and secure.

Discussion (3)

Collapse
bsgreenb profile image
Ben Greenberg 🧢

Montana Mendy is a powerhouse. This article was a thorough explanation of how Travis uses public key cryptography to allow the storage of secrets in a comprehensive way within the CI context. Honestly, I'm about to pack up my bags and switch to TravisCI. Netlify is dope and all, but I don't even like saying the word "TOML". I digress. Montana's article is on point and I'm gonna give TravisCI a spinnerooni.

Collapse
montana profile image
Montana Mendy Author

Thanks Ben,

I'm glad on my first Dokcer article you gave Travis a spin! I hope you like these articles. This article is from Bob Reselman, and some few edits from me!

I'm glad you've commented on a numerous articles loving these. More to come. Stay tuned.

  • Montana Mendy (Travis CI)
Collapse
bsgreenb profile image
Ben Greenberg 🧢 • Edited

Bob Reselman is quite the blogger, though I'm sure there's a bit of Mendy magic mixed in. Keep at it, Travis team!