fleet/server/authz/policy.rego
Lucas Manuel Rodriguez da171d3b8d
Merge pull request from GHSA-pr2g-j78h-84cr
* Fix access control issues with users

* Fix access control issues with packs

* Fix access control issues with software

* Changes suggested by Martin

* All users can access the global schedule

* Restrict access to activities

* Add explicit test for team admin escalation vuln

* All global users should be able to read all software

* Handbook editor pass - Security - GitHub Security (#5108)

* Update security.md

All edits are recorded by line:

395 replaced “open-source” with “open source”
411 replaced “open-source” with “open source”
439 added “the” before “comment”; replaced “repositories,” with “repositories”
445 deleted “being” before “located”
458 added “and” after “PR”
489 replaced “on” with “in”
493 replaced “open-source” with “open source”; Replaced “privileges,” with “privileges”

* Update security.md

line 479

* Update security.md

added (static analysis tools used to identify problems in code) to line 479

* Fix UI

* Fix UI

* revert api v1 to latest in documentation (#5149)

* revert api v1 to latest in documentation

* Update fleetctl doc page

Co-authored-by: Noah Talerman <noahtal@umich.edu>

* Add team admin team policy automation; fix e2e

* Update to company page of the handbook (#5164)

Updated "Why do we use a wireframe-first approach?" section of company.md

* removed extra data on smaller screens (#5154)

* Update for team automations; e2e

* Jira Integration: Cypress e2e tests only (#5055)

* Update company.md (#5170)

This is to update the formatting under "empathy" and to fix the spelling of "help text."
This was done as per @mikermcneil .
This is related to #https://github.com/fleetdm/fleet/pull/4941 and https://github.com/fleetdm/fleet/issues/4902

* fix update updated_at for aggregated_stats (#5112)

Update the updated_at column when using ON DUPLICATE UPDATE so that
the counts_updated_at is up to date

* basic sql formatting in code ie whitespace around operators

* Fix e2e test

* Fix tests in server/authz

Co-authored-by: gillespi314 <73313222+gillespi314@users.noreply.github.com>
Co-authored-by: Desmi-Dizney <99777687+Desmi-Dizney@users.noreply.github.com>
Co-authored-by: Michal Nicpon <39177923+michalnicp@users.noreply.github.com>
Co-authored-by: Noah Talerman <noahtal@umich.edu>
Co-authored-by: Mike Thomas <78363703+mike-j-thomas@users.noreply.github.com>
Co-authored-by: Martavis Parker <47053705+martavis@users.noreply.github.com>
Co-authored-by: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
2022-04-18 10:27:30 -07:00

512 lines
11 KiB
Rego

# See OPA documentation for specification of this policy language:
# https://www.openpolicyagent.org/docs/latest/policy-language/
package authz
import input.action
import input.object
import input.subject
# Constants
# Actions
read := "read"
list := "list"
write := "write"
# User specific actions
write_role := "write_role"
change_password := "change_password"
# Query specific actions
run := "run"
run_new := "run_new"
# Roles
admin := "admin"
maintainer := "maintainer"
observer := "observer"
# Default deny
default allow = false
# team_role gets the role that the subject has for the team, returning undefined
# if the user has no explicit role for that team.
team_role(subject, team_id) = role {
subject_team := subject.teams[_]
subject_team.id == team_id
role := subject_team.role
}
##
# Global config
##
# Any logged in user can read global config
allow {
object.type == "app_config"
not is_null(subject)
action == read
}
# Admin can write global config
allow {
object.type == "app_config"
subject.global_role == admin
action == write
}
##
# Teams
##
# Any logged in user can read teams (service must filter appropriately based on
# access) if the overall object is specified
allow {
object.type == "team"
object.id == 0
not is_null(subject)
action == read
}
# For specific teams, only members can read
allow {
object.type == "team"
object.id != 0
team_role(subject, object.id) == [admin,maintainer][_]
action == read
}
# or global admins or global maintainers
allow {
object.type == "team"
object.id != 0
subject.global_role == [admin, maintainer][_]
action == read
}
# Admin can write teams
allow {
object.type == "team"
subject.global_role == admin
action == write
}
# Team admin can write teams
allow {
object.type == "team"
team_role(subject, object.id) == admin
action == write
}
##
# Users
#
# NOTE: More rules apply to users but they are implemented in Go code.
# Our end goal is to move all the authorization logic here.
##
# Any user can read and write self and change their own password.
allow {
object.type == "user"
object.id == subject.id
object.id != 0
action == [read, write, change_password][_]
}
# Global admins can perform all operations on all users.
allow {
object.type == "user"
subject.global_role == admin
action == [read, write, write_role, change_password][_]
}
# Team admins can perform all operations on the team users (except changing their password).
allow {
object.type == "user"
team_role(subject, object.teams[_].id) == admin
action == [read, write, write_role][_]
}
##
# Invites
##
# Global admins may read/write invites
allow {
object.type == "invite"
subject.global_role == admin
action == [read,write][_]
}
##
# Activities
##
# Only global users can read activities
allow {
not is_null(subject.global_role)
object.type == "activity"
action == read
}
##
# Sessions
##
# Any user can read/write own session
allow {
object.type == "session"
object.user_id == subject.id
action == [read, write][_]
}
# Admins can read/write all user sessions
allow {
object.type == "session"
subject.global_role == admin
action == [read, write][_]
}
##
# Enroll Secrets
##
# Global admins and maintainers can read/write all
allow {
object.type == "enroll_secret"
subject.global_role == [admin, maintainer][_]
action == [read, write][_]
}
# Team admins and maintainers can read/write for appropriate teams
allow {
object.type == "enroll_secret"
team_role(subject, object.team_id) == [admin, maintainer][_]
action == [read, write][_]
}
# (Observers are not granted read for enroll secrets)
##
# Hosts
##
# Allow anyone to list (must be filtered appropriately by the service).
allow {
object.type == "host"
not is_null(subject)
action == list
}
# Allow read/write for global admin/maintainer
allow {
object.type == "host"
subject.global_role = admin
action == [read, write][_]
}
allow {
object.type == "host"
subject.global_role = maintainer
action == [read, write][_]
}
# Allow read for global observer
allow {
object.type == "host"
subject.global_role = observer
action == read
}
# Allow read for matching team admin/maintainer/observer
allow {
object.type == "host"
team_role(subject, object.team_id) == [admin, maintainer, observer][_]
action == read
}
# Team admins and maintainers can write to hosts of their own team
allow {
object.type == "host"
team_role(subject, object.team_id) == [admin,maintainer][_]
action == write
}
##
# Labels
##
# All users can read labels
allow {
object.type == "label"
not is_null(subject)
action == read
}
# Only global admins and maintainers can write labels
allow {
object.type == "label"
subject.global_role == admin
action == write
}
allow {
object.type == "label"
subject.global_role == maintainer
action == write
}
##
# Queries
##
# All users can read queries
allow {
not is_null(subject)
object.type == "query"
action == read
}
# Global admins and maintainers can write queries
allow {
object.type == "query"
subject.global_role == admin
action == write
}
allow {
object.type == "query"
subject.global_role == maintainer
action == write
}
# Team admins and maintainers can create new queries
allow {
object.id == 0 # new queries have ID zero
object.type == "query"
team_role(subject, subject.teams[_].id) == [admin, maintainer][_]
action == write
}
# Team admins and maintainers can edit and delete only their own queries
allow {
object.author_id == subject.id
object.type == "query"
team_role(subject, subject.teams[_].id) == [admin,maintainer][_]
action == write
}
# Global admins and maintainers can run any
allow {
object.type == "targeted_query"
subject.global_role == admin
action = run
}
allow {
object.type == "targeted_query"
subject.global_role == maintainer
action = run
}
allow {
object.type == "query"
subject.global_role == admin
action = run_new
}
allow {
object.type == "query"
subject.global_role == maintainer
action = run_new
}
# Team admin and maintainer running a non-observers_can_run query must have the targets
# filtered to only teams that they maintain.
allow {
object.type == "targeted_query"
object.observer_can_run == false
is_null(subject.global_role)
action == run
not is_null(object.host_targets.teams)
ok_teams := { tmid | tmid := object.host_targets.teams[_]; team_role(subject, tmid) == [admin,maintainer][_] }
count(ok_teams) == count(object.host_targets.teams)
}
# Team admin and maintainer running a non-observers_can_run query when no target teams
# are specified.
allow {
object.type == "targeted_query"
object.observer_can_run == false
is_null(subject.global_role)
action == run
# If role is admin or maintainer on any team
team_role(subject, subject.teams[_].id) == [admin,maintainer][_]
# and there are no team targets
is_null(object.host_targets.teams)
}
# Team admin and maintainer can run a new query
allow {
object.type == "query"
# If role is admin or maintainer on any team
team_role(subject, subject.teams[_].id) == [admin,maintainer][_]
action == run_new
}
# Observers can run only if observers_can_run
allow {
object.type == "targeted_query"
object.observer_can_run == true
subject.global_role == observer
action = run
}
# Team observer running a observers_can_run query must have the targets
# filtered to only teams that they observe.
allow {
object.type == "targeted_query"
object.observer_can_run == true
is_null(subject.global_role)
action == run
not is_null(object.host_targets.teams)
ok_teams := { tmid | tmid := object.host_targets.teams[_]; team_role(subject, tmid) == [admin,maintainer,observer][_] }
count(ok_teams) == count(object.host_targets.teams)
}
# Team observer running a observers_can_run query and there are no
# target teams.
allow {
object.type == "targeted_query"
object.observer_can_run == true
is_null(subject.global_role)
action == run
# If role is admin, maintainer or observer on any team
team_role(subject, subject.teams[_].id) == [admin,maintainer,observer][_]
# and there are no team targets
is_null(object.host_targets.teams)
}
##
# Targets
##
# All users can read targets (filtered appropriately based on their
# teams/roles).
allow {
not is_null(subject)
object.type == "target"
action == read
}
##
# Packs
##
# Global admins and maintainers can read/write all packs.
allow {
object.type == "pack"
subject.global_role == [admin, maintainer][_]
action == [read, write][_]
}
# All users can read the global pack.
allow {
object.type == "pack"
not is_null(subject)
object.is_global_pack == true
action == read
}
# Team admins, maintainers and observers can read their team's pack.
#
# NOTE: Action "read" on a team's pack includes listing its scheduled queries.
allow {
object.type == "pack"
not is_null(object.pack_team_id)
team_role(subject, object.pack_team_id) == [admin, maintainer, observer][_]
action == read
}
# Team admins and maintainers can add/remove scheduled queries from/to their team's pack.
#
# NOTE: The team's pack is not editable per-se, it's a special pack to group
# all the team's scheduled queries. So the "write" operation only covers
# adding/removing scheduled queries from the pack.
allow {
object.type == "pack"
not is_null(object.pack_team_id)
team_role(subject, object.pack_team_id) == [admin, maintainer][_]
action == write
}
##
# File Carves
##
# Only global admins can read/write carves
allow {
object.type == "carve"
subject.global_role == admin
action == [read, write][_]
}
##
# Policies
##
# Global Admin and Maintainer can read and write policies
allow {
object.type == "policy"
subject.global_role == [admin,maintainer][_]
action == [read, write][_]
}
# Global Observer can read any policies
allow {
object.type == "policy"
subject.global_role == observer
action == read
}
# Team admin and maintainers can read and write policies for their teams
allow {
not is_null(object.team_id)
object.type == "policy"
team_role(subject, object.team_id) == [admin,maintainer][_]
action == [read, write][_]
}
# Team admin, maintainers and observers can read global policies
allow {
is_null(object.team_id)
object.type == "policy"
team_role(subject, subject.teams[_].id) == [admin,maintainer,observer][_]
action == read
}
# Team Observer can read policies for their teams
allow {
not is_null(object.team_id)
object.type == "policy"
team_role(subject, object.team_id) == observer
action == read
}
##
# Software
##
# Global users can read all software.
allow {
object.type == "software_inventory"
subject.global_role == [admin, maintainer, observer][_]
action == read
}
# Team users can read all software in their teams.
allow {
not is_null(object.team_id)
object.type == "software_inventory"
team_role(subject, object.team_id) == [admin, maintainer, observer][_]
action == read
}