Securing deployment credentials stored in home directory
If I were a cybercriminal, software developers would be my favorite target. First of all, if they are open source maintainers, they almost always store package repository access tokens as plain unsecured files. Gaining access to these files would allow an attacker to publish a new version of an open source library with an addition of malicious code that will be run on all machines that download the new version and to which the code using the new version is deployed. This is not just a theoretical threat – such attack was launched on numerous occasions, eslint-scope being the most prominent one.
Of course not everyone is a maintainer of a notable open source library, but they can still be an attractive target. Developers tend to have access to production systems – if the systems are managed via the command-line utilities (such as Heroku), the credentials are stored as unsecured files, and stealing the files would result in gaining access to their systems.
The problem is serious and well-known. This is one of the reasons you added a passphrase to our SSH keys. NPM already implemented two-factor authentication in their CLI, while RubyGems maintainers actively work on adding 2FA to the platform.
What about the other files? Here are typical files with access credentials:
~/.netrc
— standard Unix location storing data for logging in to remote hosts~/.aws/credentials
— Amazon Web Services credentials are used by a number of tools, Serverless.js being the most notable one~/.heroku/
~/.eyrc
— Engine Yard~/.s3cfg
— s3cmd~/.gmvault/gmvault_defaults.conf
How exactly is it a threat?
As developers, we run third-party code all the time. I'm pretty sure you did not audit every single line of your dependencies. Even running yarn
/ bundle install
to install dependencies is potentially dangerous as one of the libraries could have a malicious post-install script that is automatically executed by package managers. One doesn't even need to publish a malicious version of a notable open-source library – typosquatting a popular library is another way to execute arbitrary code on developer's machines.
Is there a solution?
Waiting for all CLI utilities to implement two-factor authentication is unrealistic so we have to take matters to our own hands. My recommendation is to have two accounts on your operating system:
- Regular account the does all the development work. This account would also install all dependencies.
- Separate account that would only store credentials and deploy. This account should never install anything (the deployment tools should be installed by your regular account) or do anything other than deployment.
The rest of the article assumes using macOS but the idea is the same on other operating systems.
Step 1: multi-user homebrew setup
The deployer should have access to homebrew to run all utilities installed by the regular user. The most elegant solution is to create a separate homebrew
group:
1sudo dscl . create /Groups/homebrew2sudo dscl . create /Groups/homebrew RealName "brew.sh users"3sudo dscl . create /Groups/homebrew gid 5994sudo dscl . create /Groups/homebrew GroupMembership $(whoami)5sudo chgrp -R homebrew $(brew --prefix)/*6sudo chmod -R g+rwX $(brew --prefix)/*
The number 599 is just an example, run dscl . list /Groups PrimaryGroupID
and pick a number that is not used by any other group.
Step 2: create the user
Make sure to run dscl . list /Users UniqueID
to select a number that is not taken by any other user. In the below example, we'll use 499:
1sudo dscl . create /Users/deploy UniqueID 4992sudo dscl . create /Users/deploy PrimaryGroupID 20 # staff3sudo dscl . create /Users/deploy UserShell /bin/bash45# optional, if you prefer a non-standard location6sudo dscl . create /Users/deploy NFSHomeDirectory /var/deploy78sudo dscl . append /Groups/homebrew GroupMembership deploy9sudo chown -R deploy /var/deploy
Step 3: shell configuration
Run su deploy
and edit ~/.bashrc
:
1# Add homebrew to PATH2export PATH=/usr/local/bin:$PATH3# Let's have a distinct prompt4export PS1="\e[1;30m\][\[\e[1;34m\]\u@\h\[\e[1;30m\] \[\e[1;33m\]\w\[\e[0;37m] \n\$ "5# We'll be using Rubies and gems installed by the regular user6export RBENV_ROOT=/Users/---your-regular-user-name---/.rbenv7# Initialize rbenv8eval "$(rbenv init -)"
Step 4: move your credentials to deployer's home directory.
It wouldn't hurt to test the credentials. Also, try to access the credentials from the regular user's account: you should have permission denied error.
Step 5: use deployer account to deploy
From now on, use only the deployer account to deploy, sync files etc.
Conclusion
As developers, we install and run third-party code all the time. Don't let a malicious post-install script steal your production access credentials.