> ## Documentation Index
> Fetch the complete documentation index at: https://docs.infraudit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Managing Secrets and Credential Encryption in InfraAudit

> How InfraAudit encrypts cloud credentials at rest with AES-256-GCM, how to generate and rotate the encryption key, and production secrets best practices.

InfraAudit stores cloud provider credentials (AWS access keys, GCP service account JSON, Azure service principal secrets, kubeconfig files) in the database. Before writing any credential to disk, the API encrypts it using AES-256-GCM. This page explains how that encryption works, how to manage the key, and what to do when you need to rotate secrets.

## How credential encryption works

When you connect a cloud account, InfraAudit encrypts the credentials before inserting them into the database:

* **Algorithm:** AES-256-GCM
* **Key source:** `ENCRYPTION_KEY` environment variable
* **Key format:** 32-byte value, hex-encoded (64 hex characters)
* **Nonce:** A unique random nonce is generated for each encrypted value and prepended to the ciphertext

The `ENCRYPTION_KEY` is never written to the database. If someone gains access to the database but not the key, the stored credentials are unreadable.

### Generate an encryption key

```bash theme={null}
openssl rand -hex 32
# Example output: a3f1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5e6f7a8b9c0d1e2
```

Set the output as `ENCRYPTION_KEY` in your `.env` or Kubernetes Secret.

<Warning>
  If you lose `ENCRYPTION_KEY`, all stored cloud credentials become permanently unreadable. You will need to disconnect and reconnect every provider. Back up the key in a secrets manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault) before going to production.
</Warning>

## JWT authentication

InfraAudit validates JWTs using the `SUPABASE_JWT_SECRET` from your Supabase project. Requests with expired or invalid tokens return `401 Unauthorized`.

The `SUPABASE_SERVICE_ROLE_KEY` is used server-side for privileged Supabase operations such as looking up users by ID. This key bypasses Supabase row-level security — keep it confidential and never expose it to the browser or public APIs.

## Managing secrets in production

Choose the approach that fits your deployment:

<Tabs>
  <Tab title="Docker Compose">
    Avoid committing secrets to the `.env` file if the file is visible in CI or on a shared server. Pass secrets as environment variables directly at runtime:

    ```bash theme={null}
    SUPABASE_JWT_SECRET=your-secret \
    ENCRYPTION_KEY=your-key \
    docker compose up -d
    ```

    Alternatively, use Docker Swarm secrets for more structured secret management.
  </Tab>

  <Tab title="Kubernetes">
    Store secrets in a Kubernetes `Secret` and inject them as environment variables:

    ```yaml theme={null}
    apiVersion: v1
    kind: Secret
    metadata:
      name: infraudit-secrets
      namespace: infraudit
    type: Opaque
    stringData:
      SUPABASE_JWT_SECRET: "your-secret"
      ENCRYPTION_KEY: "your-key"
    ```

    Reference the secret in your deployment:

    ```yaml theme={null}
    envFrom:
      - secretRef:
          name: infraudit-secrets
    ```

    Do not commit `secret.yaml` to source control.
  </Tab>

  <Tab title="External secrets manager">
    Use the [External Secrets Operator](https://external-secrets.io/) to sync secrets from AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault into Kubernetes Secrets automatically. This keeps the source of truth in your secrets manager while still making secrets available to pods as environment variables.
  </Tab>
</Tabs>

## Rotating the encryption key

InfraAudit does not automatically re-encrypt credentials when the key changes. You must manually re-enter provider credentials after rotation.

<Steps>
  <Step title="Export your provider list">
    Note all connected providers and their credential details. You'll need to re-enter them after the rotation.
  </Step>

  <Step title="Stop the API">
    ```bash theme={null}
    docker compose stop api
    ```

    Or for Kubernetes, scale the deployment to zero:

    ```bash theme={null}
    kubectl scale deployment infraudit-api --replicas=0 -n infraudit
    ```
  </Step>

  <Step title="Update the encryption key">
    Generate a new key and update it in your secrets store or `.env` file:

    ```bash theme={null}
    openssl rand -hex 32
    ```
  </Step>

  <Step title="Restart the API">
    ```bash theme={null}
    docker compose start api
    ```

    Or restore the Kubernetes replica count:

    ```bash theme={null}
    kubectl scale deployment infraudit-api --replicas=1 -n infraudit
    ```
  </Step>

  <Step title="Reconnect all providers">
    In the InfraAudit UI, disconnect each cloud provider and reconnect it with fresh credentials. The new credentials will be encrypted with the new key.
  </Step>
</Steps>

## Rotating the Supabase JWT secret

Rotating the JWT secret in Supabase invalidates all active user sessions immediately. Update your deployment at the same time to avoid downtime.

<Steps>
  <Step title="Rotate the secret in Supabase">
    In your Supabase project, go to **Settings → API → JWT Settings** and click **Rotate JWT Secret**. Copy the new value.
  </Step>

  <Step title="Update your deployment">
    Update `SUPABASE_JWT_SECRET` in your `.env` file or Kubernetes Secret with the new value.
  </Step>

  <Step title="Restart the API">
    ```bash theme={null}
    docker compose restart api
    ```

    The API will start validating tokens against the new secret immediately.
  </Step>
</Steps>

<Note>
  Users will need to log in again after a JWT secret rotation. Their data is unaffected.
</Note>

## Production security checklist

Review these items before going live:

* [ ] `ENCRYPTION_KEY` was generated with `openssl rand -hex 32` — not a placeholder or default value
* [ ] `DB_PASSWORD` is unique and not the default `infraudit123`
* [ ] `SUPABASE_SERVICE_ROLE_KEY` is not in version control
* [ ] `ENVIRONMENT=production` is set (disables Swagger UI and debug endpoints)
* [ ] `DB_SSLMODE=require` is set if the database is not on localhost
* [ ] `ALLOWED_ORIGINS` is set to your frontend URL, not `*`
* [ ] `/metrics` endpoint is protected with `METRICS_AUTH_TOKEN` if internet-accessible
* [ ] `ENCRYPTION_KEY` is backed up in a secrets manager
