Skip to content

How-to guides

Securing a deployment

Use this guide to secure an Exofind deployment by bootstrapping an initial administrative key and creating scoped keys for your services. Exofind nodes check credentials by default.

Before you begin, make sure that you have:

  • An Exofind deployment with nodes running in keys mode (the default).
  • A tool such as OpenSSL to generate random strings.
  • Network access to an Exofind node.

To create the first administrative key, provide a root key to one node:

  1. Generate a root key secret:

    Terminal window
    openssl rand -base64 32 # keep this somewhere a person can reach it

    Store this value securely. The root key has full permissions, is not stored anywhere, and ensures that a deployment always has an entry point.

  2. Set the root key on one node using the EXOFIND_AUTH_ROOT_KEY environment variable:

    Terminal window
    EXOFIND_AUTH_ROOT_KEY=<that value> ...

    Prefer setting a hashed key so the plaintext value does not appear in an environment variable:

    Terminal window
    EXOFIND_AUTH_ROOT_KEY=sha256:$(printf %s "<that value>" | shasum -a 256 | cut -d' ' -f1)
  3. Create the administrative key that manages authentication from then on:

    POST /v1alpha1/admin/keys
    Authorization: Bearer <root key>
    Content-Type: application/json
    {
    "description": "deployment pipeline",
    "grants": [{ "role": "admin", "indexes": ["*"] }]
    }

    The response returns the credential once. Store it in your continuous integration (CI) secrets store; you cannot retrieve it again.

Three identities cover almost every deployment. Because they have different lifecycles, create separate keys for each component instead of sharing one key:

  1. Create an administrative key for definition management. Components that apply definitions, such as CI or deploy jobs, require the admin role over the indexes they manage:

    { "description": "ci", "grants": [{ "role": "admin", "indexes": ["*"] }] }

    Definitions live in version control, so assign this key to a pipeline rather than an individual.

  2. Create a writer key for loading documents. Components that load documents require the writer role:

    { "description": "product feed", "grants": [{ "role": "writer", "indexes": ["products"] }] }

    A writer cannot change definitions, preventing a runaway loader from reshaping a schema.

  3. Create a reader key for search queries. Components that serve searches, such as your application backend, require the reader role scoped to the indexes they query:

    { "description": "web backend", "grants": [{ "role": "reader", "indexes": ["products", "articles"] }] }

    This key has the widest deployment surface and the least privilege.

Scope every key with index patterns. A pattern is a name or a prefix followed by *. For example, use a prefix pattern for per-tenant scoping:

{ "grants": [{ "role": "writer", "indexes": ["tenant-42-*"] }] }

Name indexes exactly where possible. A key granted products accesses the index and no generations of it, following the index across rollouts without being able to address or list the generations it moves between. Add products@* only to keys that perform rollouts. For more details, see patterns and generations.

To confirm your deployment security setup:

  • Verify that your components can authenticate and perform their allowed actions with their assigned keys.
  • Once a stored key holds keys.write, verify that additional nodes start without a root key configured. A node running in keys mode refuses to start only when it cannot find a root key or a stored key with keys.write.

To replace the credential of a key while keeping everything the key allows, rotate it:

POST /v1alpha1/admin/keys/{id}/actions/rotate

The response carries the new credential. The key ID, description, grants, and expiry are all kept, so nothing that records the key ID has to change. Update the service that uses the key with the new credential.

The previous credential keeps working on other nodes until their next storage read, within EXOFIND_AUTH_REFRESH_INTERVAL (10s by default). Rotate on a schedule, and after a credential has been exposed.

What a key allows cannot be changed. To grant a service something else:

  1. Create the new key with the required grants.

  2. Update the service that uses the key and confirm that it works.

  3. Revoke the old key:

    DELETE /v1alpha1/admin/keys/{id}

This order ensures that both keys work momentarily during the transition, preventing downtime.

Revocation takes effect immediately on the node that served the request, and on every other node within its EXOFIND_AUTH_REFRESH_INTERVAL. A key leaked into a public repository stops working in seconds.

A node refuses to revoke two keys: the last key granted keys.write when the node has no root key, and the key named by EXOFIND_AUTH_ANONYMOUS_KEY. Create a replacement, or point the configuration elsewhere, and send the request again. For both, see keys that cannot be revoked.

Managing keys does not require reaching a specific node, so both procedures work while no candidate is up to take writes.

To make short-lived credentials expire automatically, set expiresAt when creating the key. Use this for temporary access, such as for a contractor or a one-off migration.

Put your own backend in front of search requests. A key reaches every index its patterns cover and cannot be narrowed per end user, so a page that holds one hands every visitor that reach - see Trust model.

The exception is a page that you permit anyone to search in full, as described in a demo node.

Setting EXOFIND_AUTH_MODE=none answers every request as though it were allowed everything. Dev mode runs this way. In any environment reachable by more than your own laptop, this setting allows anyone who can reach the port to delete every index. For both modes, see Modes.

Exofind is built by Level Four AB and is available under the Apache License 2.0.