Sources of Secrets by HashiCorp Vault - Revisited


Bicycle

Same Goal, Different Pattern

In the previous article we synchronized 1Password items into Vault by pulling data with Terraform and writing it into a KV engine. That pattern is great when you want Vault to own the canonical copy of a secret. But sometimes you just want Vault to expose secrets that are already governed elsewhere so that existing Vault clients can keep working with a consistent workflow.

Instead of copying the data, we can let Vault act as a smart proxy. The 1Password vault plugin exposes a dedicated secrets engine (op-connect) that delegates CRUD operations to the 1Password Connect API. Vault users read, list, create, and rotate items without ever seeing the underlying 1Password Connect credentials.

Architecture at a Glance

  • Vault loads the plugin binary into its plugin catalog and mounts it under a path such as op/.
  • The plugin talks to your self-hosted 1Password Connect server using an access token scoped to the vaults/items you want to share.
  • Vault ACL policies control who can call op/*, so you inherit the same RBAC and audit logging you already use for other secret engines.
  • There is no second storage location: 1Password remains the single source of truth while Vault gives consistent access patterns to your workloads.

Prerequisites

  1. A running Vault server (OSS or Enterprise) and the vault CLI.
  2. A deployed 1Password Connect server plus an access token for the vaults you plan to expose.
  3. Either a released plugin binary or a local Go toolchain if you want to build from source.

You can evaluate everything locally by running Vault in dev mode:

1mkdir -p ./vault/plugins
2vault server -dev -dev-root-token-id=root -dev-plugin-dir=./vault/plugins -log-level=debug

⚠️ Dev mode keeps Vault unsealed and in-memory. Never run it outside of experiments.

Installing the Plugin

Download the release that matches your Vault host architecture:

1# Example for linux/amd64, adjust version/filename as needed
2unzip ./vault-plugin-secrets-onepassword_1.1.0_linux_amd64.zip
3mv ./vault-plugin-secrets-onepassword_v1.1.0 ./vault/plugins/op-connect

Prefer to build it yourself?

1# Place the binary directly in Vault's plugin directory
2GOBIN=$(pwd)/vault/plugins go build -o ./vault/plugins/op-connect \
3  github.com/1Password/vault-plugin-secrets-onepassword

Next, start (or restart) Vault with the plugin directory configured. A minimal server configuration looks like this:

1plugin_directory = "${PWD}/vault/plugins"
2api_addr         = "http://127.0.0.1:8200"
3
4storage "inmem" {}
5
6listener "tcp" {
7  address     = "127.0.0.1:8200"
8  tls_disable = "true"
9}

Running 1Password Connect with Docker Compose

If you do not already operate 1Password Connect, you can spin it up locally with Docker Compose. The API component handles Vault traffic, while the Sync component keeps data current with 1Password. Both containers share the same credential file and data volume so they stay in lockstep.

 1services:
 2  op-connect-api:
 3    image: 1password/connect-api:latest
 4    ports:
 5      - "127.0.0.1:8080:8080"
 6    volumes:
 7      - "/opt/opconnect/1password-credentials.json:/home/opuser/.op/1password-credentials.json"
 8      - "data:/home/opuser/.op/data"
 9  op-connect-sync:
10    image: 1password/connect-sync:latest
11    ports:
12      - "127.0.0.1:8081:8080"
13    volumes:
14      - "/opt/opconnect/1password-credentials.json:/home/opuser/.op/1password-credentials.json"
15      - "data:/home/opuser/.op/data"
16
17
18volumes:
19  data:

Store the 1password-credentials.json file (downloaded from your Connect setup flow) at /opt/opconnect/ on the host, and ensure directory permissions prevent unintended reads. After running docker compose up -d, you can set OP_CONNECT_HOST=http://127.0.0.1:8080 and OP_CONNECT_TOKEN to the access token generated in 1Password; Vault can then talk to the local Connect API immediately.

Registering and Mounting the Secrets Engine

Once Vault is running and unsealed, calculate the binary checksum and register the plugin. On macOS:

1export VAULT_ADDR=http://127.0.0.1:8200
2SHA256_CHECKSUM=$(shasum -a 256 ./vault/plugins/op-connect | cut -d ' ' -f1)
3vault plugin register -sha256="$SHA256_CHECKSUM" -version=v1.1.0 secret op-connect

Mount the engine and hand over your 1Password Connect details:

1vault secrets enable --path=op op-connect
2vault write op/config \
3  op_connect_host=$OP_CONNECT_HOST \
4  op_connect_token=$OP_CONNECT_TOKEN

You can also load the configuration from JSON if you prefer keeping the values out of your terminal history:

1{
2  "op_connect_host": "https://op-connect.example.com:8443/",
3  "op_connect_token": "your_access_token"
4}
1vault write op/config @op-connect-config.json

For Vault Enterprise namespaces repeat the enable + config commands within each namespace (vault secrets enable -namespace=finance op).

Consuming Secrets from 1Password via Vault

Now every Vault client can treat op/ like any other secrets engine:

1# Discover vaults exposed by the Connect token
2vault list op/vaults
3
4# List items inside a specific 1Password vault
5vault list op/vaults/engineering/items
6
7# Read a single item (title or UUID)
8vault read op/vaults/engineering/items/docker-hub-service-account

Creating or updating items works with JSON payloads that follow the Connect API schema:

1{
2  "category": "login",
3  "title": "Example Login",
4  "fields": [
5    { "id": "username", "type": "STRING", "purpose": "USERNAME", "value": "my_user" },
6    { "id": "password", "type": "CONCEALED", "purpose": "PASSWORD", "generate": true }
7  ]
8}
1vault write op/vaults/engineering/items @login.json
2vault delete op/vaults/engineering/items/docker-hub-service-account

Behind the scenes the plugin forwards the request to 1Password Connect, so rotations, audits, and access revocations stay centralized in 1Password.

Policy Considerations

Treat the op/* path like any other engine when crafting ACL policies. A minimal, read-only policy could look like this:

path "op/data" {
  capabilities = ["list"]
}

path "op/vaults/engineering/*" {
  capabilities = ["read", "list"]
}

Because Vault merely proxies the data, principle of least privilege matters in both systems:

  1. Scope the 1Password Connect token to the smallest possible vaults.
  2. Limit which Vault roles can reach op/* paths.
  3. Keep plugin binaries updated (latest release is currently v1.1.0).

When to Choose the Plugin Pattern

  • You already manage operational secrets in 1Password and want to expose them to workloads that only speak Vault.
  • You prefer not to copy data into Vault’s storage backend to avoid drift and duplicate rotation pipelines.
  • You need bi-directional actions (list, read, create, update, delete) directly from Vault tooling.

If you instead require Vault to become the new source of truth, stick to the synchronization flow from the original article. Otherwise, the 1Password secrets engine is the fastest way to bridge both ecosystems while keeping governance inside 1Password.

Let us know if you want help hardening the deployment or integrating it with Terraform, Nomad, Kubernetes, or HCP.

Go Back explore our courses

We are here for you

You are interested in our courses or you simply have a question that needs answering? You can contact us at anytime! We will do our best to answer all your questions.

Contact us