Reverse Proxy Game Servers

1. Caddy version: 2.5.1

2. How I installed, and run Caddy: Systemd service via apt on Ubuntu server

a. System environment:

Ubuntu Server LXC in Proxmox

b. Command:

systemctl start caddy.service

c. Service/unit/compose file:

caddy.service - Caddy
     Loaded: loaded (/lib/systemd/system/caddy.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-01-20 16:05:21 CST; 6h ago
       Docs: https://caddyserver.com/docs/
   Main PID: 146 (caddy)
      Tasks: 18 (limit: 9261)
     Memory: 29.0M
        CPU: 8.950s
     CGroup: /system.slice/caddy.service
             `-146 /usr/bin/caddy run --environ --config /etc/caddy/Caddyfile

d. My complete Caddy config:

{
        debug
        email austin.eschweiler@gmail.com
}

(headers) {
        header {
                Strict-Transport-Security max-age=31536000
                X-Content-Type-Options nosniff
                X-Frame-Options DENY
                Referrer-Policy no-referrer-when-downgrade
                X-XSS-Protection 1
        }
}

(trustedproxy) {
        trusted_proxies 172.16.0.0/24 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22
}

bookshelf.$DOMAIN {
        tls internal
        encode gzip zstd
        reverse_proxy 172.16.0.231:13378 {
                import trustedproxy
        }
}

drive.$DOMAIN {
        tls internal
        header {
                Strict-Transport-Security max-age=31536000
                X-Content-Type-Options nosniff
                X-Frame-Options SAMEORIGIN
                Referrer-Policy no-referrer-when-downgrade
                X-XSS-Protection 1; mode=block
        }
        reverse_proxy 172.16.0.135:8080 {
                import trustedproxy
        }
}

foundry.$DOMAIN {
        tls internal
        encode gzip zstd
        reverse_proxy 172.16.0.127:35205 {
                import trustedproxy
        }
}

jellyfin.$DOMAIN {
        tls internal
        encode gzip zstd
        reverse_proxy 172.16.0.115:8096 {
                import trustedproxy
        }
}

reading.$DOMAIN {
        tls internal
        encode gzip zstd
        reverse_proxy 172.16.0.231:8083 {
                import trustedproxy
        }
}

satisfactory.$DOMAIN {
        tls internal
        reverse_proxy udp/172.16.0.238:7777 {
                import trustedproxy
        }
}

valheim.$DOMAIN {
        tls internal
        reverse_proxy 172.16.0.238:2456 {
                import trustedproxy
        }
}

3. The problem I’m having:

I’m trying to reverse proxy a Satisfactory server. I swear I was able to do it before but after nuking a setup I’m unable to get it working again.

4. Error messages and/or full log output:

Jan 20 18:19:08 caddy caddy[146]: {"level":"warn","ts":1674260348.1488008,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [satisfactory.$DOMAIN]: no OCSP server specified in certificate","identifiers":["satisfactory.$DOMAIN"]}
Jan 20 18:19:08 caddy caddy[146]: {"level":"debug","ts":1674260348.14881,"logger":"tls.cache","msg":"added certificate to cache","subjects":["satisfactory.$DOMAIN"],"expiration":1674302526,"managed":true,"issuer_key":"local","hash":"6c6dc10649aac4cb54bfade29ff8f9803c21785a3cb8ab4a0eb5e1d1414a689c","cache_size":4,"cache_capacity":10000}

5. What I already tried:

Tried on both 443 and 80 for incoming traffic, as both are routed through Caddy. Also tried adding udp/ to the internal URL to no avail.

I am not familiar with Satisfactory, but from what I can tell it isn’t using http nor TLS.
If that’s the case, you could still, in theory, use GitHub - mholt/caddy-l4: Layer 4 (TCP/UDP) app for Caddy

Though, it wouldn’t make much sense.

Any particular reason why you want to put Caddy in front of your Satisfactory server? :eyes:

So I don’t need to open up more ports and people joining can easily connect instead of managing my non-static IP address.

Please upgrade to the latest version of Caddy, v2.6.2

I don’t think this makes sense. HTTP over UDP is only a thing with HTTP/3, but Caddy’s proxy doesn’t support HTTP/3 yet (Caddy only handles it as a server, but not as a client to another upstream).

But are you sure this game server is actually HTTP and not some other TCP or UDP protocol?

You haven’t shown any relevant logs or evidence that show an actual problem. You just said

That doesn’t tell us anything about what might be the problem.

Whoops, updated.

I’m not, I was hoping somebody could point me in the right direction with that. I’ve tried both Satisfactory and Valheim to no success.

Looking at Dedicated servers - Official Satisfactory Wiki, it definitely looks like it’s UDP, so I’m pretty sure it’s not HTTP.

You won’t be able to use vanilla Caddy to proxy it. Caddy ships with an HTTP server and proxy.

If you want to proxy raw TCP or UDP, then you’d need to use caddy-l4, as mentioned earlier.

Ah ok, thanks for the help.

1 Like

Since I have your ear, could you help me figure out why my json isn’t working? I’ve been trying to go through the documentation of the module but I think I’ve done something wrong.

        "layer4": {
            "servers": {
                "satisfactory": {
                    "listen": [":971"],
                    "routes": [
                        {
                            "match": [
                                {
                                    "http": [
                                        {"host": ["satisfactory.eschbach.house"]}
                                    ]
                                }
                            ],
                            "handle": [
                                {
                                    "handler": "proxy",
                                    "upstreams": [
                                        {"dial": ["172.16.0.238:7777"]}
                                    ]
                                }
                            ]
                        }
                    ]
                }
            }
        }

You can’t use the http matcher, because it’s not HTTP!

In this case, since you’re serving it on a different port, just remove the matcher altogether.

When posting config/code on the forums, don’t use the quote button, use the preformatted text button (the </> one)

Oh man, copy/paste from examples foiled again, it’s definitely Friday. I’ll see if that does it.

Thanks for the patience.

1 Like

It doesn’t appear to be working still. I’ve confirmed the port is right, but it’s still not letting me connect via satisfactory.eschbach.house through port 971 (which I’ve verified is forwarded in my firewall to Caddy).

Any extra thoughts before I just say “forget it” for now?

        "layer4": {
            "servers": {
                "satisfactory": {
                    "listen": [":971"],
                    "routes": [
                        {
                            "handle": [
                                {
                                    "handler": "proxy",
                                    "upstreams": [
                                        {
                                            "dial": [
                                                "172.16.0.238:15777"
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            }
        }

We’ll need more detail than “it’s not working”.

What’s in your logs? What behaviour are you seeing, exactly?

Why do you need a proxy, anyway? Why not just expose the game server publicly? The proxy isn’t giving you any benefit here, it doesn’t add any kind of security at all.

The only thing that shows up in logs is

2023/01/30 04:17:53.829 DEBUG   layer4  listening       {"address": "tcp/[::]:971"}

Nothing follows for layer4

I don’t have a static IP, so instead of constantly updating my friends with whatever the new IP address is when it changes, it’s easier to just have a domain name.

You don’t need a proxy to do that. You just need a tool to continually update your DNS records as your IP address changes.

Right, and I do (via an add-on in Home Assistant), but I need a way to direct the incoming traffic to the server itself.

You can do that with your router’s port forwarding.

Yeah, but I try to limit the number of ports I have open since I also host a few services in my server rack. If Caddy isn’t able to manage it then I guess I’ll do it that way while I find a different solution.

I don’t think the problem is with Caddy, I just think it’ll be hard to diagnose the issue because I don’t use caddy-l4 myself (I don’t have a usecase for it personally) and since you don’t have much experience with this stuff either, it’ll be hard to guide you through it without spending a lot more time and effort to figure it out.

I don’t think you need to worry about limiting the amount of ports you’re using. That’s not really a relevant metric. Each port can serve its own purpose.

Since it sounds like you’re only using this game server for a small group of friends, I don’t think you have much to worry about. If you were going to make this a public server (i.e. advertising that it exists in public discords or whatever), then you might have to worry about some less friendly people trying to find exploits in the game server’s logic to mess with it.

Yeah that’s valid, it’s going to just be me and a few friends/family.

If I was ever to do a bigger public server, I’d probably put it on a VPS anyway, instead of the rack in my basement.

Appreciate all the help, and the bright side is that I get to go back to a Caddyfile instead of JSON!

1 Like