Store credentials and apply policies with Vault

You can use HashiCorp Vault (Vault) to both securely store credentials and apply an AuthPolicy custom resource (CR) with MCP gateway. The AuthPolicy CR retrieves and inject those credentials into your MCP server request flow.

The essentials of the workflow include the following elements:

  • MCP gateway: Acts as the entry point for AI clients that are accessing backend MCP servers.

  • Vault: The source of truth for secrets.

  • AuthPolicy CR: The Connectivity Link resource that defines how to authenticate the user and retrieve their specific secret from Vault.

  • Authorino: Triggered by AuthPolicy CRs, Authorino is the external authorization service used by Connectivity Link to validate identities and retrieve external metadata. Authorino adds authorization and authentication to APIs that do not have credential checks built-in. In this specific Vault workflow, Authorino uses the instructions from the AuthPolicy CR to connect to Vault by using JSON web token (JWT) authentication. After it connects to Vault, Authorino retrieves the stored secret, then gives an allow or deny decision for the traffic.

Set up a trust relationship between Vault and Authorino

To ensure that the Authorino service can act on an AuthPolicy custom resource (CR), you must set up a secure, identity-based trust relationship between the Authorino service and your HashiCorp Vault (Vault) database.

Your goal is to allow the Authorino service to dynamically retrieve sensitive credentials from Vault.

Note

In production environments, using a tool such as Terraform or GitOps to manage policies is a best practice. Automation ensures that if a cluster is deleted, then your security policies are automatically recreated.

Prerequisites
  • You completed the installation for MCP gateway.

  • You created a Gateway object.

  • You have a Vault database.

  • You created a namespace for your MCP server secrets.

Procedure
  1. Enable JSON-web-token (JWT) authentication in your Vault server by following the instructions in the Vault documentation.

  2. Create a Vault policy that saves the Access Control List (ACL) that defines the read and list capabilities for Authorino by running the following command:

    $ curl -H "X-Vault-Token: $VAULT_TOKEN" -H 'Content-Type: application/json' -X POST \
      --data '{
        "policy": "path _<"secret/data/mcp_gateway/*\">_ {\n  capabilities = [\"read\", \"list\"]\n}"
      }' \
      <vault_service_hostname:port>/v1/sys/policies/acl/authorino
    • Replace <"secret/data/mcp_gateway/*\"> with the path to the namespace for your MCP server secrets.

    • Replace <vault_service_hostname:port> with the Vault service address.

  3. Create a Vault role that stores a set of rules for Authorino by running the following command:

    $ curl -H "X-Vault-Token: $VAULT_TOKEN" \
      -H 'Content-Type: application/json' \
      -X POST \
      --data '{
        "role_type": "jwt",
        "bound_audiences": ["authorino"],
        "user_claim": "sub",
        "policies": ["authorino"],
        "ttl": "1h"
      }' \
      <vault_service_url:port>/v1/auth/jwt/role/authorino-role

    Replace <vault_service_url:port> with the Vault service address.

Create a single-server-scoped AuthPolicy CR for Vault

Create an AuthPolicy custom resource (CR) to connect an external OIDC Identity Provider (IdP) with HashiCorp Vault (Vault). The AuthPolicy CR enables getting a Vault token on behalf of the user or service that needs access to the MCP server data.

The following example AuthPolicy CR is scoped to a single MCP server. Single-server scoping creates granular security. For example, you can configure the Authorino service to examine individual secret-containing paths for each MCP server in your backend. You can also specify customized role-based logic for each MCP server.

Prerequisites
  • You installed the OpenShift CLI (oc).

  • You are logged into an OpenShift Container Platform cluster as an administrator.

  • You have a central authorization tool that uses standard protocols such as OpenID Connect or OAuth 2.0.

  • You have an MCP server ready to connect.

  • You set up a trust relationship between the Authorino service and HashiCorp Vault.

Procedure
  1. Create an AuthPolicy CR targeting a single MCP server by using the following example:

    Example single MCP server AuthPolicy CR
    apiVersion: kuadrant.io/v1
    kind: AuthPolicy
    metadata:
      name: <vault_policy_mcpserver1>
      namespace: <mcpserver1>
    spec:
      targetRef:
        group: gateway.networking.k8s.io
        kind: HTTPRoute
        name: <mcp_server_route>
      rules:
        authentication:
          "mcp-clients":
            jwt:
              issuerUrl: <issuer_URL>
        metadata:
          "oauth-token":
            priority: 0
            http:
              url: <oauth_token_issuer_endpoint>
              method: POST
              credentials:
                authorizationHeader:
                  prefix: Basic
              sharedSecretRef:
                name: authorino-oauth-client
                key: client_secret
              bodyParameters:
                grant_type:
                  value: client_credentials
                scope:
                  value: openid
            cache:
              key:
                value: 'singleton'
              ttl: 1800
          "vault-login":
            priority: 1
            when:
            - predicate: auth.metadata.exists(p, p == "oauth-token") && has(auth.metadata["oauth-token"].access_token)
            http:
              url: https://vault.vault.svc.cluster.local:8200/v1/auth/jwt/login
              method: POST
              body:
                expression: |
                  "{\"role\": \"authorino\", \"jwt\": \"" + auth.metadata["oauth-token"].access_token + "\"}"
            cache:
              key:
                value: 'singleton'
              ttl: 3600
          "vault":
            priority: 2
            when:
            - predicate: auth.metadata.exists(p, p == "vault-login") && has(auth.metadata["vault-login"].auth ) && has(auth.metadata["vault-login"].auth.client_token)
            http:
              urlExpression: |
                "https://vault.vault.svc.cluster.local:8200/v1/secret/data/mcp-gateway/" + auth.identity.sub
              method: GET
              headers:
                "X-Vault-Token":
                  expression: auth.metadata["vault-login"].auth.client_token
        authorization:
          "found-vault-secret":
            patternMatching:
              patterns:
              - predicate: |
                  has(auth.metadata.vault.data) && has(auth.metadata.vault.data.data) && has(auth.metadata.vault.data.data.test_server2_pat) && type(auth.metadata.vault.data.data.test_server2_pat) == string
        response:
          success:
            headers:
              "Authorization":
                plain:
                  expression: |
                    "Bearer " + auth.metadata.vault.data.data.test_server2_pat
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: authorino-oauth-client
      namespace: <kuadrant_system>
    stringData:
      client_secret: <secret_value>
    type: Opaque
    • See the "Parameter values for a single-server-scoped AuthPolicy CR" reference for information about user-replaceable values.

    • The authorino-oauth-client Secret must be applied in the namespace of your Connectivity Link deployment for the AuthPolicy CR to work. Replace <kuadrant_system> with the namespace of your Connectivity Link deployment.

  2. Apply the AuthPolicy CR by running the following command:

    $ oc apply -f <auth_policy_vault.yaml>

    Replace <auth_policy_vault.yaml> with the name of your AuthPolicy YAML.

Parameter values for a single-server-scoped AuthPolicy CR

When you create an AuthPolicy custom resource (CR) to connect an external OIDC Identity Provider (IdP) with HashiCorp Vault (Vault), you must use custom values in many fields. Use the following reference information to create your CR.

Table 6. Parameters for a single-server-scoped AuthPolicy CR
Parameter Definition

metadata.name

A unique name for this AuthPolicy CR.

metadata.namespace

The namespace of your MCP server.

spec.targetRef.name

The name of the HTTPRoute CR of your MCP server.

rules.authentication"mcp-clients".jwt.issuerUrl

The issuer URL of your OpenId Connect SSO provider. You can also use .jwksUrl instead of .issuerUrl: for authentication servers that do not implement OIDC. This is the primary authentication step that validates who the user is.

spec.metadata."oauth-token".https.url

The token endpoint of the OAuth provider used to authenticate to Vault. Typically, this URL ends with /token.

spec.rules.metadata.cache.ttl

Time in seconds that Authorino caches the specific metadata source within the rules list. The example value is 30 minutes. This value overrides the global spec.metadata.cache.ttl. When user permissions from Vault rarely change, the value is longer. When user quota or usage changes often, you can set a short time or disable it entirely for the specific rule.

spec.metadata."vault-login".https.url

The Vault JWT login endpoint. This value typically ends in /v1/auth/jwt/login. This step performs the JWT authentication against Vault.

spec.metadata.cache.ttl

Time in seconds that Authorino caches and reuses the metadata after it is returned by the external service, such as OAuth token or Vault client token. The example value is 60 minutes.

spec.metadata."vault"

These parameter values specify the secret retrieval from Vault.

spec.rules.metadata."vault-login".https.urlExpression

Your Vault service name, namespace, and port. Replace as required if TLS is disabled at the vault service, or if the other values do not match your scenario.

spec.rules.metadata."vault".https.urlExpression

Your Vault server URL and secret path. Replace the auth.identity.sub variable according to how your claims uniquely identify an MCP gateway user if needed. The Vault path in the expression must match the KV Secrets Engine version.

spec.rules.authorization."found-vault-secret"

A safety check to ensure that the Secret has the expected key before allowing the request. In this example, test_server2_pat is the expected key.

spec.rules.authorization."found-vault-secret".patternMatching.patterns.- predicate

In the example, test_server2_pat is hard-coded as the entry inside of the Vault secret that contains the user’s PAT to authenticate with the targeted MCP server. If you change the key name in Vault, you must update both the authorization predicate and the response.headers expression, or the PAT does not get injected into the header.

Test your MCP gateway and Vault integration

You can test your Model Context Protocol (MCP) gateway and HashiCorp Vault (Vault) integration by using the general steps that follow. Tailor your testing for your use case.

Prerequisites
  • You installed the OpenShift CLI (oc).

  • You are logged into an OpenShift Container Platform cluster as an administrator.

  • You set up a trust relationship between Authorino and HashiCorp Vault.

  • You created an AuthPolicy custom resource.

Procedure
  1. Store a secret in Vault by running a command specific to your environment.

    Example curl command to store a vault token
    $ curl -s -H "X-Vault-Token: $VAULT_TOKEN" -H 'Content-Type: application/json' -X POST \
      --data '{"data":{"test_server2_pat":"s3cr3t"}}' \
      <vault_service_url:port>/v1/secret/data/mcp_gateway/<sub>

    Replace <vault_service_url:port>/v1/secret/data/mcp_gateway/<sub> with your secret endpoint. Be sure to either replace <sub> with your specific sub-path or remove the trailing forward slash.

  2. Get an access token by running a command specific to your environment.

    Example access token request
    $ ACCESS_TOKEN=$(curl <issuer_url> -s -d 'grant_type=client_credentials' -d 'client_id=<mcp_client_id>' -d 'client_secret=<mcp_client_secret>' -d 'scope=openid profile groups roles' | jq -r .access_token)
    • Replace <issuer_url> with your issuer URL.

    • Replace <mcp_client_id> with your MCP client ID.

    • Replace <mcp_client_secret> with your MCP client secret.

  3. Start a session with the MCP gateway by running a command specific to your environment.

    Example curl command to start a session with the MCP gateway
    $ curl -N -s -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -X POST \
      -d '{"capabilities": {}}' \
      <\https://mcp-gateway-mcp-system.apps.onecluster.example.com/v1/mcp/initialize>

    Replace <https://mcp-gateway-mcp-system.apps.onecluster.example.com> with your OpenShift Container Platform route URL for the MCP gateway.

  4. Send a request to the MCP server route that requires fetching credentials from Vault by running a command specific to your environment.

    Example output
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "content": [
          {
            "type": "text",
            "text": "Authorization: [Bearer s3cr3t]"
          },
          …
        ],
        …
      }
    }

    The output shows that the request was successful and that the Authorization header was set with the Secret information retrieved from Vault.

Use a Vault root token

You can use a HashiCorp Vault (Vault) root token for a limited time and purpose. Using the Vault root token for the Authorino service to authenticate to Vault gives Authorino full access to read and write any secret stored in Vault.

Use the root token only in the following scenarios:

  • When you first deploy Vault on OpenShift Container Platform. The Vault Operator init command generates the initial root token.

  • When you need to bootstrap Auth. You can use a root token once to set up your Kubernetes and JWT Auth methods. After the Authorino service and your other services can log in using their own identities, the root token is no longer needed.

  • For disaster recovery. If all other admin-level accounts are locked out or the configuration is corrupted, you can generate a new root token to fix the system by using recovery keys.

Warning

Using a root token is not intended for production environments. Root tokens are much less secure than using tokens configured with policies.

Prerequisites
  • You installed the OpenShift CLI (oc).

  • You are logged into an OpenShift Container Platform cluster as an administrator.

  • You are in a non-production environment.

  • You have an MCP server ready to connect.

  • You set up a trust relationship between Authorino and HashiCorp Vault.

Procedure
  1. Create the Vault root-token AuthPolicy CR by using the following example:

    Example Vault root-token AuthPolicy CR
    apiVersion: kuadrant.io/v1
    kind: AuthPolicy
    metadata:
      name: <vault_root_token_policy>
      namespace: <root_token_test>
    spec:
      targetRef:
        group: gateway.networking.k8s.io
        kind: HTTPRoute
        name: <mcp_server2_route>
      rules:
        authentication:
          "mcp-clients":
            jwt:
              issuerUrl: <issuer_url_endpoint>
        metadata:
          "vault":
            http:
              urlExpression: |
                "http://vault.vault.svc.cluster.local:8200/v1/secret/data/mcp-gateway/" + auth.identity.sub
              method: GET
              credentials:
                customHeader:
                  name: X-Vault-Token
              sharedSecretRef:
                name: vault-secret
                key: root-token
        authorization:
          "found-vault-secret":
            patternMatching:
              patterns:
              - predicate: |
                  has(auth.metadata.vault.data) && has(auth.metadata.vault.data.data) && has(auth.metadata.vault.data.data.test_server2_pat) && type(auth.metadata.vault.data.data.test_server2_pat) == string
        response:
          success:
            headers:
              "Authorization":
                plain:
                  expression: |
                    "Bearer " + auth.metadata.vault.data.data.test_server2_pat
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: vault-secret
      namespace: <kuadrant_system>
    stringData:
      root-token: root
    type: Opaque
    • See the "Parameter values for a single-server-scoped AuthPolicy CR" reference for information about the user-replaceable values used in this root-token policy.

    • Replace <kuadrant_system> in the Secret with the namespace where you deployed Connectivity Link.

  2. Apply the AuthPolicy CR by running the following command:

    $ oc apply -f <vault_root_auth_policy.yaml>

    Replace <vault_root_auth_policy.yaml> with the name of your root-level Vault AuthPolicy CR.