Security
Andromeda authenticates nobody. Anyone who can reach the broker's API port can call every operation, in every tenant. To restrict access, put a reverse proxy or an API gateway in front of the broker, and keep the broker itself on a network that only the proxy reaches.
- ClientsApplications, devices, other brokers
- HTTPS
- Reverse proxy or API gatewayChecks who is calling, applies per-caller rules and rate limits
- only the requests it lets through, on a private network
- AndromedaAnswers every request that reaches it
What the broker leaves to you
Putting a proxy in front
Andromeda leaves authentication to whatever sits in front of it. That can be an API gateway, a reverse proxy that checks OAuth 2.0 or OIDC tokens, API keys or client certificates, or a dataspace connector. The layout is the same every time.
- Clients reach the proxy, over TLS.
- The proxy checks who is calling, and forwards only the requests it lets through.
- The broker listens on a private interface or a container network, so nothing but the proxy reaches it.
Per-caller rules belong at the proxy too, because the broker can't tell callers apart. Who may write, who may use which tenant, and who may create subscriptions and registrations are all rules on the NGSILD-Tenant header, the method and the path.
Keep http.forwarded.trust at "none" unless the proxy overwrites the forwarded headers. When it does, prefer "proxies", with the proxy's addresses in http.forwarded.proxies, to "any". With "any", a caller who reaches the broker directly can choose the scheme and host of the URLs the broker stores. Trusting forwarded fields safely explains when to change it.
The management port, 1086 by default, serves health, readiness and metrics without any checks either. Let only your health checks and monitoring reach it, as Monitoring describes.
Client certificates
With tls.client_authentication.mode = "required", the broker accepts a connection only from a client that holds a certificate signed by tls.client_authentication.ca. That decides who can connect, not what they can do, so every client with such a certificate can call every operation. Client certificates has the settings.
Tenants
The NGSILD-Tenant header picks the tenant, and nothing checks that the caller may use it. Any caller can read and write any tenant, and the first write to a new name creates a tenant and its database, as Tenants describes. If callers mustn't see each other's data, check the header at the proxy. If you don't use tenants at all, turn them off with tenancy.mode = "disabled".
With the tenant management extension on, any caller can also list tenants and suspend them. Leave it off, or allow /ngsi-ld/v1/tenants only to operators at the proxy.
Requests the broker sends for a caller
Three features make the broker send requests to an address the caller names. Unless you turn on the checks below, nothing checks where that address points. A caller can then aim the broker at internal services, cloud metadata endpoints or other hosts on the broker's network.
| Feature | The broker sends | Settings under |
|---|---|---|
| Subscriptions | Notifications to endpoint.uri | notifications.delivery |
@context URLs | GET for each @context a caller names | jsonld.fetch |
| Context source registrations | Forwarded queries and writes to endpoint | federation.forwarding |
Each of the three has the same three settings.
How a refusal shows differs between the three.
If callers aren't fully trusted, go further.
- Limit outgoing traffic with a firewall, so the broker reaches only the database, the notification endpoints and the context sources you expect.
- Allow
POSTandPATCHto/ngsi-ld/v1/subscriptionsand/ngsi-ld/v1/csourceRegistrationsonly to trusted callers at the proxy. - Set
jsonld.fetch.mode = "disabled"and list the@contextdocuments your clients use as[[jsonld.preloaded_contexts]]tables, so nothing is fetched at all.
Credentials stored in subscriptions and registrations
A subscription's receiverInfo, its endpoint URI with an MQTT user name and password in it, and a registration's contextSourceInfo often hold credentials. Andromeda stores them as written and returns them as written, to anyone who reads the subscription or registration. Any caller can read them, so treat them as visible to everyone who can reach the API, and restrict reads at the proxy if that matters.
A registration can ask for headers of the request that triggered a forwarded request. A contextSourceInfo pair whose value is urn:ngsi-ld:request sends the triggering request's header of the same name to the source. So a registration with {"key": "Authorization", "value": "urn:ngsi-ld:request"} receives the Authorization header of every caller whose query it matches. Have the proxy remove the credentials it checked before it forwards a request, or let only trusted callers register sources.
The broker keeps credentials out of its own output. Request traces don't show the Authorization, Cookie and Proxy-Authorization headers or a response's Set-Cookie, and andromeda configuration show prints the database connections redacted.
Limits on a request
These bound what one request can cost. They protect the broker from mistakes and oversized requests, not from a determined caller, so set rate limits and connection limits at the proxy as well.
| Setting | Default | Bounds |
|---|---|---|
request_limits.timeout | 30 seconds | One request, its body included |
request_limits.max_body | 64 MiB | The body, in bytes |
request_limits.max_json_values | 4,000,000 | The body, in JSON values |
request_limits.max_header_section | 32 KiB | The header section |
federation.query.max_entities | 10,000 | Distinct entities a forwarded query reads back |
federation.query.max_source_rows | 30,000 | Entities fetched from sources, duplicates included |
federation.query.max_instances | 100,000 | Attribute instances fetched from sources |
A client that sends its body too slowly gets 408 when request_limits.timeout runs out, so a slow sender holds a connection for no longer than that. HTTP, Server and Federation in the configuration reference have every limit.
Browser applications
The default CORS policy, http.cors.mode = "permissive", lets any web page call the broker from a browser, without cookies or HTTP authentication, and CSRF protection is off. Both suit machine clients behind a proxy. For a browser application, restrict CORS to its origins and turn on CSRF protection, as Browser clients describes.
Checking it works
From a machine outside the private network, the broker's ports shouldn't answer. Both of these should time out or be refused.
curl --max-time 5 http://broker.internal:1056/ngsi-ld/v1/version
curl --max-time 5 http://broker.internal:1086/readyz
Through the proxy, a request without credentials should get the proxy's 401, not an answer from the broker.
curl -i 'https://broker.example.org/ngsi-ld/v1/entities?type=Building'
From inside the broker's container, an address you don't want it to reach should be blocked.
curl --max-time 5 http://169.254.169.254/
If it goes wrong
Where Andromeda interprets the specification
The specification defines no authentication or authorisation, and leaves both to the deployment. Values taken from the triggering request (urn:ngsi-ld:request), the receiverInfo of a subscription and the contextSourceInfo of a registration are all defined by it, and Andromeda stores and returns them as written.