L4 Module - SSH reverse proxy example

I am looking for an example ssh reverse proxy config using the L4 module. The documentation for the module is pretty sparse, and a working example would be helpful.

The machine running caddy will listen on a specific port for ssh, let’s use 2222 and then will reverse proxy that port back to a machine over tailscale (port 22) that I already use for a HTTP reverse proxy (works nicely).

Trying to piece one together from the docs…

what do the following keys actually get used for? They seem more like placeholders for config structure only.

  • “apps”
  • “servers”
  • “example”
{
  "apps": {
    "layer4": {
      "servers": {
        "example": {
          "listen": [
            "0.0.0.0:2222"
          ],
          "routes": [
            {
              "match": [
                {
                  "ssh": []
                }
              ],
              "handle": [
                {
                  "handler": "proxy",
                  "upstreams": [
                    {
                      "dial": [
                        "server_over_tailscale:22"
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Caddy at its core is a “configuration platform”, and it can run one or more “apps” to do whatever you configure it to do (as long as there’s an app compiled in that does what you want, obviously). See the architecture doc to get a deeper explanation:

The layer4 server can have multiple servers configured, so it needs somewhere to hold the config in a structure.

JSON config gets transformed into Go structures, and the layer4 app’s struct looks like this:

This is the name of the server you’re configuring. The name doesn’t really do anything, it’s just a unique key for that server in the config. You can have more than one server.

If you were to use the admin API to push config changes, then knowing the name of the server you want to change is important so you’re not changing the config of the wrong server, etc.

2 Likes

Thank you for the explanation around the configuration structure. Would the example I posted be included below existing configuration for reverse proxies?

nextcloud.frigidcode.dev {
  encode gzip
  reverse_proxy demigod:11000
}

{
  "apps": {
    "layer4": {
      "servers": {
        "example": {
          "listen": [
            "0.0.0.0:2222"
          ],
          "routes": [
            {
              "match": [
                {
                  "ssh": []
                }
              ],
              "handle": [
                {
                  "handler": "proxy",
                  "upstreams": [
                    {
                      "dial": [
                        "server_over_tailscale:22"
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Unfortunately, you can’t mix JSON and Caddyfile config together. You need to either only use Caddyfile or only JSON.

Of course caddy-l4 doesn’t support Caddyfile, so your only option is to use only JSON in this case.

What you need to do is write your Caddyfile for your HTTP server, then run caddy adapt --config <path-to-config> to convert it to JSON.

There’s no way to go from JSON back to Caddyfile, so just keep your Caddyfile around for reference, and it can give you a head-start on what the equivalent thing is in JSON from adapting it.

1 Like

I see that the JSON document can be consumed by Caddy via HTTP to configure the running instance, but how do I use a json file in the same fashion as the Caddyfile? I looked but couldn’t find a way to load it via CLI tooling, and couldn’t find mention of it being read from disk.

The exact same way.

caddy run --config /path/to/your/config.json

To clarify the docs, the default config language for Caddy is JSON, and is how Caddy runs under the hood. But most users use the Caddyfile because it’s much easier to user overall. The Caddyfile is an adapter that turns the config into JSON which Caddy actually runs with.

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