Serve unforwarded port outside of local network

1. The problem I’m having:

I’m often outside of my home network, so I use caddy and freedns.afraid.org to get a free subdomain to access. I want to try serving a port that I don’t wish to forward. Let’s say I have tdarr, and I want to check on the status of it transcoding video files. Now I’d solve this problem by using basic_auth, reverse_proxy, and forwarding port 8265. However, that’s insecure, because someone could still just type in the ip and port, and bypass basic_auth. However, I know there’s a way to serve an unforwarded port, because code-server (self hosted web vscode) has a port proxy where if you access /proxy/port (ie: /proxy/1234), you can access the port, even if you’re outside the home network. All I want to do, is have caddy serve an unforwarded port, so that I can minimize security risk.

2. Error messages and/or full log output:

N/A

3. Caddy version:

v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=
(xcaddy build v2.7.6 --with GitHub - caddyserver/replace-response: Caddy module that performs replacements in response bodies)

4. How I installed and ran Caddy:

I edited a caddy AUR package and made it build with replace-response instead of naiveproxy

a. System environment:

Distro: Arch Linux
Kernel: 6.8.7-hardened1-2-hardened
Caddy running bare-metal, no docker
Thinkpad T440

b. Command:

sudo systemctl enable --now caddy

c. Service/unit/compose file:

[Unit]
Description=Caddy webserver
Documentation=https://caddyserver.com/docs/
After=network-online.target
Wants=network-online.target systemd-networkd-wait-online.service
StartLimitIntervalSec=14400
StartLimitBurst=100

[Service]
User=caddy
Group=caddy

# environment: store secrets here such as API tokens
EnvironmentFile=-/var/lib/caddy/envfile
# data directory: uses $XDG_DATA_HOME/caddy
# TLS certificates and other assets are stored here
Environment=XDG_DATA_HOME=/var/lib
# config directory: uses $XDG_CONFIG_HOME/caddy
Environment=XDG_CONFIG_HOME=/etc

# do not print --environ here, as it may contain API tokens!!
ExecStart=/usr/bin/caddy run --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile

# Do not allow the process to be restarted in a tight loop.
Restart=on-abnormal

# Use graceful shutdown with a reasonable timeout
KillMode=mixed
KillSignal=SIGQUIT
TimeoutStopSec=5s

# Sufficient resource limits
LimitNOFILE=1048576
LimitNPROC=512

# Grants binding to port 443...
AmbientCapabilities=CAP_NET_BIND_SERVICE
# ...and limits potentially inherited capabilities to this
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

# Hardening options
LockPersonality=true
NoNewPrivileges=true

PrivateTmp=true
PrivateDevices=true

ProtectControlGroups=true
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectSystem=strict

ReadWritePaths=/var/lib/caddy
ReadOnlyPaths=/etc/caddy
ReadOnlyPaths=-/var/lib/caddy/envfile

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

N/A
// what i'd like to do is reverse_proxy localhost:port
// but that doesnt work outside the network
// i think that when you do localhost:port
// it accesses that same link on the user's network
// instead of the host's network

Hi @SuspiciousDuck, welcome to the Caddy community.

This isn’t how it works in practice.

If Caddy is listening on port 443 (and that port is forwarded), and you reverse proxy to an internal host on port 8265, you do not need port 8265 forwarded externally. That proxy connection would happen within the local network, out of sight of the client talking to Caddy.

To be absolutely clear, in a reverse proxy situation, it doesn’t matter what you (the client) have access to. The only things that matter are that you have access to Caddy, and that Caddy has access to the internal resource. You do not need direct access to the internal resource as the request is routed through Caddy.

This is in fact arguably the major use case for reverse-proxy software and it would be quite a problem if it didn’t work this way!

If you can’t access Caddy outside of your local network, that will probably need to be troubleshooted.

If you can access Caddy inside and outside your local network, but the reverse proxy only works inside the local network - there’s something very strange going on and that will need to be troubleshooted as well.

First, I’d like to thank you so much for giving an in-depth response. Fortunately, there’s no critical bug or something that caused this. In all the documentation, I always saw localhost being used for reverse proxies. When I first tried using caddy (in docker compose), I did exactly that, and it didn’t work. It was also my first time using docker as well, so I didn’t know that caddy couldn’t access the ports I was referencing to. This led me to believe that I had to port forward. Since then, I’ve learned more about docker and have also switched to running caddy uncontainerized. But I never got to clear up that misconception however, which led to this post here. Thanks to you clearing up everything about reverse proxies, I tested it now (using local ip just to be safe) and it looks like it works. I kinda feel bad that it was such a simple solution. Nevertheless, I’d really like to see a small change in the documentation, so that others wont make my mistake. Maybe a small tip box that says “In Docker, using localhost may not work, try using a local ip instead.”

1 Like

No worries! I’m glad to hear you’ve got a win now.

We’ll take that advice on board regarding the documentation. If I may ask - where specifically in the documentation would it have helped you the most to find that Docker-specific tip? Where do you think it makes the most sense to put that?

I think it should happen next to the examples where the documentation directs to use “localhost”. In the quick-start reverse_proxy docs specifically, it says to use :9000, which is essentially the same as localhost I presume. It also says to use localhost in quick-start https. I’m sure there’s other references to localhost, but it probably wouldn’t make sense to write it every time its mentioned. Maybe specifically in the quick-start sections and the reverse_proxy api docs should the notice be put. Those are the spots I would look if I were getting started with caddy/reverse proxies.