Vault: securing passwords on my laptop

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.


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.


    "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": "",  <-- 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.


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

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

Initialize vault with

  • export VAULT_ADDR=
  • vault operator init
  • Store unseal keys - you need them every time vault is restarted and also right now.
  • Unseal vault via vault operator 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:

VAULT_ADDR= 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


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 login
  2. lvault write secert/password @..
    "username": "<username>",
    "value": "<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 "${2:-secret/password}" | jq -r "${}"

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


Configure msmtp and offlineimap


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

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


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

remotepasseval = lvault_read()

And this line to the general section:

pythonfile = ~/.offlineimap/

And store these contents in ~/.offlineimap/

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()


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

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

You'll only receive email when they publish something new.

More from JC
All posts