Repository navigation
Docs(Security): document the --security anonymous option - #778
Open
matthewmcneely wants to merge 2 commits into
Open
matthewmcneely wants to merge 2 commits into
matthewmcneely wants to merge 2 commits into
Conversation
Adds an Anonymous Access page for the new --security "anonymous=full|data|none" option (dgraph-io/dgraph#9844), and updates the superflag reference, the Admin Endpoint Security page, and the alpha and zero CLI references to match. Unreleased: everything here is in the "next" docs/ tree only, which is not published (includeCurrentVersion: false) until a version cut copies it forward. The page also carries an "Unreleased feature" admonition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
4 tasks done
The page said schema changes were unaffected by every posture, and did not mention drops at all. That matched the code at the time and was wrong: review on dgraph-io/dgraph#9844 found that only drop_all reached the posture, so under anonymous=data an anonymous caller with an open whitelist could still change the schema, drop a predicate, or empty a namespace with drop_op DATA. The fix gates every network Alter under data and none. This moves schema changes and drops into the administrative table, adds a caution that drop_op DATA empties a namespace, tells operators that an application changing its own schema now needs the token, and syncs the alpha help text with the new binary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Pushed a correction. The first version said schema changes were unaffected by every posture and did not mention drops. That matched the code at the time but described a bug: review on dgraph-io/dgraph#9844 found that only dgraph-io/dgraph@79de59da6 now gates every network Alter under
Re-verified: Vale reports 0 errors, the build with the current docs compiled in has no broken links or anchors, and the help blocks match the binary byte for byte. This was all in the unpublished |
matthewmcneely
added a commit
to dgraph-io/dgraph
that referenced
this pull request
Oct 6, 2026
…to full (#9844) **Description** Adds a `--security "anonymous=full|data|none"` superflag key that says directly what a caller with no verified identity may do. The default is `full`, which is the behavior of every earlier release, so nothing about an existing cluster changes. **Why** Three controls gate Alpha's privileged operations: the `--security` whitelist, the `--security` token, and ACL. Each one passes when its own feature is unconfigured, so the protection an operator gets is whatever they configured rather than the union of the three. The gap that leaves is specific: the whitelist answers *where a request came from* and carries no credential in it, so widening the range is by itself enough to make administrative operations reachable anonymously from anywhere inside it. `whitelist=0.0.0.0/0` is a common setting, including in our own `dgraph/standalone` quickstart image, and in that posture there is nothing left to check. | Value | What an anonymous caller may do | | --- | --- | | `full` (default) | Whatever the whitelist, token, and ACL settings decide. Unchanged from every earlier release. | | `data` | Query, mutate, commit, log in, read `/health`. Every `Capability` check and every `Alter` (schema changes and drops) denies regardless of the whitelist. | | `none` | Log in, `CheckVersion`, and the health and readiness endpoints. | "Anonymous" means the request produced no `x.Principal`: it presented no credential, or the one it presented did not verify. **How it is enforced** The posture is an `AccessController` that **wraps** whichever policy is installed rather than replacing it. "Has this caller been identified at all" is prior to and independent of "what may this identity do", and answering the first one inside the capability rules would mean every policy, built in or installed, reimplementing the same check and keeping it consistent. It is installed after `ConfigureIdentity()` so a deployment policy gets wrapped instead of silently discarding the floor, and under `anonymous=full` it installs nothing at all. Two operations don't go through a capability check, so they get explicit gates of their own: - **Alter.** Only `drop_all` reaches a `Capability`, so `RequireIdentifiedAdmin` gates every network `Alter` under `data` and `none`: schema changes, `drop_attr`, and every `drop_op`. Without it, an anonymous caller with an open whitelist could still empty a namespace with `drop_op: DATA`. It runs after the `NoAuthorize` short-circuit, so `AlterNoAuth` is unaffected. - **The data plane under `none`.** `RequireIdentifiedCaller` gates `doQuery` (which covers `Query`, `RunDQL`, `/query`, `/mutate`, `/graphql`, and subscription polls) and `CommitOrAbort`. `--security "token=..."` now mints a `Principal` with `x.MethodPreshared` instead of answering a yes or no. That is what makes a closed posture usable without standing up full ACL: before this, a token-bearing caller was indistinguishable from an anonymous one. Two details worth a look: - ACL is tried first, so a request carrying both resolves to the user rather than the service, which also keeps `userDataFromPrincipal`'s fast path (it requires `Method == MethodACL`). - `Principal.Groups` is left empty on purpose. `x.IsSuperAdmin` reads it by name, so a `guardians` entry would turn a shared secret into cluster authority without passing through `authorizeClusterAdmin` and its ACL-on exclusion. - `Subject` is `audit.PoorManAuth`, because audit prefers the resolved `Principal` over re-parsing the credential and would otherwise change what every token-bearing request logs. Pinned by `TestPresharedSubjectMatchesAudit`. Zero honors the same key, since `--security` shares one defaults string. Its admin HTTP handler stops letting a whitelisted source IP stand in for the token once the posture is closed, and enforces the non-strict routes (`/state`, `/assign`) that are otherwise open until something is configured. Two startup warnings, both pure functions with a table test: a widened whitelist with no credential, and a closed posture with no credential, where every capability check denies including the operator's own and the cluster cannot be administered at all. **One fix that came out of running it** `/query`, `/mutate`, `/commit`, `/state` and `/health?all` attached only the access JWT, never the auth token. Unit tests passed; a live cluster with `anonymous=data` and a token configured then refused a caller who was presenting that token on `/state`, because the header never became `auth-token` metadata. All five now use `x.AttachRequestIdentity`. `TestHTTPEdgeResolvesIdentityThroughOneHelper` pins it: handlers in `dgraph/cmd/alpha` either go through `x.AttachRequestIdentity` or appear in `identityExceptions` with a reason, and a stale exception fails the test too. `loginHandler` is the one exception, since login must not depend on a credential it is the means of issuing. **Testing** Unit tests across `x`, `edgraph`, `dgraph/cmd/alpha`, and `dgraph/cmd/zero`. The ones that carry the argument: - `TestBreakGlassIsNotAnIdentity`: with ACL off and no token, break-glass grants cluster admin to any whitelisted source IP. Under `anonymous=data` the same context is refused. - `TestSecurityTokenIsAnIdentityUnderClosedPosture`: the token identifies a caller, and does not bypass the whitelist while doing so. - `TestAnonymousPostureCoversEveryCapability`: the floor is capability independent, so a new `Capability` constant cannot quietly escape it. - `TestAnonymousFullIsTheZeroValue`: the v25 default is reachable by doing nothing. Also verified against a live local cluster across all three values: | Config | Result | | --- | --- | | stock `dgraph alpha`, no `--security` | byte-for-byte unchanged, no warnings logged | | `whitelist=0.0.0.0/0`, no token, `anonymous=full` | `/state` and `listBackups` answered unauthenticated, exposure warning logged | | same, `anonymous=data` | both `Unauthenticated`; queries and mutations still work | | `whitelist=0.0.0.0/0; token=...; anonymous=data` | token re-opens `/state` and `/health?all`; no warnings | | `anonymous=none` | data plane closed, `/health` still open for readiness | `go vet` output is unchanged from `main` (the same pre-existing `copylocks` findings). **Notes for review** 1. `--security` is shared with Zero and `z.SuperFlag` calls `log.Fatal` on an unknown key, so a config that sets `anonymous=` will not start a pre-v25 binary. Nothing in-tree sets it: no `docker-compose*.yml` and no `dgraphtest` default was changed. It does constrain how `TAGS=upgrade` can exercise this. 2. Under `anonymous=data`, `/state` requires a credential, because reading cluster topology is a `CapTenantAdmin` check. That is correct by the flag's definition, but `dgraphapi/cluster.go` polls `/state` for readiness, so adopting the closed posture in test infra needs a token. 3. Rollout intent is `full` in v25 with the warnings above, and flipping the default to `data` in v26. Feedback on that, and on whether the three-value split is the right cut, is the main thing I am after here. This is deliberately scoped to the flag. The related completeness work (admin GraphQL resolvers and admin HTTP routes that are registered with no middleware at all, the external-snapshot `DropData` arming requirement, and the `pb.Zero/AssignIds` proxy that discards the server interceptor) is untouched and wants its own PR against `main`. **Checklist** - [x] The PR title follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) syntax, leading with `fix:`, `feat:`, `chore:`, `ci:`, etc. - [x] Code compiles correctly and linting (via trunk) passes locally - [x] Tests added for new functionality, or regression tests for bug fixes added as applicable - [x] For public APIs, new features, etc., a PR on the [docs repo](https://github.com/dgraph-io/dgraph-docs) staged and linked here: dgraph-io/dgraph-docs#778 (unreleased; lives in the unpublished `docs/` tree). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/dgraph-io/codesmith/dgraph/pr/9844?autoLogin=true&ref=codesmith_pr_footer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1793555309&installation_model_id=8575&pr_number=9844&ref=codesmith_pr_footer&repository=dgraph-io%2Fdgraph&return_to=https%3A%2F%2Fgithub.com%2Fdgraph-io%2Fdgraph%2Fpull%2F9844&signature=d80fb377d1ad73fa97791de98226b27be51e51925255dd2ad8ab9f9730200b31"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added configurable anonymous access modes: `full` preserves existing behavior, `data` requires an identified caller for administrative actions, and `none` also requires identity for queries, mutations, and commits. * Security tokens can provide caller identity when ACL credentials do not. * In stricter modes, administrative endpoints require authentication; IP allowlisting alone does not grant access. * Startup warnings flag security configurations that may deny requests or allow access without credentials. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Documents the new
--security "anonymous=full|data|none"option from dgraph-io/dgraph#9844.What changed
docs/admin/security/anonymous-access.mdcurl/ gRPC / YAML / env-var examples, Zero behavior, startup warnings, a step-by-step migration to a closed posture, known limitations, and the mixed-version upgrade constraintsidebars.ts,docs/admin/security/index.mddocs/cli/superflags.mdanonymousrow in the--securitytable, a value table, and an example. Also notes thatwhitelistis a location check, not authenticationdocs/admin/security/admin-endpoint-security.mdanonymous=dataexample, and points the Zero section at the new optiondocs/cli/alpha.md,docs/cli/zero.md--helpreference blocks updated verbatim from a binary built from #9844; Zero gets a short section on the optionHow the claims were checked
Every behavior and example on the new page was run against a live local cluster built from #9844, not inferred from the code:
/state,/health?all,/graphql,/probe/graphql,/login, export with and without the token, and Zero's/stateand/health.dgraphmain, which rejects the key exactly as quoted.dgraph live --auth_tokencannot load data underdataornone. The loader's UID lease (AllocateIDs) goes out without the token, is refused, and the loader retries silently forever.dgraph live --credswith ACL works and is what the page recommends. The client-side fix is tracked separately in the dgraph repo.Tooling:
.github/.vale.ini: 0 errors, 0 warnings across all six changed files.docusaurus buildwithincludeCurrentVersiontemporarily switched on so thedocs/tree actually compiles (the default config skips it, so a plain build passes vacuously): exit 0, no broken links or anchors underonBrokenLinks: 'throw'. That config change is not part of this PR.Open question for review
The "Default value and future releases" section says a future major release is planned to make
datathe default. That matches the proposal in #9844 but is not decided yet. Happy to soften or drop it if we'd rather not commit to it in docs.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.