---
description: Use HashiCorp Vault with KumoMTA to securely store DKIM signing keys and secrets outside your policy code, loading them dynamically at runtime.
---

# Using HashiCorp Vault

## Introduction

[HashiCorp Vault](https://developer.hashicorp.com/vault) is a secure storage tool for maintaining a centralized and secure store of passwords, certificates and other secrets. Vault is only one of the ways you can store secrets outside of your running code, but it is very popular and KumoMTA has [native integration](https://docs.kumomta.com/reference/keysource/?h=hashi#hashicorp-vault).

Vault helps you keep passwords and other secrets separated from running code to help reduce the possibility of security leaks such as accidentally saving your API key in a GitHub repo.

## Configuring KumoMTA to use HashiCorp Vault

The documentation [in the reference manual](https://docs.kumomta.com/reference/keysource/?h=hashi#hashicorp-vault) is straightforward, but does have some nuance.

In the example shown there and below, the DKIM signing key is stored as a file in Vault so it can be loaded dynamically. Including the Vault token in the script, however, is not particularly secure. It is recommended to place the vault address and token in environment variables that are accessible to KumoMTA. In most cases, that will mean modifying the systemd unit service file.

```lua
local vault_signer = kumo.dkim.rsa_sha256_signer {
  key = {
    vault_mount = 'secret',
    vault_path = 'dkim/' .. msg:from_header().domain,
    -- Optional: specify a custom key name (defaults to "key")
    -- vault_key = "private_key"
    -- vault_address = "http://127.0.0.1:8200"
    -- vault_token = "hvs.TOKENTOKENTOKEN"
  },
}
```

To modify the systemd service file, use the built in edit command in systemctl. The [man page is here](https://man7.org/linux/man-pages/man1/systemctl.1.html), but DigitalOcean has an excellent [tutorial](https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units) that explains it in plain English.

The short version is that you can use `systemctl edit` to edit the file and add "Environment" values under the `[Service]` section so that those values will be available when the system service daemon starts KumoMTA. The example below modified the FULL service config. The `--full` option can be removed to modify a snippet instead of the full config.

```bash
sudo systemctl edit --full kumomta.service
```

You should disregard everything except the `[Service]` section.
At the bottom of that section, add 2 lines:

```txt
Environment=VAULT_ADDR='http://<YOUR_SERVER_LOCATION>:8200'
Environment=VAULT_TOKEN='<YOUR_ACCESS_TOKEN>'
```

When done, it should look something like this:

```txt
[Unit]
Description=KumoMTA SMTP service
After=syslog.target network.target
Conflicts=sendmail.service exim.service postfix.service

[Service]
Type=simple
Restart=always
ExecStart=/opt/kumomta/sbin/kumod --policy /opt/kumomta/etc/policy/init.lua --user kumod
# Allow sufficient time to wrap up in-flight tasks and safely
# write out pending data
TimeoutStopSec=300
Environment=VAULT_ADDR='http://127.0.0.1:8200'
Environment=VAULT_TOKEN='SAMPLE-TOKEN'

[Install]
WantedBy=multi-user.target
```

Save the file, then reload with the `sudo systemctl daemon-reload` command.

## Storing secrets for later use

There are a number of ways to store secrets, and the method depends on how the vault was created. If configured via the Vault CLI, then a V2 password can be stored as follows:

```bash
vault kv put -mount=secret dkim/example.org key=@example-private-dkim-key.pem
```

It is important to ensure you are storing Version-2 secrets with a "key=<value>" format. In the preceding example, the `key` points to a filename `example-private-dkim-key.pem`.

## Using Custom Key Names
{{since('2025.10.06-5ec871ab')}}
By default, KumoMTA looks for a field named `key` in your vault secret. If you want to use a different field name, you can specify it with the `vault_key` parameter:

```lua
local vault_signer = kumo.dkim.rsa_sha256_signer {
  key = {
    vault_mount = 'secret',
    vault_path = 'dkim/' .. msg:from_header().domain,
    vault_key = 'private_key', -- Look for 'private_key' instead of 'key'
  },
}
```

And store it in vault like this:

```bash
vault kv put -mount=secret dkim/example.org private_key=@example-private-dkim-key.pem
```

This is particularly useful when you have multiple keys stored in the same vault secret or when your vault secrets follow a different naming convention.

## Ways to Use Vault With KumoMTA

Vault has a number of advantages over statically storing secrets. Aside from the obvious security benefits of not exposing your passwords and security keys in your code, it also allows you to _physically_ separate the information. One key use case is storing the vault server in a private network while the KumoMTA instances are deployed around the world or in public colocation or cloud services. If a remote server is compromised, the local vault server can be secured to prevent data leakage.

Another advantage is being able to dynamically load keys on demand. This can be very helpful with DKIM key rotation. With the keys stored within the vault, they can be loaded as-needed when messages pass through the server that need a particular key:

```lua
local vault_signer = kumo.dkim.rsa_sha256_signer {
  key = {
    vault_mount = 'secret',
    vault_path = 'dkim/' .. msg:from_header().domain,
  },
}
```
