diff --git a/images/tailscale-exit-nodes/src/saga.ts b/images/tailscale-exit-nodes/src/saga.ts index ffd04b5..a07d3d3 100644 --- a/images/tailscale-exit-nodes/src/saga.ts +++ b/images/tailscale-exit-nodes/src/saga.ts @@ -142,7 +142,7 @@ const defaultSleep = (ms: number): Promise => /** * Run the full provisioning saga: mint a key, create the instance, record its - * handles, lock the firewall (AWS), poll for readiness, then mark active. Any + * handles, poll for readiness, lock the firewall (AWS), then mark active. Any * failure is recorded on the row as `failed` (the reaper reconciles it) and * swallowed — the saga runs detached, so there is no caller to throw to. */ @@ -163,10 +163,6 @@ export async function provisionSaga( // it even if a later step fails. await db.updateInstanceRefs(params.deploymentId, refs.instanceRef, refs.firewallRef); - if (params.provider === 'aws') { - await lockLightsailFirewall(creds, refs.instanceRef, params.providerRegion); - } - let publicIp: string | null = null; for (let attempt = 1; attempt <= pollAttempts; attempt++) { publicIp = await checkInstanceReady(creds, params, refs.instanceRef); @@ -177,6 +173,13 @@ export async function provisionSaga( throw new Error(`instance ${refs.instanceRef} did not become ready in time`); } + // AWS only: lock the public firewall to UDP 41641. This MUST run after the + // instance is `running` — Lightsail rejects PutInstancePublicPorts while the + // instance is still in transition (pending). Vultr locks down at create time. + if (params.provider === 'aws') { + await lockLightsailFirewall(creds, refs.instanceRef, params.providerRegion); + } + await db.markActive(params.deploymentId, publicIp); console.log( `provision ${params.deploymentId} active provider=${params.provider} ip=${publicIp}`,