Blog

  • A VPS to Create a Site-to-Site VPN Tunnel Back to a Home Network Behind a CGNAT — Part 2: Actually Building It


    This is the follow-up to the intro post where I outlined the problem. If you haven’t read that one, the short version: I’m switching to Cityside Fiber, which uses CGNAT. No public IPv4 address means no inbound connections — no VPN, no self-hosted services, nothing. The fix is an Oracle Cloud VPS as a permanent public-facing endpoint, with WireGuard tunneling everything back to the home network.

    Here’s how I actually built it.


    Prerequisites — What This Post Assumes

    Your domain is already on Cloudflare. This guide uses Cloudflare for DNS management throughout — creating A records, setting proxy status, and generating the API token that SWAG uses for Let’s Encrypt certificate validation. If your domain is still on your registrar’s nameservers, transfer it to Cloudflare before starting. It’s free, takes about 15 minutes, and you’ll need it for everything that follows.

    The Cloudflare dependency is worth calling out because it does two things here, not just one. The obvious one is DNS records. The less obvious one is that SWAG’s certificate validation happens through Cloudflare’s DNS API — this means your VPS doesn’t need to be publicly accessible on port 80 to get a valid Let’s Encrypt certificate. It’s a significant advantage over HTTP-based validation, especially during initial setup.

    If you want to go further with security, Cloudflare’s WAF (Web Application Firewall) is available on the free plan and can be applied to your proxied subdomains. It adds bot protection and basic rule-based filtering in front of SWAG without any configuration on the VPS itself. That’s outside the scope of this post, but it’s worth knowing the option is there.


    Before You Start — You Need a Second Device on Your Home LAN

    This is the part most write-ups skip, and it will save you a lot of frustration.

    Your home router probably won’t work as the WireGuard site-to-site peer. Many consumer routers (including mine, a TP-Link BE9300) support WireGuard in their VPN Client section, but that feature is for routing your LAN devices out through a VPN — like Mullvad or NordVPN. It cannot accept inbound traffic from a WireGuard tunnel and forward it to your LAN devices. The tunnel establishes fine, handshakes look clean, but nothing on your network is actually reachable. I confirmed this the hard way.

    What you need is a separate device on your home LAN that initiates the WireGuard tunnel outbound to the VPS. This device acts as the gateway. Options include:

    • A NAS or home server running Linux — this is what I use; Unraid has native WireGuard support built in
    • A Raspberry Pi running WireGuard
    • A mini PC running any Linux distro
    • OPNsense or pfSense running as your actual router/firewall
    • Any always-on Linux machine on the LAN

    The key requirement: it needs to be on 24/7 and run WireGuard in client mode (initiating outbound). CGNAT blocks inbound connections, not outbound ones. Once your gateway device establishes the tunnel outbound to the VPS, traffic flows both ways through it.

    If you don’t have any of these, a $35 Raspberry Pi Zero 2 W running Raspberry Pi OS handles this fine for a home lab.


    Overview of What Gets Built

    • Oracle Cloud VPS — WireGuard server + SWAG reverse proxy
    • WireGuard UI — peer management and config generation, runs as a Docker container on the VPS
    • SWAG — Nginx reverse proxy with automatic Let’s Encrypt SSL via Cloudflare DNS validation, also Docker on the VPS
    • WireGuard on a home LAN device — initiates site-to-site tunnel outbound, acts as gateway for the home subnet
    • Static route on your router — tells the router to send VPN client traffic through the LAN gateway device

    The end result: VPN clients connect to the VPS and can reach everything on the home network. SWAG proxies web services through the tunnel to home LAN containers. All of this works whether or not the home connection has a real public IPv4 address.


    Step 1: Oracle Cloud VPS

    Spin up an Oracle Free Tier VM running Ubuntu 24.04. Download your private key when prompted — you cannot recover it later.

    SSH in and update first:

    sudo apt update && sudo apt upgrade -y

    Install Docker using the official documentation: https://docs.docker.com/engine/install/ubuntu/


    Step 2: Docker Compose Setup

    Create your working directory:

    sudo mkdir -p /opt/edge-stack
    cd /opt/edge-stack

    Create the compose file:

    sudo nano docker-compose.yml

    Paste the following. The important part is the network section with defined fixed IPs — more on why this matters later.

    services:
      wg-ui:
        image: ngoduykhanh/wireguard-ui:latest
        container_name: wg-ui
        restart: unless-stopped
        cap_add:
          - NET_ADMIN
          - SYS_MODULE
        environment:
          - SERVER=auto
          - SERVER_PORT=51820
        volumes:
          - ./config:/etc/wireguard
          - ./db:/app/db
          - /lib/modules:/lib/modules
        ports:
          - "51820:51820/udp"
        networks:
          edge:
            ipv4_address: 172.20.0.2
    
      swag:
        image: lscr.io/linuxserver/swag:latest
        container_name: swag
        cap_add:
          - NET_ADMIN
        environment:
          - PUID=1000
          - PGID=1000
          - TZ=${TZ}
          - URL=${URL}
          - SUBDOMAINS=${SUBDOMAINS}
          - VALIDATION=dns
          - DNSPLUGIN=cloudflare
          - EMAIL=${EMAIL}
          - ONLY_SUBDOMAINS=true
        volumes:
          - ./swag:/config
        restart: unless-stopped
        ports:
          - "443:443"
        networks:
          edge:
            ipv4_address: 172.20.0.3
    
    networks:
      edge:
        driver: bridge
        ipam:
          config:
            - subnet: 172.20.0.0/24
              gateway: 172.20.0.1

    Create the environment file:

    sudo nano .env
    TZ=America/Los_Angeles
    URL=yourdomain.com
    SUBDOMAINS=gateway,wg,overseerr,tautulli,immich
    [email protected]

    Why fixed Docker IPs matter: Docker assigns IPs dynamically by default. If you restart or recreate containers, the wg-ui container could get a different IP. Every startup script and route that references that IP breaks silently. Defining a subnet with fixed addresses in docker-compose.yml prevents this entirely.


    Step 3: Cloudflare API Token for SWAG

    SWAG uses Cloudflare DNS validation to get Let’s Encrypt certificates. This works even without a web server being publicly accessible on port 80, which is why it’s the right choice here.

    Create a Cloudflare API token with Zone:DNS:Edit permissions for your domain. Then:

    sudo nano ./swag/dns-conf/cloudflare.ini
    dns_cloudflare_api_token = your_token_here

    Step 4: Firewall — UFW and Oracle Security Rules

    On the VPS:

    sudo apt install ufw -y
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow 22/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow 51820/udp
    sudo ufw enable

    In the Oracle Cloud console, add ingress rules to your Security List:

    • TCP 443 from 0.0.0.0/0
    • UDP 51820 from 0.0.0.0/0
    • TCP 22 from 0.0.0.0/0 (SSH)

    Step 5: Start the Containers

    cd /opt/edge-stack
    sudo docker compose up -d

    Verify both are running:

    sudo docker ps

    SWAG will request certificates on first startup. Give it 30–60 seconds, then check logs:

    sudo docker logs swag --tail 50

    Look for Server ready — that confirms certificates issued successfully.


    Step 6: WireGuard UI — Configure the Server

    Open a browser and go to http://YOUR_VPS_IP:5000 to access WireGuard UI. Set a username and password.

    Navigate to Wireguard Server and configure:

    • Server Interface Addresses: 10.8.0.1/24
    • Listen Port: 51820
    • MTU: 1420 (important — explained below)

    PostUp Script:

    iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o wg0 ! -s 10.8.0.0/24 -j MASQUERADE; iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

    PostDown Script:

    iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o wg0 ! -s 10.8.0.0/24 -j MASQUERADE; iptables -t mangle -D FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

    Save, then apply the config.

    A note on MTU and that last iptables rule: WireGuard adds roughly 60 bytes of overhead to every packet it encrypts. If the inner packet is too large, the outer UDP datagram exceeds the 1500-byte network MTU, and since WireGuard sets the Don’t Fragment bit, those packets get silently dropped. The symptom is that small responses work fine (redirects, API health checks) but full page loads hang indefinitely. Setting MTU to 1420 and clamping TCP MSS to 1360 ensures packets fit cleanly through the tunnel. The math: 1360 TCP payload + 40 TCP/IP headers = 1400 inner + 60 WireGuard overhead = 1460 outer, comfortably under 1500.


    Step 7: Create WireGuard Peers

    For the home LAN gateway device:

    In WireGuard UI, click New Client:

    • Name: Unraid-SiteToSite (or whatever your device is)
    • IP Allocation: 10.8.0.10/32
    • Allowed IPs: 10.8.0.10/32
    • Extra Allowed IPs: 192.168.11.0/24 ← replace with your home LAN subnet

    That last field — Extra Allowed IPs — is the critical one. It tells the VPS that your home LAN subnet lives behind this peer. Without it, the VPS has no route to your home network.

    Save and download the config file. You’ll need it in the next step.

    For VPN client devices (iPhone, laptop, etc.):

    Create separate peers for each device.

    For split tunnel (recommended — only home LAN and VPN traffic goes through, internet stays local):

    • AllowedIPs on the client: 10.8.0.0/24, 192.168.11.0/24

    For full tunnel (all traffic through VPS):

    • AllowedIPs on the client: 0.0.0.0/0

    Step 8: Configure the Home LAN Gateway Device

    If using Unraid:

    Go to Settings → VPN Manager → Add Tunnel → Import the config file you downloaded.

    Change Peer type of access to LAN to LAN access. This is the setting that makes Unraid initiate the connection outbound — compatible with CGNAT.

    Fill in:

    • Peer tunnel address: 10.8.0.1
    • Peer endpoint: gateway.yourdomain.com:51820
    • Peer allowed IPs: 10.8.0.0/24
    • Persistent keepalive: 25

    Peer allowed IPs on the Unraid side is 10.8.0.0/24 only — not 0.0.0.0/0, not your home LAN subnet. This is the split tunnel on the site-to-site leg. Home LAN internet traffic continues out through the router normally.

    Apply and enable the tunnel.

    If using a Raspberry Pi or other Linux device:

    sudo apt install wireguard -y

    Copy the downloaded config file to /etc/wireguard/wg0.conf and edit the [Peer] section so AllowedIPs = 10.8.0.0/24, then enable it:

    sudo systemctl enable wg-quick@wg0
    sudo systemctl start wg-quick@wg0

    Step 9: Add the Static Route on Your Router

    This step is easy to miss and causes confusing failures. When a home LAN device responds to a VPN client (IP in the 10.8.0.x range), that response needs to go back through the WireGuard gateway device — not out the WAN to the internet.

    On your router, add a static route:

    • Destination: 10.8.0.0
    • Subnet Mask: 255.255.255.0
    • Gateway: LAN IP of your WireGuard gateway device (e.g., 192.168.11.25)
    • Interface: LAN

    Without this, VPN clients can receive initial responses but subsequent packets from LAN devices are dropped. Connections appear to half-work or time out unpredictably.


    Step 10: Verify the Tunnel

    On the VPS:

    sudo docker exec -it wg-ui wg show

    Look for your home LAN gateway peer. You should see:

    • latest handshake: — within the last minute
    • transfer: — bytes in both directions

    Then verify the VPS can actually reach your home LAN:

    sudo docker exec -it wg-ui ping -c 3 192.168.11.25

    If that returns replies, the full routing chain is functional.


    Step 11: Startup Script — Keeping Routes Persistent

    Docker containers lose routing changes on restart. Create a startup script that re-applies everything after each reboot:

    sudo nano /opt/edge-stack/start-wg-ui-tunnel.sh
    #!/bin/bash
    sleep 15
    docker exec wg-ui wg-quick up wg0 || true
    ip route add 192.168.11.0/24 via 172.20.0.2 2>/dev/null || true
    docker exec swag ip route add 192.168.11.0/24 via 172.20.0.2 2>/dev/null || true
    sudo chmod +x /opt/edge-stack/start-wg-ui-tunnel.sh

    Create a systemd service to run it at boot:

    sudo nano /etc/systemd/system/start-wg-ui-tunnel.service
    [Unit]
    Description=Start WireGuard tunnel inside wg-ui container
    After=docker.service
    Requires=docker.service
    
    [Service]
    Type=oneshot
    ExecStart=/opt/edge-stack/start-wg-ui-tunnel.sh
    RemainAfterExit=yes
    
    [Install]
    WantedBy=multi-user.target
    sudo systemctl daemon-reload
    sudo systemctl enable start-wg-ui-tunnel.service

    Important: this script runs at boot, not on docker compose down/up. If you restart your containers, re-run the script manually:

    sudo /opt/edge-stack/start-wg-ui-tunnel.sh

    Step 12: SWAG Reverse Proxy Configs

    For each service you want to proxy, create a subdomain conf in /opt/edge-stack/swag/nginx/proxy-confs/.

    Example for Overseerr running on your home LAN at 192.168.11.25:5055:

    sudo nano /opt/edge-stack/swag/nginx/proxy-confs/overseerr.subdomain.conf
    server {
        listen 443 ssl;
        server_name overseerr.*;
        include /config/nginx/ssl.conf;
        client_max_body_size 0;
        location / {
            include /config/nginx/proxy.conf;
            include /config/nginx/resolver.conf;
            set $upstream_app 192.168.11.25;
            set $upstream_port 5055;
            set $upstream_proto http;
            proxy_pass $upstream_proto://$upstream_app:$upstream_port;
        }
    }

    Repeat for each service, changing the server_name and upstream port.

    For Immich specifically, add proxy_request_buffering off; before the location block — it’s required for photo and video uploads to work properly.

    After adding configs, test and reload nginx:

    sudo docker exec swag nginx -t && sudo docker exec swag nginx -s reload

    Step 13: Cloudflare DNS

    For each subdomain, create an A record in Cloudflare pointing to your VPS public IP.

    Proxy settings matter:

    • gateway.yourdomain.comGrey cloud (DNS only). This is your WireGuard UDP endpoint. Cloudflare’s proxy is TCP only. If this ever gets set to orange cloud, all VPN connections silently fail.
    • Everything else (wg, overseerr, tautulli, etc.) — Orange cloud (proxied). SWAG handles SSL directly with its Let’s Encrypt cert. The Cloudflare proxy is additive — it hides your VPS IP, provides DDoS mitigation, and enables the WAF.

    Optional: Cloudflare WAF

    For orange cloud subdomains, Cloudflare’s WAF is available on the free plan under Security → WAF. The free tier includes managed rules that block common attack patterns (SQLi, XSS, known bad bots) before requests reach SWAG. For publicly exposed services, enabling the free managed ruleset is a low-effort improvement with no downside. If a legitimate request gets blocked, check Security → Events and create an exception.


    Verifying the Full Chain

    Test that SWAG can reach your home LAN services through the tunnel:

    sudo docker exec swag curl -I http://192.168.11.25:5055

    You should get an HTTP response (307, 200, 303 — anything except a timeout or connection refused). If it times out, run the startup script and check that routes are in place:

    ip route show | grep 192.168.11
    sudo docker exec swag ip route show | grep 192.168.11

    Both should show a route to 192.168.11.0/24 via 172.20.0.2.


    Why This Works Under CGNAT

    CGNAT blocks inbound connections to your home IP. It does not block outbound connections. The home LAN gateway device (Unraid, Raspberry Pi, etc.) initiates the WireGuard handshake outbound to the VPS. Once that tunnel is established, the VPS can push traffic back through it in both directions. From that point, it behaves like any other WireGuard site-to-site setup — the CGNAT becomes irrelevant.

    This is also why a VPN client profile with AllowedIPs = 0.0.0.0/0 (full tunnel) works correctly: the VPN client connects to the VPS outbound, traffic flows back through the VPS to the home tunnel, and responses return the same way. No inbound connection to the home network ever happens.


    Securing the WireGuard UI

    The WireGuard UI at wg.yourdomain.com is a management panel that can create, modify, and revoke VPN peers — meaning full control over who has access to your home network. It needs to be treated accordingly.

    Exposing it publicly via SWAG is a convenience trade-off. The alternative — only accessing it while already connected to the VPN — is more secure but adds friction every time you need to add a peer or check tunnel status.

    Minimum baseline if exposing it publicly:

    • Set a strong, unique password in the WireGuard UI settings — not something reused elsewhere
    • Access is HTTPS only via SWAG — the management port (5000) is not exposed directly
    • wg.yourdomain.com should be orange cloud (Cloudflare proxied) — this hides the VPS IP and lets Cloudflare absorb probes before they reach SWAG

    If you want a stronger posture — Cloudflare Zero Trust Access:

    Cloudflare Zero Trust (free tier) lets you put an identity gate in front of any proxied subdomain. Anyone who hits wg.yourdomain.com gets redirected to a Cloudflare login page first, where they authenticate with an email OTP, Google, GitHub, or similar. Only after that do they reach SWAG and the WireGuard UI login.

    This is a meaningful upgrade because it eliminates the attack surface of the WireGuard UI login form entirely from the public internet — brute force attempts, credential stuffing, and any future vulnerabilities in WireGuard UI’s auth layer are all blocked before the request reaches your VPS.

    Setup is in the Cloudflare Zero Trust dashboard: Access → Applications → Add an application → Self-hosted. Point it at wg.yourdomain.com, set your access policy, and it’s active within a few minutes. No changes needed on the VPS side.

    Whether you add Zero Trust or not, the SWAG + Cloudflare proxy combination is already a significant improvement over exposing the management port directly. But for a panel that controls access to your entire home network, the extra layer is worth the 15 minutes it takes to configure.


    Final Notes

    A few things I would do from the start if doing this again:

    Define fixed Docker IPs immediately. I didn’t do this early enough and spent time debugging a startup script that silently stopped working when container IPs changed after a compose restart.

    Don’t use your home router as the WireGuard peer. Spend 20 minutes up front confirming whether your router actually supports inbound tunnel forwarding before going down that path. Most consumer firmware VPN Client modes do not.

    Test SWAG → LAN service connectivity before touching DNS. Run the curl test from inside the SWAG container before creating any Cloudflare records. If the route isn’t working, changing DNS will just make troubleshooting harder.

    If page loads work but full content hangs — it’s MTU. Small API responses and redirects passing while full page loads time out is the exact signature of WireGuard MTU fragmentation. The PostUp script covers this, but if you’re building on a different base, add the MSS clamp rule.


    Infrastructure: Oracle Cloud Free Tier (AMD) · Ubuntu 24.04 · Docker Compose V2 · WireGuard UI by ngoduykhanh · SWAG by linuxserver · Cloudflare DNS

  • A VPS to create a site to site VPN tunnel back to a home network behind a CGNAT

    A VPS to create a site to site VPN tunnel back to a home network behind a CGNAT

    Looks like I am going to start a little home IT project to allow the home lab to reverse proxy some containers, as well as create a VPN to allow remote access to the home network.

    Normally (with a decent enough router) one can WireGuard VPN into your home network, regardless if you have a static IP or DHCP one. In the near future, I plan on getting Cityside Fiber, which unfortunately is behind a CGNAT. This is unacceptable since I run reverse proxies to self host containers, as well as needing the ability to remote to the home network if I need to administer it when not at home.

    Objective: create a cheap VPS to host the reverse proxy on there, while connecting back to the home network using a site to site VPN tunnel that is running on Wireguard. By doing so I establish a connection, regardless if I have a “real” IPv4 address or not.

    This won’t be using Pangolin, since I believe that over complicates a solution. At least I can set this up now before I “have to” when I get fiber Internet.

    Details to follow.

  • Open source Remote Desktop software, hosted on Oracle, for free

    Open source Remote Desktop software, hosted on Oracle, for free

    Objective: Move from Anydesk (or other remote desktop software) and self host an Open Source Rustdesk server instance in Oracle Cloud Free Tier. More info about RustDesk can be found here:

    https://github.com/rustdesk/rustdesk

    ** EDIT – I am adding parts about increasing the security of this cloud VPS. Adding in having a firewall (UFW), Fail2Ban, and auto updating with security patches. Putting the relevant parts within the correct order. This was originally published on 12/28/2024. Updating to newer date for visibility on the blog.

    General overview of the steps I have taken to achieve this:
    •  Create an VM instance in Oracle Free Tier, running Ubuntu
    •  Run Rustdesk Server as a Docker container
    •  Allow ports that Rustdesk uses to be routed to server container by adding ingress rules
    •  Using a dynamic DNS host to get a stable, accessible address
    •  Setting up target remote clients

     

    Go to Oracle Cloud Free Tier at this website:

    https://www.oracle.com/cloud/free/

    Most of the inspiration to do this was from this youtube video, in which I made a few changes.

     

    – Go through setup, adding in credit card info (which they will not charge as long as you do not go past the free tier limits, which you will not with this instance running)
    – Please note your cloud account name, and your username, which is your email address that you have setup.
    – Create a VM instance, I recommend Ubuntu linux version 22.02 or higher.
    – When you create this instance, MAKE SURE TO DOWNLOAD the private and public key for the instance. You will use this to SSH into Ubuntu. There is no password. IF YOU FORGET THIS, you will have to delete the instance and make another one since there is no way to get the private key after the VM is spun up.
    – give a name to your VNIC, change your internal IP address to the VM, and a name for your subnet.
    – Now it is time to SSH into the VM Ubuntu instance
    – For my own sake, I am on MacOS and I used the app Shellfish, and I was able to attach the private key to the login.
    – user name would be “ubuntu”, and no password. The public IP address would be listed in the instance information from Oracle Cloud. That is what you would SSH into.
    – In Windows, you can use PuTTY and attach the private key
    – In Terminal on MacOS (and Linux) you can attach the key within the login info in as “ssh <SSH-key> ubuntu@<oracle-cloud-ip-address>”
    – Once remote into the terminal session, first thing is to run “sudo apt update && sudo apt upgrade”
    – This is to run updates to the Ubuntu image before doing anything else.
    – Now it is time to install Docker, Docker Compose, DuckDNS (your choice) and RustDesk.
    – With DuckDNS and Rustdesk, we will use a compose.yaml file (which interacts with Docker Compose). This file will pull down the containers and start them automatically.

     

    Docker is installed using the Docker documentation and pasting it in the terminal window. (documentation here: https://docs.docker.com/engine/install/ubuntu/)
    – Using apt, copy paste these commands in the SSH terminal and wait for them to complete:
    # Add Docker’s official GPG key:
    sudo apt-get update
    sudo apt-get install ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    # Add the repository to Apt sources:
    echo \
      “deb [arch=$(dpkg –print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo “$VERSION_CODENAME”) stable” | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update

     

    Install the lastest Docker package:
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

     

    Verify installation is succesful by running this command:
    sudo docker run hello-world

     

    – Now this is the part where we create a docker-compose file
    – type in this command “sudo touch compose.yaml”
    – then type “sudo nano compose.yaml”
    The following is what I have pasted in my YAML file. Edit as you see fit. 
    – An explanation of what is going on in my YAML file
    – duckdns to point a DNS to an IP address of your choice (more info here: https://www.duckdns.org/about.jsp)
    – Make sure to create a free DuckDNS account, create a subdomain, and take note of the token. You would need to add the value on the compose YAML file.
    – hbbs and hbbr are the Rustdesk servers that need to be run to allow self-hosting. hbbs is the service that gets the client’s IP address to know where to make the direct connection. hbbr is the service that allows relaying if direct connection cannot be established.
    – hbbs would need the -r switch to point to the IP address of the instance, in this one I used the DuckDNAS subdomain which points straight back to where hbbs resides
    – Watchtower is used to auto-update containers daily at 3am.
    – the YAML file will pull down all the containers, install them, and run.
    services:
      duckdns:
        image: lscr.io/linuxserver/duckdns:latest
        container_name: duckdns
        network_mode: host #optional
        environment:
          – PUID=1000 #optional
          – PGID=1000 #optional
          – TZ=America/Los_Angeles #optional
          – SUBDOMAINS=example #enter your subdomain here
          – TOKEN=12345 #enter your token here
          – UPDATE_IP=ipv4 #optional
          – LOG_FILE=false #optional
        restart: unless-stopped
      hbbs:
        container_name: hbbs
        ports:
          – 21115:21115
          – 21116:21116
          – 21116:21116/udp
        image: rustdesk/rustdesk-server:latest
        command: hbbs -r #enter your full domain name here that points to your server, or the public IP of the instance -k #put your private key here. 
        volumes:
          – ./docker/rustdesk:/root
        restart: unless-stopped
      hbbr:
        container_name: hbbr
        ports:
          – 21117:21117
          – 21119:21119
        image: rustdesk/rustdesk-server:latest
        command: hbbr
        volumes:
          – ./docker/rustdesk:/root
        restart: unless-stopped
      watchtower:
        image: containrrr/watchtower:latest
        container_name: watchtower
        network_mode: bridge
        volumes:
          – /var/run/docker.sock:/var/run/docker.sock
        environment:
          TZ: Americas/Los_Angeles
        command: –cleanup –schedule “0 0 3 * * *” hbbr hbbs duckdns
        restart: always
    – Make sure to save the compose.yaml file when you exit.

     

    – Now it is time to run the containers in Docker
    – Start running them by typing in “sudo docker-compose up -d”
    – the -d switch will allow you to run the containers in detached mode, allowing you to close your SSH session without shutting off the containers.
    – You can verify at any time that the containers are running by using the command “sudo docker ps”

     

    – Once everything is running, double check the key files that gets generated at “./docker/rustdesk”. The files should be named id_ed25519 and id_ed25519.pub
    – Use the cat command “cat id_ed25519.pub” and note the output
    – The output is the public key for the asymmetric encryption for Rustdesk connections.
    – This key would be inputted in the “Settings-Network” section of RustDesk client, in the same area where the server ID info would be put in.
    – Use the cat command “cat id_ed25519” and note the output. This is your private key. I recommend re-editing your compose YAML file with the -k switch to enforce private connections. No one else can connect to your server relay via Rustdesk without the public key.
    Build up your host firewall:
    – within the SSH session, do the following commands:
    sudo apt install ufw

    sudo ufw default deny incoming
    sudo ufw default allow outgoing

    sudo ufw allow 21115/tcp
    sudo ufw allow 21116/tcp
    sudo ufw allow 21116/udp
    sudo ufw allow 21117/tcp

    sudo ufw allow 22/tcp

    sudo ufw enable

    Enable Fail2Ban. This bans IP addresses if they keep hammering your server:
    sudo apt install fail2ban -y
    sudo systemctl enable fail2ban
    sudo systemctl start fail2ban
    sudo fail2ban-client status 

     

    Enable automatic security upgrades on the server. It will save your sanity.
    sudo apt install unattended-upgrades
    sudo dpkg-reconfigure unattended-upgrades
    Poke holes in your VPS firewall!

     

    – Fastest way is to go to your Oracle Cloud console instance page and look at the details of your instance. Click the name of your subnet (under the section “Primary VNIC”).
    – Click Default Security List
    – Add ingress rules, with source 0.0.0.0/0, TCP protocol and destination port range of 21115-21117
    – second rule would be the same, but with UDP protocol and destination port 21116
     
    – Double check DuckDNS is pointing to correct public IP of the instance.
    – Using ping will resolve an IP address, but only once! The second ping will show unreachable. This is okay.

     

    Install client on your own computer, and configure it.

     

    Below would be the instructions if I was walking a client on how to set it up so I can start remote desktop services without me being physically there.
    If you wanted just temporary, attended access (like a one time look), you can adapt these instructions by just downloading the Rustdesk client, and then just putting in the ServerID in settings, network. From there you would just need the computer ID and the one time password to remote in.

     

    Minimum to get client to install on their system to enable permanent remote unattended desktop access
    – Open web browser, go to rustdesk.com/download
    – Have them download the client that matches their computer
    – Have them open the file
    – If on Mac, drag Rustdesk to Application folder and open it
    – Enable all permissions before continuing
    – If on Windows, run the RustDesk exe file
    – Install it as a service to prevent UAC from interfering with it.
    – If on Linux, they can figure it out themselves
    – Go to settings by clicking the 3 dots next to ID number
    – Go to Network and have them type in the ID Server address
    – This will require elevated permissions to do this
    – The ID server in my case is something.something.com, but please input what you have setup
    – Everything can be blank for now
    – Go to security settings and make sure to ENABLE remote configuration modification.
    – This is important so you can change settings on the application itself.
    – Have the client tell you what their ID number is, and the one time password to remote into the system.
    – Once that is achieved, go ahead ahead and use a permanent password, set up 2FA (optional) and enter the server public key for encryption

     

    With RustDesk client installed, you can verify that it is connecting to your relay server if the bottom of the window says “ready”
    Use the above install info to install on another computer you want to remote to.

     

    Once that is all done and it works…you are good!

     

    Here is a few other things I have done. Totally optional, but this is just my preferences.

     

    – I have my own FQDN. I use that instead of the duckDNS.org one since it doesn’t look pretty. This is easily achievable by creating a CNAME that points to the subdomain of duckDNS.org.
    – I use Cloudflare as my DNS record holder, but make sure to TURN OFF proxy for the DNS subdomain. For some reason, the proxied data does not carry over to the hbbs container in the VPS. But an unproxied connection works fine.
    – In my linux instance I did an APT INSTALL MC since I wanted to use Midnight Commander to look through the server in a semi GUI fashion. Totally not necessary (especially if you like to use the ls command), but I was thinking why not?
    – Another thing to keep in mind is that Oracle can reclaim idle resources back when you are on the always free tier. This does not apply if you have a card on file and do the “Pay As You Go” model. The PAYG allows you to avoid this issue, and if you don’t go past your free limits (you probably won’t), you won’t get charged.

    – However if you do not want to give out your card, you can follow this link to generate load on your VM instance so it won’t show idle.

    https://medium.com/@poornamith/a-guide-to-stress-testing-your-virtual-machine-overcoming-oracle-cloud-reclaiming-idle-computer-7094de32dd9b

  • Well shit, thanks Gravy Analytics!

    Gravy Analytics, a major location data broker, recently disclosed a data breach that may have exposed precise location information of millions of individuals. The breach, identified on January 4, 2025, involved unauthorized access to the company’s AWS cloud storage. A sample of the leaked data, shared on a Russian forum, contained over 30 million location points, including sensitive sites like the White House and military bases.

    Gravy Analytics is investigating the breach to determine its scope and whether personal data was compromised. Preliminary findings suggest that if personal data is involved, it likely pertains to users of third-party services that supply data to Gravy Analytics.

    This incident coincides with recent regulatory scrutiny. In December 2024, the Federal Trade Commission (FTC) filed a complaint against Gravy Analytics and its subsidiary, Venntel, for unlawfully collecting and selling user location data without consent, including data related to sensitive locations.

    The breach underscores ongoing concerns about privacy and the security of personal data handled by data brokers. It highlights the potential risks associated with the collection and sale of location information, especially when such data can reveal sensitive or personal aspects of individuals’ lives.

    Source: https://www.theverge.com/2025/1/13/24342694/gravy-analytics-location-data-broker-breach-hack-disclosed

    From what I have read, if you had Apple’s “Allow Apps to Request to Track” off, you SHOULD be good. But I would have to look into it more.

  • Apple refreshes their security support document regarding smishing attacks for iPhone and iPad users

    Apple has updated its security support document to help iPhone, iPad, and Mac users recognize and avoid social engineering schemes such as phishing messages and fake support calls. This update comes in response to reports of “smishing” attacks targeting Apple IDs, where users receive SMS messages attempting to steal their Apple ID credentials via a fake iCloud website.

    Key guidelines from Apple include:
    – Ignore suspicious messages and links.
    – Apple will never ask for Apple ID passwords, verification codes, or request users to log into a website, disable security features, or use Apple Gift Cards for payments.
    – Always contact Apple directly through official channels for support.
    – Protect your Apple ID by using two-factor authentication and keeping contact information secure.
    – Only download software from trusted sources.
    – Avoid following links or opening attachments in unsolicited messages and do not respond to suspicious phone calls or messages claiming to be from Apple.

    Apple emphasizes vigilance against scammers who use scare tactics to create urgency and seek login information and security codes. Users should avoid downloading unrecognized software and follow Apple’s advice on spotting and reporting suspicious activities.

  • Telegram Combolists and 361M Email Addresses

    Looks like in HIBP (Have I Been Pwned) released a new notice that 122GB of user data (emails, passwords and associated websites) have been released via Telegram channel.

    Basically use DIFFERENT passwords for every websites that you have an account on, and turn on multi factor authentication on websites if you have not already. Barring that, keep your eye out for any odd activity.

    https://www.troyhunt.com/telegram-combolists-and-361m-email-addresses/

  • Okta releases info that they have seen an uptick in credential stuffing

    https://arstechnica.com/security/2024/04/everyday-devices-are-used-to-hide-ongoing-account-compromise-campaign/

    Authentication service Okta is warning about the “unprecedented scale” of an ongoing campaign that routes fraudulent login requests through the mobile devices and browsers of everyday users in an attempt to conceal the malicious behavior.

    I’m not too surprised that this happening. Combined with the fact most average users reuse passwords on various websites, this is definitely not a good thing.

  • UTM – an open source emulation software for Apple Silicon Macs

    Well I stumbled on something interesting when I was trying to virtualize Windows Server 2019  on MacOS Sonoma 14.4.1: I completely forgot that it only runs on x86-x64 architecture and my simple mind forgot that I can run only ARM64 compatible OSes in Vmware Fusion. (shout out to the free player Vmware provides for us to use to play around with these things!)

    Basically the reason for doing this was so I can learn and teach myself the ins and outs of Active Directory so I can put it in my home lab homework history.

    Anyways I stumbled upon a software application called UTM: specifically made for MacOS and it allows EMULATION, which means I can run any OS architecture, albeit at a penalized state (aka not optimized). Apparently running with multiple cores would help speed up things, especially on my M2 based ARM64 processor.

    The link is here: https://tcsfiles.blob.core.windows.net/documents/AIST3720Notes/WindowsServeronanM1Mac.html

    Currently in Vmware Fusion I have Ubuntu 22.04 LTS and Windows 11 virtual machines sitting in the library, and while I am posting this getting UTM to install Windows Server 2019 and Kali Linux 2023. If there is a computer “issue”, there is usually a computer solution.

    On the home network I have a separate hardware device running video transcodes in the form of a small form factor HP (HP ProDesk 400 G5 Desktop Mini i5-9500T 8GB DDR4) using Ubuntu 22.04 LTS and another NAS appliance running UnRaid (Intel Xeon CPU E31220 @ 3.10GHz with 15 SATA connections, 16GB DDR3 Single-bit ECC), which also hosts 18 Docker containers. Trying to figure out how to build out a Pi-hole/OpnSense device to supplement the built in firewall from the TP-Link Archer BE550. All configurations and ideas mostly came from serverbuilds.net.