Treetop support - #596
Open
terjekv wants to merge 35 commits into
Open
Treetop support#596terjekv wants to merge 35 commits into
terjekv wants to merge 35 commits into
Conversation
terjekv
marked this pull request as draft
November 12, 2025 10:41
Collaborator
terjekv
force-pushed
the
treetop-support
branch
from
January 8, 2026 08:16
42002d4 to
f606c57
Compare
* Add Prometheus metrics middleware and expose metrics endpoint - Implement PrometheusRequestMiddleware to track a selection of HTTP, DB, and LDAP metrics. - Create MetricsView to serve metrics at /api/meta/metrics. - Update URL routing to include metrics endpoint. - Add tests for metrics endpoint and metrics recording. - Include prometheus-client dependency in project configuration.
…ons. - Also adds documentation for the policy framework and setup.
- Introduced new Prometheus metrics for policy decisions, legacy decisions, and parity results. - Log file management ensures the parity log is truncated only once in the main process. - Updated tests to validate the new metrics and log file behavior.
terjekv
force-pushed
the
treetop-support
branch
from
February 15, 2026 01:38
9f01491 to
48b4656
Compare
- Add detailed docstrings for clarity on function purposes and side effects. - Deduplicate policy-engine authorize + metrics/error handling
terjekv
marked this pull request as ready for review
February 16, 2026 06:39
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.
Summary
This PR adds bundle-based TreeTop/Cedar authorization to MREG. The
MREG_POLICY_MODEenvironment variable explicitly selectsoff, observationalshadow, or authoritativeenforcebehavior.offshadow(default)enforceMREG_POLICY_MODEdefaults toshadow. An emptyMREG_POLICY_BASE_URLmakesshadowbehave likeoff.enforcerequires a URL during Django configuration and never falls back to legacy authorization after a TreeTop error.The integration uses
treetop-client0.0.12+,treetop-rest0.0.14, and a deterministic unsigned bundle built withtreetop-bundle0.0.5. Bundle loading belongs totreetop-rest; the client needs no bundle-specific changes.Request-path design
Authorization has to complete before endpoint processing can continue. An async HTTP client would only change how the application waits, and a queue would either allow work to proceed before authorization or still require the request to wait for a queued result. Consequently:
shadowandenforcecall TreeTop synchronously;Endpoint trees contain
PolicyLeaf,PolicyAll, andPolicyAnynodes. All leaves are sent in oneauthorizebatch, and MREG composes the ordered results locally. This models old-and-new rename targets, any eligible host IP, ownership constraints, and other dependent checks without serial HTTP calls.The authorization state is owned by the underlying Django request, not middleware or a global context. An identical repeated stack reuses its cached decision. A second different stack is rejected, increments
mreg_policy_stack_conflicts_total, returns legacy behavior inshadow, and fails closed inenforce. Large stacks remain visible throughmreg_policy_stack_size; high-count endpoints can later be consolidated into coarser endpoint-level Cedar decisions.Each application process owns a reusable synchronous client and a thread-safe closed/open/half-open circuit breaker. The client is PID-aware and is closed at process exit.
Authority boundary
MREG continues to own authentication, parsing and serializer validation, object lookup, database transactions, conflicts, and non-authorization business invariants. TreeTop owns authorization for protected endpoints only when mode is
enforce. Token creation, health, metrics, schema, and Django admin routes are explicit exemptions.Every leaf contains:
mreg/policy/contracts.pyis the dependency-free source of truth for resource types, attributes, CRUD operations, custom actions, and schema generation. Registered adapters resolve model/view data; unknown resource types must be added explicitly. Boolean and IP wire attributes are typed from the contract rather than guessed from their string values.disable_policy_parity()can suppress narrow shadow-mode comparisons in tests, but cannot bypass authoritative enforcement.Permission mapping
user_info_readHostPolicyRoleresource plus raw candidate hostname/IPMREG does not calculate TreeTop name labels. For name-based rules it sends
hostnameand, when applicable,ip.treetop-restapplies the patterns inlabels.jsonto thehostnamefield and suppliesnameLabelsto Cedar. DNS wildcard/underscore forbids and NetGroup permits consume those derived labels.The static Cedar policy also covers restricted-address assignment, ownership constraints, and administrative permits. The committed restricted-address ranges are sanitized examples and must be replaced/reviewed for the deployment before enabling
enforce.Converting database permissions
TreeTop cannot be authoritative while mutable
NetGroupRegexPermissionrows independently decide the same access. This PR therefore generates the bundle policy from a deterministic, reviewable snapshot of the existing MREG JSON APIs. It does not requiremreg-cli, and it deliberately does not add the future one-shot export endpoint yet.The generator reads and paginates:
/api/v1/labels/?ordering=name/api/v1/permissions/netgroupregex/?ordering=range,group/api/v1/hostpolicy/roles/?ordering=nameIt authenticates with
Authorization: Token, resolves integer label IDs to their names, rejects malformed or cross-origin pagination responses, and writes only the source fields needed for conversion totreetop/fixtures/policy-source.json. The token is read from the environment and never persisted. The checked-in normalized snapshot keeps generation and CI offline and deterministic; a later export endpoint can produce the same snapshot schema without changing the Cedar generator.Refresh the snapshot and generated files against the MREG instance whose policy is being migrated:
Use HTTPS outside a trusted local environment. The token needs authenticated read access to all three endpoints.
MREG_API_TIMEOUToptionally changes the 20-second per-page timeout.The converter validates every CIDR and regular expression, removes duplicate permission rows, collapses redundant ranges, and emits stable hashed policy/label IDs. It generates:
treetop/fixtures/policy-source.json: normalized source snapshot;treetop/data/labels.json: hostname patterns used bytreetop-rest;treetop/data/netgroup.cedar: NetGroup and exact host-policy-role permits;treetop/data/netgroup-conversion-report.json: counts, generated IDs, and unmatched/unused labels.Database fields map as follows:
grouprangeip.isInRange(...)or exact network comparisonregexlabels.jsonlabelsHostPolicyRolenamesLegacy permission and role labels are never runtime facts. They are used only while converting the endpoint responses to determine which exact role resources a permission previously covered.
In
enforce, the NetGroup permission API remains readable but returns HTTP 409 for POST/PUT/PATCH/DELETE, preventing a database edit from appearing to change authoritative policy. Writes retain legacy behavior inoffandshadow, which supports a staged comparison before bundle publication.The checked-in API snapshot is a sanitized example, not production policy. A deployment must refresh it, run the converter, review the snapshot, report, and Cedar diff, publish the resulting bundle, and complete a shadow observation window before enforcing.
Bundle workflow and local setup
The MREG module loads the hand-written endpoint policy, generated NetGroup policy, generated schema, and one combined
labels.json. A global module contains the global superuser policy.The build script creates and validates the archive in a temporary directory before replacing the committed artifact. CI verifies both generators, validates the unsigned archive, rebuilds it, and compares it byte-for-byte. Signed bundles are deliberately out of scope; local
treetop-restusesTREETOP_BUNDLE_SIGNATURE_POLICY=allow-unsigned.Start
treetop-restand its bundle file server with:Then observe before enforcing:
Restart MREG processes after changing mode. If MREG runs in a container, the base URL must be reachable from that container rather than referring to its own
localhost. Roll back by selectingshadoworoffand restarting. There is no TreeTop database migration to apply.Environment variables
Runtime authorization settings:
MREG_POLICY_MODEshadowoff, observationalshadow, or authoritativeenforceMREG_POLICY_PARITY_ENABLEDTrueMREG_POLICY_BASE_URLtreetop-restbase URL; required inenforceMREG_POLICY_NAMESPACEMREGMREG_POLICY_TIMEOUT_SECONDS5.0MREG_POLICY_CIRCUIT_FAILURES5MREG_POLICY_CIRCUIT_RESET_SECONDS30.0MREG_POLICY_PARITY_LOG_LEVELWARNINGMREG_POLICY_PARITY_LOG_DETAILSFalseMREG_POLICY_ROLLOUT_MIN_COMPARISONS10000MREG_POLICY_ROLLOUT_MAX_MISMATCH_RATE0.001MREG_POLICY_ROLLOUT_MAX_ERROR_RATE0.001Bundle-generation settings (used by the script, not Django at runtime):
MREG_API_BASE_URLMREG_API_TOKENMREG_API_BASE_URL; never persistedMREG_API_TIMEOUT20Failure handling and observability
shadow: record comparison/error telemetry and return the complete legacy endpoint decision.enforce: return the TreeTop decision; on timeout, transport error, invalid response count/ID/index, per-result error, circuit rejection, or stack conflict, log critically and deny.manage.py check_policy_rollout --prometheus-url URL --window 24hchecks observation volume and configured mismatch/error thresholds before authority is enabled.Scope cleanup and supporting changes
master.treetop-client>=0.0.12), Python 3.12/3.13/3.14 CI, workflow concurrency, pinned mreg-cli compatibility-test revision, and general readiness/cleanup hardening.Validation
makemigrations --check --dry-runreports no changes.treetop-bundlevalidates the manifests and unsigned archive; rebuilding is byte-for-byte reproducible.Documentation
docs/policies.mddocs/env.mddocs/parity_testing.mddocs/metrics.md