For legibility I split the post into: my current setup; the problem Iām trying to solve; the constraints for solving the problem; what Iāve tried and failed to do; and key questions.
When roasting me in the comments, go nuts, Iām not a complete beginner, but I wouldnāt rank myself as an intermediate yet. My lab is almost entirely tteck scripts, and what isnāt built by tteck are docker containers. My inexperience informs some of my decisions for example: Iām using nginxproxymanager because Nginx documentation is beyond me, I couldnāt write a nginx.config and NPM makes reverse proxies accessible to me.
My Current setup
I have a Proxmox based home server running multiple services as LXCs (a servarr, jellyfin, immich, syncthing, paperless, etc. Locally my fiancĆ©e and I connect to our services. Using pihole-NginxProxyManager(NPM) @ āservice.serverā and thatās good. Remotely we connect to key services over tailscale using tailscaleās magic DNS @ ālxcname:portā and that worksā¦ fine. We each have a list of āservice: addressā and itās tolerable. Finally, my parents have a home server, that I manage, it is Debian based with much the same services running all in Docker (I need to move it to Podman, but I got shit to do). We run each othersā off-site backup over tailscale-syncthing and that seems good. But, our media and photos are our own ecosystems.
The Problem
I would like to give someone (Bob) a box (a Pi, a minipc, a whatever). The sole function of this box is to act as a gateway for Bobās devices to connect to key LXCs on my tailnet. Thus Bob can enjoy my legally obtained media and back up their photos.
The constraints
These are in order of importance, I would be giving ground from the bottom up. The top two are non negotiable though.
A VPS has low to zero WAF. Otherwise I would have followed the well trodden ground.
Failsafe. If the box dies bob canāt access jellyfin until I can be arsed to fix it. Otherwise, they experience no other inconvenience.
No requirement to install tailscale on Bobās devices. Some devices arenāt compatible with tailscale: Amazon fire stick. A different bob doesāt want to install a VPN on their phone. Some devices I donāt trust to be up to date and secure, I donāt want them on my tailnetā¦ I have no idea if the one degree of separation is any more secure, but it gives me the willies.
Iām pretty sure I can solve this using pihole-nginx-tailscale with my skillset. But then I have to get into bobās router, and maybe bob might not like that. If I could just give them a preconfigured box that would be ideal. They would have pretty addresses though.
I donāt currently have a domain, I do plan to get one. I just donāt currently have one.
My attempts and failures to solve the problem.
Iāve built a little VM to act as a box (box), it requests a static IP. On it I installed Mint (production would probably be DietPi or Debian) Tailscale,Docker (bare metal) and NPM as a container. In NPM I set a proxy host 192.168.box.IP to forward to 100.jellyfin.tailscale.IP:8096. I tested it by going to box.IP and jellyfin works. Next up Jellyseerrā¦ I canāt make another proxy host with the same domain name for obvious reasons.
I tried ābox.IP:8096ā as a domain name and NPM rejected it. I tried ābox.IP/jellyfinā and NPM rejected that too (Iāll try Locations in a bit). I tried both āservice.box.IPā and ābox.IP.serviceā and Iād obviously need to set up DNS for that. Look, Iām an idiot, I make no apologies. I know I can solve it by getting into their router, setting Pihole as their DNS, and going that route.
Next I tried Locations. The required hostname and port I set up as jellyfin.lxc.tailnet.IP:8096 and I set /jellyseerr to go to jellyseerr.lxc.tailnet.IP and immich set up the same way. Then I tested the services. Jellyfin works. Jellyseerr connects then immediately rewrites the URL from ābox.IP/jellyseerrā to ābox.IP/loginā and then hangs. Immich does much the same thing. In desperation I asked chatGPTā¦ the less said about that the better. Just know Iāve been at this a while.
Hereās where Iām at: I have two Google terms left to learn about in an attempt to solve this. The first is āIP tablesā the second is ātailscale subnet routersā and I have effort left to learn about one of them.
During this process I learned I could solve this problem thusly: give Bob a box. On this box is a number of virtual machines(vm). Each vm is dedicated to a single service, and what the fuck is that for a solution?! It would satisfy my all of my constraints though, its just ugly.
Key questions
Is my problem solvable by just giving someone a Pi with the setup pre-installed? If not Iāll go the pihole-npm-tailnet and be happy. Bobāll connect to āservice.boxā and itāll proxy to āservice.lxc.tailnet.IPā.
Assuming I can give them a box. Is nginx the way forward? Should I be learning /Locations configs to stop jellyseerrās rewrite request. Forcing it to go to ābox.IP/jellyseerr/loginā. Or, is there some other Google term I should be learning about.
Asssuming I can give them a box, and nginx alone is not useful to me. Is it subnet routers I should be learning about? They seem like a promising solution, but Iāll need to learn how the addressing worksā¦ Or how any of it worksā¦ IP tables seem like another solution on the face of it. But both I donāt know where to send bob without doing local DNS/CNAME shenanigans
Finally assuming Iām completely in the weeds and hopelessly lostā¦ What is it I should I be learning about? A VPS I guessā¦ Thereās a reason everyone is going that route., Documentation on this āboxā concept isnāt readily findable for a reason I imagine.
In NPM I set a proxy host 192.168.box.IP to forward to 100.jellyfin.tailscale.IP:8096. I tested it by going to box.IP and jellyfin works.
Iām not surprised this worked, numbers are allowed in FQDNs, but an IP address is not entirely equivalent.
I tried ābox.IP:8096ā as a domain name and NPM rejected it. I tried ābox.IP/jellyfinā and NPM rejected that too (Iāll try Locations in a bit)
I would strongly suggest you to read up on the OSI model.
Nginx only understands HTTP and HTTPS requests at Layer 7 (implicitly and strictly ports :80 and :443), and forwards or redirects them to Layer 4 destinations. (Nginx can technically handle other protocol requests via plugins, but that isnāt what you are looking for.)
In NPM, the proxy host name should at least contain the Raspberry Piās hostname, e.g.
jellyfin.your-rpi-name
. Or you could use the path location option, e.g.your-rpi-name
with location/jellyfin
. (I think the second option might work with network hostname auto-discovery, in which case pihole as a DNS may not be strictly necessary.)Thank you for the reading material, itāll be tonight project. I think Iām just going to tell people if they want to join in the family immich/mealie/etc theyāll just have to let me into their router. Theyāll get memorable addresses out of it and adblocking too. Iām pretty sure that setup is comfortably within my skill set. I thought long and hard about opening ports but the security needed is beyond me currently. Down side is cost and Iāll be managing a bunch of boxe. But I can add updating them into the monthly maintenance and if/when they come back they can be repurposed into other projects.
I tried /locations but my service would rewrite the URL and break itself. Iād navigate to ābox.ip/immichā and immich would change the address to ābox.ip/loginā and hang.
Iād need to learn how to have npm lock ābox.ip/immichā and let immich append ā/loginā. Iāll leave my test VM up and just chip away at it. I think I need the ārewriteā flag but Iām getting dangerously close to just learning how to write an nginx config instead of having npm do it for me.
Thanks again for the pointers
I donāt know a lot about tailscale, but I think thatās likely not relevant to whatās possible (but maybe relevant to how to accomplish it).
It sounds like the main issue here is dns. If you wanted to/were okay with just IP based connections, then you could assign each service to a different port on Bobās box, and then have nginx point those ports at the relevant services. This should be very easy to do with a raw nginx config. I could write one for you if you wanted. Itās pretty easy if youāre not dealing with https/certificates (in which case this method wonāt work anyway).
Looking quickly on google for npm (which Iāve never used), this might require adding the ports to the docker config and then using that port in npn (Like here). This is likely the simplest solution.
If you want hostnames/https, then you need some sort of DNS. This is a bit harder. You can take over their router like you suggested. You could use public DNS that points at a private IP (this is the only way Iām suggesting to get public trusted ssl certificates).
You might be able to use mdns to get local DNS at Bobās house automatically, which would be very clean. Youād basically register like
jellyseer.local
andjellyfin.local
on Bobās network from the box and then setup the proxy manager to proxy based on those domains. You might be able to just doavahi-publish -a -R jellyseer.local 192.168.box.ip
and thenavahi-publish -a -R jellyfin.local 192.168.box.ip
. And then any client that supports mdns/avahi will be able to find the service at that host. You can then register those names nginx/npn and I think things should just workTo answer your questions directly
- Yes I think just a box can work
- I think locations are going to be a nightmare. donāt use them if you can avoid it. Also plain nginx really isnāt so bad, and you can learn pretty quickly. this is probably fine in npn though
- I donāt think you need to dive into iptables or anything like that. Iptables would provide a lower level proxying (level 3 instead of like level 7), which could be useful if youāre running non http services, but isnāt necessary for 99% of web stuff.
- I think part of the problem might be working with higher level systems like npn, but a lot of it is just that networking involves so many layers and there are multiple solutions to any problem, all of which require knowing somewhat whatās happening under the surface to understand why theyāre failing
Iād be happy to try and give more specifics if you choose a path similar to one of the above things.
My initial inception of this box was to have it request a static IP so I knew ābox.ipā. Then tape then tape some thing like this:
Box.ip Service1:port Service2:port ā¦
Onto the case. Then in NPM have it proxy requests to ābox.ip:8096ā to ātailscale.ip:8096ā. But alas, I couldnāt figure it out. I could get 1 service to work but not multiple.
I couldnāt ask someone to write the config for me, but if youāre certain itās doable then Iāll learn to write a config. Thank you for the offer. Iām guessing for each service I tell nginx to ālistenā at āportā instead of only listening to ports 80,443 and 81.
MDNS seems like an interesting solution though, Iām going to read about that now actually, thank you for highlighting that solution to me. If I could get that working that would be ideal. Iāll have to check if the expected devices are compatible but that would make everyoneās life easier if I could just setup a cronjob on startup.
Can you share what the final desired goal is? It sounds like your goal is actually to provide your services to Bob securely over the internet, is that a fair description? You mentioned eventually grabbing a domain, how do you feel about publicly exposed services with authentication? For instance, I use authentik in front of Jellyfin and paperless myself for a little extra authentication juice.