Honestly, reverse proxying anything with Caddy has been so easy that I can’t use anything else anymore.
Docker containers are utterly easy to proxy, the defaults (e.g. the php_fastcgi directive) are sane and mostly work out of the box, the documentation is great, and everything seems so well thought out that one has to wonder why we put up so long with the convolutions of Apache and Nginx.
Indeed, but still, one SSRF vulnerability in anything on the host and the attacker can reconfigure Caddy to serve up any other resource on any of the networks the host can access, or deny access to any resources served by Caddy.
It's an unnecessary security risk is all I'm saying, and I personally would have preferred it was authenticated or off by default.
I really love Caddy, you've built an amazing piece of software, I just disagree with your design decision on this one little thing. It's good that you put that notice in the docs at least!
SSRF and RCE are different things. Being able to throw requests to localhost and accessing the host filesystem don't necessarily have to be the same vulnerabilities, but I'll concede that SSRF vulns are uncommon.
I'm just worried that Caddy will be the source of "security misconfiguration" (1) findings in penetration test reports. It's my opinion that we as software engineers should strive not to leave our software insecure by default, is what I'm saying, and that's how I see Caddy 2's admin API.
Can you demonstrate an exploit in Caddy itself (i.e. isn't actually exploiting something else, or configuring yourself into a hole first -- we can't do much about external factors)? If it's valid we'll see about a patch.
I'm lovin' Caddy! Below is a typical reverse proxy + www redirect + static file serving. It's so much more readable and simple than Apache/Nginx that I can't imagine switching back.
www.example.com {
redir https://example.com{uri}
}
example.com {
# tell Caddy where your favicon files are
@favicon {
path /favicon.ico
# + other favicon files...
# ...
# ...
}
# serve your static files
route /static/* {
root * /var/cache/example.com
header Cache-Control max-age=31536000 # 1 year
file_server
}
# serve your favicon files from your favicon route
route @favicon {
root * /var/cache/example.com/favicons
file_server
}
encode zstd gzip
# reverse proxy to your app
reverse_proxy 127.0.0.1:8080
# do some logging
log {
format json
output file /var/log/caddy/example.com/access.log {
roll_size 100MiB
roll_keep 10
roll_keep_for 2160h # 90 days
}
}
}
Caddy as a proxy is great - I only whish it was easier to copy apache-style auth/authz "satisfy any" (eg: whitelist some ips, require basic auth from others).
I also expect that as setups age, one is likely to miss mod_rewrite - but at that point/style of setup, maybe apache traffic server start to make sense. Or, just apache httpd of course.
You can do that easily with the `remote_ip`[0] matcher (pair it with the `not` matcher to invert the match). For example to require `basicauth`[1] for all non-private IPv4 ranges:
@needs-auth not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
basicauth @needs-auth {
Bob JDJhJDEwJEVCNmdaNEg2Ti5iejRMYkF3MFZhZ3VtV3E1SzBWZEZ5Q3VWc0tzOEJwZE9TaFlZdEVkZDhX
}
And for rewrites, there's the `rewrite`[2] handler. Not sure what you're missing?
One problem, as a lazy bastard, I've always had with the Caddy docs is it can sometimes be hard to glance at a page in the documentation and see "ah, that's how I'd do <common thing>".
Take the basicauth portion for example. If you had been reading the docs like a book, started in the references/tutorial sections, understood all there is to know about how request matcher syntax works as a result, and then read the basicauth page you would have a rock solid understanding of how to make basicauth do what you want here. If you land at the basicauth page from a Google search trying to stand up a quick file-server weekend project in a way your friends can access you either are really on top of it and notice "[<matcher>]" (not even mentioned in the Syntax breakdowns of the page) is what's used in the single example below and happens to be a path but might be a lot more or you leave without a hint of how to do basicauth the way you wanted. It'd be great if the syntax section breakouts just mentioned something that triggered more or less a "and hey dumbass, if you haven't learned how matchers work yet you need to go do that to fully utilized this directive".
I realize this is awfully needy, the docs have everything you need if you read them carefully, and I absolutely LOVE using Caddy so it's not an attempt to say it's bad overall by any means. I wanted to point it out though since this exact example is something I ran into a few weekends ago. I think the problem is exacerbated by v2 syntax being new, as well as the competing JSON syntax, making it harder for people to find use case examples outside of what's in the official docs.
Protip: you can click almost everything in code blocks in the docs. For example, if you click `[<matcher>]`, it brings you right to the request matcher syntax section, which explains what you can fill in there.
It would be redundant to write on every page what you can use as a matcher. The Caddyfile reference docs assume you've read https://caddyserver.com/docs/caddyfile/concepts which walks you through how the Caddyfile is structured, and it'll give you the fundamentals you need to understand the rest of the docs (I think, anyway).
If you think we need more examples for a specific usecase, we can definitely include those. Feel free to propose some changes on https://github.com/caddyserver/website, we could always use the help!
I had a huge facepalm the day I realized all of the syntax was clickable :). It's not even a uncommon feature I just hadn't tried clicking it at for some reason!
Yeah I wouldn't say necessarily every page needs examples of different matchers and certainly not about what all of the matcher options are. More a "if the token is shown in the syntax it should have a bullet in the syntax section" kind of approach which in the case of [<matcher>] could be as plain and short as "[<matcher>] is a token which allows you to control the scope a directive applies to. For details on how see Request Matchers." to raise the "you probably want to know more about this first" flag to anyone that just jumped in from Google or what have you to go read about matchers before trying to understand the directive.
If that makes any sense I'd be glad to raise it more formally over on the GitHub!
Right, I know it's possible (and thanks for the example) - I still whish it was easier to specify "only authorized given conditions x, y, z".
In the above, access is implied, then revoked without a valid username/password, then login is excepted for certain conditions (IPs in this case) - and access is implied (again).
IMNHO one of the strengths of apache is how authentication providers and authorization is separated and allow for easy(ish) combinations.
That said, there's something to be said for doubling down on a single type of handling for access and rewrites (matchers).
I still prefer matchers (which resource) combined with action/policy (allow/deny/rewrite).
I'm not totally sure I follow how you'd like for it to be structured. Could you give a config example? I think one of the issues is `basicauth` needs to write a response header to work correctly (i.e. WWW-Authenticate to tell the browser to prompt) so it can't only act as a matcher.
Caddy looks interesting, I currently use apache to proxy a few hundered sites and it works well enough, some are protected by client certificates, others by oidc, all then pass the authenticated user to the downstream server in a header, job done.
I've managed to do this with openresty (nginx not supporting oidc out of the box), but it doesn't fill me with confidence, I guess it's all the lua. A quick glance at caddy shows it likewise doesn't support oidc integration out of the box, but instead I have to use another module that's no longer maintained ( https://github.com/thspinto/caddy-oidc )
Yeah, we defer to plugins to provide auth solutions, because it's... a whole thing. It's best maintained outside of the standard distribution, because there's so many ways to approach it.
The caddy-oidc plugin you linked was written for Caddy v1, so it's no longer compatible. The most complete auth plugin for Caddy v2 is https://github.com/greenpau/caddy-security, and I think it probably does what you need.
Let me know if you need me to clarify, if you have any concerns.