authz

package
v0.7.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2021 License: Apache-2.0 Imports: 5 Imported by: 0

README

Package authz

The authz package contains basic data structures that are used to specify/communicate information about a request (user info, queues, actions) to a system that can authorize that request.

It is built to make it relatively easy to make new authorizers, systems that can handle the actual authorization, without actually implementing any of that authorization inside EntroQ, itself. This way EntroQ is only responsible for what it uniquely knows about: the semantics of the request itself.

In particular, EntroQ has no concept of users or authentication. It only knows about Authorization headers in an opaque sense: it can pass them along.

Open Policy Agent (OPA)

One implementation is the OPA HTTP subsystem. It is responsible for packaging up an API request for an OPA server at a given URL. The URL is completely specified, and requests/responses are given in this authz module.

An OPA server must be running for this to be used, and only part of the configuration can be specified here. The core configuration files needed for OPA to function in an EntroQ setting are located in the opadata/core directory. Those files should be given to an OPA server as part of its policy Rego bundles.

The system user or deployer must provide a few things:

  • Data (information about user permissions, role permissions, etc.)
  • An entroq.permissions Rego package that provides allowed_queues.
  • An entroq.user Rego package that provides username.

Authorization Flow

The authorization flow from EntroQ's perspective is pretty simple:

  • Client
    • sends EntroQ a request over gRPC.
  • EntroQ
    • packages up an authorization query.
    • extracts the value of the Authorization header from gRPC context.
    • sends that query, with an Authorization header, to OPA.
  • OPA
    • returns a response containing all mismatched queues (or an error).
  • EntroQ
    • proceeds or returns an error to the client based on that response.

It's a pretty simple subrequest-based authorization scheme, again, from EntroQ's perspective.

From OPA's perspective, things can be relatively complex, because EntroQ is doing nothing with authentication or authorization. EntroQ also provides no information about what is allowed for whom. All of that is part of the OPA configuration and may even be part of a larger system environment.

For example, if bearer tokens are used, they might be opaque, in which case OPA would need to be configured to reach out to another service to obtain user information for a given token (including whether that token is expired, for example). If the bearer token is a JWT (not uncommon), OPA can simply unpack it, verify it, and pull the sub field from its claims using built-in functions for that purpose.

OPA has the ability to use data from a filesystem, and to watch for changes to that data, so some will prefer to provide data (permitted queue specs for users and roles) in that way. It can also reach out to another service to obtain data specific to a user. Again, this is up to the user or deployer to configure, and EntroQ has no opinions about how it is done.

User-provided Policy

Examples of what username and permissions files might look like can be found in the opadata/example directory. There, a simple (and bad!) approach to providing the username can be found, as can an approach for parsing provided data about users and roles.

In that example, the users and roles are organized like this:

{
  "users": [{
    "name": "auser",
    "roles": ["role1", "role2"],
    "queues": [{
      "exact": "aqueue",
      "actions": ["*"]
    }, {
      "prefix": "/mystuff/",
      "actions": ["CLAIM", "CHANGE", "DELETE"]
    }]
  }],
  "roles": [{
    "name": "*",
    "queues": [{
      "prefix": "/free-for-all/",
      "actions": ["*"]
    }],
  }]
}

If this structure suits your data purposes, feel free to copy the example permissions from the example directory.

The username should usually be taken from input.identity, following instructions in the OPA security documentation.

EntroQ will, when using the OPA HTTP module, pass an Authorization header from the gRPC call context through to OPA during an authorization request. This can be parsed by OPA by telling it to use this with appropriate --authentication and --authorization settings for your use case.

User-Provided Data

There are many ways to get permissions data into OPA, which is one of the reasons that EntroQ has no opinion about it. Shape the data how you like, get it to OPA how you like, and craft your entroq.permissions package to convert that data (along with the current user credentials) into a set of allowed_queues queue specs that are allowed for this user. The core logic does the rest.

Permissions data is one of the fastest changing things in an authorization context like this. OPA can watch files for changes, so you might try running it in a container with a data bundle mounted into it, for example. This idea is compatible with many systems, such as Kubernetes secrets.

However you provide data and configuration to OPA, simply ensure that the core files are also loaded.

Documentation

Overview

Package authz contains standard data structures for representing permissions and authorization requests / responses.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsAuthz

func IsAuthz(err error) bool

IsAuthz determines whether an error is an authorization error.

Types

type Action

type Action string

Action is an authorization-style action that can be requested (and allowed or denied) for a particular queue spec.

const (
	Claim  Action = "CLAIM"
	Delete Action = "DELETE"
	Change Action = "CHANGE"
	Insert Action = "INSERT"
	Read   Action = "READ"
	All    Action = "*"
)

type Authorization

type Authorization struct {
	// An HTTP Authorization header is split into its type and credentials and
	// included here when available.
	Type        string `json:"type,omitempty"`
	Credentials string `json:"credentials,omitempty"`

	// Never use this in practice. This allows the user to be set directly for testing.
	// The code checks for this and creates an error if present, unless
	// specifically allowed for testing.
	TestUser string `json:"testuser,omitempty"`
}

AUthorization represents per-request authz information. It can ideally come in many forms. The first supported form is a "token", such as from an Authorization header.

func NewHeaderAuthorization

func NewHeaderAuthorization(val string) *Authorization

NewHeaderAuthorization creates an authorization structure from a header value.

func (*Authorization) String

func (a *Authorization) String() string

String returns the authoriation header value.

type Authorizer

type Authorizer interface {
	// Authorize sends a request with context to see if something is allowed.
	// The request contains information about the entity making the request and
	// a nil error indicates that the request is permitted. A non-nil error can
	// be returned for system errors, or for permission denied reasons. The
	// latter will be of type AuthzError and can be detected in standard
	// errors.Is/As ways..
	Authorize(context.Context, *Request) error

	// Close cleans up any resources, or policy watchdogs, that the authorizer
	// might need in order to do its work.
	Close() error
}

Authorizer is an abstraction over Rego policy. Provide one of these to manage policy files and changes. The query is expected to return a nil error when authorized, and a non-nil error when not authorized (or smoething else goes wrong). If the non-nil error is an AuthzError, it can be unpacked for information about which queues and actions were disallowed.

type AuthzError

type AuthzError struct {
	// If Allow is true, then authorization succeeded and we can proceed.
	// The reason we don't just go with "empty error and failures" is that
	// non-affirmative things like that tend to cause unwanted authorizations
	// for other reasons, like parsing JSON with no known fields present.
	Allow bool `json:"allow"`

	// Failed contains the queue information for things that were not
	// found to be allowed by the policy. It will only contain the actions that
	// were not matched. If multiple actions were desired for a single queue,
	// only those disallowed are expected to be given back in the response.
	Failed []*Queue `json:"failed"`

	// For other kinds of errors.
	Errors []string `json:"errors"`
}

AuthzError contains the reply from OPA.

func (*AuthzError) Error

func (e *AuthzError) Error() string

Error satisfies the error interface, producing a string error that contains unmatched queue/action information.

type Queue

type Queue struct {
	// An exact name to match.
	Exact string `yaml:",omitempty" json:"exact,omitempty"`
	// The kind of matching to do (default exact)
	Prefix string `yaml:",omitempty" json:"prefix,omitempty"`
	// Actions contains the desired things to be done with this queue.
	Actions []Action `yaml:",flow" json:"actions"`
}

Queue contains information about a single queue (it is expected that only one match string will be specified. Behavior of multiple specifications is not necessarily well defined, and depends on policy execution order.

func (*Queue) String

func (q *Queue) String() string

String produces a simple string representing this queue spec.

type Request

type Request struct {
	// Authz contains information that came in with the request (headers).
	Authz *Authorization `json:"authz"`
	// Queues contains information about what is desired: what queues to
	// operate on, and what should be done to them.
	Queues []*Queue `json:"queues"`
}

Request conatins an authorization request to send to OPA.

func NewYAMLRequest

func NewYAMLRequest(y string) (*Request, error)

NewYAMLRequest creates a request from YAML/JSON.

Directories

Path Synopsis
Package opahttp implements the authz.Authorizer using an Open Policy Agent (OPA).
Package opahttp implements the authz.Authorizer using an Open Policy Agent (OPA).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL