Skip to content

feat: add private nodes support via networkConfig#149

Closed
pedro-tamandua wants to merge 1 commit intocloudpilot-ai:mainfrom
pedro-tamandua:feature/private-nodes-support
Closed

feat: add private nodes support via networkConfig#149
pedro-tamandua wants to merge 1 commit intocloudpilot-ai:mainfrom
pedro-tamandua:feature/private-nodes-support

Conversation

@pedro-tamandua
Copy link
Copy Markdown
Contributor

Add NetworkConfig to GCENodeClass spec to control external IP allocation and subnet selection for provisioned nodes.

Features:

  • Add NetworkConfig struct with enableExternalIPAccess and subnetwork fields
  • Implement resolveAccessConfigs() to conditionally disable external IPs
  • Implement resolveSubnetwork() to allow custom subnet configuration
  • Update CRD with new networkConfig fields
  • Simplify CRD validation rules to meet Kubernetes cost limits
  • Add unit tests for network configuration resolution
  • Add example manifests for private nodes and custom subnets

When enableExternalIPAccess is false, nodes are provisioned without external IP addresses (private nodes), improving security posture.

Usage Example

apiVersion: karpenter.k8s.gcp/v1alpha1
kind: GCENodeClass
metadata:
name: private-nodes
spec:
networkConfig:
enableExternalIPAccess: false
imageSelectorTerms:
- alias: ContainerOptimizedOS@latest## Testing

  • Unit tests added for resolveAccessConfigs and resolveSubnetwork
  • All tests passing (5/5)
  • Manually tested in GKE cluster - nodes provisioned without external IPs

Backward Compatibility

Fully backward compatible. When networkConfig is not specified, behavior remains unchanged (nodes get external IPs by default).

@gitautomator
Copy link
Copy Markdown
Contributor

gitautomator bot commented Nov 11, 2025

Thanks to your contribution, the maintainers will review it as soon as they can!

@gitautomator
Copy link
Copy Markdown
Contributor

gitautomator bot commented Nov 11, 2025

The release note is either empty or incomplete, please consider: Add support for private nodes via networkConfig in GCENodeClass to improve security by allowing nodes to be provisioned without external IP addresses. Users can now configure enableExternalIPAccess and subnetwork fields to control network settings.

@gitautomator gitautomator bot added enhancement New feature or request go Pull requests that update go code labels Nov 11, 2025
@pedro-tamandua pedro-tamandua force-pushed the feature/private-nodes-support branch from 09d35f2 to 015529a Compare November 11, 2025 23:08
@jwcesign
Copy link
Copy Markdown
Contributor

Hi, @pedro-tamandua thanks for your contribution, can you help fix the DCO workflow?

Add NetworkConfig to GCENodeClass spec to control external IP allocation
and subnet selection for provisioned nodes.

Features:
- Add NetworkConfig struct with enableExternalIPAccess and subnetwork fields
- Implement resolveAccessConfigs() to conditionally disable external IPs
- Implement resolveSubnetwork() to allow custom subnet configuration
- Update CRD with new networkConfig fields
- Simplify CRD validation rules to meet Kubernetes cost limits
- Add unit tests for network configuration resolution
- Add example manifests for private nodes and custom subnets

When enableExternalIPAccess is false, nodes are provisioned without
external IP addresses (private nodes), improving security posture.

Backward compatible: defaults to existing behavior when networkConfig
is not specified.

RELEASE NOTE:
Add support for private nodes via networkConfig in GCENodeClass to improve security by allowing nodes to be provisioned without external IP addresses. Users can now configure enableExternalIPAccess and subnetwork fields to control network settings.

Signed-off-by: pedro-tamandua <[email protected]>
@pedro-tamandua pedro-tamandua force-pushed the feature/private-nodes-support branch from 015529a to 170b1ad Compare November 12, 2025 12:21
@pedro-tamandua
Copy link
Copy Markdown
Contributor Author

DCO workflow fixed.

Copy link
Copy Markdown
Contributor

@jwcesign jwcesign left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pedro-tamandua
Other lgtm

NetworkTags []string `json:"networkTags,omitempty"`
// NetworkConfig specifies network configuration for instances
// +optional
NetworkConfig *NetworkConfig `json:"networkConfig,omitempty"`
Copy link
Copy Markdown
Contributor

@jwcesign jwcesign Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on GCE pages:
image

  1. Put NetworkTags into NetworkConfig field
  2. It could have multiple network interface, so, EnableExternalIPAccess and Subnetwork should be array list. Something like:
type xxx struct {
..
  NetworkConfig NetworkConfig
}

type NetworkConfig {
  Tags []string
   NetworkInterfaces []NetworkInterface
}

type NetworkInterface {
  EnableExternalIPAccess bool
  Subnetwork string
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pedro-tamandua any movement on this?

dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Mar 28, 2026
…odes)

Add NetworkConfig to GCENodeClassSpec to allow overriding per-network-interface
settings on provisioned nodes. The primary use case is private nodes: setting
enableExternalIPAccess: false on the primary interface removes the AccessConfig
entry, resulting in a node with no external (public) IP address.

- New NetworkConfig struct with NetworkInterfaces []NetworkInterface field
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces are matched to node pool template by position (index)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#149

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Mar 28, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Apr 1, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Apr 2, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Apr 9, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Apr 9, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
thameezb pushed a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Apr 11, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
thameezb pushed a commit that referenced this pull request Apr 12, 2026
…odes) (#229)

* feat: add NetworkConfig for per-interface network settings (private nodes)

Redesign and complete the initial approach from pedro-tamandua (#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes #182

Based on initial work by @pedro-tamandua:
#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

* review: fix ForceSendFields/NullFields slice aliasing and test gaps

Address findings from code review of the NetworkConfig PR:

- Remove no-op []*compute.AccessConfig(...) type conversion
- Always copy ForceSendFields and NullFields from the template interface
  rather than aliasing the backing array; prevents latent mutation if a
  template cache is ever introduced
- Rename shadowed `t` variable in makeTemplateWithAccessConfig closure
- Add ForceSendFields mutation-safety assertion to the private-node test
- Add missing test for the lo.Contains deduplication branch (template
  already carries "AccessConfigs" in ForceSendFields)
- Tighten NetworkInterface doc comments: consolidate repeated index-
  matching prose and rewrite EnableExternalIPAccess to lead with its
  asymmetric semantics (only false acts; true/nil both inherit)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

* review: de-AI pass and regenerate CRDs

- Remove duplicate "matched by index" sentence from GCENodeClassSpec.NetworkConfig
  field comment (already stated on NetworkInterfaces); trim to single purpose line
- Rename accessConfigsCount → count in deduplication test (trivially-scoped counter)
- Regenerate CRD yaml and deepcopy with updated comments (make update)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

* docs: expand NetworkConfig examples with prerequisites and subnetwork override

- private-nodes-gcenodeclass.yaml: add background section explaining that
  Karpenter manages the karpenter-default/ubuntu node pool templates and
  inherits the cluster's default network config; add Cloud NAT, VPC-native
  cluster, and firewall prerequisites; note behaviour on fully-private clusters
- subnetwork-override-gcenodeclass.yaml: new example showing how to place
  Karpenter nodes in a specific subnetwork via networkConfig.networkInterfaces

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

* docs: add private-nodes.md covering cluster modes, prerequisites, and NetworkConfig

Explains the full picture that was previously only in YAML comments:
- How Karpenter creates and uses karpenter-default/ubuntu node pool templates
- Fully-private clusters work transparently (GKE handles enablePrivateNodes)
- When to use enableExternalIPAccess: false (standard cluster, selective privacy)
- Cloud NAT, VPC-native, and IAP prerequisites
- Subnetwork override usage and URL format
- Multi-interface configuration
- Why networkTags is top-level (GCP Instance vs NetworkInterface API design)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

* docs: retract unverified claim about fully-private cluster support

The karpenter-default node pool template is created without setting
NodePool.NetworkConfig.EnablePrivateNodes. Behaviour on clusters with
enablePrivateNodes at the cluster level is untested — replace the confident
"works transparently" claim with an honest note and a recommendation to
set enableExternalIPAccess: false explicitly regardless.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

* docs: update private-nodes issue link from #TODO to #230

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>

---------

Signed-off-by: Dmitry Chepurovskiy <[email protected]>
Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
@dm3ch
Copy link
Copy Markdown
Collaborator

dm3ch commented Apr 12, 2026

Closing this as it was done in #229 .
TY for your efforts to bring this up

@dm3ch dm3ch closed this Apr 12, 2026
dm3ch added a commit to dm3ch/karpenter-provider-gcp that referenced this pull request Apr 12, 2026
…odes)

Redesign and complete the initial approach from pedro-tamandua (cloudpilot-ai#149):
the original concept had a flat NetworkConfig (single enableExternalIPAccess +
subnetwork), this rework promotes it to a per-interface list so multiple
NICs on multi-homed nodes can each be controlled independently.

Changes:
- New NetworkConfig struct with NetworkInterfaces []NetworkInterface
- NetworkInterface supports enableExternalIPAccess (*bool) and subnetwork (string)
- Interfaces matched to node pool template by position (index)
- Deep-copy AccessConfigs on each interface to prevent template mutation
- Force-send empty AccessConfigs slice when disabling external IP so the GCP
  API does not default-insert ONE_TO_ONE_NAT (was silent security gap)
- Unset fields inherit from the node pool template (backward compatible)
- Updated CRD and deepcopy; added tests and example YAML

Closes cloudpilot-ai#182

Based on initial work by @pedro-tamandua:
cloudpilot-ai#149

Co-authored-by: pedro-tamandua <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Dmitry Chepurovskiy <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants