Homelab & Selfhosting Thread - One Day it will be a Home Datacenter, you just gotta believe!

  • Want to keep track of this thread?
    Accounts can bookmark posts, watch threads for updates, and jump back to where you stopped reading.
    Create account

Is it worth the Power Bill?


  • Total voters
    154
I think you're right, but the company is taking a different posture.
You can either:

- accept you don't know who is sending traffic in any case and spend your time doing your best to plug the visible holes
- accept you don't know who is sending traffic in any case and spend your time making the underlying systems more resilient with this in mind.

Firing employees doesn't fix bad security, it just covers your ass for a little bit.
Option 3 is to treat any elevated risk with suspicion. Firing an employee is cheaper then tasking a security team to ongoing monitoring something that would normally just be blocked in its entirety.
 
Option 3 is to treat any elevated risk with suspicion. Firing an employee is cheaper then tasking a security team to ongoing monitoring something that would normally just be blocked in its entirety.
Reject the fact you don't know who is sending traffic? I suppose.
 
Sounds like a coverup for bad security? Many companies today bitch and moan when they can't use your IP to track you. This sounds like that but way worse.
Its kindof weird how there is an almost 50/50 mixture here of people with knowledge and then leering retards with completely made up opinions based on nothing lol
 
I could be wrong, but I don't know if any security system can distinguish between legitimate traffic and attack traffic if it's coming from the same IP address. If hackers breach their network with stolen credentials and they trace it to the IP address of an employee, how can the employee prove that it was not them but someone using the tor node they host? I suspect some companies even get squeamish when they detect a Plex server considering there has been a couple cases in the news where a company was hacked via an employee's Plex server.
I would say you're in for a bad time if you're IT staff because there is a much more reasonable expectation that you might know how to pull it off and you're in a position of trust.
If you are a tech-illiterate Jane in accounting that has this happen, it becomes pretty clear to everyone you didn't do it.

The Plex incident has a couple ways to skin it.
You shouldn't allow large bandwidth pulls to be happening to or from residential IPs
You shouldn't allow the Plex app on your endpoints.
You shouldn't let people download files from residential IPs
 
Given that there has been issues with people leaking their residential IPs of late I wanted to provide a guide for self-hosters to funnel traffic specifically for Kiwifarms through a ProtonVPN tunnel facilitated by their router.

Reason being I often have to finegle with my VPN as I travel (or my bank does not like my VPN a given Tuesday) and I often forget that I disabled something by the time I reach somewhere I wouldn't want to leak the IP of like my house.

I generated this through Claude this time around, I really cannot advertise how useful this is for removing "UGH" factor from projects at the configuration and troubleshooting level, which cause me to get distracted and walk away from a lot of projects. You can even take this markdown guide and upload it to Claude to personalize it to your needs and reduce your token use from generating it yourself.

Markdown (GitHub flavored):
# Routing kiwifarms.st through ProtonVPN on OPNsense

A walkthrough for using OPNsense policy-based routing to send traffic for a specific destination out a ProtonVPN WireGuard tunnel, while leaving the rest of your traffic on your normal WAN.

---

## 1. Generate the ProtonVPN WireGuard config

Log into [account.protonvpn.com](https://account.protonvpn.com) → **Downloads** → **WireGuard configuration**.

- Pick a server (any country works; if you want a specific exit, note where it is).
- Give the config a name like `opnsense-kf`.
- Leave NetShield / Moderate NAT / etc. at defaults unless you have a reason.
- Download the `.conf` file.

Open it in a text editor — you'll need these values:

| Field | Section | Example |
|---|---|---|
| `PrivateKey` | `[Interface]` | (your private key) |
| `Address` | `[Interface]` | `10.2.0.2/32` |
| `DNS` | `[Interface]` | `10.2.0.1` |
| `PublicKey` | `[Peer]` | (server's public key) |
| `Endpoint` | `[Peer]` | `185.159.157.1:51820` |
| `AllowedIPs` | `[Peer]` | `0.0.0.0/0, ::/0` |

---

## 2. Create the WireGuard instance in OPNsense

**VPN → WireGuard → Instances → +**

- **Name:** `ProtonVPN`
- **Private key:** paste the `PrivateKey` from the config (OPNsense derives the public key)
- **Listen port:** leave blank or set `51820`
- **Tunnel address:** the `Address` value (e.g. `10.2.0.2/32`)
- **DNS server:** leave blank for selective routing (handle DNS separately, see step 10)

Save, but don't enable yet.

---

## 3. Create the peer

**VPN → WireGuard → Peers → +**

- **Name:** `ProtonVPN-Server`
- **Public key:** the `PublicKey` from `[Peer]`
- **Allowed IPs:** `0.0.0.0/0, ::/0`
- **Endpoint address:** the IP from `Endpoint` (before the colon)
- **Endpoint port:** the port from `Endpoint` (after the colon)
- **Keepalive:** `25`

Save.

Go back to your Instance, attach this Peer to it, enable it, and tick the master **Enable WireGuard** checkbox at the top of the Instances page.

---

## 4. Assign the WireGuard interface

**Interfaces → Assignments**

- Pick `wg0` (or whatever the WG instance shows) from the dropdown → **Add**
- Click the new `OPT*` interface
- Enable it, name the description `PROTONVPN`
- Leave IPv4/IPv6 config type as **None**
- Save, apply

---

## 5. Create the gateway

**System → Gateways → Configuration → Add**

- **Name:** `PROTONVPN_GW`
- **Interface:** `PROTONVPN`
- **Address family:** IPv4
- **Gateway:** the Proton tunnel gateway, typically `10.2.0.1`
- **Far Gateway:** ✓ check this (it's outside the interface subnet since you have a `/32`)
- **Monitor IP:** `10.2.0.1` or `1.1.1.1` — something reachable through the tunnel

Save, apply.

> Confirm the gateway shows **Online** under **System → Gateways → Status** after a minute. If it doesn't, the tunnel isn't actually up — check **VPN → WireGuard → Status** for handshake.

---

## 6. Outbound NAT for the tunnel

**Firewall → NAT → Outbound**

If you're on **Automatic**, switch to **Hybrid** and save. Then add a rule:

- **Interface:** `PROTONVPN`
- **Source:** `LAN net` (or specific clients/network you want to allow out the tunnel)
- **Destination:** any
- **Translation:** Interface address

Save, apply.

---

## 7. Alias for kiwifarms.st

**Firewall → Aliases → Add**

- **Name:** `KIWIFARMS`
- **Type:** **Host(s)** with the FQDN entered (OPNsense will resolve and refresh it periodically — older versions called this "Hostname")
- **Content:** `kiwifarms.st` (and `www.kiwifarms.st` if you want)
- **Description:** anything you like

Save, apply.

> **Note:** kiwifarms.st has had hosting/IP churn over the past few years. The hostname alias handles that automatically as long as the domain still resolves. If they switch domains entirely, update the alias. If they're behind a reverse proxy or CDN-style host, the IP set may include addresses shared with other sites.

---

## 8. The policy routing rule

**Firewall → Rules → LAN → Add**

> Place this **above** your default LAN → any allow rule.

- **Action:** Pass
- **Interface:** LAN
- **Direction:** in
- **Protocol:** any
- **Source:** `LAN net` (or a specific host/alias if only one machine should use the tunnel)
- **Destination:** single host or alias → `KIWIFARMS`
- **Description:** `KF via ProtonVPN`
- Click **Advanced features** → scroll to **Gateway** → select `PROTONVPN_GW`

Save, apply.

---

## 9. Kill-switch rule (recommended)

If the tunnel drops, the policy rule above stops matching and traffic falls back to your normal WAN — defeating the point.

Add a **block** rule **below** the pass rule:

- **Action:** Block
- **Interface:** LAN
- **Source:** `LAN net`
- **Destination:** `KIWIFARMS`
- **Description:** `Block KF if VPN down`

> **Order matters:** pass-via-VPN first, block second.

For more reliable kill-switch behavior, also enable **System → Settings → General → "Skip rules when gateway is down"** (or the per-rule equivalent). This makes the pass rule disappear when Proton is offline, letting the block rule catch the traffic.

---

## 10. DNS consideration

Your router (Unbound, by default) will resolve `kiwifarms.st` over your normal WAN. That lookup leaks the fact that *someone* on your network asked for that domain to your ISP/DNS provider.

If that matters to you:

- **Option A (simpler):** Add a domain override in **Services → Unbound DNS → Overrides** for `kiwifarms.st` pointing to a DNS server reachable through the tunnel (e.g. `10.2.0.1`, Proton's DNS).
- **Option B:** Run a second Unbound or dnscrypt-proxy bound to the VPN interface and forward only that domain to it.

Option A is usually sufficient.

---

## 11. Test

From a LAN client:

```bash
# Should show your normal WAN IP
curl https://ifconfig.me

# Should show an IP that exits via Proton
curl --resolve kiwifarms.st:443:$(dig +short kiwifarms.st | head -1) \
     https://kiwifarms.st -o /dev/null -s -w "%{remote_ip}\n"
```

Easier check: load the site in a browser and look at any "what's my IP" widget the site itself shows, or check Proton's dashboard for active sessions.
Dead stupid check: add ipconfig.me to the earlier alias and then see if your IP changes after you remove it. If it does, it works!

---

## Troubleshooting

If the tunnel handshake fails, the most common causes are:

- Wrong `PrivateKey` paste
- Wrong `Endpoint` port
- The Proton account already has that config "in use" elsewhere — each downloaded config is bound to a session, so generate a fresh one if in doubt
 
@SchloppyI.T.Man, nice guide. Something of note for Kiwis that want to adapt that to work with other sites. I may be wrong but as best I can tell that will only resolve www.website.tld so if the site you’re going to have subdomains eg www.accounts.website.tld that will need its own entry in the hosts list as you will expose your IP by going to what is technically a different site. I’ll post the script I run on an interval for Mikrotik hardware to deal with this in a bit. It’s a dirty script that I made Claude make but it does the job.
 
I run this script on a 4 hour interval just in case any of the IP addresses change. Any IP addresses that are marked with "wg-domains" in the Address Lists area of my Mikrotik router are sent over my VPN. I also have a separate VLAN on my network where all outgoing traffic is routed through the VPN, this is for torrent clients and NZB downloaders and a couple of VMs.

The way the script works is that I need to add just the top domain for any website, eg kiwifarms.st to the main list. That top level as well as any subdomain possibilities listed below in the script are checked and added to the "wg-domains" list. There is also a section to manually add in IP addresses that will also be on the list, I use this for the various VPSs that I have as I am careful to only create accounts (from a VM on the VPN only VLAN) with companies that have a very lax KYC over my VPN and then to pay using Monero. This way these companies never get my real IP and I avoid the trap of connecting to the VPS from my real IP too.

It is only when the script has finished checking all the domains that the updated "wg-domains" addresses are inserted. Early versions of the script would wipe that list and add IPs as they were resolved meaning there would have been gaps where my IP could leak.

I know this could be cleaner, but my coding abilities are comparable to that of a journalist.

Code:
# --- CONFIG ---
:local addressList "wg-domains"
:local tmpList "$addressList-tmp"
:local domains {"kiwifarms.net";"kiwifarms.st"}

# --- STATIC IPs ---
# Add specific IPs or CIDR ranges here, e.g. 1.2.3.4 or 10.0.0.0/8
:local staticIPs {"x.x.x.x";"x.x.x.x"}
:local staticComment "static-entry"

# --- SUBDOMAINS ---
:local subNames { "www";"ww1";"ww2";"m";"mobile";"wap";"info";"about";"press";"news";"blog";"login";"signin";"signup";"auth";"secure";"sso";"oauth";"cas";"id";"account";"accounts";"my";"me";"profile";"user";"users";"member";"members";"dashboard";"portal";"console";"cp";"admin";"panel";"manage";"intranet";"shop";"store";"cart";"checkout";"order";"orders";"pay";"payment";"payments";"billing";"invoice";"invoices";"subscription";"subscriptions";"cdn";"cdn1";"cdn2";"cdn3";"cdn01";"cdn02";"cdn03";"cdn04";"cdn05";"cdn06";"cdn07";"cdn08";"cdn09";"cdn10";"cdn11";"cdn12";"cdn13";"cdn14";"cdn15";"cdn16";"cdn17";"cdn18";"cdn19";"cdn20";"cdn21";"cdn22";"cdn23";"cdn24";"cdn25";"cdn26";"cdn27";"cdn28";"cdn29";"cdn30";"cdn31";"cdn32";"cdn33";"cdn34";"cdn35";"cdn36";"cdn37";"cdn38";"cdn39";"cdn40";"cdn41";"cdn42";"cdn43";"cdn44";"cdn45";"cdn46";"cdn47";"cdn48";"cdn49";"cdn50";"static";"static1";"static2";"assets";"asset";"resources";"img";"imgs";"images";"media";"static-media";"video";"videos";"upload";"uploads";"download";"downloads";"dl";"files";"file";"stream";"streams";"vod";"live";"api";"api1";"api2";"api3";"api4";"api5";"api6";"api7";"api8";"api9";"api10";"api11";"api12";"api13";"api14";"api15";"api16";"api17";"api18";"api19";"api20";"api21";"api22";"api23";"api24";"api25";"api26";"api27";"api28";"api29";"api30";"api31";"api32";"api33";"api34";"api35";"api36";"api37";"api38";"api39";"api40";"api41";"api42";"api43";"api44";"api45";"api46";"api47";"api48";"api49";"api50";"graphql";"gw";"gateway";"srv";"service";"services";"node";"node1";"node2";"node3";"node4";"node5";"node6";"node7";"node8";"node9";"node10";"node11";"node12";"node13";"node14";"node15";"node16";"node17";"node18";"node19";"node20";"node21";"node22";"node23";"node24";"node25";"node26";"node27";"node28";"node29";"node30";"node31";"node32";"node33";"node34";"node35";"node36";"node37";"node38";"node39";"node40";"node41";"node42";"node43";"node44";"node45";"node46";"node47";"node48";"node49";"node50";"edge";"edges";"balancer";"proxy";"reverse-proxy";"server";"server1";"server2";"host";"host1";"host2";"support";"help";"status";"uptime";"docs";"doc";"documentation";"kb";"faq";"community";"forum";"mail";"mx";"smtp";"imap";"pop";"webmail";"email";"dev";"developer";"api-dev";"staging";"stage";"test";"qa";"beta";"demo";"sandbox";"chat";"msg";"messages";"social";"feed";"cloud";"storage";"drive";"bucket";"s3";"oss";"cdn-storage";"s1";"s2";"s3";"s4";"s5";"s6";"s7";"s8";"s9";"s10";"s11";"s12";"s13";"s14";"s15";"s16";"s17";"s18";"s19";"s20";"s21";"s22";"s23";"s24";"s25";"s26";"s27";"s28";"s29";"s30";"s31";"s32";"s33";"s34";"s35";"s36";"s37";"s38";"s39";"s40";"s41";"s42";"s43";"s44";"s45";"s46";"s47";"s48";"s49";"s50";"a1";"a2";"a3";"b1";"b2";"c1";"c2";"c3";"us1";"us2";"us3";"us4";"us5";"us6";"us7";"us8";"us9";"us10";"eu1";"eu2";"eu3";"eu4";"eu5";"eu6";"eu7";"eu8";"eu9";"eu10";"uk1";"uk2";"uk3";"uk4";"uk5";"ap1";"ap2";"ap3";"ap4";"ap5";"ap6";"ap7";"ap8";"ap9";"ap10";"asia1";"asia2";"asia3";"asia4";"asia5";"au1";"au2";"au3";"au4";"au5";"ca1";"ca2";"ca3";"ca4";"ca5";"na1";"na2";"na3";"sa1";"sa2";"sa3";"latam1";"latam2";"latam3";"af1";"af2";"af3";"mea1";"mea2";"mea3";"ind1";"ind2";"jp1";"jp2";"kr1";"kr2";"cn1";"cn2";"origin";"origin1";"origin2";"edge1";"edge2";"edge3";"cache";"cache1";"cache2";"lb";"lb1";"lb2";"lb3";"track";"tracking";"metrics";"stats";"analytics";"marketing";"campaign";"ads";"ad";"pixel";"event";"events";"realtime";"ws";"socket";"mqtt";"customers";"client";"clients";"partner";"partners";"accounting";"reports";"report";"crm";"de";"fr";"es";"it";"nl";"be";"pl";"se";"no";"dk";"fi";"br";"mx";"us";"ar";"cl";"jp";"kr";"hk";"sg";"cert";"ocsp";"ca";"keys";"key";"vault";"integration";"int";"uat";"preview";"next";"edge";"releases";"release";"download1";"download2";"updates";"update";"patch";"patches";"voip";"sip";"webrtc";"conference";"meet";"meeting";"ml";"ai";"inference";"model";"models" }

# --- CLEAN TMP LIST ---
/ip firewall address-list remove [/ip firewall address-list find list=$tmpList]

# --- BUILD TMP LIST (DNS RESOLVED) ---
:foreach d in=$domains do={
    :do {
        :local ip [:resolve $d]
        /ip firewall address-list add list=$tmpList address=$ip comment=$d
    } on-error={}
    :foreach s in=$subNames do={
        :do {
            :local fqdn "$s.$d"
            :local ip [:resolve $fqdn]
            /ip firewall address-list add list=$tmpList address=$ip comment=$fqdn
        } on-error={}
    }
}

# --- SWAP TMP -> FINAL WITH TIMESTAMP ---
:local ts ([:pick [/system clock get date] 0 10] . " " . [/system clock get time])
/ip firewall address-list remove [/ip firewall address-list find list=$addressList]
:foreach i in=[/ip firewall address-list find list=$tmpList] do={
    :local addr [/ip firewall address-list get $i address]
    :local comm [/ip firewall address-list get $i comment]
    /ip firewall address-list add list=$addressList address=$addr comment=($comm . " updated-" . $ts)
}

# --- CLEAN TMP LIST (FINAL) ---
/ip firewall address-list remove [/ip firewall address-list find list=$tmpList]

# --- ADD STATIC IPs ---
:foreach sip in=$staticIPs do={
    :do {
        :if ([:len [/ip firewall address-list find list=$addressList address=$sip]] = 0) do={
            /ip firewall address-list add list=$addressList address=$sip comment=($staticComment . " updated-" . $ts)
        } else={
            :log info ("wg-domains: static IP $sip already present, skipping")
        }
    } on-error={
        :log warning ("wg-domains: failed to add static IP: $sip")
    }
}

# --- SUMMARY LOG ---
:local count [/ip firewall address-list print count-only where list=$addressList]
:log info ("wg-domains refreshed with $count entries at $ts")
I plan to overhaul all of this in the autumn when the weather turns. With Mikrotik's updated container stuff that can be run on rOS devices I plan to have a small container perform a much more graceful poll for every IP that belongs to a domain and then feed that into a csv file or similar that will be visible to an updated script and then to have that import instead. This will be cleaner and save me from having to think about updating the subdomains list when I realise I have forgotten something.
 
Reason being I often have to finegle with my VPN as I travel (or my bank does not like my VPN a given Tuesday) and I often forget that I disabled something by the time I reach somewhere I wouldn't want to leak the IP of like my house.
Biased toward desktop users, but you can make it work if you set a system proxy, e.g. on your mobile device etc: I think my approach would be to run one privoxy instance on my home net, and one on a VPS in a datacenter somewhere. You configure each with e.g. whitelisted domains for that node, and pick one to be the default. Then you can point your device or devices at one privoxy node and have them sort which node they come out of. If you're not familliar with privoxy, it's an http proxy (that automagically eats ad domains, btw). I use it in conjunction with tord/i2pd to make browsing the clearnet and onionsites/eepsites seamless in furryfox, since you can configure rules like .onion -> socks5t localhost in it, and point furryfocks at privoxy.
 
Biased toward desktop users, but you can make it work if you set a system proxy, e.g. on your mobile device etc: I think my approach would be to run one privoxy instance on my home net, and one on a VPS in a datacenter somewhere. You configure each with e.g. whitelisted domains for that node, and pick one to be the default. Then you can point your device or devices at one privoxy node and have them sort which node they come out of. If you're not familliar with privoxy, it's an http proxy (that automagically eats ad domains, btw). I use it in conjunction with tord/i2pd to make browsing the clearnet and onionsites/eepsites seamless in furryfox, since you can configure rules like .onion -> socks5t localhost in it, and point furryfocks at privoxy.
I'm sure you can also offset this by relegating this activity to the router. Although I do like this proxy idea for my given devices in general, it covers yourself a lot better. I figured the router to tunnel solution was best initially because I always know that the farms will accept VPN traffic and that I'm regularly going to come home, not thinking about what device configs I've changed that would otherwise expose me. I think the next implementation for coverage is going to be explicitly for my phone since that's PII
 
Last edited:
I'm sure you can also offset this by relegating this activity to the router.
Personally, I have a box on my network who is the designated wireguard handler, and my router has static routes for private ips to route through that box. I cobbled all this together years ago before wireguard was in the kernel and was working with a dinky router fw and couldn't put wireguard on it (I have since swapped to openwrt). It's been working for me so I haven't reworked it from scratch but honestly I'm probably overdue for a refresh. Kinda thinking of doing a pfsense or opensense router on some ewaste x86 hardware I got lying around
 
Back
Top Bottom