I built my home AI lab in the afternoon. By evening, it was already under automated attack from four countries.
Disclaimer: This article documents a personal technical experiment on my own infrastructure. The configurations, rules, and steps described reflect what worked in my specific environment and should not be treated as security advice, professional recommendations, or guaranteed protection against any threat. Every environment is different. Always validate independently, test in a non-production setting first, and consult qualified security professionals before applying any security-related configuration to production systems. NosisTech LLC accepts no liability for outcomes resulting from use of this content.
Not a targeted attack, just the internet’s permanent background noise. But the timing was a jarring reminder that the moment you put anything on a public URL, the entire internet starts probing it. Bots do not sleep, do not take weekends, and do not care that you just finished setting things up.
I want to cover what happened, what I saw, what I did not understand at first, and the full security hardening process I went through to lock down the lab. I will explain every concept before I use it, because the best security decisions come from understanding the threat, not just copying commands.
What I Had Built and What Was Exposed
Before I get to the attack traffic, let me describe what I had running and what was publicly accessible.
My home AI lab consists of three components:
- Ollama: an AI model inference engine running locally, serving a REST API on port 11434
- Open WebUI: a browser-based chat interface connecting to Ollama, running on port 8080
- Cloudflare Tunnel: a secure outbound connection from my machine to Cloudflare’s global network, making both services accessible via public subdomains
The subdomains were live and resolving publicly. The Cloudflare Tunnel was working correctly. From the outside world, anyone who knew the URLs could reach my Ollama API and my Open WebUI interface directly.
Note: Ollama has no authentication by default. In my setup, this meant anyone who reached the API subdomain could potentially call my API, run inference on my GPU, list my models, and consume my compute without any credentials.
I knew this was a problem in theory. The Cloudflare dashboard made it viscerally real.
What I Saw in the Dashboard: Reading Attack Traffic
Within four hours of the tunnel going live, Cloudflare’s analytics showed request activity from multiple countries, over a thousand requests from one country, hundreds from Bulgaria, hundreds from the United States, and smaller volumes from the Netherlands and Singapore.
My first instinct was that someone had found me and was actively targeting my lab. That instinct was wrong, and understanding why it was wrong is important.
What this traffic actually was
The internet has a permanent layer of automated scanning activity that touches every public IP address and every public hostname continuously. This is not targeted at you personally, it is systematic. Tools like Shodan, Censys, and countless vulnerability scanners sweep the entire IPv4 address space and all publicly resolvable DNS names, looking for:
- Open ports with known services
- Default credentials on login pages
- Known software vulnerabilities (CVEs)
- API endpoints without authentication
- Admin panels and management interfaces
The moment a new DNS record appears and a new URL starts resolving, automated systems find it, often within minutes, always within hours. The traffic I was seeing was not a human attacker who had discovered my lab. It was the background radiation of the internet, the same scanning activity that hits every public server on the planet every day.
The correct mental model: putting a server on the public internet without protection is like leaving a front door open in a city that never sleeps. No one has to target you specifically. They just walk every street, trying every door, all the time.
What Cloudflare was already doing
The dashboard title said “mitigated and served”, and that word mitigated matters. Cloudflare’s network was already blocking and challenging a significant portion of this traffic before it reached my tunnel. Cloudflare’s WAF, bot management, and DDoS protection were filtering out the most obviously malicious requests automatically.
But mitigation at the network level is not the same as authentication at the application level. A request that passes Cloudflare’s network filters and reaches my tunnel would still hit an unauthenticated Ollama API. I needed a layer that checked identity, not just traffic patterns.
The Solution: Cloudflare Zero Trust Access
Cloudflare Zero Trust is a security framework built on a simple principle: never trust, always verify. In traditional network security, being inside the network perimeter means you are trusted. In Zero Trust, being inside the network means nothing, every request must prove identity before it is allowed through.
Cloudflare Access is the specific product that implements this for web applications. It sits in front of your application and presents a login screen to every visitor before any request reaches your server. Only users whose identity matches your policy get through. Everyone else, including bots, scanners, and unauthorized humans, is blocked at Cloudflare’s edge, before they ever touch your infrastructure.
Why this is better than a VPN
The traditional solution to this problem would be a VPN: put your services behind a VPN, and only allow VPN users to access them. VPNs work, but they have significant operational overhead, you need to manage VPN clients on every device, handle certificate distribution, and maintain a VPN server. For a personal lab that you want to access from any browser anywhere in the world, VPN friction adds up quickly.
Cloudflare Access requires no client software. You open a browser, navigate to your URL, authenticate once, and you are in. The session persists for a configurable duration. From a hotel on the other side of the world, you need nothing installed, just a browser and access to your email or authenticator app.
In my setup, Zero Trust Access gave me VPN-level security with browser-level convenience. That trade-off worked well for a personal lab accessed from multiple locations — your priorities and environment may lead you to different conclusions.
What I Knew Going In and What I Did Not
The gap between theoretical knowledge and operational reality is where most security mistakes happen.
What I knew
I understood the conceptual model well. I knew that unauthenticated APIs are a risk. I knew that Cloudflare Tunnel was the right approach for exposing local services without opening firewall ports. I knew that Zero Trust was the appropriate security model for this architecture. The threat model was not a surprise to me.
What I did not know
What I had not done was translate that conceptual understanding into a configured, operational security layer before going live. I built the infrastructure first and secured it second. That is a common pattern and it is the wrong order for my setup.
The second gap was more specific: I had not verified the authentication state of my Cloudflare account itself. Two-factor authentication on the account that controls your DNS, your tunnel, and your access policies is not optional in my architecture — it is the master lock on the entire system. I had not checked it. When I did check, Cloudflare 2FA was inactive. That was a meaningful gap in my security posture that I fixed immediately.
Note: In my architecture, my Cloudflare account controls my DNS, which controls my tunnel, which controls access to my infrastructure. In this specific setup, a compromised Cloudflare account would put everything downstream at risk. For my configuration, enabling 2FA on that account was not optional.
The lesson
In my case, security should have been configured before go-live, not after I noticed attack traffic in the dashboard. The sequence I would follow now is: build the infrastructure, configure authentication, verify 2FA on all control plane accounts, then make it public. I did it in the wrong order and got lucky that Cloudflare’s default protections held while I caught up.
The Hardening Process: Step by Step
Step 1: Cloudflare Zero Trust – Setting Up the Organization
The first step was enabling Cloudflare Zero Trust on my account. This creates a Zero Trust organization with its own subdomain on cloudflareaccess.com, which becomes the authentication layer for all protected applications. I selected the Free plan, which includes full Access policy enforcement and supports up to 50 seats, more than sufficient for personal use.
The free plan includes everything needed for this use case: agentless Zero Trust remote access, identity provider integration, device traffic filtering, and full Access policy enforcement. There is no meaningful security difference between the free and paid tiers for a single-user personal lab.
Step 2: Protecting Open WebUI
I configured the first Access application for my chat interface. The configuration defines:
- The public hostname that Access will protect
- The internal service that Access will route authenticated traffic to
- The policy that determines who is allowed through
The policy I used is the simplest possible: a single approved email address. When someone navigates to the chat subdomain, Cloudflare presents a login screen. The visitor enters their email address. If the email matches the approved list, Cloudflare sends a one-time PIN to that address. The visitor enters the PIN, and Cloudflare issues a signed JWT session cookie that authenticates all subsequent requests for the configured session duration.
If the email does not match the approved list, the request is denied entirely. The visitor never reaches the application. No request touches my server.
Step 3: Protecting the Ollama API
I repeated the same process for the Ollama API subdomain. This one matters more from a security perspective in my setup — an unauthenticated API endpoint is a more serious exposure than a login-gated UI, because APIs can be called programmatically at scale. A bot that found my Ollama API could make thousands of inference requests per minute, consuming GPU resources and potentially exposing model information.
The same email-based policy now gates the API. Any programmatic access to the API also requires a valid Cloudflare Access token, which means even automated scripts I write to call my own API need to authenticate first.
Step 4: Enabling Two-Factor Authentication on Cloudflare
With both applications protected, I turned to the control plane: my Cloudflare account itself.
I enabled TOTP (Time-based One-Time Password) authentication using a mobile authenticator app. TOTP works by generating a 6-digit code that changes every 30 seconds, derived from a shared secret between your authenticator app and the service. Even if an attacker has your password, they cannot log in without also having physical access to your phone, because the TOTP code required to complete login is generated on-device and expires in 30 seconds.
I chose TOTP over email-based 2FA deliberately. Email-based 2FA sends the second factor to your email inbox. If your email account is compromised, both factors are in the same place — which means it is not really two factors. TOTP keeps the second factor on a separate physical device, maintaining true two-factor separation.
Two-factor authentication means two separate things that an attacker would have to compromise independently. If both factors can be accessed by compromising the same account, you do not actually have two factors.
I also saved the backup recovery codes in a secure location offline. Recovery codes are single-use emergency codes that allow account access if you lose your authenticator device. They need to be stored somewhere that is not your email inbox, because if you are locked out, your email may be what you are locked out of.
Step 5: Verifying the Full Security Chain
After configuring everything, I tested the complete chain by opening an incognito browser window and navigating to each protected subdomain. The Cloudflare Access login screen appeared correctly. Entering an unauthorized email address resulted in denial. Entering the authorized email triggered a one-time PIN delivery. Entering the correct PIN granted access with a session cookie.
In my setup, the attack surface was reduced from anyone on the internet to whoever has access to my email inbox and can generate a TOTP code from my physical device. Your specific risk reduction will depend on your own configuration and environment.
The Remaining Attack Surface
Locking down the public URLs is not the complete picture. Here is an honest assessment of what remains in my specific setup.
Local network access. Ollama and Open WebUI are still accessible without authentication from within the local network. Anyone connected to the same WiFi network can reach the services directly on their local ports. For my home lab this is an acceptable risk — physical or WiFi access is required for unauthenticated use.
The email account itself. The entire Cloudflare Access authentication chain in my setup terminates at the authorized email address. If the email account is compromised, an attacker can complete the Access authentication flow. This is why the email account needs its own strong security — a strong unique password and 2FA enabled at the email provider level. I verified that 2FA was already active on my email hosting account before completing this setup.
Ollama’s internal lack of authentication. Once through the Cloudflare Access gate, the Ollama API has no additional authentication layer. For my single-user personal lab where only one email address can authenticate, this is an acceptable tradeoff. In my assessment, if access were ever shared with others, adding Ollama-level authentication or rate limiting as an additional layer would be worth evaluating for that specific use case.
Open WebUI credentials. Open WebUI has its own account system with an admin user. That admin password is an independent credential that should be strong and unique. Someone who reaches the Open WebUI interface, after passing Cloudflare Access authentication, still needs valid Open WebUI credentials to use the system fully.
What the Cloudflare Free Plan Actually Gives You
You do not need to pay for enterprise security tools to protect a personal lab. The Cloudflare free plan includes:
- Cloudflare Tunnel: secure outbound tunneling, no firewall changes required
- Cloudflare Access: full Zero Trust authentication for up to 50 users
- WAF and DDoS protection: automatic mitigation of common attack patterns
- Bot management: detection and blocking of automated scanning traffic
- Global CDN: traffic routed through Cloudflare’s edge network for performance and security
The tools that protect major enterprises from the same threats that hit a home lab are available for free. The gap is not access to the tools — it is knowing which tools to use and how to configure them correctly for your specific environment.
The Complete Security Architecture
Here is the full stack as it stands after hardening in my setup:
- Request arrives at the public subdomain
- Cloudflare edge receives the request — WAF and bot management filters apply
- Cloudflare Access intercepts — login screen presented if no valid session
- User authenticates with email OTP — only the authorized address passes
- Cloudflare issues signed JWT session cookie — valid for the configured session duration
- Authenticated request is forwarded through the tunnel to the local service
- Local service responds
- Response returned to the user through Cloudflare’s encrypted edge
In my configuration, no unauthenticated request reaches the local machine. The tunnel is outbound-only — the machine connects out to Cloudflare, not the other way around. No ports are open on the router. The home IP address is never exposed.
What I Would Do Differently
If I were building this again from scratch, the security configuration would happen before the DNS records went live. The sequence I would follow:
- Build and test the services locally
- Configure Cloudflare Tunnel — but do not create public DNS records yet
- Set up Cloudflare Access policies for both applications
- Enable 2FA on all control plane accounts
- Create the DNS records — making services public only after authentication is in place
- Test from an external network to verify the full authentication flow
This sequence means there is no window where the services are publicly accessible without authentication. The attack surface goes from zero to protected in a single step, rather than going from zero to exposed and then being hardened under fire.
Closing Thoughts
The bots that found my lab within four hours were not sophisticated. They were not running targeted attacks. They were doing what bots do — systematically probing everything on the internet, looking for the easiest targets.
The right response to that reality is not to hide — you cannot hide a public URL. The right response is to make your infrastructure the wrong target: authenticated, monitored, and configured so that the cost of attacking it exceeds any potential benefit to an attacker.
In my experience, Cloudflare Zero Trust configured on the free plan addressed that risk for my personal AI lab. The bots will keep scanning. They will keep finding the subdomains. They will keep hitting the Cloudflare Access login screen. In my setup, they are denied at the edge before any request touches the hardware — though outcomes depend on correct configuration and your specific environment.
Security is not about being invisible. It is about making the cost of attacking you higher than the value of what an attacker would gain. For my personal AI lab, Cloudflare Zero Trust addressed that goal effectively — and costs nothing on the free plan.