caddy-l4
                                
                                 caddy-l4 copied to clipboard
                                
                                    caddy-l4 copied to clipboard
                            
                            
                            
                        Feature Request: Support Caddyfile
I'm creating this issue just to be able to gauge support for Caddyfile being supported in addition to JSON. I'm sure this is already a niche plugin/module and the desire for Caddyfile support is even more niche within that niche...but nonetheless.
I think https://github.com/caddyserver/caddy/pull/3990 should theoretically make this possible, but uh, it'll still be pretty awkward because it'll be configured via global options, if taking that route.
Ultimately, I think this plugin would need to provide its own config adapter that works similarly to the Caddyfile, but the top-level bits will likely need to work differently. It's not a simple thing to implement though. Maybe call it "Connfile" or "L4file" 😂
The Caddyfile adapter that ships with vanilla Caddy is pretty tightly bound to the http app, which isn't really compatible with the way caddy-l4 is configured (via a layer4 Caddy app, that looks kinda similar to the http app)
The Caddyfile format is itself extensible, and is designed to be interpreted by modules other than the http server type. So this is very doable, just needs to be done. I've been very busy lately but we'll see where the sponsorships sway. (Right now they're very heavily leaning in the mainstream Caddy direction.)
The docs at the bottom of this page are relevant: https://caddyserver.com/docs/extending-caddy/caddyfile#server-types
Will be awesome to be able to use with the reverse proxy docker image. There is an opposite request for JSON support in that instead, but Caddyfile support in this would be awesome.
Hi guys, I'm so sorry to necro this, I just wanted to put my support behind this as well, but.... Thank you so much, mholt, for the fantastic work on Caddy. It's really quite awesome(almost insane). I hope some day we do get Caddyfile support for this module, but no stress and for sure no rush. You and all the module makers are doing phenomenal work. Thank y'all!
Thanks for the feedback, and the interest in this.
I suppose anyone is welcome to contribute this, it would probably be faster than waiting for me to do it on my own. It's not a small task by any means, but it doesn't have to be as crazy complex as the HTTP Caddyfile, either.
Thanks for the feedback, and the interest in this.
I suppose anyone is welcome to contribute this, it would probably be faster than waiting for me to do it on my own. It's not a small task by any means, but it doesn't have to be as crazy complex as the HTTP Caddyfile, either.
I am new to go, but I will give it a shot! It'll be a good learning experience.
It's a big bite to chew if you're new to Go, but I won't stop you. Good luck!
Since the best format of multi-app Caddyfile still needs a further discussion (https://github.com/caddyserver/caddy/pull/3990), here is a quick implementation via global options (although not recommended): caddy-ext/layer4.
Thanks @RussellLuo! That's a good start. I agree it probably makes sense to have that support as a plugin outside of caddy-l4 for now since there's still room for eventually having proper Caddyfile support as not-global-options 😅
Basically, to get this to actually work properly, the Caddyfile adapter in Caddy's main repo will need significant work to make it more flexible. Right now, too many features are tied to the HTTP Caddyfile so it's not quite viable.
A shower-thought, I think we could add a feature to the core Caddyfile adapter so that passes control of parsing a site block to an alternate implementation (i.e. not HTTP) based on the schemes used in the site address. For example, caddy-l4 could register itself as a handler for tcp:// and udp:// schemes, then a site block with addresses like tcp://:8080, udp://:8080 { would get passed to the caddy-l4 Caddyfile adapter. Obviously the default HTTP Caddyfile would register itself for http:// and https://, but also be the default when no scheme is given. If unknown schemes are seen, or not all schemes in a site address match a single adapter, then it would error out (i.e. no mixing HTTP and L4 addresses for a single site).
I haven't tried implementing this yet cause I don't feel I have time to see this through to the end right now, but unfortunately I feel like Matt or I are probably the ones most equipped to implement this. I still want to welcome enterprising individuals to take a crack at it, but I just have to warn that uh, it's ambitious 😄
I think we could add a feature to the core Caddyfile adapter so that passes control of parsing a site block to an alternate implementation (i.e. not HTTP) based on the schemes used in the site address. For example, caddy-l4 could register itself as a handler for tcp:// and udp:// schemes
This is exactly what I was thinking or at least something akin to it. Although I 100% think that the same subdomain should be able to have both caddy-l4 as well as http caddy rules so a webserver could be run on port 80 while a game server is run on its native port.
I am very invested to see this implemented and ideally as unobstructively to the caddyfile itself as possible so ideally, it becomes a drop-in replacement with only minor or no changes having to be made.
+1 👍🏼 100/10 for that idea.
@francislavoie
Right now, too many features are tied to the HTTP Caddyfile so it's not quite viable.
I think it depends what you need to do. Currently, the caddyfile package has all you need to parse a generic Caddyfile. It's up to each individual server type to make the blocks and directives meaningful. We've kind of hacked the "global options" together in the HTTP Caddyfile and it has been used to configure other apps that "support" the HTTP server, but yeah, there's no reason it can't be used for other server types entirely... I guess.
A shower-thought, I think we could add a feature to the core Caddyfile adapter so that passes control of parsing a site block to an alternate implementation (i.e. not HTTP) based on the schemes used in the site address. For example, ...
It's an interesting idea, but I'm afraid it would be a little too limiting. For example, "tcp" is not different from "http", rather, it is a superset of "http"; more precisely, HTTP is served over TCP (and UDP for HTTP/3 -- which is even more confusing), and TCP can be used for other protocols/applications like SSH, etc. I'm not super keen on making server types implicit. At least not for now.
For now, I think the best approach for an L4 caddyfile is to either implement it as its own proper ServerType, or as a global option in the HTTP Caddyfile. Major kudos to @RussellLuo for doing the latter, that's awesome.
AFAIK, Nginx uses two blocks http and stream:
http {
    #...
    server {
        #...
    }
}
   
stream {
    #...
    server {
        #...
    }
}
almost the same idea as described by Matt.
Given that there are some general options (e.g. logging) for all apps, this may be a possible format of the new Caddyfile:
# General options
{
        debug
}
# HTTP app
http {
        example1.com {
                root * /www/example.com
                file_server
        }
        example2.com {
                reverse_proxy localhost:9000
        }
}
# Layer4 app
layer4 {
        :7070 {
                proxy localhost:8080
        }
}
@RussellLuo That's actually pretty close to what I wish I had done for Caddy 2. But it was too late.
Nothing's stopping anyone from making a Caddyfile like that though! caddy adapt --adapter multicaddyfile or something like that. (I don't love the adapter name "multicaddyfile" but you get the idea.)
Nothing's stopping anyone from making a Caddyfile like that though!
Actually, what is stopping it is that tons of the code for registering directives and global options are within the httpcaddyfile package. It will require refactoring in caddyserver/caddy for anything like this to be possible.
A config adapter doesn't always have to be super extensible, even a different Caddyfile -- I don't think this one will need that. It's a much smaller-scoped project.
I feel a completely separate file that follows the caddy file layout but defines the rules for L4 would be an excellent compromise. wouldn't require a refactor of the caddy project and would have all the benefits of a familiar layout. the only pain would be having to swap back and fourth between the two files to configure sites on both however that seems like a minor price to pay for the ability to use this great package
Just going to add that I would also love to see this functionality, I'm currently trying to run a VPN in a docker container and using this alongside lucaslorentz/caddy-docker-proxy would be so much better than actually binding a bunch of ports directly to the container.
(Remember there is a start of an implementation linked above you can try/use for now!)
caddy-l4 could register itself as a handler for tcp:// and udp:// schemes, then a site block with addresses like tcp://:8080, udp://:8080 { would get passed to the caddy-l4 Caddyfile adapter.
I really like that as a design.
I've run across this issue again as the Caddy JSON format has never really clicked with me and I have a need for Caddy's automatic TLS wrapping another application.
At least as a temporary solution, would it be possible to put a chunk of JSON into the Caddyfile that is then forwarded to the respective module? This way you could continue using the Caddyfile for HTTP and don't need to switch to JSON just to use the layer4 module.
Something like this:
{
  layer4 {
    json `
      {
        "servers": {
          "example": {
            "listen": [ "127.0.0.1:5000" ],
            "routes": [
              {
                "handle": [
                  { "handler": "echo" }
                ]
              }
            ]
          }
        }
      }
    `
  }
}
mydomain.com {
  root /var/www
}
In case this is helpful for anyone else - an alternative approach is to convert your caddyfile to json and then add the layer4 details, I've built this into my deployment actions too.
My repository has a caddy folder with the following files:
./caddy -- caddyfile (my regular caddyfile with my server's configuration) -- build-caddy.sh (the below script) -- layer4.txt (my configuration for l4)
#!/bin/bash
echo "Removing existing caddy.json file"
rm ./caddy.json
echo "Formatting Caddyfile"
caddy fmt --overwrite ./caddyfile
echo "Generating caddy.json file"
caddy adapt --config ./caddyfile --pretty --validate > caddy.json
echo "Reading layer4.txt"
layer4_content=$(<./layer4.txt)
echo "Inserting layer4 content into caddy.json"
jq --argjson layer4 "$layer4_content" '.apps.layer4 = $layer4' ./caddy.json > ./temp.json && mv ./temp.json ./caddy.json