Documentation menu

Software

AutoHSM

A small daemon that keeps a self-hosted HashiCorp Vault unsealed across restarts, using unseal shares wrapped by a unique, non-extractable HSM key on each node.

What it is

Vault's built-in PKCS#11 auto-unseal is an Enterprise feature. A Community Edition server that reboots stays sealed until people enter enough unseal keys. AutoHSM closes that gap for teams running their own Vault, and it treats visibility as the more important half:

  • Availability: the server comes back unsealed after a reboot without a person at the keyboard.
  • Visibility: every poll that finds the server sealed raises an alarm, even when the unseal then succeeds, so you learn that it restarted at all.

Who it is for

  • Operators of a self-hosted Vault using Shamir unseal keys who want unattended recovery after reboots.
  • Teams that already run, or can run, an HSM or SoftHSM token on each node.
  • Anyone who wants a sealed server to be loud rather than silently unavailable.

How it works

Each node holds one or more unseal shares, each stored as an AES-256-GCM envelope encrypted by a key that never leaves the node's HSM:

autohsm-v1.<base64(nonce)>.<base64(ciphertext||tag)>
AAD: autohsm-v1|node=<node_id>|idx=<n>
SituationResult
Share used with a different node labelFails: AAD mismatch
Share replayed under another indexFails: AAD index mismatch
Ciphertext tampered withFails: GCM tag
Disk, backup or snapshot stolenInert: the key never leaves the HSM
Wrong CA presented by the serverFails: pinned CA, no system-root fallback
Plaintext http:// server addressRefused at config load

The daemon polls seal status, and when the server is sealed it unwraps this node's shares inside the HSM and submits them. Accepted share indexes are recorded per unseal attempt so a crash does not resubmit.

The real protection is distribution. Give each node fewer shares than the unseal threshold. With 5 shares and a threshold of 3, one share on each of three nodes means no single compromised node can unseal alone. The HSM PIN must be available at boot, so code execution on a node can ask the HSM to unwrap just as the daemon does.

Install

The repository is public under the MIT license. PKCS#11 needs cgo, so build on the target platform (a Linux host or Linux container for Linux servers). With Go 1.24 or newer and a C compiler:

go install github.com/afterdarksys/secretserver-autohsm/cmd/autohsm@latest

Or build from a checkout:

git clone https://github.com/afterdarksys/secretserver-autohsm.git
cd secretserver-autohsm
make build          # CGO_ENABLED=1 go build -trimpath -o bin/autohsm ./cmd/autohsm
# make build-linux  # GOOS=linux GOARCH=amd64 build, writes bin/autohsm-linux-amd64

Install files and the service user

sudo install -m 0755 bin/autohsm /usr/local/bin/autohsm
# after make build-linux: sudo install -m 0755 bin/autohsm-linux-amd64 /usr/local/bin/autohsm
sudo useradd --system --no-create-home --shell /usr/sbin/nologin autohsm
sudo install -d -o root -g autohsm -m 0750 /etc/autohsm
sudo install -o root -g autohsm -m 0640 examples/autohsm.yaml /etc/autohsm/autohsm.yaml
sudo install -o root -g autohsm -m 0640 /path/to/vault-ca.pem /etc/autohsm/vault-ca.pem
sudo install -o root -g autohsm -m 0640 /secure/path/hsm-pin /etc/autohsm/pin
sudo cp deploy/autohsm.service /etc/systemd/system/

Create the wrapping key (SoftHSM shown)

sudo usermod -aG softhsm autohsm
sudo -u autohsm softhsm2-util --init-token --slot 0 --label autohsm --so-pin <SO_PIN> --pin <PIN>
sudo -u autohsm AUTOHSM_PIN=<PIN> pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
  --login --pin env:AUTOHSM_PIN \
  --keygen --key-type aes:32 --label autohsm-wrap --private --sensitive \
  --usage-decrypt

Create the token as the autohsm user; a token created as root is unreadable to the service, and every later step then fails with pkcs11 initialize: CKR_GENERAL_ERROR.

Wrap this node's share, verify, enable

sudo -u autohsm autohsm wrap --index 1 < /path/to/share-1.txt \
  | sudo tee /etc/autohsm/share-1.wrapped
sudo chown root:autohsm /etc/autohsm/share-1.wrapped
sudo chmod 0640 /etc/autohsm/share-1.wrapped

sudo -u autohsm autohsm selftest
sudo systemctl daemon-reload
sudo systemctl enable --now autohsm

autohsm status exits 2 when the server is sealed and never touches the HSM, which makes it a cheap external monitor from cron.

Platform support

ComponentStatus
Linux with systemdSupported target; README install and the shipped unit tested on Debian 12
SoftHSM 2.6 / 2.7Integration and end-to-end tested
Vault 1.20, Shamir seal, 3 nodesEnd-to-end tested, including negative cases
Hardware HSMs (vendor PKCS#11)Written against PKCS#11 v2.40 AES-GCM but not yet exercised on production hardware. Run selftest against your token first.
Go toolchainGo 1.24 or newer, cgo enabled