On_demand_tls with dynamic content paths

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

I’m only running this on localhost on my raspberry pi while I learn and develop.

a. System environment:

Linux raspberrypi 5.15.76-v71+ #1597

b. Command:

sudo caddy run

c. Service/unit/compose file:

N/A

d. My complete Caddy config:

{
        on_demand_tls {
                ask http://localhost/check
                interval 2m
                burst 5
        }
}

http://localhost {
        route /check {
                # I had to `ln -s /bin/sh /bin/exec` to get this to work.
                exec /check -c /bin/ls /var/www/{query.domain} {
                        foreground true
                        status 404 on error
                }
        }
}

https:// {
        tls {
                on_demand
        }

        file_server *

        route * {
                root * /var/www/{host}
        }
}

3. The problem I’m having:

I am trying to do the following as simply as possible (Caddyfile?):

  • On-demand TLS.
    • Only if the content directory matching the request host exists.
  • Serve content from directories that match the host being requested if it exists. If the directory does not exist then redirect to another URL. I haven’t gotten to the redirect yet.

I’m trying to make a simple/easy way to host multiple sites under a single instance of caddy.

Maybe I’m going about it all wrong?

4. Error messages and/or full log output:

2022/12/15 03:29:38.131 INFO    http.handlers.exec.exit         {"command": ["exec", "-c", "/bin/ls", "/var/www/abc.com"], "duration": 0.012492108}
2022/12/15 03:29:40.647 INFO    http.handlers.exec.exit         {"command": ["exec", "-c", "/bin/ls", "/var/www/abc.com"], "duration": 0.012492108}
  • The first log line was from a request to /check?domain=abc.com. And returned the correct error message.
  • The second log line was from a request to /check?domain=localhost. This returns the same error but should have been successful.

5. What I already tried:

  • I’ve tried using an updated exec plugin from git001 that supports passing query string values to exec to run ls to check for the existence of the content directory but this only works once and then the same query string/domain is passed every time thereafter. Also, I can see how running ls /var/www/{query.domain} could potentially be a security issue by including unsanitized user input in the command.

  • I’ve tried looking to see if I could “refresh” the exec command.

  • I’ve tried looking to see if I could force caddy to not cache the command/query string.

  • I’ve tried looking to see if I could set an env variable and pass it to the exec command.

6. Links to relevant resources:

Howdy @richcorbs, welcome to the Caddy community.

I remember chatting about this exact use case somewhere not too long ago… can’t remember where, exactly. Curious, though.

I’d just use the file matcher instead of calling an executable.

The below example is completely untested.

{
  on_demand_tls {
    ask http://localhost/check
  }
}

http://localhost/check {
  root * /var/www
  @deny not file /{query.domain}/
  respond @deny 404
}

https:// {
  tls {
    on_demand
  }

  @redirect not file /var/www/{host}/
  redir @redirect https://www.google.com

  root /var/www
  rewrite /{host}{uri}
  file_server
}

Reckon the redirect won’t work, anyway, because the automatic HTTP->S upgrade listener will only ever issue redirects to HTTPS, and the HTTPS listener will simply refuse to send a certificate if it doesn’t have one, which it wont if your ask endpoint doesn’t allow it, so the HTTPS connection will end at the handshake stage and won’t ever get to the point of issuing that redirect.

2 Likes

Thank you @Whitestrake for the welcome and response.

Looks like I have more learning to do:

  • deny and redirect are variables set to the value of their matchers.
  • deny and redirect are used to respond and redir conditionally.

Am I on the right track?

On my system, only /var/www/localhost exists.

I get a 404 response every time I run curl http://localhost/check?domain=<domain> -v, even when domain is localhost. I even tried hard-coding the deny file matcher to /var/www/localhost which exists and should always return true.

1 Like

Absolutely! We refer to these as named matchers rather than variables, though.

Have a look here: https://caddyserver.com/docs/caddyfile/matchers#named-matchers

That’s my fault, I made a silly mistake.

I set no site root, and then tried to check /var/www/{query.domain}/ inside the implicit site root; not gonna fly, because Caddy checks this against the relative file path (from the site root) rather than the absolute location on the file system. This is because the file matcher is typically used to check whether a supplied URI will locate a real file within the configured root, normally.

Try this instead:

http://localhost/check {
  root * /var/www
  @deny not file /{query.domain}/
  respond @deny 404
}
2 Likes

@Whitestrake You’re awesome. Thank you!

It makes perfect sense now and works.

2 Likes

Glad to hear it!

I’ll should throw in fair warning, as well, that including a URI in the site address (e.g. http://localhost/check above) is technically deprecated behaviour, and the strictly correct method of doing this would be:

http://localhost {
  handle /check {
    # Site config goes here
  }
}

And Caddy should output messages to advise you as such if you start it with a Caddyfile with a URI in the site address.

That said, I use the deprecated method regardless of the warning because I think it’s better. /shrug

2 Likes

This topic was automatically closed after 30 days. New replies are no longer allowed.