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.
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, UNLOCKrw
: GET, HEAD, PROPFIND, OPTIONS, LOCK, UNLOCK, POST, PUT, DELETE, MKCOL, PROPPATCHws
: WEBSOCKETany
: 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.
The HTTP Methods MOVE
, COPY
and PATCH
are handled a bit more special:
MOVE
: source path is treated asDELETE
and destination path asPUT
COPY
: source path is treated asGET
and destination path asPUT
PATCH
:- If a the
Destination
Header is present:- If
Action
Header iscopy
: source path is treated asGET
and destination path asPUT
- If
Action
Header is notcopy
: source path is treated asDELETE
and destination path asPUT
- If
- If no
Destination
Header is present: treat asPATCH
- If a the
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)
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
}
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
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
orhttps
) in theX-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.
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
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
-debug-permission
enables debug and error messages (to stdout)-error-permission
enables error messages (to stdout)
These will give you messages like this:
[permission] failed to get user from api:MyWebsite: Get http://localhost:8080/caddyapi: dial tcp 127.0.0.1:8080: connect: connection refused
[permission] failed to get user permit from api:MyWebsite: Get http://localhost:8080/caddyapi/greg: dial tcp 127.0.0.1:8080: connect: connection refused
[permission] [tls: greg] basic granted access: GET /tmp/