Improve Caddy auth system

This is a copy of issue 1863, moved here for discussion.

Authentication in Caddy currently seems to be a bit of a mess. I would like to introduce you to a concept of how Caddy could improve this situation drastically:

The Caddy core is responsible for checking permissions on requests, plugins provide user authentication and authorization.
The basicauth plugin may for example authenticate a user and then save that a certain request is made by a particular user. Another plugin (e.g. providing a static list of permissions) is then queried for the permissions that this user has. Permission could look like this:

auth_users {
  # Alice is allowed to access anything (explicit allow default rule), but is
  # only allowed to use GET and HEAD HTTP methods on paths starting with /api
  Alice {
    default allow
    GET,HEAD /api
  }
  # Bob is allowed to access anything (allow default rule from default user),
  # and is allowed to use GET, HEAD, POST, PUT and DELETE HTTP methods on
  # paths starting with /api
  Bob {
    rw /api # rw is a shortcut for GET,HEAD,POST,PUT,DELETE
  }
  # Charlie is only allowed to access his own space in /charlie and nothing else.
  Charlie {
    default deny
    * /charlie
  }
  # * specifies default rules for all authenticated users. If omitted,
  # default rule is "deny". If no path of an authenticated user matches,
  # these paths will be checked before reverting to their own or this
  # default rule.
  * {
    default allow
    - /api
  }
  # - specifies the rules if authentication failed / the user did not
  # provide authentication.
  - {
    default deny
    ro /public # ro is a shortcut for GET,HEAD
  }
}

Methods are HTTP methods and HTTP commands:
GET, POST, HEAD, PUT, MOVE, DELETE, WEBSOCKET (the permission to initiate a websocket upgrade), etc…

Added/Changed Caddyfile directives:

  • auth
    defines a plugin that both handles user authentication and authorization
  • auth_users
    defines a list of plugins and their order in which they are tried to authenticate a user
  • auth_perms
    either defines a static list of permissions (as above) or defines a plugin that handles user authorization

There may be only one list/plugin defining permissions per vhost, but many plugins doing authorization.

There numerous great use cases for such a system:

# use TLS client certificates for authentication (CN=username),
# then ask a backend service for permissions of that user
auth_users tls
auth_perms request {
  10.0.0.1/auth/{username}
}

# use a backend service for both authentication and authorization
auth request {
  # note: both HTTP basic auth and cookies are forwarded
  10.0.0.1/auth
}

# or completely defined inside the Caddy config file
auth_users basicauth {
  Alice   [password]
  Bob     [password]
  Charlie [password]
}
auth_perms {
  Alice {
    rw /api
  }
  * {
    default allow
    r /api
  }
  - {
    default deny
    r /public
  }
}

Such a system would really bring much more flexibility and power to Caddy, as it the can integrate seamlessly into any existing environment for handling authentication and authorization, making it easy to extend existing web applications with powerful Caddy plugins. This is a big thing and something I think a lot of people would love to have.

I hope I have explained the concept enough to make sense, I will of course try to explain further if that is not the case.

The challenge in creating such a central authentication management is to keep the system as simple as possible. That was my aim for this concept, but I am pretty sure that I missed something. :wink:

Keep up the great work!

1 Like

I was planning to implement something similar to this just combined with the webdav plugin as a standalone server, but have decided that I will implement this as a Caddy plugin. (That is why I chose the “Plugin” category instead of “Coding”)

There is of course a huge drawback in implementing this as a plugin, instead of putting the core procedures into the Caddy core itself: All backends must be implemented directly in this plugin and cannot be as easily extended.

But for now it will prove the concept and hopefully make it into the Caddy core in the next big breaking release.

So, please give lots of feedback! Coding will start very soon.

Thanks Daniel, for writing this up!

I wanted to shed some light on one thing:

Did you know that the caddytls package which provides all of Caddy’s TLS (including its certificate renewal stuff) is a plugin? And that all the DNS provider plugins plug into the caddytls package? Point is, it’s OK to have plugins to plugins. It’s all the same to Caddy, technically, and almost the same to the users.

However, if this works out really well and is useful to Caddy users at large, we can look into making this part of Caddy core, to unify all the authentication. I like that idea. And we can make auth plugins a special plugin type on the website that users can select.

It’s been a while, I’ve been very busy lately.

So here’s an update:
I finished the first version of the plugin and am already using it in production.
I use it for handing over authentication for the filemanager and webdav plugin to my backend.

Have a look: GitHub - dhaavi/caddy-permission: Authentication and authorization middleware for Caddy

The next thing I want to try is to use client certificates to protect only some paths, but not others. Let’s see how browsers react to this. :stuck_out_tongue_winking_eye:

Cool! When will you have some docs up about how to use it?

As for the client certificates protecting only some paths, how do you intend to do that?

Soon, I hope :wink:

I do not know yet. Depends on how browsers react to optional client certificates.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.