# Enable security

ODD Platform has **two independent authentication surfaces**, each governed by its own configuration flag. Enabling one does not protect the other.

| Surface              | What it protects                                                            | Configuration                                                                                                                                                                                                                    |
| -------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User interface / API | Human users browsing the catalog and programmatic clients calling `/api/**` | `auth.type` (DISABLED / LOGIN\_FORM / OAUTH2 / LDAP) — see [Authentication](/configuration-and-deployment/enable-security/authentication.md) and [Authorization](/configuration-and-deployment/enable-security/authorization.md) |
| Ingestion            | Collectors and push adapters calling `/ingestion/**`                        | `auth.ingestion.filter.enabled` (default `false`) — see below                                                                                                                                                                    |

A platform with OAuth2 enabled for the UI but the ingestion filter disabled is a platform with a protected catalog UI and an open write endpoint. Operators must configure both.

## Ingestion authentication

The `/ingestion/**` namespace is whitelisted in Spring Security (`SecurityConstants.WHITELIST_PATHS`), so it never traverses the UI authentication chain regardless of `auth.type`. Instead, two dedicated `WebFilter`s protect specific ingestion paths.

| Endpoint                                                                                                                                         | Filter                        | Active when                                     | Behavior                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /ingestion/datasources`                                                                                                                    | `IngestionDataSourceFilter`   | **always** (unconditional)                      | Requires `Authorization: Bearer <token>`; looks up the collector by token; responds 401 if the token is missing or unknown                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `POST /ingestion/entities`                                                                                                                       | `IngestionDataEntitiesFilter` | only when `auth.ingestion.filter.enabled: true` | Requires `Authorization: Bearer <token>`; validates the token against the datasource's stored token (falls back to the collector's token)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| All other `/ingestion/*` paths (e.g. `/ingestion/alert/alertmanager`, `/ingestion/entities/degs/children`, `/ingestion/entities/datasets/stats`) | —                             | —                                               | **Unauthenticated under `auth.type` = `DISABLED`, `OAUTH2`, or `LDAP`** — the `/ingestion/**` glob in `SecurityConstants.WHITELIST_PATHS` carries all sibling paths through `permitAll` (under `DISABLED`, every exchange permits regardless). Under `auth.type=LOGIN_FORM`, sibling paths are instead **session-gated** by the catch-all `pathMatchers("/**").authenticated()` rule — but [LOGIN\_FORM is documented as dev-only](/configuration-and-deployment/enable-security/authentication/login-form.md), so this distinction does not change the operational guidance below. The filters above match exact path patterns; they do not cover sibling paths. |

{% hint style="danger" %}
**`auth.ingestion.filter.enabled` defaults to `false`.** With the default in place and the platform reachable on the network, any caller who can speak the ingress API can `POST /ingestion/entities` with a spec-valid `DataEntityList`. If the `data_source_oddrn` in the payload matches an existing datasource, fake entities, schemas, lineage edges, owners, and tags are upserted into the catalog and rendered to every user as authoritative metadata. The payload is spec-aligned, so this is not an exploit — it is the documented ingress flow being called by an unauthenticated party. ODDRN values follow predictable patterns (`//postgresql/host:port/databases/...`), so guessing is feasible.
{% endhint %}

Enable the ingestion filter for **any** deployment where the platform is reachable from an untrusted network — which, in practice, is any non-local-dev deployment. Collectors using `odd-collector-sdk` already attach `Authorization: Bearer <token>` on every call, so turning the flag on does not require collector-side changes as long as the collector's token is registered in the platform.

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
  ingestion:
    filter:
      enabled: true
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_INGESTION_FILTER_ENABLED=true
```

{% endtab %}
{% endtabs %}

### Ingestion paths the filter does not cover

`IngestionDataEntitiesFilter` uses an exact path matcher (`/ingestion/entities`, POST). Sibling endpoints under `/ingestion/*` — `/ingestion/alert/alertmanager`, `/ingestion/entities/degs/children`, `/ingestion/entities/datasets/stats`, and others — remain outside the ingestion filter's coverage even when `auth.ingestion.filter.enabled: true` is set; whether they fall back to UI authentication depends on `auth.type` (see the table above). The AlertManager webhook is the most operationally relevant of these: see the warning on the [Configure ODD Platform](/configuration-and-deployment/odd-platform.md) page, `Alerts → Prometheus AlertManager → Authentication` section. Apply perimeter controls (network segmentation, authenticating reverse proxy, mTLS) for any deployment where these endpoints are reachable from outside the trusted network.

A platform-side fix to broaden the ingestion filter's coverage is tracked upstream.

## Authentication and authorization

For details on the UI authentication options and the authorization model that governs what authenticated users can do, see the [Authentication](/configuration-and-deployment/enable-security/authentication.md) and [Authorization](/configuration-and-deployment/enable-security/authorization.md) sections.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/configuration-and-deployment/enable-security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
