Uh oh!
There was an error while loading. Please reload this page.
') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })();
There was an error while loading. Please reload this page.
Bug Description
The FedRAMP High / Moderate networking stage provisions its Network Virtual Appliance cluster on Spot instances:
nva.tf:83-84— thenva-templateoptionsblock setsspot = trueandtermination_action = "STOP"(machine typen2d-standard-2,nva.tf:57).modules/compute-vm/main.tf:294-298, that resolves on the instance template toprovisioning_model = "SPOT",preemptible = true,instance_termination_action = "STOP"and — because the module computesautomatic_restart = !var.options.spot—automatic_restart = false.nva.tf:107-118—nva-mig,target_size = 2, autohealing on a TCP:22 health check withinitial_delay_sec = 30.variables.tf:161-168—regionsis typedobject({ primary = string }), so thefor_each = var.regionson the template and the MIG (nva.tf:52,108) resolves to exactly one element. The cluster is two Spot instances in one regional MIG in every deployment, not only small ones.Those two instances are the organization's only route to the internet, and that stays true however large the deployment gets. Workload subnets are free-form in region —
subnetsis typedmap(list(object({ ... region = string ... })))(variables.tf:240-262) — but every part of the egress path is pinned to the single primary region: the NVA template, the NVA MIG and both internal load balancers key offvar.regions(nva.tf:52,108,127,156), and Cloud NAT is a singledmz-nat-primaryatvar.regions.primary(net-vdss.tf:122-130). Both ILB forwarding rules setglobal_access = true(nva.tf:135,164), so subnets outside the primary region reach the primary ILB and hairpin their egress cross-region into those same two appliances.nva.tf:184-190then createsdefault-route-nva—dest_range = "0.0.0.0/0",next_hop_ilbpointing at the primary region's vdss ILB — so a multi-region deployment and a single-region one have the identical choke point. While the NVAs are down the route still resolves to the ILB and traffic blackholes: no internet egress anywhere in the organization, in any region, and no inspected path. The larger the deployment, the more it is concentrated on two reclaimable instances.Spot capacity is reclaimed on 30 seconds' notice whenever Compute Engine needs it back, and reclamation is driven by regional capacity pressure rather than by anything per-instance — so the two instances are not the independent failure domains that
target_size = 2suggests. Withtermination_action = "STOP"andautomatic_restart = false, a preempted NVA stays stopped; recovery depends entirely on MIG autohealing repairing it, and that repair has to obtain Spot capacity too. If the region is short of it, the cluster cannot come back, and the outage lasts as long as the shortage does.The IL5 option makes the opposite choice for the same role —
ngfw.tf:203-206:On-demand, restarted automatically, live-migrated on host maintenance. Its MIG also rolls updates with
max_unavailable = 0andmin_ready_sec = 300(ngfw.tf:288-296) — that stage treats the appliance tier as something that must not lose capacity, while the FedRAMP High stage puts the same tier on instances that can be reclaimed at any moment.Nothing in the repository records the choice. Neither the stage README nor
docs/tdd.mdmentions Spot, andgit log -S 'spot' -- fast/stages-aw/2-networking-a-fedramp-high/nva.tfreturns a single commit — an unrelated upgrade of the project module — so the setting has never been introduced or changed by a commit that explains it. An operator reading the stage has no way to tell whether it is deliberate.Environment and Deployment Context
v2.13.0(8f5b67a6); unchanged atv3.0.0(f64ce6cd), where the directory is renamed2-networking-a-fedrampand the samespot = true/termination_action = "STOP"pair sits atnva.tf:83-84. Becausev3.0.0folds FedRAMP Moderate into this stage, the Spot NVA cluster is now the shipped pattern for both regimes.a-fedrampoptionSteps to Reproduce
fast/stages-aw/2-networking-a-fedramp(FedRAMP High or FedRAMP Moderate).gcloud compute instances list --project <prefix>-net-vdss-host --filter='name~nva' --format='table(name,zone,status,scheduling.provisioningModel,scheduling.instanceTerminationAction,scheduling.automaticRestart)'Every NVA reports
SPOT/STOP/False.2-networking-b-il5-ngfwdeployment for comparison: its appliances reportSTANDARD/True.Expected Behavior
The appliance tier that carries the organization's only egress and inspection path runs on standard (on-demand) instances by default, as the IL5 option's does — or, if Spot is a deliberate cost decision, it is exposed as a stage variable that defaults to standard, and the stage README states the availability trade-off so an operator can make the choice knowingly.
Actual Behavior
spot = trueis hardcoded in the instance template with no variable and no documentation, and the derivedautomatic_restart = falsemeans a preempted appliance is not brought back by Compute Engine at all. Verified in a built FedRAMP High organization: the NVA instances reportprovisioningModel: SPOT, and the organization's billing for 2026-08-14 → 2026-08-20 charges them under the Spot N2D core and RAM SKUs.Relevant Logs and Errors
None. The apply succeeds and emits no warning — which is the substance of the report: nothing in the plan, the apply output or the stage documentation surfaces that the egress path is preemptible.
Additional Context
We have not observed a preemption-caused outage in our own deployment; this is reported from the configuration and the failure mode it implies, not from an incident. That is deliberate — the point is that the exposure exists by default and is invisible to whoever inherits it.
Two further multi-region observations, recorded here rather than filed separately. First, the NVA's trusted-side return routes are generated from a hardcoded
"${var.regions.primary}/..."subnet lookup (nva.tf:30-32), so a workload subnet outside the primary region receives no return route on the appliance — the failure mode reported in #146, along its region dimension rather than its subnet-index one. Second, thefor_each = var.regionsidiom reads as though written for a multi-region map: were theregionstype widened to add asecondary, the NVA template, the MIG and both ILBs would fan out on their own, butdmz-nat-primaryandgoogle_compute_route.defaultare pinned tovar.regions.primaryand would not follow — the second region's appliances would be built and never used.Related: the same file's KMS gap is #193. Both are cases where the FedRAMP High networking stage and the IL5 stage diverge on the appliance tier without the divergence being recorded anywhere.