Efficient password management in distributed teams

Secure Transfer

Heroku as a PaaS Host

By relying on a PaaS solution, you avoid the need to manage either bare metal servers or virtual machines. Everything happens at the application level. Heroku is one of the veterans of the PaaS market and is particularly suitable for simple examples – as in this case.

To start an existing project on Heroku, all you have to do is create an account on the Heroku website, download the Heroku command-line tool, and log in (Listing 1).

Listing 1

Heroku Login

$ heroku login
Enter your Heroku credentials:
Email: reader@linuxmagazin.de
Password: *************************
Logged in as reader@linuxmagazin.de

Before you can run an application in the Heroku cloud, you need a local Git repository that can then be used to register the application with Heroku (Listing 2). The new application is then available online (e.g., in this example under the linux-magazin-secrets name).

Listing 2

Creating an Application

$ cd ~/code/app/linux-magazin-secrets
$ heroku create linux-magazin-secrets
Creating linux-magazin-secrets... done
https://linux-magazin-secrets.herokuapp.com/ | https://git.heroku.com/linux-magazin-secrets.git

The application does not yet have any functionality in this raw version. The next steps add the necessary files to make the application executable.

First, you must create a process definition that describes which processes Heroku needs to start and manage. For a JavaScript application, this step is very simple. The file name must be Procfile and has to be located in the repository's root directory.

The file instructs the platform to manage a service called web. The service consists of a Node.js process that executes the index.js file.

web: node index.js

For the JavaScript application to run on Heroku, it requires a package definition, and it is especially important to define JavaScript package dependencies that the platform automatically installs.

Because the application provides an HTTP endpoint, it requires a web framework. The express.js JavaScript library is suitable and is therefore defined as a dependency in the package definition. This file is also stored in the root directory, but under the name package.json (Listing 3).

Listing 3

package.json

01 {
02   "name": "linux-magazin-secrets",
03   "version": "1.0.0",
04   main() "index.js",
05   "license": "ISC",
06   "dependencies": {
07     "express": "^4.14.0"
08   }
09 }

The server endpoint is located in the index.js file. On the basis of the express.js web framework, it implements an HTTP API endpoint that delivers information about the GitHub user who needs the front end (Listing 4).

Listing 4

index.js

01 var express = require('express')
02 var app = express()
03
04 var user = process.env.GITHUB_USER
05 var apiToken = process.env.GITHUB_API_TOKEN
06
07 var port = process.env.PORT || 5000
08
09 app.get('/', function (req, res) {
10   // Implement GitHub API call
11 })
12
13 app.listen(port, function () {
14   console.log('App listening on port ' + port)
15 })

The application is configured with three environment variables. The PORT variable automatically provides the platform's run-time environment. The convention is that a service must listen on this port to be able to run on Heroku. The other two variables, GITHUB_USER and GITHUB_API_TOKEN, represent the secret information to be provided to the application.

A simple git push deploys the current status on Heroku. The corresponding Git remote repository, called heroku, has already been configured automatically by the previous heroku create call:

$ git push heroku master:master

In principle, this is all you need to get the application up and running. However, because the two GitHub environment variables are not initialized, the application will cancel with an error.

The Heroku command line allows you to set up configurations provided to the application in the form of environment variables. Changes to this configuration automatically restart all services. The two missing variables can be set up with the command:

$ heroku config:set GITHUB_USER="holderbaum" GITHUB_API_TOKEN="sn4k3oil"

This means the target has been achieved; that is, no secret information is stored in the source code or in other files. However, the API token and username must be known to execute the previous configuration command.

Because anything you type into the shell remains in the history for a long time, you should not simply type the secret information there; rather, you should find a secure way to store the secret information and distribute it to the people who are supposed to configure the application. This is where the password manager comes into play.

Directory as a Secret Pool

As already mentioned, Pass is applied to a single directory. To initialize storage of this type, you need to save the directory path in an environment variable, for example, with:

$ export PASSWORD_STORE_DIR=~/code/app/secrets
$ export MY_ID=5244D411CD7CBA95
$ pass init $MY_ID

The argument for pass init is the author's GnuPG ID. However, you can also transfer any list of IDs. To keep the example simple, it uses a single ID.

To add values to memory, use pass add. It expects the name of the secret to be added to the memory as an argument and asks via standard input for the actual secret (e.g., the password or API token), which has to be stored encrypted:

$ export PASSWORD_STORE_DIR=~/code/app/secrets
$ pass add production/user
$ pass add production/api_token

To replay a secret again, use the pass show command. If the calling user is in possession of the private key from one of the certificates specified at init, calling the command decrypts the stored secret and outputs it on standard output:

$ export PASSWORD_STORE_DIR=~/code/app/secrets
$ pass show production/api_token
"sn4k3oil"

A previous step created a password store that resided in a single directory and was based on separate files. It can thus be easily distributed in a team with the help of a version control system.

The ~/code/myapp/secrets directory now looks like this:

$ find ~/code/app/secrets
~/code/app/secrets/.gpg_id
~/code/app/secrets/production/user.gpg
~/code/app/secrets/production/api_token.gpg

The *.gpg files contain the previously added encrypted secrets. The .gpg_id file contains the list of the GnuPG IDs used to encrypt the memory content.

It is also possible to store more .gpg_id files in subdirectories, so you can implement different access authorizations for different subdirectories. Of course, a tool like find is not needed to list the contents of such memory.

The pass ls command displays the memory tree structure on standard output:

$ export PASSWORD_STORE_DIR=~/code/app/secrets
$ pass ls
+-- production
    |-- api_token
    +-- user

By combining the passport-based memory with the Heroku command line, the application can be configured without having to enter explicit secrets (Listing 5). This code creates a store for secrets, which allows only one person to access the stored secrets. However, the goal of managing the secrets for an entire team remains.

Listing 5

Variable Configuration

export PASSWORD_STORE_DIR=~/code/app/secrets
export USER=`pass show production/user`
export TOKEN=`pass show production/api_token`
heroku config:set GITHUB_USER=$USER GITHUB_API_TOKEN=$TOKEN

Teamwork

Two scenarios are particularly important for security: roll-on and roll-off. In a roll-on scenario, the trustworthy group of team members is extended by one person, which usually means that the new team member receives certain access rights, such as access to project secrets or infrastructure.

Pass can do this effortlessly: You only need the GnuPG ID of the new member and the corresponding public key. You then call pass init with all the IDs, including the new ones. Pass re-encrypts all information in the store with the public keys of the specified IDs (Listing 6), which is sufficient for accessing the secret store.

Listing 6

Re-Encryption

export PASSWORD_STORE_DIR=~/code/app/secrets
export MY_ID=5244D411CD7CBA95
export ADAS_ID=44A7B1E354AF81E2
export ALANS_ID=BA29EE533AF39B21
pass init $MY_ID $ADAS_ID $ALANS_ID

This process can, of course, be repeated for each new team member in the same way. If you want to add confidential data to memory, you need to possess all the public keys that are to be specified during initialization, and it is the only way to encrypt the data correctly when added.

During the roll-off of a team member, the person in question leaves the team and is no longer allowed access to the secrets. This time, call pass init again, but omit the corresponding GnuPG ID.

Again, Pass encrypts all secrets in the store, but this time without the ID that was omitted. It should be noted that the new encryption only means that the former employee is no longer able to decrypt secrets from now on. However, if they keep a copy of the repository before re-encryption, they still have access to the secrets. Therefore, changing all confidential information must be an integral part of the roll-off.

Because Pass keeps the secrets in a central location – the store it has created – it is much easier to manage rotation after a roll-off than if they were randomly distributed across the project.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus