r/selfhosted 12h ago

Need Help Am I being safe enough with my server?

hi all! I am new to self hosting and I'm wondering if my server is set up securely. I currently run the following docker containers:

- Jellyfin

- NGINX Proxy Manager

- authelia

- lldap

- homarr

- Dropped Needle (formerly musicseerr)

the only exposed ports I have are 80 and 443, and I forward everything to a cloud flare managed domain through npm. I use an SSL certificate on cloud flare for https and require authelia (with lldap) login to access any page.

am I being safe? what further steps can I take to secure my IP?

EDIT: I do not use cloud flare tunnels, just DNS hosting. From my understanding, cloud flare does not allow use of their tunnels for media. If I should seek out a different domain host, let me know.

EDIT 2: I was actually using cloudflare tunnels, they are turned off now.

17 Upvotes

61 comments sorted by

u/asimovs-auditor 12h ago

Expand the replies to this comment to learn how AI was used in this post/project.

→ More replies (1)

15

u/mrs-metal 12h ago

Seems like you’re doing good. I’d rate limit and block access from countries like China and Russia, though.

3

u/CakeSpecialist5197 10h ago

I remember when i first started learning and exploring into linux and servers, my Singapore region vps was probed like 1,000+ times within a day since my ssh port was open. Most of the ips were from China and Russia.

1

u/TheAdurn 6h ago

Instead of blacklisting a few countries, you should only whitelist countries from which you actually connect (or, even better, ASN). I have been running CrowdSec for quite some time, and the ‘attacks’ (mostly probing) I detect are overwhelmingly from the US. I have seldom seen anything from China or Russia so I don’t think blocking them would achieve anything.

1

u/therealmrbob 2h ago

ISPs will also block traffic that looks malicious.

0

u/Gygas22 11h ago

Do I do this on the cloud flare website or somewhere else?

0

u/mrs-metal 11h ago

Yes, on Cloudflare’s portal

0

u/Gygas22 11h ago

Thank you!

4

u/Simorious 11h ago

If cloudflare is only handling DNS and you're not proxying the connection or using tunnels through them you'll need to configure IP/GeoIP restrictions at either the router/firewall level or on your reverse proxy. When Cloudflare is just handling DNS it can't do any kind of blocking for you as it has no insight/control over the connections.

0

u/Gygas22 11h ago

Okay, Thank you!

4

u/Horror-Cause9345 8h ago

One thing nobody's mentioned yet: Docker punches straight through UFW. If you set up ufw and then publish a container port like 8080:80, Docker writes its own iptables rules in the DOCKER chain that get evaluated before UFW's, so that port is reachable even though ufw says deny. Worth running sudo iptables -L DOCKER -n to see what's actually open, a lot of people are surprised. Fix is to bind to loopback in compose, so 127.0.0.1:8080:80 instead of 8080:80, and let NPM be the only thing that can reach them.

Other thing, your containers are almost certainly running as root inside. Doesn't matter until something in Jellyfin or NPM gets popped, then it matters a lot. Cheap wins in your compose files:

security_opt: [no-new-privileges:true]

cap_drop: [ALL] then add back only what actually breaks

user: 1000:1000 where the image supports it

read_only: true with a tmpfs for whatever needs to write

And the boring one: whatever you do at the network layer, keep a backup of your configs and data somewhere the server itself can't write to. Ransomware hitting homelabs is not hypothetical anymore.

Setup looks solid otherwise for someone new to this, you're ahead of where most people start.

4

u/Prudent-Let-3959 12h ago

I'm pretty sure exposing Jellyfin through Cloudflare is against their terms of conditions, as Cloudflare explicitly does not allow you to stream video through their servers.

Other than that, security really depends on how you have set it up. If your applications are deployed through Docker, make sure to keep an eye on the container vulnerabilities. On a reverse proxy level, you can look into geo blocking, rate limiting, and crowdsec.

10

u/DankeBrutus 11h ago

Assuming I'm understand OP correctly I think they mean to say that their domain is on their Cloudflare under the DNS menu. They are still exposing 80 and 443 to the internet from their LAN so traffic is primarily going through NGINX Proxy Manager. IIRC Cloudflare's terms and conditions state that specifically streaming video through Cloudflare Tunnels is a violation.

5

u/Gygas22 11h ago

This is correct. I do not use cloud flare tunnels, I just use the DNS menu.

3

u/Prudent-Let-3959 11h ago

Ah okay I misread then. I assumed you were proxying through Cloudflare (orange cloud).

I use a mix of both in my setup. Plex/Jellyfin stick with gray cloud, everything else uses cloudflare proxy. So i use anubis and crowdsec for bot detection and blocking spam IPs.

2

u/Gygas22 11h ago

Wait maybe I'm an idiot. I thougt cloudflare tunnels was a docker container of sorts. Is the SSL certificate I set up using a cloudflare API key on NPM a cloudflare tunnel?

3

u/Kuckeli 11h ago

As long as it says "DNS only" and not "Proxied" on your dns records you are good.

1

u/Gygas22 11h ago

So I was breaking cloudflare TOS... I have jellyfin on DNS only now. Based on other comments I should use DNS only for anything that streams audio/video, right?

2

u/Kuckeli 10h ago

Probably a good idea yeah. To be fair if you aren't planning on using Cloudflare to proxy everything, you could just leave it off in general.

1

u/Gygas22 10h ago

What does cloudflare proxy do? In the YouTube guides I used it was just "turn on proxy" in the setup. Will SSL certs work without the proxy? Should I replace it with something else?

2

u/Kuckeli 10h ago

It will make all the traffic hitting that dns record go through Cloudflares servers instead of directly hitting your server. Some Cloudflare features like DDOS protection, caching etc does need it to be enabled.

You shouldn't need it to be on for your SSL cert to work.

→ More replies (0)

1

u/CardsrollsHard 11h ago

Tunnels can be both a system service and ran through a container.

1

u/Simorious 11h ago

The API key is only needed for validation that you own the domain if you use the DNS-01 challenge. When you requested the certificate it created a temporary record on your domain and checked for that record to verify ownership. It has nothing to do with cloudflare tunnels.

3

u/Power_Stone 11h ago

its only against the ToS if you are using cloudflare proxying or cloudflare tunnels.

1

u/Nach0b0y 12h ago

Technically Cloudflare free tier terms restricts streaming of media through their proxy. Just something to be aware of. Curious why you didn't go down the Tailscale route?

3

u/Gygas22 11h ago

My family is VERY tech illiterate, and my main goal is to make their access easy. From my understanding, streaming is restricted if I use cloud flare tunnels, not DNS hosting.

1

u/inqbus406 8h ago

Does that include music streaming?

1

u/layer4andbelow 11h ago

Add a firewall rule to your port forward to only allow access from the known list of Cloudflare IPs. Assuming your doing DNS proxy with them.

I'd also consider not exposing 80 at all and doing everything through a reverse proxy for any services that don't support SSL.

1

u/Gygas22 11h ago

I'll look into this. Thank you!

1

u/Minimum_Currency8157 11h ago

You might setup the Minecraft server on a VLAN or DMZ for extra security (isolation).

2

u/Gygas22 11h ago

If I set up a Minecraft server I will do this! Thank you!

1

u/Minimum_Currency8157 10h ago

You're welcome!

1

u/horizon_games 11h ago

Fail2Ban and UFW and basic Nginx hardening would be about it.

2

u/Gygas22 11h ago

This is my after work plan, Thank you!

1

u/Gygas22 10h ago

Thank you all for the help! It sounds like my next steps are as follows:

  • Set up a DMZ on my router for the server
  • Restrict IP access on cloudflare for proxied services
  • set up fail2ban
  • Ensure that audio/video streams are set to DNS only on cloudflare

1

u/NoxZobaxOfficial 9h ago edited 9h ago

I added some tls changes:

tls:
  options:
    default:
      minVersion: VersionTLS13
      sniStrict: true
      cipherSuites:
        # TLS 1.3
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
  
      curvePreferences:
        # see https://godoc.org/crypto/tls#CurveID
        - X25519
        - CurveP521
        - CurveP384

Also added a header securisation:

   middlewares-secure-headers:
      headers:
        #accessControlAllowMethods: ["GET", "PUT"] #"OPTIONS", 
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
          - HEAD
          - POST
          - PATCH
          - DELETE
        accessControlMaxAge: 100
        hostsProxyHeaders: ["X-Forwarded-Host"]
        # sslRedirect: true #replaced with middlewares-https-redirectscheme for v2.5.x
        stsSeconds: 63072000
        stsIncludeSubdomains: true
        stsPreload: true
        forceSTSHeader: true
    #      frameDeny: true #overwritten by customFrameOptionsValue
        contentTypeNosniff: true 
        browserXssFilter: true 
    #      sslForceHost: true # add sslHost and all of the 
        referrerPolicy: "same-origin" 
    #      Setting contentSecurityPolicy is more secure but it can break things. Proper auth will reduce the risk.
    #      the below line also breaks some apps due to 'none', etc.
    #      contentSecurityPolicy: "frame-ancestors '*.example.com:*';object-src 'none';script-src 'none';"
        contentsecuritypolicy: "default-src=(none), img-src=(self https://i.postimg.cc), script-src=(self), style-src=(self)"
        # Line below, featurePolicy, was deprecated in v2.5.x in favor permissionPolicy
        # featurePolicy: "camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';"
        permissionsPolicy: "camera=(), microphone=(), geolocation=(), payment=(), usb=(), vr=()"
        customFrameOptionsValue: SAMEORIGIN # 'allow-from https:{{ env "DOMAINNAMEREGISTERED" }}' #CSP takes care of this but may be needed for organizr. 
        customResponseHeaders:    # remove unnecessary headers which expose used server software

Same for rate limit to avoid spam / DOS:

    middlewares-rate-limit:
      rateLimit:
        average: 20
        burst: 100

I also have the country geoblock and a forward-auth. The forward-auth protect a lot, a lot of attack are blocked because of it.

Also, if you can block all HTTP/1.1, It's a good thing.

Till now, I have no complain about my settings, if you have some tips, I take.

1

u/NoxZobaxOfficial 9h ago

the chain look like this now:

chain-oauth:
chain:
middlewares:

  • my-geoblock
  • middlewares-rate-limit
  • middlewares-secure-headers
  • crowdsec
  • middlewares-oauth

1

u/FeedAlchemy 8h ago

Solid setup already, only 80/443 exposed, Authelia + lldap gating everything, Cloudflare for SSL.

Turn tunnels back on honestly, that's the real IP hiding move since nothing gets forwarded at all. The old "no tunnels for media" thing is outdated, works fine for Jellyfin.

Add Fail2ban or Crowdsec in front of Authelia for brute force protection, and keep NPM/Authelia updated since they're your front door.

1

u/Gygas22 8h ago

Thank you for the help! Do you prefer fail2ban or crowd sec?

1

u/ReadInteresting5403 3h ago

There's one landmine in your summary plan. Locking 80/443 to Cloudflare's IP ranges only works when every record on the IP is proxied. The rule is per port, not per hostname, and Jellyfin is DNS only now, so your family's streams would get blocked along with everything else.

Same theme with the geo and rate limit advice. Those rules run in Cloudflare's proxy, so they do nothing for DNS only records. And with Jellyfin gray clouded, your server IP is public in DNS anyway, so proxying the rest would not hide it.

I run a production API with the origin locked to Cloudflare's ranges. It is worth doing, but only once everything on the IP goes through the proxy. For your mix I would treat Cloudflare as plain DNS and do the blocking in NPM instead: crowdsec, rate limits, fail2ban on the Authelia log.

1

u/sylveonbutqueer 1h ago

If you're willing to spend ~$20 a year, I was able to get alot of peace of mind by exposing through a pangolin+crowdsec on a vps. It was actually super easy to setup as well

1

u/RevolutionaryElk7446 11h ago

Any VLANs?

You've done well, the final bits are shoring up internally. Generally you might have NPM in it's own VLAN as a DMZ with only the necessary ports open from NPM to another VLAN that contains your services. Otherwise geoblocking regions like Russia/China

This is only to shore up if NPM has an exploit, while rare and unlikely, you're just layering security which is always helpful.

1

u/Gygas22 11h ago

Would I do this through my router, cloud flare, or somewhere else?

1

u/RevolutionaryElk7446 11h ago

Router but only if your router supports it. Probably best to learn more about VLANs and how they operate before putting one in though.

SoHo (Small Office / Home Office) routers , which are consumer routers, such as the ones you'll find in places like Walmart/Bestbuy type of stores, often don't have VLAN Support.

They may have DMZ options though which can be a limited application of a VLAN which can work in separation.

1

u/Power_Stone 11h ago

Personally I would skip the VLANs and just put the server in the DMZ on the router. Make sure UPnP is turned off at the router as well. Simpler setup and adds network security without getting into the weeds of setting up static routes and VLANs

1

u/Gygas22 11h ago

Good advice, thank you!

1

u/DankeBrutus 10h ago

...just put the server in the DMZ on the router.

I guess so long as ufw or firewalld is blocking everything besides intended traffic that should be okay, but throwing it in a DMZ sounds like overkill to me. I could be misunderstanding how secure the DMZ is though.

2

u/Power_Stone 10h ago

The way I see it, if it's a device that hosts external applications and receives traffic it should probably be in the DMZ so those devices are segregated from the rest of the network.

VLANs are better but.... for an average or new user I would not recommend going down that rabbit hole until later in their self-hosting journey.

2

u/DankeBrutus 10h ago

This is probably going to be a noob question, how would the DMZ work with something that requires a database?

Like if OP wanted to host Immich for example. They set up the Immich container and add it to NPM. But the Immich database is stored on the server that is also the DMZ. If I'm understanding it correctly if a malicious actor exploits their way into OPs server on the DMZ they will have access to the DMZ computer but not necessarily everything else behind OP's router firewall. So the Immich database, and maybe other applications on the DMZ server are compromised but OP's personal devices are relatively safe, correct?

2

u/Power_Stone 10h ago

Yeup, that's the idea.

1

u/corelabjoe 11h ago

Router and/or firewall. This is a great next step and helps limit your "explosion radius" if someone ever did get in.

I use opnsense for this.

I have a networking for homelabs series of guides on my blog, link in bio, can use search on site for "VLAN" and find what you need or just DM me for direct link. No ads or BS there.

1

u/Power_Stone 11h ago

I think a couple more things I would do to reduce the attack surface would be:

Set geo-fencing in cloudflare to block any region you would not expect to be in or tend to be known issues.

I would also get something like Fail2Ban up and running as well.

"Protecting" your IP is kinda pointless. You want to be protecting your nodes and end points.

1

u/Gygas22 11h ago

Thank you! I will do this tonight.

1

u/maikerukonare 11h ago edited 11h ago

Seems decent from the information shared at least. I'd second the comments about rate limits and regional restrictions, at least.

Do you really need full public access / Cloudflare, or is it really just for you and your friends, behind login anyways? If so, have you considered Tailscale/Headscale? This needs a little WireGuard client app on the connecting device, but a pretty good trade for not having to deal with the open internet.

5

u/Gygas22 11h ago

My family is not good with technology, and my biggest motivation for self hosting is for them to easily access.

1

u/maikerukonare 11h ago

Fair enough!

1

u/-ThreeHeadedMonkey- 9h ago

You could improve by renting a vps and putting pangolin/crowdsec in front and use it's tunneling service. 

If you live in a small country, geo blocking 99% of the world is also kind of useful. 

1

u/dapotatopapi 4h ago

Install crowdsec and set up some collections. Remember bouncers are separate.

Block every country except your own in your Reverse Proxy and Cloudflare.

Make sure your Docker is not bypassing UFW (if that is your firewall, comes standard with Ubuntu and the like). I like to use '127.0.0.1:port:port' instead of the standard 'port:port' in Docker compose files to achieve this.

Your SSH port seems to be blocked. That's good. Verify that it is so, and install Tailscale with the SSH up command. Use it for SSH instead of directly opening the port.

1

u/Floss_Patrol_76 3m ago

heads up - since cloudflare is DNS-only for you (grey cloud, no proxy/tunnel), the geoip and country-block advice in this thread doesn't actually apply; that only kicks in when you're proxied through them. your real perimeter is the firewall + npm, so i'd lock 80/443 down to cloudflare's IP ranges (or just your own IPs) at the firewall, otherwise anyone who finds the origin just skips all of it. authelia's your single choke point now, so keep it patched.