Vault: securing passwords on my laptop

December 31, 2017

Oh no, passwords!

My laptop is my main computing device and I have lots of passwords that I keep in a KeePass store. This store I access interactively through the Kee Firefox Extension which works great. However, I have a bunch of applictions on my laptop that need access to my plain text groupware password and this didn't work so great with KeePass. For a long time I kept the passwords in various configuration files including netrc. This is not very secure plus it's a pain to change my password because of many places it's stored at.

For improving this situation I probably could've used the system password store. I decided against it because I didn't want to deal with the various wallet applications that come attached to it, e.g. Kwallet. Instead, I decided to give vault a try. It's less a single user application and more an enterprise grade password store with very detailed access control mechanisms that might come in handy if I decide to store many more things in there.

In this first exploration I set up a local vault installation and integrate it with msmtp, offlineimap and vdirsyncer for a comprehensive and secure groupware and email setup.

Installation

Make sure that the vault executable on Linux has the appropriate capabilities to use mlock without root privileges. This command sets the capabilities:

sudo setcap cap_ipc_lock=+ep $(readlink -f $(which vault))

Set up consul (not required!!!)

First I got the impression that consul is required in order to run vault. Only later on I realized that this is not the case because vault also supports storing everything on the local filesystem which is sufficient for me. I'll leave the configuration here in case I need to set up a distributed vault server.

Consul is the storage backend for Vault and should therefore be set up first.

/home/jceb/.config/vault/consul.json:

{
    "datacenter": "jc",
    "data_dir": "/home/jceb/.config/vault/consul",
    "log_level": "INFO",  <-- has to be adjusted later on to WARN or so
    "node_name": "torch",
    "server": true,
    "bootstrap": true,  <-- Key to have it run as the first (only) server
    "bind_addr": "127.0.0.1",  <-- Run it only privately on this computer
    "ports": {  <-- disable unused services
        "dns": -1,
        "https": -1
    }
}

Run server with consul agent -config-file /home/jceb/.config/vault/consul.json

Set up vault

Actually, vault doesn't need consul. It can also run with a file backend.

/home/jceb/.config/vault/vault.json:

{
    "storage": {
        "file": {
            "path": "~/.config/vault/vault"
        }
    },
    "listener": {
        "tcp": {
            "address": "127.0.0.1:8200",
            "tls_disable": 1
        }
    }
}

Run server with vault server -log-level=info -config /home/jceb/.config/vault/vault.json

Initialize vault with

  • export VAULT_ADDR=http://127.0.0.1:8200
  • vault init
  • Store unseal keys - you need them every time vault is restarted and also right now.
  • Unseal vault via vault unseal

lvault command

For convenience reasons I created the lvault command that will access the local vault server without having to export VAULT_ADDRESS globally:

#!/bin/bash
VAULT_ADDR=http://127.0.0.1:8200 exec vault "$@"

Create a policy and a role

Policies are there to restrict/grant access in one place for as many concrete tokens as you like. Policies can be combined into roles that make it even easier to control access.

First, create a policy that only grants access to the password that will be store in the vault: lvault policy-write password -

path "secret/password" {
    capabilities = ["read"]
}

Second, create a role that integrates the just created policy: lvault write auth/token/roles/simpleapp @..

{
  "allowed_policies": "password",
  "name": "simpleapp",
  "orphan": false,
  "renewable": true
}

Production

Create an acces token for an application

Create the new token: lvault token-create -role=simpleapp -display-name=msmtp

Store token (not the accessor token which is only the identifier of the token) in an encrypted file gpg -eq > ~/.msmtp.key.gpg.

Store secret in vault

Authenticate as root and store the secert:

  1. lvault auth
  2. lvault write secert/password @..

Authenticate with token and access secret

Unfortunately, the vault command will store the current token in ~/.vault-token which will cause different programs to compete with one another for storing their token in there.
gpg -dq ~/.msmtp.key.gpg | lvault auth -

lvault read -field=value secret/password

Solution: don't store the token instead use curl. Store the following line in file lvault-read

gpg -dq "$1" | paste ~/.local/bin/lvault-read.header - | curl -s --header @- --request GET "http://127.0.0.1:8200/v1/${2:-secret/password}" | jq -r "${3:-.data.value}"

File ~/.local/bin/lvault-read.header contains:

X-Vault-Token: 

Configure msmtp and offlineimap

msmtp

Add the following line to your account in ~/.msmtprc:

passwordeval lvault-read ~/.msmtp.key.gpg

offlineimap

Add the following line to your repository in ~/.offlineimaprc:

remotepasseval = lvault_read()

And this line to the general section:

pythonfile = ~/.offlineimap/remotepasseval.py

And store these contents in ~/.offlineimap/remotepasseval.py:

from subprocess import check_output
from os.path import expanduser


def lvault_read():
    res = check_output(['lvault-read', expanduser('~/.offlineimap.key.gpg')])
    return res.decode().strip()

vdirsyncer

Add the following lines to the storage configuration in ~/.config/vdirsyncer/config:

username.fetch = ["command", "lvault-read", "~/.config/vdirsyncer/vdirsyncer.key.gpg", "", ".data.email"]
password.fetch = ["command", "lvault-read", "~/.config/vdirsyncer/vdirsyncer.key.gpg"]