feat(gcp): tunable Cloud NAT ports and Cloud SQL private IP#369
Open
Cre-eD wants to merge 2 commits into
Open
Conversation
Add opt-in Cloud NAT port/mapping tuning to externalEgressIp (minPortsPerVm, maxPortsPerVm, dynamicPortAllocation, endpointIndependentMapping) and Cloud SQL private-IP support (privateNetwork, publicIpEnabled). The in-cluster cloud-sql-proxy dials --private-ip whenever a private network is configured. All new fields are optional and default to the previous behaviour (64 min ports, endpoint-independent mapping on, dynamic port allocation off, public IPv4 on), so existing clusters and instances are unchanged until they opt in. Rationale: high-egress workloads that open many concurrent outbound connections can exhaust NAT source ports under the fixed 64-port, endpoint-independent defaults; dropped SYNs then show up as dial i/o timeouts on new connections. The new knobs allow raising the port budget and enabling dynamic port allocation, or removing the public egress path for the database entirely via private IP. Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Cre-eD
requested review from
Laboratory,
smecsia and
universe-ops
as code owners
July 22, 2026 08:12
Semgrep Scan ResultsRepository:
Scanned at 2026-07-22 11:49 UTC |
Security Scan ResultsRepository:
Scanned at 2026-07-22 11:50 UTC |
📊 Statement coverageMeasured on the documented included set (see
Baseline: |
Addresses multi-model review of the NAT-tuning + Cloud SQL private-IP change: - NAT validation now checks EFFECTIVE port bounds (defaults applied) against GCP's real range (floor 32, ceiling 65536) and requires max > min under dynamic port allocation, so a lone maxPortsPerVm below the default minimum or a sub-floor value is rejected up front instead of failing late at pulumi up. - resolveNatPortSettings forces endpoint-independent mapping off whenever dynamic port allocation is on, keeping the resolved NAT args self-consistent regardless of validation order. - EnableDynamicPortAllocation is emitted only when true, so the default path is byte-identical to the previous NAT resource (no spurious update on existing clusters). - The in-cluster proxy switches to --private-ip only once the public IP is disabled (UsesPrivateIpProxy), not the instant privateNetwork is set, so adding a private IP while public stays on is a safe no-cutover step. - publicIpEnabled:true is now a genuine no-op in ipConfiguration (was starting to manage the IP block and could wipe out-of-band authorized networks); empty privateNetwork strings are treated as unset. - Cloud SQL config gains a Validate() (availabilityType, privateNetwork format, publicIpEnabled requires privateNetwork) plus HasPrivateNetwork/ UsesPrivateIpProxy helpers; instance-create errors hint at the Private Services Access prerequisite. - Shared port-bound constants; regenerated JSON schemas for the new fields; guide note on NAT port tuning. Added tests for the new validation branches, requireSsl=false, publicIpEnabled no-op, empty private network, the config to proxy derivation, and the container-args --private-ip path. Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two opt-in GCP capabilities, defaults unchanged:
1. Tunable Cloud NAT (
externalEgressIp)minPortsPerVm,maxPortsPerVm— NAT source-port budget per VM.dynamicPortAllocation— let a VM scale ports between min and max on demand.endpointIndependentMapping— toggle EIM (must be off to use dynamic allocation).2. Cloud SQL private IP (
gcp-cloudsql-postgres)privateNetwork— give the instance a private IP on a VPC (requires Private Services Access).publicIpEnabled— toggle the public IPv4 (default true).cloud-sql-proxy(runtime sidecar and init-job) automatically dials--private-ipwheneverprivateNetworkis set.Why
The Cloud NAT created for
externalEgressIpis hardcoded to 64 min ports per VM with endpoint-independent mapping on and no dynamic port allocation. Workloads that open many concurrent outbound connections (e.g. per-task DB connections plus outbound API calls) can exhaust NAT source ports; the dropped SYNs surface downstream asdial ... i/o timeouton new connections and, for a proxied Cloud SQL, as intermittentserver closed the connection unexpectedlyin clients. These knobs let operators raise the port budget / enable dynamic allocation, or remove the public egress path for the database entirely by moving the proxy to private IP.Backwards compatibility
Every new field is a pointer and defaults to the prior behaviour (64 min ports, EIM on, dynamic allocation off, public IPv4 on). A nil/unset config produces the exact same NAT and IP configuration as before, so existing clusters and instances are untouched until they opt in. The only wire-level change on re-provision with default config is
enableDynamicPortAllocationgoing from unset to an explicitfalse, which is a no-op in GCP.Validation
dynamicPortAllocationrequiresendpointIndependentMapping: falseand power-of-two port bounds (GCP constraints), enforced inExternalEgressIpConfig.Validate().publicIpEnabled: falserequiresprivateNetwork(otherwise the instance would be unreachable), enforced in the Postgres provisioner.Tests
resolveNatPortSettings: defaults preserved for nil/empty config; overrides applied.ipConfiguration: nil when unset (leaves instance IP config untouched),requireSsl-only unchanged, private network keeps public on by default,publicIpEnabled: falsedisables IPv4.--private-ipemitted only when requested, for both runtime and init-job proxies.go build ./...,go vet, andgo test ./pkg/clouds/...all pass.