berglabs / cloudflare professional services take-home

The homework is production.

Three assignments, answered as live infrastructure on berglabs.net and presented from the platform under evaluation. Every claim on this page links to the config, the commit, or the probe that proves it.

verified against the live estate · 2026-07-10
origin, through cloudflare
the edge serving you
health checks · 24h
this page's build

drawing index

The dossier, in order

Ten seconds: the strip above is live. Two minutes: sections 01 and 02. Ten: open the sheets and click the bench.

01The briefThree assignments, one thesis: build it as production, on the platform being evaluated.2 min 02The architectureTwo hostnames, one origin, forced by a plan gate. Then one request walked end to end.diagram 03Sheet A · Application ServicesFull (Strict) with a real certificate, mutual TLS at the origin, an identity Worker with zero secrets.live demo 04Sheet B · Zero TrustA testers group with a deny-list, one-time PIN for reviewers, Gateway policy where order is the policy.live demo 05Sheet C · Network ServicesMagic Transit on paper: BGP, GRE overhead, DSR asymmetry, the firewall table debugged.report 06The test benchEvery claim, probed against production while you watch.6 probes 07Estate as codeA public Worker repo, a private infra repo, Terraform that imports rather than recreates.gitlab 08The processHow it was actually built, including the parts that did not go clean.rev table
01

The brief

The three Professional Services assignments are designed for a free-tier account and a quick proof of concept. This submission takes the opposite bet: do the homework as real infrastructure on a platform I already operate, the way a PS team would engage a customer estate, and make the submission itself run on the thing being evaluated.

live

Application Services

Asks: an origin that echoes its request headers, proxied through Cloudflare; a non-Cloudflare certificate at Full (Strict); a tunnel; an SSO IdP; a locked path; an identity Worker serving flags from private R2, built with Wrangler in a public repo.

Answered as two hostnames on one origin, with mutual TLS carrying the no-bypass requirement and the Worker holding zero secrets.

open sheet a →
live

Zero Trust Services

Asks: a self-hosted app gated to a testers group with a deny-list; a SaaS app behind Cloudflare as IdP; WARP enrollment restricted to the IdP group; Gateway DNS and HTTP policy with named exceptions.

App, groups, and Gateway policy live over the API; the human-at-a-device steps staged with exact payloads and honestly marked.

open sheet b →
report

Network Services

Asks: the CTO's analysis for onboarding Magic Transit with a single /24: BGP advertisement, GRE consequences, DSR asymmetry, and a firewall rule table to debug.

A written report that walks the packet both directions and debugs the table line by line. Paper by design; the diagram below is the argument.

open sheet c →
02

The architecture

Two hostnames, one origin. Forced by a plan gate, not chosen for taste: Full (Strict) needs an origin certificate to validate, a tunnel deletes that handshake, and splitting paths on one hostname to different origins needs an Enterprise-only Origin Rule. On the free tier the assignment targets, this is the only architecture where one hop is a genuinely validated edge-to-origin handshake and the other genuinely rides a tunnel.

cloudflare edge origin lxc 107 let's encrypt server cert · full (strict) validates it client cert from the berglabs ca · aop · refused without it
Two certificates, opposite directions

The origin proves itself to Cloudflare with Let's Encrypt; Cloudflare proves itself back with a client certificate my own CA issued. Conflating the two is the trap the assignment sets, and untangling it is most of the learning.

AOP has three scopes that do not interact

Global proves "from the Cloudflare network," any customer. Zone and per-hostname use a cert you upload and prove "from my account." We run per-hostname, the strongest, after the zone toggle silently presented nothing at all.

Workers run before the cache

A route is an intercept keyed on hostname and path; the origin is the fallback. /secure* belongs to the Worker, every other path falls through, down the tunnel.

one request, walked

https://tunnel.berglabs.net/secure the assignment's deliverable url
hop 01 · resolve
The DNS record picks the transport

A proxied CNAME to a tunnel address: the resolver's answer is the anycast edge, never my network. The record type has already decided the origin will be reached by an outbound-dialed tunnel, not a public IP.

tunnel 300 IN CNAME d014ddc8…cfargotunnel.com · proxied
hop 02 · terminate
TLS ends at the edge, on purpose

Universal SSL terminates the visitor session at the nearest data center, TLS 1.2 floor on the zone. Plaintext between the hops is not a leak; it is the mechanism that lets WAF, caching, and Workers exist at all.

edge cert · TLS 1.3 · min floor 1.2 · always_use_https on
hop 03 · gate
Access answers before the origin exists

The path is covered by an Access app, so an unauthenticated request goes no further: a 302 to the team login. The policy admits me and any @cloudflare.com address; what comes back is a signed JWT the rest of the path can verify for itself.

HTTP 302 → airs-labs.cloudflareaccess.com · one-time pin for reviewers
hop 04 · match
A Workers route claims the path

Deliberately a route, not a Custom Domain: a Custom Domain would make the Worker the origin for the whole hostname and swallow the tunnel serving every other path.

route /secure* → takehome-identity · everything else: fallthrough
hop 05 · run
The Worker holds zero secrets

An isolate starts in about five milliseconds and re-verifies the Access JWT against the team's public keys rather than trusting the front door. Email from the token, country from the request, flag bytes from a private bucket through a binding. The binding is the authorization; there is no credential to steal.

env.FLAGS.get("flags/us.svg") · no key, no token, no config
hop 06 · return
Identity-specific, so never cached

The response names you, so it ships no-store: the edge cache keys on URL, not identity, and caching a gated page is the classic footgun. The flag is browser-private for an hour.

cache-control: no-store · flag: private, max-age=3600
SHT A / 03

Application Services

live verified 2026-07-10

in plain terms

Cloudflare stands in front of a web server that runs in my home lab. Visitors reach the server only through Cloudflare's network: the server proves its identity to Cloudflare with a real certificate, and Cloudflare proves its identity back with one of mine, so a connection from anywhere else is refused. A second doorway checks who you are first, then a small program running inside Cloudflare's network, not on my server, greets you by email and fetches your country's flag from a locked storage bucket.

visitor one edge universal ssl access · waf origin rule :8443 workers routes worker tunnel./secure* origin cloudflared app. · full (strict) + aop route match dns a record every other path · dialed outbound
The origin is hardened in layers

A Palo Alto admits only Cloudflare's published ranges, on a non-standard port an Origin Rule dials. Then mutual TLS carries the real requirement: without the client certificate, the connection is refused whoever you are. IP allowlists are spoofable at layer 3; the certificate is not.

DNS-01, so port 80 never opens

The Let's Encrypt cert is issued over the DNS challenge with a token scoped to one zone's DNS. The firewall stays locked to Cloudflare's ranges from the first minute, and renewal never needs an inbound door.

The comparison I built and did not ship

The same handler runs as a Worker and as a Node origin in one codebase, two adapters. The Worker needs zero stored credentials; the origin twin needs a scoped R2 key on the box. /secure stays on the Worker, and the comparison lives in the code and this argument.

probe it, live


$ curl -s https://app.berglabs.net/probe
press run: this fetches the live origin through Cloudflare and paints the raw echo here.

why this shape, what i traded

Coexistence over migration: the reachable-IP leg stays up, hardened by mutual TLS, because the assignment asks for evidence of both worlds. Step 3 needs a reachable origin; step 6 says nobody may reach it; those pull against each other, and naming the tension is the answer. In production I would run tunnel-only, where no-bypass holds by construction rather than by firewall policy that can drift.

SHT B / 04

Zero Trust Services

live gates verified 2026-07-10

in plain terms

People reach the private app only after proving who they are; everyone else meets a login wall before the app even exists to them. Testers are named as a group, not as individuals, with one explicit deny on top. On managed devices, traffic passes a policy ladder that blocks dangerous categories outright and blocks entertainment with two named exceptions. Order is the policy: the allow sits above the block, and the first match wins.

device + warp gateway · first match wins 100 · allow netflix · youtube 200 · block entertainment dns · block anonymizer + malware internet reviewer access · zt.berglabs.net allow: testers group exclude: the deny-list one-time pin idp for reviewers the app
The gate answers before the app exists

zt.berglabs.net 302s to the team login for anyone unauthenticated; the app behind it is reachable only through the same dedicated tunnel as the rest of the estate. Probe it on the bench and watch the redirect arrive.

Order is the policy

The Netflix and YouTube allow sits at higher precedence than the Entertainment block; first match wins, so swapping the order turns the carve-out into dead code. The DNS security block (Anonymizer, Malware) runs regardless.

The honest edges

WARP device enrollment and the SaaS leg (Cloudflare as IdP for Salesforce) need a human at a device and an admin console. Both are staged with exact payloads and marked pending rather than quietly claimed.

probe it, live


authenticate at zt. ↗
$ curl -sI https://zt.berglabs.net/
press run: this asks the live gate, unauthenticated, and paints the redirect it answers with.

On a WARP-enrolled device, the Gateway policies are testable against anonymizer.testcategory.com and malware.testcategory.com.

why this shape, what i traded

Groups over emails: the policy names a Testers group so membership changes without touching policy, and the deny-list rides Exclude on the same policy object. One-time PIN stays enabled purely as the reviewer's door. At enterprise scale I would bind the groups to the IdP, add device posture to the policy, and require WARP sessions; the free tier demonstrates the shape without the fleet.

SHT C / 05

Network Services

written analysis

in plain terms

Acme's own internet addresses get announced to the world by Cloudflare instead of Acme, so attacks and visitors alike land on Cloudflare's three-hundred-plus front doors first. Clean traffic rides a private tunnel home. Replies go straight back to the user without the return trip through Cloudflare, which is faster, but it means the security appliance in the middle only ever sees half of each conversation. The analysis prices that in.

user 1500 b mtu cloudflare edge · anycast advertises 203.0.113.0/24 ddos scrub magic firewall, ingress only acme dc · /24 gre · +24 b → mss 1436 return path · direct server return · cloudflare never sees it ⇒ the firewall is stateless by construction
The /24 floor belongs to the internet

Global routing tables ignore anything smaller, so a /24 is the minimum unit of advertisement; a letter of authorization and IRR/RPKI records make Cloudflare's announcement of it legitimate, and the transit world is told before the switch flips.

GRE charges 24 bytes per packet

Encapsulation eats headroom inside a 1500-byte MTU, so TCP's MSS is clamped at the tunnel interfaces to 1436. It is applied at the three-way handshake because the handshake is where endpoints learn the ceiling; fixing it later means fragmentation or black holes.

DSR makes the firewall stateless

Return traffic never crosses Cloudflare, so Magic Firewall sees ingress only and cannot track connections. Rules must reason about ephemeral source ports explicitly, which is exactly where the assignment's six-rule table hides its traps: DNS over TCP, an ICMP length cap, a /29 that misses the host in question, and HTTP/3 living on UDP 443.

the deliverable


why this shape, what i traded

This assignment is paper by design: Magic Transit is an enterprise product you cannot buy to practice on. The analysis treats that honestly, walking the packet in both directions and debugging the firewall table line by line instead of pretending at a lab. The diagram above is the argument compressed; the report carries the working.

06

Don't take my word for any of it.

Six instruments, wired to the live assignment infrastructure. Every run hits production and paints the raw response; nothing here is a screenshot.

prb-01Origin echo, through Cloudflarelive

The assignment's required endpoint: the origin returns the request headers it receives. Watch the edge hand it client context nothing else can forge.

$ curl -s https://app.berglabs.net/probe
awaiting run…
prb-06Your own requestlive

This page runs on the same estate, so the edge can tell you about yourself.

reading the edge…

Served by a V8 isolate that started in about five milliseconds, in a data center near you, not a server you could name.

prb-02No-bypass, three vantagesverified 2026-07-10
via cloudflarecurl https://app.berglabs.net/probe200 ok
internet, directcurl --connect-timeout 5 https://<origin wan>:8443/dropped · firewall
cf range, no certcurl -sk https://origin/ -H "Host: app.berglabs.net"400 · no client cert

Layer one is a Palo Alto admitting only Cloudflare's published ranges. Layer two is mutual TLS: no client certificate from my CA, no service, whoever you are. Rows one and three re-verify on a schedule from vantages that can see the origin; the middle row is yours to run from anywhere.

prb-03The Access gateslive
/secure ↗
$ curl -sI https://tunnel.berglabs.net/secure
awaiting run…

Any @cloudflare.com address is on the /secure policy: authenticate as yourself and the Worker answers with your email, your country, and your flag from the private bucket.

prb-04Private bucket, zero keyslive

takehome-flags has no public URL. A Worker binding is the only door, and this page's Worker holds one too.

flag renders here

env.FLAGS.get("flags/<cc>.svg") · no key, no token

prb-05Origin health, from a cron Workerchecks every 5 min
checks passed · 24h
median edge → origin

Cloudflare only health-probes origins on paid plans, so a scheduled Worker generates the synthetic traffic itself: status, latency, alert on failure. Free, on-thesis, and exactly the traffic the origin logs were missing. This page's Worker keeps the history you are reading.

prb-00This page's own telemetry
page loads counted
probes run by visitors
flags fetched
counting since

Counters live in a SQLite Durable Object at the edge; the beacon is navigator.sendBeacon. No analytics vendor, no cookies, nothing about you.

07

Estate as code

The repositories split by visibility, not tooling: the Worker is public because the assignment requires it; the edge config is private because a repo that maps your hostnames, access policies, and firewall posture is reconnaissance material even with every identifier stripped.

public gitlab.com/sean.youngberg/cf-app-services-takehome

One handler, two deployment targets, which is the zero-secrets argument in code form.

  • src/handler.ts is a pure function over Fetch types; adapters for Workers and Node
  • a Dockerfile that proves the same-code-two-packagings claim; CI builds the origin image
  • worker:deploy is a manual gate; the cron health-check Worker lives in the same repo
  • zero credentials, zero account identifiers: IDs inject at deploy from CI variables
private gitlab.com/sean.youngberg/berglabs-infra

Terraform for the edge, run in hosted CI with GitLab-managed state.

  • live resources adopted by import, not destroyed and recreated: the real enterprise scenario
  • DNS and Access pipeline-owned; the API-managed rest documented as such, honestly
  • identifiers arrive from a file-type CI variable; the token is a masked env var
  • the boundary is drawn by network reachability: cloud IaC in hosted CI, on-prem behind the LAN
$ terraform plan
No changes. Your infrastructure matches the configuration.

the honest limit

There is no GitLab-OIDC-to-Cloudflare federation, so deploys ride scoped, masked, rotated tokens rather than short-lived identities. Named as the compensating control it is. And the one instructive failure: an Access policy created through the app endpoint imports cleanly and then 404s on update, because it was app-scoped rather than reusable. Terraform now creates its own reusable policy; identical rules, stable lifecycle.

08

The process, honestly

Recognition, then a bet

The assignments overlapped infrastructure I already run on this zone, so I made the early call that shaped everything: do the homework as production I would keep, not three throwaway proofs of concept.

A plan I could see

All three assignments decomposed into one decision-ready plan on an annotate-and-review surface. It stayed the living record; every later decision threads back into it, so the plan ended as a history of the reasoning, not a snapshot of the answer.

An autonomous first pass, inside guardrails

A deliberately autonomous agent run against the real account, with every credential resolved per role from an encrypted store, operational tokens scoped and IP-locked, and the one key that can mint other keys behind a hardware fingerprint. It put the Application Services core live in a sitting.

The turn: interrogate the plan

Then I stopped taking the plan on faith, and the questions redirected the build. Where do certificates actually go when a tunnel exists: validation relocates to the connector, it does not disappear. Why two hostnames instead of a routing rule: the rule is Enterprise-only, so the constraint is forced, and saying why is worth more than quietly complying. Whether infrastructure-as-code still matters when an agent can drive the primitives: authoring got cheap, the reconciliation loop did not, and if a sentence can change production I want the deterministic diff and the gate more than ever.

The rebuild, to production shape

Terraform adopted the live estate by import. The repos split by visibility. The app became one codebase with two deployment targets. The origin became a container on my own cluster behind a dedicated tunnel, and the zone went to Full (Strict) after verifying nothing live could break.

revision history

Built in the open, mistakes kept

revdatechangeby
012026-07-08Zero Trust leg over the API: the Testers group, the zt. Access app with a deny-list policy, One-Time-PIN IdP; the Gateway payloads staged against the exact token scopes they need.lilu
022026-07-09Repositories split by visibility. The Worker refactored into one pure handler with edge and Node adapters; account identifiers parameterized out of the public repo entirely.lilu
032026-07-10Origin stood up the boring, correct way: LXC 107 on the home Proxmox cluster, nginx, a real Let's Encrypt certificate over DNS-01 so port 80 never opens to the world.lilu
042026-07-10Cloudflare's own Origin CA certificate considered and declined on purpose: step 3 wants a non-Cloudflare cert at Full (Strict), so Let's Encrypt is the honest path. The shortcut is named in the report instead.berg + lilu
052026-07-10An Origin Rule dials the origin on 8443 so the residential WAN keeps 443 dark; destination-port override is one of the few Origin Rules features on the free plan.berg
062026-07-10An ephemeral token got minted for the Origin Rule and immediately regretted: scope folded into the existing token, throwaways revoked. Credential sprawl declined in real time.berg
072026-07-10522s root-caused twice at the firewall: the service matched only default HTTPS so 8443 dropped silently, then the policy keyed on the post-NAT address where the firewall evaluates pre-NAT. Both kept for the write-up.berg
082026-07-10Zone-global AOP never presented a client certificate: toggle on, no cert uploaded, origin trusting a CA that was never enabled. Replaced with per-hostname AOP signed by our own CA; refused-without-cert verified live.berg + lilu
092026-07-10Terraform reconciled to the honest boundary: DNS and Access pipeline-owned with live resources imported rather than recreated; the API-managed rest documented as such. The plan converges to No changes.lilu
102026-07-10Cloudflare does not health-probe a plain proxied origin on the free plan, so a cron Worker now generates the synthetic traffic: status and latency every five minutes, alert on failure. This page charts it.lilu

berg is Sean. lilu is the agent he runs. The division of labor is real and so is the review gate between them.