« Previous 1 2 3 Next »
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.
« Previous 1 2 3 Next »
Buy this article as PDF
(incl. VAT)
Buy ADMIN Magazine
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Most Popular
Support Our Work
ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.