New to KubeVault? Please start here.

Manage Milvus credentials using the KubeVault operator

OpenBao’s milvus-database-plugin is a dynamic-credentials database plugin for Milvus. The plugin provisions credentials by connecting to Milvus using the official Milvus Go SDK (v2) over gRPC: each issued credential becomes a Milvus user and is bound to one or more pre-existing roles on the target cluster. The plugin does not create roles; it only manages users and their role bindings, so every role referenced from creationStatements must already exist on the Milvus cluster.

The same CRD shape is used both for the in-process milvus-database-plugin and for the hub-spoke remote-milvus-plugin; the difference is whether the Vault AppBinding referenced by SecretEngine.spec.vaultRef is marked deploymentMode: RemoteAgent (then the SecretEngine controller rewrites plugin_name to remote-milvus-plugin and attaches spoke_name).

You need to be familiar with the following CRDs:

Before you begin

  • Install KubeVault operator in your cluster from here.
  • Run a Milvus cluster reachable over gRPC (default port 19530); managed Milvus on Zilliz Cloud exposes an https://... URL with token-based auth.
  • Pre-create any custom Milvus roles you want to bind credentials to, or use the built-in public role. The plugin only binds — it does not create roles. See Users and Roles for the Milvus role model.
$ kubectl create ns demo
namespace/demo created

Vault Server

Deploy a Vault Server using the KubeVault operator: Deploy Vault Server. The KubeVault operator will create an AppBinding wiring up Kubernetes auth.

$ kubectl get appbinding -n demo vault -o yaml

AppBinding for Milvus

Create an AppBinding pointing at the Milvus server endpoint (default port 19530). Unlike most database engines, the URL here is not a JDBC connection URI — it is the Milvus endpoint address (e.g. http://milvus.demo.svc:19530 or milvus.demo.svc:19530). The referenced Secret carries either Basic Auth credentials (username + password) or an API token (token) for Zilliz Cloud / API-token style auth. When the secret carries a token key, the operator forwards it as token= to the plugin instead of username/password.

Self-hosted Milvus (Basic Auth)

apiVersion: appcatalog.appscode.com/v1alpha1
kind: AppBinding
metadata:
  name: milvus
  namespace: demo
spec:
  clientConfig:
    url: http://milvus.demo.svc:19530
  secret:
    kind: Secret
    name: milvus-cred
---
apiVersion: v1
kind: Secret
metadata:
  name: milvus-cred
  namespace: demo
type: kubernetes.io/basic-auth
stringData:
  username: root
  password: Milvus

Zilliz Cloud (Bearer token)

apiVersion: appcatalog.appscode.com/v1alpha1
kind: AppBinding
metadata:
  name: milvus
  namespace: demo
spec:
  clientConfig:
    url: https://in03-xxxxxxxxxxxxxxx.api.gcp-us-west1.zillizcloud.com
  secret:
    kind: Secret
    name: milvus-cred
---
apiVersion: v1
kind: Secret
metadata:
  name: milvus-cred
  namespace: demo
type: Opaque
stringData:
  token: db_admin:zilliz-api-token-here

If you front Milvus with a self-signed TLS cert (e.g. the standalone quickstart with TLS enabled), set SecretEngine.spec.milvus.insecure: true below. Drop the knob once you front Milvus with a real CA-issued certificate.

Enable and Configure Milvus Secret Engine

When a SecretEngine crd object is created, the KubeVault operator will enable a secret engine on a specified path and configure the secret engine with the given configuration.

A sample SecretEngine for Milvus:

apiVersion: engine.kubevault.com/v1alpha1
kind: SecretEngine
metadata:
  name: milvus-engine
  namespace: demo
spec:
  vaultRef:
    name: vault
  milvus:
    databaseRef:
      name: milvus
      namespace: demo
    pluginName: milvus-database-plugin   # optional; this is the default
    allowedRoles:
      - "*"
    dbName: default                      # optional; forwarded as the `dbName` request header on every API call
    insecure: false                      # set true only for self-signed dev clusters

Apply it and wait for STATUS=Success:

$ kubectl apply -f milvus-engine.yaml
secretengine.engine.kubevault.com/milvus-engine created

$ kubectl get secretengines -n demo
NAME            STATUS    AGE
milvus-engine   Success   10s

Use kubectl describe secretengine -n demo milvus-engine to inspect error events, if any.

Create a MilvusRole

A MilvusRole describes how the plugin should mint a dynamic credential. creationStatements is a string slice holding a JSON role document specifying pre-existing roles and/or custom role definitions. Pre-existing roles must already exist on the target Milvus cluster, while custom roles are created and granted privileges dynamically.

apiVersion: engine.kubevault.com/v1alpha1
kind: MilvusRole
metadata:
  name: milvus-readonly
  namespace: demo
spec:
  secretEngineRef:
    name: milvus-engine
  creationStatements:
    - '{"roles":["public"]}'
  defaultTTL: 1h
  maxTTL: 24h

Custom roles can also be defined inline under custom_roles:

  creationStatements:
    - |
      {
        "roles": [
          "public"
        ],
        "custom_roles": [
          {
            "name": "collection_reader",
            "privileges": [
              {
                "object_type": "Collection",
                "object_name": "Products",
                "privilege": "Search"
              }
            ]
          }
        ]
      }      

Apply and verify:

$ kubectl apply -f milvus-role.yaml
milvusrole.engine.kubevault.com/milvus-readonly created

$ kubectl get milvusrole -n demo
NAME              STATUS    AGE
milvus-readonly   Success   12s

The role name in Vault follows the format k8s.{clusterName}.{metadata.namespace}.{metadata.name}, so you can verify directly with the Vault CLI:

$ vault read your-database-path/roles/k8s.-.demo.milvus-readonly
Key                      Value
---                      -----
creation_statements      [{"roles":["public"]}]
db_name                  k8s.-.demo.milvus
default_ttl              1h
max_ttl                  24h

Deleting the MilvusRole removes the role from Vault.

Issue Milvus credentials

Request a dynamic credential by creating a SecretAccessRequest:

apiVersion: engine.kubevault.com/v1alpha1
kind: SecretAccessRequest
metadata:
  name: milvus-cred-rqst
  namespace: demo
spec:
  roleRef:
    kind: MilvusRole
    name: milvus-readonly
  subjects:
    - kind: ServiceAccount
      name: demo-sa
      namespace: demo

Approve it through the KubeVault CLI:

$ kubectl vault approve secretaccessrequest milvus-cred-rqst -n demo
approved

Once approved, the operator issues the credential, stores it in a Secret, and binds the listed subjects via a Role/RoleBinding. The plugin creates a new Milvus user and grants the listed roles (public) on the target Milvus cluster. The credential lives on the lease until you delete the SecretAccessRequest or it expires; on lease revocation the plugin removes the Milvus user.

$ kubectl get secretaccessrequest milvus-cred-rqst -n demo -o json | jq '.status'
{
  "lease": {
    "duration": "1h0m0s",
    "id": "your-database-path/creds/k8s.-.demo.milvus-readonly/abc...",
    "renewable": true
  },
  "secret": {
    "name": "milvus-cred-rqst-xxxxxx"
  }
}

$ kubectl get secret -n demo milvus-cred-rqst-xxxxxx -o jsonpath='{.data.username}' | base64 -d
v-kubernetes-demo-XXXXXXXX

$ kubectl get secret -n demo milvus-cred-rqst-xxxxxx -o jsonpath='{.data.password}' | base64 -d
xxxxxxxxxxxxxxxxx

Use the issued username / password to authenticate against Milvus over gRPC or REST. The credential is revoked when the SecretAccessRequest is deleted.

Further reading