permission

package module
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2019 License: Apache-2.0 Imports: 14 Imported by: 0

README

Caddy Permission Plugin

This plugin allows the user to provide generic authentication and authorization based on HTTP methods. It's main use case is to add (SSO) authentication and authorization to unsupported web services.

Usage

The Permission plugin works by filtering HTTP methods and request paths. The most common methods are included in the shortcuts readonly ro, read/write rw, websockets ws, any and none:

  • ro: GET, HEAD, PROPFIND, OPTIONS, LOCK, UNLOCK
  • rw: GET, HEAD, PROPFIND, OPTIONS, LOCK, UNLOCK, POST, PUT, DELETE, MKCOL, PROPPATCH
  • ws: WEBSOCKET
  • any: any (may not be combined)
  • none: none (may not be combined)

If you want to specify your methods yourself, be sure to not have any spaces between them: GET,HEAD,.... If you prepend the list of methods with ~, you can invert their meaning, effectively turning the whitelist into a blacklist. The Permission plugin works on a prefix basis. Every path provided matches the exact path and every path that starts with the provided path. Be very careful with using the none option, the rules are already in a whitelist mode, you will not use this often. Please refer to the Combining Backends chapter to learn in which order rules are evaluated.

Important Note: The Permission plugin is only secure if you can verify if the application you want to protect is compatible, meaning that it must conform to these standard HTTP methods to interact with the web service. Also, it can only deny websocket connections, but cannot filter within them. You should always treat websocket connections as a full write access action.

Special Handling

The HTTP Methods MOVE, COPY and PATCH are handled a bit more special:

  • MOVE: source path is treated as DELETE and destination path as PUT
  • COPY: source path is treated as GET and destination path as PUT
  • PATCH:
    • If a the Destination Header is present:
      • If Action Header is copy: source path is treated as GET and destination path as PUT
      • If Action Header is not copy: source path is treated as DELETE and destination path as PUT
    • If no Destination Header is present: treat as PATCH

Backends

The different backends may be combined - They are handled in order of declaration in the Caddyfile.

Check out the test directory and play around with the different backends to get a feel for it.

Currently, three different backends are supported:

  • HTTP BasicAuth (authentation & authorization)
  • TLS client authentication (authentation only)
  • API (authentation & authorization)
HTTP Basic Auth
permission basic {
  user greg qwerty1 # This is greg, his password is qwerty1
  rw /tmp/ # he may read and write to /tmp/!

  user george # This is george, he does not have a password, another backend will have to authenticate him
  rw /admin/

  default # applies to all logged-in users
  rw /api/users/0 #

  public # applies to everyone, also anonymous users
  none /internal/ # deny internal space for everyone
  ro / # allow reading everything

  GET,HEAD /other
}
TLS Auth

This plugin requires TLS client authentication. It simply sets the CN to the username. You can use the HTTP Basic Auth and/or API Auth plugins for handling permissions.

permission tls
API Auth

This is a custom API, that you can implement in your existing system - it's extremely simple. Here is how you would configure it within caddy:

permission api {
  name MyWebsite # name of website
  user http://localhost:8080/caddyapi # main authentication api
  permit http://localhost:8080/caddyapi/{{username}} # refetch a permit of a user
  login http://localhost:8080/login?next={{resource}} # redirect here for logging in (resource is original URL)
  add_prefix /api/resource /files # add prefixes to returned paths
  add_without_prefix # if add_prefix is used, but you still want to also add the original paths
  cache 600 # how to long to cache authenticated users
  cleanup 3600 # when to clean out authenticated users
}

user Endpoint:

The Permission plugin creates a request user authentication at the configured URL with:

  • The original Host Header.
  • The originating IP in the X-Real-IP Header.
  • The originating IP in the X-Forwarded-For Header.
  • The original protocol (http or https) in the X-Forwarded-For Header.
  • The original BasicAuth credentials, if present.
  • All cookies.

It expects a JSON Object in return with the following fields:

{
  "BasicAuth":   false,
  "Cookie":      "cookieName=cookieValue",
  "Username":    "username",
  "Permissions": {}
}

BasicAuth or Cookie are used to declare how to identify this user in the future. Either set BasicAuth to true, or set Cookie to the authenticating cookie.

Example:

{
  "BasicAuth":   false,
  "Cookie":      "PHPSESSID=12345",
  "Username":    "tom",
  "Permissions": {
    "/tmp/": "rw",
    "/static": "ro",
    "/other": "GET,HEAD"
  }
}

permit Endpoint:

Works very similar to the user endpoint, but instead of forwarding all these headers and cookies, the username is replaced in the URL.

login Endpoint:

If current permissions are insufficient to complete a request and the user is not yet authenticated, she is redirected to this URL.

Combining Backends

Rules within a ruleset (user, default, public) are evaulated in the order they are configured. When combining different backends, the backend defined earlier is always asked first. This is handled as follows:

  • Try to authenticate the user with every backend, stop if successful.
  • If authenticated:
    • Check the user's permissions and default permissions for every backend, stop if a match is found.
  • Check the public permit for every backend, stop if allowed.
  • Let the first backend to support login handle login.

So, for example, with the API and Basic backend the order will be:

  • API user ruleset
  • API default ruleset
  • Basic user ruleset
  • Basic default ruleset
  • API public ruleset
  • Basic public ruleset

Other Options

There are also a couple other options regardless of backend:

permission realm "Restricted Site" # sets name
permission allow_reading_parent_paths # applies read rights to parent paths
set_basicauth username password # set basic auth on forwarded request
set_cookie name value # set cookie on forwarded request, may be used multiple times

Documentation

Index

Constants

View Source
const (
	BackendBasic uint8 = iota
	BackendAPI
	BackendTLS

	BackendBasicName = "basic"
	BackendAPIName   = "api"
	BackendTLSName   = "tls"

	DefaultIdentifier = "default"
	PublicIdentifier  = "public"
)

Backend constants

View Source
const (
	PermitTypeNo uint8 = iota
	PermitTypeUser
	PermitTypeDefault
	PermitTypePublic
)

Permit Types

Variables

This section is empty.

Functions

func Forbidden

func Forbidden(w http.ResponseWriter, r *http.Request, username, userSource string, backend Backend, permitType uint8) (int, error)

Forbidden logs why this request was forbidden and returns http.StatusForbidden

func MethodIsRo

func MethodIsRo(method string) bool

MethodIsRo returns whether the supplied method is a "read only" method.

func RegisterBackend

func RegisterBackend(name string, plugFactory BackendFactory)

RegisterBackend registers a Permission backend for use

Types

type APIBackend

type APIBackend struct {
	CustomName string

	Lock          sync.RWMutex
	Users         map[string]*User
	Permits       map[string]*Permit
	DefaultPermit *Permit
	PublicPermit  *Permit

	UserURL   string
	PermitURL string

	LoginURL string

	AddPrefixes      []string
	AddWithoutPrefix bool

	CacheTime int64
	Cleanup   int64
}

APIBackend authenticates users and gets permits through an API.

func (*APIBackend) AuthenticateUser

func (backend *APIBackend) AuthenticateUser(r *http.Request) (*User, error)

AuthenticateUser handles authentication via API.

func (*APIBackend) Cleaner

func (backend *APIBackend) Cleaner()

Cleaner periodically cleans up the APIBackend This consists of deleting all timed-out users and permits.

func (*APIBackend) CreatePermit

func (backend *APIBackend) CreatePermit(apiResponse *Response) (*Permit, error)

CreatePermit creates a new permit according to the configuration.

func (*APIBackend) GetDefaultPermit

func (backend *APIBackend) GetDefaultPermit() (*Permit, error)

GetDefaultPermit returns the default permit.

func (*APIBackend) GetPermit

func (backend *APIBackend) GetPermit(username string) (permit *Permit, err error)

GetPermit returns the user permit of a user.

func (*APIBackend) GetPublicPermit

func (backend *APIBackend) GetPublicPermit() (*Permit, error)

GetPublicPermit returns the public permit.

func (*APIBackend) GetUsername

func (backend *APIBackend) GetUsername(r *http.Request) (username string, ok bool, err error)

GetUsername authenticates and returns a username, if successful.

func (*APIBackend) Login

func (backend *APIBackend) Login(w http.ResponseWriter, r *http.Request, realm string) (bool, int, error)

Login redirects to the configured login URL.

func (*APIBackend) Name

func (backend *APIBackend) Name() string

Name returns the name of the backend.

func (*APIBackend) RefreshUserPermit

func (backend *APIBackend) RefreshUserPermit(username string) (*Permit, error)

RefreshUserPermit gets the Permit of an already authenticated user via API.

type Backend

type Backend interface {
	GetUsername(r *http.Request) (username string, authSuccess bool, err error)
	GetPermit(username string) (*Permit, error)
	GetDefaultPermit() (*Permit, error)
	GetPublicPermit() (*Permit, error)
	Login(w http.ResponseWriter, r *http.Request, realm string) (bool, int, error)
	Name() string
}

Backend is an interface for adding backend plugins

func NewAPIBackend

func NewAPIBackend(c *caddy.Controller, now int64) (Backend, error)

NewAPIBackend creates a new APIBackend.

func NewBasicBackend

func NewBasicBackend(c *caddy.Controller, now int64) (Backend, error)

NewBasicBackend creates a new BasicBackend.

func NewTLSBackend

func NewTLSBackend(c *caddy.Controller, now int64) (Backend, error)

NewTLSBackend create a new TLSBackend.

type BackendFactory

type BackendFactory func(c *caddy.Controller, now int64) (Backend, error)

BackendFactory creates a plug

func GetFactory

func GetFactory(name string) BackendFactory

GetFactory returns the factory for the given backend name

type BasicBackend

type BasicBackend struct {
	Users         map[string]string
	Permits       map[string]*Permit
	DefaultPermit *Permit
	PublicPermit  *Permit
}

BasicBackend is a permission backend that uses HTTP Basic Authentication and static users and rules.

func (*BasicBackend) GetDefaultPermit

func (backend *BasicBackend) GetDefaultPermit() (*Permit, error)

GetDefaultPermit returns the default permit.

func (*BasicBackend) GetPermit

func (backend *BasicBackend) GetPermit(username string) (*Permit, error)

GetPermit returns the user permit of a user.

func (*BasicBackend) GetPublicPermit

func (backend *BasicBackend) GetPublicPermit() (*Permit, error)

GetPublicPermit returns the public permit.

func (*BasicBackend) GetUsername

func (backend *BasicBackend) GetUsername(r *http.Request) (username string, authSuccess bool, err error)

GetUsername authenticates and returns a username, if successful.

func (*BasicBackend) Login

func (backend *BasicBackend) Login(w http.ResponseWriter, r *http.Request, realm string) (bool, int, error)

Login returns "401 Authentication Required"

func (*BasicBackend) Name

func (backend *BasicBackend) Name() string

Name returns the name of the plug.

type Handler

type Handler struct {
	Next httpserver.Handler

	Backends []Backend

	ReadParentPath bool
	RemovePrefix   string
	Realm          string

	SetBasicAuth string
	SetCookies   [][]string
}

Handler (Permission Handler) is an authentication and authorization middleware

func NewHandler

func NewHandler(c *caddy.Controller, now int64) (*Handler, error)

NewHandler creates a new Handler from configuration

func (*Handler) CheckPermits

func (handler *Handler) CheckPermits(username, method, path string, ro bool) (bool, Backend)

CheckPermits checks permissions of a request

func (*Handler) Forward

func (handler *Handler) Forward(w http.ResponseWriter, r *http.Request, username, userSource string, backend Backend, permitType uint8) (int, error)

Forward hands the request to the next middleware and adds some headers for information

func (*Handler) ServeHTTP

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)

ServeHTTP implements the httpserver.Handler interface.

type Permit

type Permit struct {
	Rules      []*Rule
	ValidUntil int64
}

Permit holds permissions and their expiration time.

func NewPermit

func NewPermit(cacheTime int64, now int64) *Permit

NewPermit creates an empty Permit with the correct cache time.

func (*Permit) AddRule

func (p *Permit) AddRule(methods, path string) error

AddRule adds a permission to the Permit.

func (*Permit) Check

func (p *Permit) Check(handler *Handler, method, path string, ro bool) (allowed bool, matched bool)

Check checks a request against this permission object.

func (*Permit) Finalize

func (p *Permit) Finalize()

Finalize does some final preparing/optimizing on the Permit.

func (Permit) Len

func (p Permit) Len() int

func (Permit) Less

func (p Permit) Less(i, j int) bool

func (Permit) Swap

func (p Permit) Swap(i, j int)

type Response

type Response struct {
	BasicAuth   bool
	Cookie      string
	Username    string
	Permissions map[string]string
}

Response is a respone to an API request.

type Rule

type Rule struct {
	Path                string
	Methods             []string
	MethodsAreBlacklist bool
}

Rule holds permission information related to a specific path

func NewRule

func NewRule(methods, path string) (*Rule, error)

NewRule creates a new permission rule with the given concatenated method string and path

func (*Rule) MatchesMethod

func (r *Rule) MatchesMethod(method string) bool

MatchesMethod checks if the permission matches the given HTTP method.

func (*Rule) MatchesParentPath

func (r *Rule) MatchesParentPath(path string) bool

MatchesParentPath checks if the HTTP request path is a parent of the permission rule path.

func (*Rule) MatchesPath

func (r *Rule) MatchesPath(path string) bool

MatchesPath checks if the permission rule matches the given HTTP request path.

type TLSBackend

type TLSBackend struct {
}

TLSBackend uses TLS client certificates for authentication.

func (*TLSBackend) GetDefaultPermit

func (backend *TLSBackend) GetDefaultPermit() (*Permit, error)

GetDefaultPermit returns nothing, as TLSBackend does not support permits.

func (*TLSBackend) GetPermit

func (backend *TLSBackend) GetPermit(username string) (*Permit, error)

GetPermit returns nothing, as TLSBackend does not support permits.

func (*TLSBackend) GetPublicPermit

func (backend *TLSBackend) GetPublicPermit() (*Permit, error)

GetPublicPermit returns nothing, as TLSBackend does not support permits.

func (*TLSBackend) GetUsername

func (backend *TLSBackend) GetUsername(r *http.Request) (string, bool, error)

GetUsername authenticates and returns a username, if successful.

func (*TLSBackend) Login

func (backend *TLSBackend) Login(w http.ResponseWriter, r *http.Request, realm string) (bool, int, error)

Login is currently disabled for TLSBackend.

func (*TLSBackend) Name

func (backend *TLSBackend) Name() string

Name returns the name of the plug.

type User

type User struct {
	Username   string
	ValidUntil int64
}

User is a simple representation of an authenticated user.

func NewUser

func NewUser(username string, cacheTime int64) *User

NewUser creates a new User with the given name and cache time.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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