Tls.dns per-vhost


At this time tls.dns seems to be a global variable (please correct me if I’m wrong). On shared-webhosting environements end-customers might host their external DNS on different providers (and different accounts). Are there any plans to make it configurable per-vhost?

Thank you!

Yep, you can definitely configure different DNS providers per domain name (“subject”, or as the docs currently erroneously say, “hosts” – will be fixed by the next release): JSON Config Structure - Caddy Documentation

Awesome! Is it configurable in Caddyfile too? I was unable to find it in tls (Caddyfile directive) — Caddy Documentation (or I just missed it)

Btw, LE accepts empty email address (no email notifications), does caddy accept empty value for it too?

And does it use 1 LE account per-server? (account registration is rate-limited, so, I’m just wondering)

Yes, you’re not required to specify it.

Yes, it’s a global option Global options (Caddyfile) — Caddy Documentation

Thanks! Any idea on Caddyfile question?

Most of the time, anything that the tls directive can do is per-site or per-hostname.

Caddy assumes that the same email means same account. If you’re serving other people’s sites, definitely just use one account as per Let’s Encrypt’s recommendations.

Thanks! I’m just not seeing anything on dns option in tls (Caddyfile directive) — Caddy Documentation. See the options listed Syntax, may you guide me which one of these should be used to define DNS provider? Sorry if I’m just overlooking it…

The v2 Caddyfile can’t express as much advanced configuration as the JSON can. There are theoretical limitations as to how expressive it can be, but there’s also just the fact that not all the features have been ported over to the Caddyfile yet.

For your use case, I’d recommend just using JSON, since you’re programmatically generating configs anyway. Caddyfiles are meant for hand-edits.

Clear, thanks. We allow admins to define some custom blocks in configuration from GUI, that’s why I thought Caddyfile might be the best option. But I guess we can simply store everything as JSON (we have templating system - easy to do) and try using caddyfile adapter for their custom config blocks to inject them directly using the API.

Just thought… We’d be unable to use include ... then, which is useful for our split-configs implementation. We could simply load them to API and overwrite the configuration blogs, but… current API doesn’t seem to be safe on a shared webhosting environment (unless I’m missing something obvious).

I’d suggest using a control socket, like “nginx unit” does (Configuration — NGINX Unit). That way /path/to/control.caddy.sock would be only accessible by root, and no one else would be able to do actions with the API, unless they have access to the socket.

What’s unsafe about it? Are untrusted users running arbitrary code on your machine or making network connections?

You can do this already: JSON Config Structure - Caddy Documentation (see Conventions — Caddy Documentation)

We provide control panel for webhosting. End-users have ability to add crons, php files, if they have ssh - execute commands.

# su - test1 -s /bin/bash
$ curl localhost:2019/config/


People could simply upload PHP file fetching data from API/posting to it, as it’s all localhost :slight_smile: That’s why Nginx Unit has control socket in addition to the request destination.

Default permissions are wrong on it too, and I didn’t see default umask/chmod option in config:

# ls -la /run/caddy.sock
srwxr-xr-x 1 root root 0 Mar 29 07:44 /run/caddy.sock

(others have read/execute perms, i’d suggest srw-------, because if I’m secure because of /run perms, other people might place their socket elsewhere)

But yes, that’s the solution! (if people run cURL 7.40 or higher). So, we’d simply use:
# curl --unix-socket /run/caddy.sock http://localhost/config/

I’d suggest listening on socket by default, because any PHP code injection could change/read Caddy’s config now.

Thanks for the assistance again!

That sounds really dangerous. Why are they not sandboxed?

Erm, Caddy doesn’t make a socket file; you have to do that, so make sure to give it the right permissions. :slight_smile:

We can’t do that because Windows doesn’t support unix sockets.

Are you using PHP on ? If yes, what if someone would find a place to inject:

$curl = curl_init('http://localhost:2019/config/');
$resp = curl_exec($curl);

Would it be sandboxed to not reach the localhost? I guess not :slight_smile:

I didn’t create that socket file, it was created automatically:

    admin unix//run/caddy.sock

Use authentication (for example, client-cert auth) on Windows or other authentication methods :slight_smile:


Right, that’s not sandboxed. If you’re running arbitrary code on the host machine, all security bets are off, unless you take specific steps to mitigate specific vulnerabilities. In general, this is a bad model.

Ohhh I bet the Go standard library is doing it in the call to net.Listen. Still, even if Caddy did create the file, how does it know which permissions to set?

Sure, but that still requires the user to set it up.

PHP wouldn’t run as root :slight_smile: Thus it would be unable to connect to the socket, and that’s why API would be secure, even if 1 user gets arbitrary code inserted, or even if that user runs those commands by himself.

For example, take MariaDB 10.4. You can get into root with just “mysql”, without specifying any password. Something similar to Caddy. The only difference - MariaDB lets you do it from root user only, and requires auth from anyone else (also unix_socket auth method): Authentication in MariaDB 10.4 — Understanding the Changes -

The principle of least privilege. As simple as that. There is a reason why you’re not putting chmod 644 on auto-generated private keys, same logics applies here as well. Just use syscall.Umask(0777) before the creation of the socket.

Better safe than sorry? :slight_smile:

And neither would Caddy… you’re not running Caddy as root, right? I mean, you can – I do on some sites, since it’s pretty hard to pop a shell from a Go program, etc. – but… that’s not good practice.

That’s a rather flippant, snide remark, despite all the help you’ve been receiving for free. What do you want us to do, ship with default authentication credentials? That’s the same as no authentication at all. Our standard threat model assumes that no untrusted code is running on your machine. If there is untrusted code running on your machine, you need to actively configure your other programs to protect against each other, it’s as simple as that and has nothing to do with Caddy specifically. This is true of any programs, on any machines.

You talk about “principle of least privilege” but you do not apply it yourself. If there is untrusted code on your loopback interface, then yes you need to configure authentication – and do a bunch of other things to keep your system secure – things that Caddy nor any other user-space program can defend against “by default”. If arbitrary code is allowed to run in the same space as a web server – or any other sensitive application – then you’ve already lost. Thankfully, the OS and even Caddy allow you to configure permissions/authentication in critical areas, but it cannot do this for you by default. If you allow untrusted code to execute in the same space, that burden to secure your system is on you, pal.

If socket is owned by root - PHP running as user would be unable to reach it. If we allow changing config just by using TCP/IP from localhost - anyone doing connection there could change it.

Don’t get me wrong. I really appreciate all the support/discussions.

No. I was just suggesting using UDS by default on Linux/Unix, and disable API on localhost:2019 by default on other systems if some authentication isn’t setup, if there is no other way to keep it secure. I’m not so sure AF_UNIX comes to Windows - Windows Command Line couldn’t be used on Windows?

When someone mentioned on twitter that they’d like to see Caddy in shared webhosting environments and received your comment, I thought to assist you there and get first control panel (DirectAdmin) ready to support Caddy.

I had no idea that Caddy is not designed for this at that time. It’s simple to solve just this case by changing defaults, however, if Caddy was never designed to run in multi-user environment, it shouldn’t be used on webhosting environments at all. Thank you for the clarification, and thank you again for all the time you’ve dedicated to us, that was really appreciated.

Interesting. Apparently this has been added in Go 1.12.1 net: Support AF_UNIX on Windows10 or later. · Issue #26072 · golang/go · GitHub

To clarify, I think you mean shared webhosting environments. Frankly, I think shared webhosting is a relic of the past. Virtualized environments (KVM, etc) are basically just as cheap at this point and offer significant advantages in terms of security and reliability. I don’t think Caddy is a good fit if you must continue to run platforms in this way.

1 Like

“shared” doesn’t change much there. As it can be a single user, let’s call it “franc”, hosting a couple of different wordpress websites on his own server.

If some plugin let’s you open an url and show it’s content, whole Caddy config could be seen even without any injection.

Anyway, when I first saw that twitter post, I thought Caddy has initiative to see the integrations in most popular control panels (cPanel/Plesk/DirectAdmin etc.). Now I know it just wasn’t designed for it and is designed to run only on the systems with 1 user (and self-domains or CMS you really trust), luckily I haven’t started to code anything for it yet :slight_smile:

@francislavoie thank you for your replies in some other threads too, I was just trying to collect all the information before going straight to coding.