Now Go 1.8 has been released, it might be useful to start looking into ways to use plugins. The fact that only Linux is currently supported will inevitably reduce the rate that this can have useful benefit for the time being. But don’t lose heart.
I haven’t yet tried building Caddy extensions using the new Go 1.8 plugin build mode (go build -buildmode=plugin
), so perhaps this would be the first step to be evaluated. As a Linux user, this is reasonably easy for me.
The positive note is that Caddy plugins are already well designed to make them fit with Go 1.8. The extra that Go 1.8 offers is the ability to load them at run time, solving (or at least reducing) the packaging problem.
In Go 1.8, the vital restriction is that the build status of every plugin must be identical to the main server, i.e. all the deps (transitively) must have the same versions. This makes me think that we should not worry about the security issues mentioned in earlier posts: only properly-built plugins can be considered for execution and only a privileged user would have write access to these files. I.e. Caddy should not (and probably never) aim to support loading arbitrary plugins off the 'Net. The caddy
binary and the plugin binaries would quite likely have the same file ownership and permissions. I think this security model would be complete and sufficient. Signing of downloads of Caddy and its plugins is a separate but related issue (necessary, but not an impediment to the use of plugins).
The key issue to tackle is the build process to make this possible. By having a single codebase of Caddy plus all its approved plugins, we would meet the consistent dependency requirement. To achieve this, the getcaddy.com server could have a new option to provide a zip package containing Caddy core and all the extensions as plugin binaries (Linux only, for the time being).
Just by loading 1.8 plugins, the existing func init()
code would call func RegisterPlugin
. No direct use of Go’s plugin api would be needed. So the loading work is easy.
But that’s not the whole story: the order of execution of middleware is important and plugins must have a way to insert themselves into the list (httpserver/plugin.go) in a suitable place. This would need an API change, e.g. add a Before string
field to type Plugin
or, equivalently, add a before string
parameter to func RegisterPlugin
.
One last thing needed would be a new --plugindir
option or similar, with a sensible default.