No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-04-17 09:11:07 +02:00
.idea updated output for policy migration 2026-04-17 09:11:07 +02:00
.gitignore Migration of KV2 working with yaml config file 2026-04-14 12:04:09 +02:00
.gitlab-ci.yml fix stages in pipeline 2026-04-16 13:03:26 +02:00
go.mod Migration of KV2 working with yaml config file 2026-04-14 12:04:09 +02:00
go.sum Migration of KV2 working with yaml config file 2026-04-14 12:04:09 +02:00
main.go updated output for policy migration 2026-04-17 09:11:07 +02:00
README.md pipeline for releasing builds of secrets migrator 2026-04-15 12:45:48 +02:00

secretsmigrator

A CLI tool for migrating secrets, policies and KV engines between HashiCorp Vault (or OpenBao) instances.

It can copy KV version1 or version2 data, as well as Vault policies, from a source server to a destination server while preserving namespaces and authentication tokens.


Table of Contents

Section Description
Features What the tool can do
Prerequisites Software you need before installing
Installation Build from source or get a binary
Configuration config.yaml format and interactive prompts
Usage CLI reference and examples
How it works Brief overview of the internals
Limitations & Known Issues Things the current version does not support
Contributing How to help improve the project
License SPDX identifier

Features

Feature Description
KV v2 migration Recursively copies all secrets from a KVv2 mount on the source to a KVv2 mount on the destination (creates the mount if missing).
KV v1 migration Same as above for the legacy KVv1 engine.
Policy migration Copies every nonsystem policy (admin, root, default, namespace-admin) from source to destination.
Namespace aware Supports Vault/OpenBao namespaces you can specify a namespace for each side.
Interactive configuration If any required field is missing from config.yaml, the CLI will prompt you for it (including hidden password entry).
Idempotent Running the same command twice does not duplicate data mounts are created only once and existing secrets are overwritten with the source values.
Zerodependency runtime The binary only depends on the official OpenBao client library and a few standard packages.

Prerequisites

Tool Minimum version Why
Go 1.22 (or newer) Required to compile the source (go build).
Vault / OpenBao Any version that supports the API used by github.com/openbao/openbao/api/v2. The tool talks to the HTTP API, not the CLI.
git (optional) To clone the repository.

Installation

1. Install from source

# Clone the repo
git clone https://helsegitlab.nhn.no/kryptodrift/secret-migrator.git
cd secrets-migrator

# Build the binary (the resulting executable will be ./secrets-migrator)
go build -o secrets-migrator .

2. Use a prebuilt binary (Linux/macOS)

# Example  replace <VERSION> with the latest tag
curl -L -o secrets-migrator "TODO: Link to release page"
chmod +x secrets-migrator

Tip: Put the binary in a directory thats on your $PATH (e.g. /usr/local/bin).


Configuration

The tool looks for a file called config.yaml in the working directory (the directory where you run the command).
If the file does not exist or some fields are empty, the program will ask you for the missing values interactively and then write them back to the file (permissions set to 0600).

Sample config.yaml

source_url: "https://vault-source.example.com"
source_namespace: "team-a"          # optional  omit or set to "root"
source_token: "s.XXXXXXXXXXXXXXXXX"

dest_url: "https://vault-dest.example.com"
dest_namespace: "root"                  # root means root namespace or no namespace.
dest_token: "s.YYYYYYYYYYYYYYYYYYY"

Fields

Field Required? Description
source_url Base URL of the source Vault/OpenBao (e.g. https://vault.example.com).
source_namespace Namespace to use for the source request. Leave blank or set to "root" for the root namespace.
source_token Token that has read access to the source data. Input is hidden when prompted.
dest_url Base URL of the destination server.
dest_namespace Destination namespace (same rules as source).
dest_token Token that has write/create rights on the destination. Hidden input when prompted.

Security note: The file is stored with mode 0600 (ownerread/write only). Keep it out of version control.


Usage

Run the binary without arguments to see the toplevel help:

$ ./secrets-migrator --help
Used for migrating secrets from one vault to another

For migrating secrets, policies, and roles from one vault or openbao to another

Usage:
  secrets-migrator [command]

Available Commands:
  KVv1        Migrate KVv1 secrets in vault/OpenBao
  KVv2        Migrate KVv2 secrets in vault/OpenBao
  policies    Migrate policies in vault/OpenBao
  help        Help about any command

All subcommands share the same configuration handling (see above).

1. Migrate KVv2

./secrets-migrator KVv2

The command will ask for the source mount and destination mount (e.g. secret/ and new-secret/).
It then:

  1. Recursively lists all secret paths under the source mount (metadata/… endpoint).
  2. Creates the destination mount if it does not exist (KV v2, version set automatically).
  3. Reads each secret (data/… endpoint) and writes it to the destination mount.

Example interactive session

$ ./secrets-migrator KVv2
sourceMount: secret/
destMount: migrated/
Created mount migrated/
KV2 migration done!

2. Migrate KVv1

./secrets-migrator KVv1

Works the same way as KVv2 but uses the KVv1 API (/v1/<mount>/…).

3. Migrate Policies

./secrets-migrator policies

Copies every policy (except the builtin ones listed in the source) from the source to the destination.
No extra user input is required.

4. Full command chain example

# 1. Create/complete config.yaml (run any subcommand, it will prompt)
./secrets-migrator KVv2   # fill in source/dest URLs, tokens, etc.

# 2. Migrate KVv2 data
./secrets-migrator KVv2

# 3. Migrate KVv1 data (if you have legacy mounts)
./secrets-migrator KVv1

# 4. Migrate policies
./secrets-migrator policies

How it works (highlevel)

  1. Configuration loading loadConfig reads config.yaml; missingConfig asks for anything still empty.
  2. Client creation newClient builds an OpenBao/Vault client (bao.Client) with the supplied URL, token and namespace.
  3. Recursive listing listRecursively walks the secret tree using the List API, distinguishing between KVv1 and KVv2 layout.
  4. Secret transfer
    • For KVv2: putSecretsFromList reads each secret via KVv2().Get and writes it with KVv2().Put.
    • For KVv1: putKV1SecretsFromList does the same with the KVv1 helpers.
  5. Policy transfer migratePolicies enumerates policies via Sys().ListPolicies(), fetches each definition with Sys().GetPolicy, and writes it to the destination with Sys().PutPolicy.

All operations are performed with a background context.Context and errors are reported to the console.


Limitations & Known Issues

Issue Impact Workaround
Mount creation only for KV engines NonKV mounts (e.g., transit, database) are ignored. Add a custom mount creation step manually before running the migration.
No role/auth method migration The code contains a placeholder for role migration but it is not implemented. Export roles manually or extend the tool (see Contributing).
No selective migration All secrets under the specified mount are copied; you cannot filter by path prefix. Run the tool against a more narrowly scoped mount or edit the source after the fact.
Plaintext token storage in config.yaml Tokens are persisted in clear text on disk. Keep the file secured (chmod 600) or delete it after the migration.
Namespace handling defaults to "root" If you leave the namespace blank, the code forces "root" which may not be appropriate for all setups. Explicitly set an empty string ("") in the yaml to use the true root namespace.
No TLS verification customization The client always uses the default Go TLS settings. Set SSL_SKIP_VERIFY environment variable before running the binary, or modify newClient to accept a custom transport.

Contributing

Contributions are welcome! Follow these steps:

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/awesomething.
  3. Make your changes, ensuring they pass go vet and go test (if tests are added).
  4. Run go fmt ./... and golint (optional) to keep code style consistent.
  5. Open a Pull Request describing what you changed and why.

Development tips

Tip Command
Run the program locally with live config prompting go run . KVv2
Lint & format go vet ./... && go fmt ./...
Add a new subcommand Create a new cobra.Command in main.go (or split into its own file) and register it in init().
Unit testing (future)** Add tests under *_test.go and run go test ./.... (Currently the repo has no tests.)

License

secrets-migrator is released under the MIT License. See the LICENSE file for full text.


Acknowledgements

  • OpenBao The client library (github.com/openbao/openbao/api/v2) provides a dropin replacement for HashiCorp Vault's Go SDK.
  • Cobra CLI framework used for command parsing (github.com/spf13/cobra).
  • Viper/YAML Configuration handling (go.yaml.in/yaml/v4).

Happy migrating! 🎉