caddy-l4 icon indicating copy to clipboard operation
caddy-l4 copied to clipboard

Feature Request: Support Caddyfile

Open ghost opened this issue 4 years ago • 29 comments

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.

ghost avatar Feb 02 '21 17:02 ghost

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)

francislavoie avatar Feb 02 '21 17:02 francislavoie

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

mholt avatar Feb 02 '21 19:02 mholt

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.

binaryben avatar Jun 04 '21 02:06 binaryben

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!

Piehthyte avatar Nov 20 '21 05:11 Piehthyte

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.

mholt avatar Nov 21 '21 06:11 mholt

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.

Piehthyte avatar Nov 26 '21 18:11 Piehthyte

It's a big bite to chew if you're new to Go, but I won't stop you. Good luck!

mholt avatar Nov 26 '21 19:11 mholt

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.

RussellLuo avatar Jan 19 '22 07:01 RussellLuo

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 😄

francislavoie avatar Jan 19 '22 11:01 francislavoie

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.

CEbbinghaus avatar Jan 22 '22 12:01 CEbbinghaus

@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.

mholt avatar Jan 24 '22 19:01 mholt

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 avatar Jan 25 '22 06:01 RussellLuo

@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.)

mholt avatar Jan 26 '22 16:01 mholt

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.

francislavoie avatar Jan 26 '22 16:01 francislavoie

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.

mholt avatar Jan 26 '22 16:01 mholt

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

CEbbinghaus avatar Apr 26 '22 17:04 CEbbinghaus

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.

KiARC avatar Jun 06 '23 14:06 KiARC

(Remember there is a start of an implementation linked above you can try/use for now!)

mholt avatar Jun 06 '23 14:06 mholt

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.

voltagex avatar Sep 05 '23 03:09 voltagex

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
}

AndreKR avatar Sep 24 '23 20:09 AndreKR

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

ausdrew avatar Oct 01 '23 11:10 ausdrew