Skip to content

feat(vpc): expose allowSubnets per subnet - #4478

Merged
Aleksei Sviridkin (lexfrei) merged 1 commit into
cozystack:mainfrom
mattia-eleuteri:feat/vpc-allow-subnets
Sep 29, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 1 commit into
cozystack:mainfrom
mattia-eleuteri:feat/vpc-allow-subnets

Conversation

@mattia-eleuteri

@mattia-eleuteri mattia-eleuteri commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

Adds an optional allowSubnets list to each entry of subnets in the VirtualPrivateCloud chart, rendered into spec.allowSubnets of the kube-ovn Subnet.

Every VPC subnet is rendered private: true, so kube-ovn drops any packet whose source is outside the subnet's own CIDR. A tenant who runs a VPN or router VM on a subnet already gets the forward path through routes (a static route with the VM as nextHopIP), but traffic arriving from the remote network is dropped by the private ACL. Today the only fix is an operator patching the cluster-scoped Subnet by hand: the tenant cannot do it, no application value records it, and it is lost whenever the subnet is recreated.

spec:
  subnets:
    - name: gateway
      cidr: 172.16.1.0/24
      allowSubnets:
        - 192.168.1.0/24
  routes:
    - cidr: 192.168.1.0/24
      nextHopIP: 172.16.1.10

The subnet stays private; the list only widens the ingress ACL of the subnet that declares it, inside the tenant's own VPC. An absent or empty list renders nothing, so existing releases produce the same manifests.

The chart had no helm-unittest suite, so this adds a test target and tests/subnet_test.yaml, covering a subnet without the field, one with it, the field staying scoped to its own subnet, and an empty list. Two of the four cases fail against the template on main.

allowSubnets is a kube-ovn field. #4034 teaches this chart to render Cozyplane objects when kube-ovn is absent, so whichever of the two lands second has to decide how the other backend treats the list.

Screenshots

Downstream repositories

Release note

feat(vpc): subnets accept an optional `allowSubnets` list of CIDRs admitted into the private subnet, so a VM on the subnet can act as a VPN or router gateway without a manual patch of the kube-ovn Subnet.

Summary by CodeRabbit

  • New Features
    • Private subnets can now allow additional source CIDR ranges, such as remote networks routed through a gateway. This setting is optional and can be configured for individual subnets.
  • Tests
    • Added checks for rendering subnet CIDRs, including cases where the setting is omitted or applied to only one subnet.

@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: d7aeb2e5-6e3c-437d-961b-4bc731229638

📥 Commits

Reviewing files that changed from the base of the PR and between 6af081c and 3c657a4.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 12096186-350e-4a46-a925-e1099df82d04

📥 Commits

Reviewing files that changed from the base of the PR and between 6748014 and 6af081c.

📒 Files selected for processing (8)
  • api/apps/v1alpha1/vpc/types.go
  • api/apps/v1alpha1/vpc/zz_generated.deepcopy.go
  • packages/apps/vpc/Makefile
  • packages/apps/vpc/templates/vpc.yaml
  • packages/apps/vpc/tests/subnet_test.yaml
  • packages/apps/vpc/values.schema.json
  • packages/apps/vpc/values.yaml
  • packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The VPC subnet API and Helm chart now support optional allowSubnets CIDR lists. The chart conditionally renders the list. Deep-copy logic, schemas, examples, and rendering tests were updated.

Changes

VPC subnet CIDRs

Layer / File(s) Summary
Subnet API field and deep copies
api/apps/v1alpha1/vpc/types.go, api/apps/v1alpha1/vpc/zz_generated.deepcopy.go
Subnet adds the optional AllowSubnets string slice. Deep-copy methods copy subnet elements and their AllowSubnets slices independently.
Chart schema and rendering
packages/apps/vpc/values.schema.json, packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml, packages/apps/vpc/templates/vpc.yaml, packages/apps/vpc/values.yaml, packages/apps/vpc/tests/subnet_test.yaml, packages/apps/vpc/Makefile
The schemas accept optional allowSubnets string lists. The chart conditionally renders the list. Values document and demonstrate the option. Rendering tests cover absent, empty, populated, and subnet-scoped values; the Makefile adds a Helm test target.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Suggested reviewers: kvaps, myasnikovdaniil

Merge Risk: ⚪ Minimal · up to 6af08

The optional subnet CIDRs are represented in the VPC configuration and rendered by the registered chart. No actionable merge-blocking issue remains after normal checks.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 6af08

The setting is scoped to the subnet that declares it, but the exposed configuration does not limit how broad an admitted source network can be. Authorization and enforcement of that limit need confirmation.

Retained concerns

  • Medium · security · inferred: The new public setting forwards source ranges without a visible CIDR-scope or tenant-ownership constraint. A caller authorized to change a VPC could configure a broad range for its private subnet; whether downstream controls reject or limit that range is unverified.
Security review details

Security Blast Radius

  • inferred — The directly evidenced configuration scope is the declaring subnet, not every subnet in its VPC. The maximum reachable network scope also depends on permitted CIDR values and kube-ovn enforcement, which are not established here.

Security Findings and Attack Paths

  • inferred — A caller able to modify VPC subnet values can submit a broad source CIDR that the chart forwards unchanged. If accepted and enforced as documented by kube-ovn, it could admit substantially more traffic to that private subnet; a cross-tenant path or actual bypass has not been demonstrated.

Trust Boundaries and Controls

  • observed — The visible translation boundary takes a public API value into a kube-ovn Subnet field. Shape validation, per-subnet rendering and private: true are visible; authorization, permitted-range policy and dataplane enforcement are not established by this evidence.

Resilience and Maintainability Implications

  • observed — The field is omitted when unset and is independently deep-copied in API objects. The inspected evidence covers normal rendering, not rejected updates, retries, cleanup or rollback of an already-applied admission list.

Hardening Proposals

  • proposed — Confirm who may set admitted source ranges, and validate CIDR syntax and acceptable network scope at an authoritative boundary. Verify downstream handling of broad ranges and removal or rollback before relying on the field as a tenant-isolated control.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: exposing the optional per-subnet allowSubnets configuration for VPCs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added area/networking Issues or PRs related to networking (ingress, gateway, vpn, metallb, kube-ovn) kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files labels Sep 25, 2026
@mattia-eleuteri
mattia-eleuteri marked this pull request as ready for review September 25, 2026 09:07

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

mattia-eleuteri LGTM. The field is a plain passthrough, and I could not find a way for it to open anything outside the tenant's own subnet.

The ACLs only ever land on the declaring subnet. In the vendored kube-ovn (v1.15.26), SetLogicalSwitchPrivate rebuilds the ingress ACLs of that one logical switch: a default drop, allow inside the subnet CIDR, allow from the node switch CIDR, and one allow rule per allowSubnets entry. No other switch is touched. Traffic from another tenant still has to reach this VPC's router first, and that needs a static route this tenant writes or a peering both sides declare. So even 0.0.0.0/0 only makes this subnet behave as non-private. The controller also re-enqueues the Subnet when spec.allowSubnets changes, so edits and removals apply without recreating it.

The tests hold the template. Removing the with block fails two cases, and replacing it with hasKey fails the empty-list case. Codegen drift and pre-commit are green on this head, and the branch merges cleanly into current main. The red E2E Tests context is from 2026-09-25, before the fork e2e lane was fixed, so it says nothing about this change.

Two non-blocking notes:

  1. Nothing checks that the values are CIDRs. kube-ovn's ValidateSubnet rejects the whole Subnet on anything net.ParseCIDR refuses, for example a bare 192.168.1.10. The HelmRelease still reports Ready, so the tenant gets a subnet that quietly stops reconciling. cidr has the same gap today, so I'm not blocking on it. A CIDR pattern on both fields would be a cheap follow-up.
  2. The field won't reach the docs. The vpc Makefile runs cozyvalues-gen without -r README.md, so the README table is still the old map form and misses routes and peers too. The website VPC page is built from that README. This is pre-existing. A VPN gateway setup needs routes and allowSubnets explained together, so it's worth fixing once for all three.

Every VPC subnet is rendered private, so kube-ovn drops any packet
whose source lies outside the subnet's own CIDR. A tenant that runs a
VPN or router VM on a subnet and points a static route at it gets the
forward path from the existing routes value, but traffic arriving
from the remote network is then dropped by the private ACL.

Subnet is cluster-scoped, so the only workaround so far has been an
operator patching spec.allowSubnets by hand: something the tenant cannot
do, that no application value records, and that is lost whenever the
subnet is recreated. Declaring it per subnet keeps the widening in the
tenant's own VPC and scoped to the one subnet that needs it.

Assisted-by: LLM
Signed-off-by: Mattia Eleuteri <[email protected]>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

mattia-eleuteri I rebased the branch onto current main so CI can run e2e. The image build failed on the old base because it still downloaded mc from dl.min.io, which main has already fixed. The rebase was clean, and the change is the one I approved in 5343554315.

@lexfrei
Aleksei Sviridkin (lexfrei) merged commit 08c344a into cozystack:main Sep 29, 2026
56 of 57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/networking Issues or PRs related to networking (ingress, gateway, vpn, metallb, kube-ovn) kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants