Skip to content

Latest commit

 

History

History
165 lines (135 loc) · 9.82 KB

datadog.md

File metadata and controls

165 lines (135 loc) · 9.82 KB

datadog

One connection is one Datadog organization. toolpass authenticates with an API key and a scoped application key, finds the user by email, reads the permissions carried by the user's roles, and for a monitor, dashboard, SLO or notebook reads the asset's restriction policy and its legacy restricted_roles and author fields. A user may change such an asset only when a role carries the write permission and the asset's restrictions, if any, name the user, one of the user's roles or teams, or the whole org. Nothing is written.

Credential

  1. An API key of the organization (api_key).
  2. An application key (credential) created for a dedicated service account or user and scoped to user_access_read, teams_read and the read scope of each asset type asked about: monitors_read, dashboards_read, slos_read, notebooks_read. Reading a restriction policy needs no named scope. An unscoped application key carries every permission of its creator, which is far more than toolpass needs.

toolpass probe validates the API key, lists one user (which proves user_access_read) and reminds you of the other scopes.

Connection

  - id: datadog-acme
    integration: datadog
    api_key: env:DATADOG_API_KEY
    credential: env:DATADOG_APP_KEY
    # url: https://api.datadoghq.eu     # default https://api.datadoghq.com; US3 api.us3.datadoghq.com, US5 api.us5.datadoghq.com, AP1 api.ap1.datadoghq.com
Key Meaning
url the site's API URL
api_key sent as DD-API-KEY, env: or file:
credential the application key, sent as DD-APPLICATION-KEY, env: or file:

Identity

GET /api/v2/users?filter=<email>&filter[status]=Active,Pending,Disabled, every page. The filter is a substring match on name, handle and email, so only the record whose email equals the address (ignoring case) is the user; none is user_not_found, two are user_ambiguous. A disabled user, or one whose status is still Pending, is denied every action. The identity carries the user's handle and the ids of the user's roles; each role's permissions come from GET /api/v2/roles/{id}/permissions, cached for five minutes. Groups sent by the caller are ignored.

Resources

Resource Meaning
monitor:<id> a monitor
dashboard:<id> a dashboard (abc-def-ghi)
slo:<id> a service level objective
notebook:<id> a notebook
org the organization, for permissions that are not about one asset

Actions

Action Permission Restriction relation Resource
monitor.edit monitors_write editor monitor
monitor.mute monitors_downtime editor (muting counts as editing a restricted monitor) monitor
monitor.read monitors_read viewer monitor
dashboard.edit dashboards_write editor dashboard
dashboard.read dashboards_read viewer dashboard
slo.edit slos_write editor slo
notebook.edit notebooks_write editor notebook
logs.read logs_read_data org
users.manage user_access_manage org
apikeys.manage api_keys_write org
raw:<permission> that permission on an asset: editor when it is one of the type's write permissions (monitors_write, monitors_downtime, dashboards_write, slos_write, notebooks_write), viewer otherwise any

Evaluation

  1. The permission: one of the user's roles must list it. On the org resource that is the answer.
  2. The asset is read (GET /api/v1/monitor/{id}, /api/v1/dashboard/{id}, /api/v1/slo/{id}, /api/v1/notebooks/{id}); a 404 is resource_not_visible whatever the permission.
  3. The restriction policy: GET /api/v2/restriction_policy/{type}:{id}. When it has bindings, the user must appear in a binding whose relation is the one needed or higher (viewer < editor; type-specific relations above editor count as editor) as user:<id>, role:<one of the user's roles>, team:<a team the user is a member of> (GET /api/v2/team/{id}/memberships, filtered by the user's email and compared by id) or org:<any>. A policy with no viewer binding restricts editing only: reads fall back to the permission.
  4. Without a policy, the legacy fields: a monitor's or dashboard's restricted_roles must include one of the user's roles for an edit, except that a dashboard's author (author_handle) may always edit. Reads are not restricted by restricted_roles.

Decisions

Situation toolpass answers
a role carries the permission and the asset has no restriction allow
a role carries the permission and a binding at the relation names the user, a role, a team the user is on, or the org allow, naming the principal kind
a role carries the permission and restricted_roles includes a role of the user, or the user authored the dashboard allow
no role carries the permission deny
the asset's restriction policy or restricted_roles name none of the user's principals deny
disabled user, or a user whose invitation is still pending deny
no user with the email deny (user_not_found)
several users unknown (user_ambiguous)
user record without disabled unknown (unsupported)
the asset, a role of the user, or a team named by the policy answers 404 unknown (resource_not_visible)
401, 403 (Datadog answers 403 for an invalid key as well as for a missing scope) unknown (credential_rejected)
429, 5xx, timeout unknown (upstream_rate_limited / upstream_error / upstream_timeout)

Error bodies are never copied into a decision text.

Routes

The secure-tools toolkit maps a tool's requests to the API (api.<site>, the site's web host and its logs intake host, from url) to these actions; toolpass routes datadog prints the table.

Requests Action and resource
a monitor, dashboard, SLO or notebook by id (/api/v1/monitor/{id}, ...) its read or edit action on the asset (monitor.edit on monitor:123, raw:slos_read on slo:<id>), so restriction policies apply
creating or listing them the permission on org (raw:monitors_write, raw:dashboards_read)
a restriction policy of one of those assets the asset's edit action (changes) or read action
a downtime naming a monitor (v1 monitor_id, v2 monitor_identifier.monitor_id) monitor.mute on the monitor
a downtime by scope or monitor tags, or changing or cancelling one by id; the downtimes matching a monitor (GET /api/v2/monitor/{id}/downtime_matches) raw:monitors_downtime on org
searching or aggregating logs; an archive search's results logs.read
reading API keys or any user's application keys, which returns the keys raw:api_keys_read or raw:org_app_keys_read on org
searching spans; reading a trace (/api/v2/trace/{id}, /api/v2/pruned_trace/{id}) raw:apm_read on org
users, roles, API and application keys, service accounts, logs and metrics configuration, metric and event queries the one permission the API description names for the operation, as raw:<permission> on org (users.manage and apikeys.manage where they are that permission)

Refused: a body that names a second asset (an id or data.id other than the path's, more than one dashboard or SLO in a bulk request); creating a user or service account with roles (assign them afterwards with POST /api/v2/roles/{id}/users); writes to assets whose restriction policies the integration does not read (synthetic tests, security rules, workflows, on-call, logs pipelines, ...); writes the API description gives no permission for, or several; sharing dashboards outside the org; the agent's own application keys and profile (/api/v2/current_user); data intake (metrics, events, logs), which needs the API key alone; and every other product's writes until a route maps them. Bodies are read as application/json only; a duplicated member or a compressed body is refused. Reads with no route are allowed and marked untrusted.

What it cannot see

  • Self-elevation: a user with user_access_manage can add themselves to any restriction policy. toolpass answers for the policy as it stands, so such a user is denied until they do.
  • Assets other than monitors, dashboards, SLOs and notebooks (synthetics tests, security rules, workflows, ...), which have restriction policies of their own but no action here yet; use raw:<permission> on org for their permissions alone.
  • Log restriction queries, index-level log permissions, and RBAC on APM services.
  • Team membership through team-links or provisioning that the memberships endpoint does not list.

Unverified

Marked # UNVERIFIED: in the code:

  • Whether a monitor's creator keeps edit rights under restricted_roles the way a dashboard's author does. Datadog documents roles only for monitors, so the creator is not exempted.
  • That a restriction policy without a viewer binding leaves viewing to the permission. The UI writes an explicit viewer binding for the org whenever it restricts an asset, which is why toolpass reads a policy without one as restricting editing only.

Assumed from Datadog's documentation rather than tested live: that the write permission is still required when a restriction policy grants editor ("the limitations are applied both in the UI and API"), and that GET /api/v2/restriction_policy/{id} answers 200 with empty bindings, not 404, for an asset without a policy (a 404 is treated the same).

Test

Unit tests run against a fake that serves users (paged), role permissions, monitors, dashboards, SLOs, notebooks, restriction policies and team memberships (paged), validating every request against Datadog's v1 and v2 OpenAPI descriptions (datadog-v1 and datadog-v2 in test/specs/fetch.sh). There is no live test; after configuring, run toolpass probe and one check for a user you know is allowed.