Add endpoint locks and split endpoints.deploy into three grants
Endpoint locks -------------- Adds EndpointLock so an admin can hold an endpoint against automated and operator action, with migration 0018 and enforcement in the enqueue path rather than only in the UI, so a lock cannot be bypassed by calling the API directly. Split endpoints.deploy ---------------------- endpoints.deploy bundled three actions of very different risk behind a single name: scheduledtask_run -> starts a task already registered and allowlisted update_app -> runs a pinned installer run_script -> arbitrary code as SYSTEM unless the descriptor pins a user context Because they shared one grant there was no way to let an operator push an application update without also handing them arbitrary SYSTEM code execution on every endpoint they could reach. The permission name described the lightest of the three while silently carrying the heaviest. commands.ELEVATED_ACTION_PERMISSIONS now maps each action to its own permission (endpoints.corptools, endpoints.updateapp, endpoints.runscript). The enqueue route reads that table, so no route logic changed. Migration 7d42 preserves existing access exactly: anyone holding endpoints.deploy receives all three replacements.
This commit is contained in:
@@ -182,32 +182,28 @@ class EndpointsPlugin(BasePlugin):
|
||||
# remote reboot is a strictly heavier grant than a routine toggle.
|
||||
('endpoints.restart',
|
||||
'Restart endpoint computers (destructive)', 'endpoints'),
|
||||
# Elevated grant for software delivery (run_script, update_app) and
|
||||
# for starting approved scheduled tasks (scheduledtask_run).
|
||||
# Required IN ADDITION to endpoints.command by the enqueue route, so
|
||||
# running a staged script or pushing an installer is a strictly
|
||||
# heavier grant than a routine service/process toggle.
|
||||
# Elevated grants for the actions gated by SCOPE, never by
|
||||
# Configuration Policy. Each is required IN ADDITION to
|
||||
# endpoints.command by the enqueue route.
|
||||
#
|
||||
# NOTE FOR THE ROLES WORK - this wire is pre-built for you.
|
||||
# scheduledtask_run was deliberately mapped onto this EXISTING grant
|
||||
# rather than minting a new permission, so nothing needs migrating.
|
||||
# Be aware that WIDENS what endpoints.deploy means: it used to cover
|
||||
# run_script + update_app, and now also covers starting any task on
|
||||
# the agent's approved-task allowlist. Splitting it out is a one-line
|
||||
# change (add endpoints.corptools here and remap it in
|
||||
# commands.ELEVATED_ACTION_PERMISSIONS) - the enqueue route reads
|
||||
# that table, so no other code changes.
|
||||
#
|
||||
# If you do split it, note the three are NOT equal risk:
|
||||
# scheduledtask_run only starts something already registered on the
|
||||
# endpoint and is allowlisted agent-side; update_app runs a pinned
|
||||
# installer; run_script is arbitrary code that runs as SYSTEM unless
|
||||
# its descriptor pins the user context. run_script is the reason this
|
||||
# grant's description has to stay blunt.
|
||||
('endpoints.deploy',
|
||||
'Run staged scripts, start approved scheduled tasks, and push '
|
||||
'application installers to endpoints (scripts run as SYSTEM; '
|
||||
'grant only to fully trusted operators)', 'endpoints'),
|
||||
# These were one permission (endpoints.deploy) covering run_script,
|
||||
# update_app and scheduledtask_run. That bundle could not express
|
||||
# "may push an application update" without also granting arbitrary
|
||||
# SYSTEM code execution, so it is split. The three are NOT equal
|
||||
# risk and their descriptions must not pretend otherwise.
|
||||
('endpoints.corptools',
|
||||
'Start approved scheduled tasks on endpoints (limited to tasks '
|
||||
'already registered and allowlisted on the agent)', 'endpoints'),
|
||||
('endpoints.updateapp',
|
||||
'Push application installers to endpoints (runs a pinned '
|
||||
'installer as SYSTEM)', 'endpoints'),
|
||||
# Blunt on purpose. This is not "system-level changes" - it is
|
||||
# arbitrary code execution as SYSTEM unless the script descriptor
|
||||
# pins a user context. Whoever grants it must see that here.
|
||||
('endpoints.runscript',
|
||||
'Run staged scripts on endpoints (ARBITRARY CODE, executes as '
|
||||
'SYSTEM unless the script pins a user context; grant only to '
|
||||
'fully trusted operators)', 'endpoints'),
|
||||
# Packaging the agent is a heavier grant than deploying to one, and
|
||||
# it is not the same job either: the generated ZIP carries a live
|
||||
# collector key stamped into its install script, so whoever holds
|
||||
@@ -334,19 +330,6 @@ class EndpointsPlugin(BasePlugin):
|
||||
'permission': 'endpoints.fleet',
|
||||
'section': 'assets',
|
||||
},
|
||||
# TEMPORARY / DEV ONLY. A manual bench to exercise the update_app and
|
||||
# run_script delivery path while Cameron builds the production UI.
|
||||
# Gated on endpoints.deploy so only delivery operators see it. Remove
|
||||
# this entry (and the DeliveryTestBench view/route) once the real
|
||||
# delivery UI ships.
|
||||
{
|
||||
'name': 'Delivery test bench',
|
||||
'icon': 'send',
|
||||
'route': '/endpoints/delivery-test',
|
||||
'position': 17,
|
||||
'permission': 'endpoints.deploy',
|
||||
'section': 'assets',
|
||||
},
|
||||
]
|
||||
|
||||
def init_app(self, app: Flask, db_instance) -> None:
|
||||
|
||||
Reference in New Issue
Block a user