Skip to content

[platform] Fix VM MAC address not preserved during migration#2169

Merged
kvaps merged 1 commit intomainfrom
fix/vm-migration-mac-address
Mar 10, 2026
Merged

[platform] Fix VM MAC address not preserved during migration#2169
kvaps merged 1 commit intomainfrom
fix/vm-migration-mac-address

Conversation

@sircthulhu
Copy link
Contributor

@sircthulhu sircthulhu commented Mar 7, 2026

What this PR does

During the virtual-machine → vm-instance migration (script 29), VM MAC addresses
are not preserved. Kube-OVN reads MAC exclusively from the pod annotation
ovn.kubernetes.io/mac_address, not from the IP resource spec.macAddress.
Without the annotation, migrated VMs get a new random MAC, breaking OS-level
network config that matches by MAC (e.g. netplan).

This adds a Helm lookup for the Kube-OVN IP resource in the vm-instance chart
template. When the resource exists, its macAddress and ipAddress are
automatically injected as pod annotations. This approach is reliable across
HelmRelease reconciliations — unlike postRenderers, the annotations cannot be
accidentally lost.

Fixes #2166

Release note

[platform] Fix VM MAC address not preserved during virtual-machine → vm-instance migration, causing network loss on VMs with MAC-based netplan config

Summary by CodeRabbit

  • Chores
    • VM templates now automatically populate network annotations (MAC and IP) from kubeovn IP records when available. This streamlines VM network setup on deployment, reduces manual annotation steps, and lowers risk of misconfiguration by ensuring VMs receive the correct address and MAC information from associated network records.

@sircthulhu sircthulhu added backport Should change be backported on previus release backport-previous labels Mar 7, 2026
@sircthulhu sircthulhu added backport Should change be backported on previus release backport-previous labels Mar 7, 2026
@dosubot dosubot bot added size:S This PR changes 10-29 lines, ignoring generated files. bug Something isn't working labels Mar 7, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where Virtual Machine MAC addresses were not preserved during the platform migration from v0.41 to v1.0, leading to network connectivity problems for VMs configured with MAC-based network settings. The solution involves enhancing the migration process to automatically inject necessary OVN MAC and IP annotations into the VM's pod template at Helm render time, thereby ensuring consistent network identity across the migration.

Highlights

  • VM MAC Address Preservation: Implemented a mechanism to preserve Virtual Machine MAC addresses during the v0.41 → v1.0 migration by injecting ovn.kubernetes.io/mac_address and ovn.kubernetes.io/ip_address annotations into the VirtualMachine pod template via a Flux postRenderers kustomize patch.
  • Migration Script Enhancement: Modified migration script 29 to dynamically generate and apply a Kustomize patch that ensures MAC and IP addresses are correctly propagated to VirtualMachine pod annotations, preventing network loss for migrated VMs.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/core/platform/images/migrations/migrations/29
    • Added logic to dynamically construct a postRenderers Kustomize patch.
    • Integrated the generated postRenderers into the HelmRelease definition for vm-instance.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sircthulhu sircthulhu self-assigned this Mar 7, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Injects a Helm lookup into the VM template to find a pre-created Kube-OVN IP resource and, if present, add ovn.kubernetes.io/mac_address and ovn.kubernetes.io/ip_address annotations to the VirtualMachine template metadata.

Changes

Cohort / File(s) Summary
VM Template Annotation Injection
packages/apps/vm-instance/templates/vm.yaml
Adds a Helm lookup block that computes the Kube-OVN IP resource name and namespace, queries ip.kubeovn.io for that object, and conditionally injects ovn.kubernetes.io/mac_address and ovn.kubernetes.io/ip_address annotations from the IP spec into the VM template metadata.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A tiny hop, a templated glance,
I find the IP and give MACs a chance.
Annotations placed where they belong,
Networks wake with a grateful song. 🥕

🚥 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 Title accurately reflects the main change: fixing VM MAC address preservation during migration through Helm template annotation injection.
Linked Issues check ✅ Passed Changes meet all primary objectives: injects ovn.kubernetes.io/mac_address and ovn.kubernetes.io/ip_address annotations via Helm lookup of Kube-OVN IP resource to preserve MAC/IP during VM migration.
Out of Scope Changes check ✅ Passed Changes are narrowly scoped to vm-instance template; adds only necessary Helm lookup logic for MAC/IP preservation with no unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/vm-migration-mac-address

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 and usage tips.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses an issue where VM MAC addresses are not preserved during migration, causing network connectivity problems, by using a Flux postRenderers kustomize patch to inject ovn.kubernetes.io/mac_address and ovn.kubernetes.io/ip_address annotations. However, the current approach introduces a critical YAML injection vulnerability in the migration script due to improper escaping of user-influenced data, potentially leading to privilege escalation. It's recommended to use safer methods like jq or yq for YAML manifest construction. Furthermore, an edge case where the IP address might not be preserved if a MAC address is unavailable should be addressed for a more robust solution.

Comment on lines +494 to +495
ovn.kubernetes.io/mac_address: \"${OVN_MAC}\"
ovn.kubernetes.io/ip_address: \"${OVN_IP}\""
Copy link
Contributor

Choose a reason for hiding this comment

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

security-high high

YAML Injection in Migration Script

The newly added POST_RENDERERS block is vulnerable to YAML injection. The script extracts MAC and IP addresses from ip.kubeovn.io resources and interpolates them directly into a string that defines a Flux postRenderers kustomize patch.

Specifically, on lines 494 and 495, ${OVN_MAC} and ${OVN_IP} are used inside double quotes within the patch string. If these values contain a double quote followed by a newline and arbitrary YAML, an attacker can break out of the intended field and inject additional fields or even entire patches into the HelmRelease specification.

Since the migration script is executed with cluster-admin privileges during a platform migration, and MAC addresses can be influenced by users via their VirtualMachine specifications, this allows a tenant to escalate privileges. For example, an attacker could inject a patch that makes their VirtualMachine privileged or mounts host paths, bypassing security isolation.

Remediation:
Avoid using string interpolation to build YAML patches. Instead, use a tool like jq or yq to safely construct the YAML structure, or ensure that the variables are properly escaped for YAML (e.g., using jq -Rs . to escape the strings).

# Without this, migrated VMs receive a new random MAC address, breaking
# OS-level network config that matches by MAC (e.g. netplan).
POST_RENDERERS=""
if [ -n "$OVN_MAC" ]; then
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current logic only creates the postRenderers patch if $OVN_MAC is set. This means if a VM has a static IP ($OVN_IP) but no static MAC, the IP address will not be preserved during the migration. To ensure that the IP address is also preserved in such cases, the condition should be updated to trigger the patch creation if either $OVN_MAC or $OVN_IP is present.

    if [ -n "$OVN_MAC" ] || [ -n "$OVN_IP" ]; then

@sircthulhu sircthulhu force-pushed the fix/vm-migration-mac-address branch from 71cf230 to 26d0a7a Compare March 10, 2026 08:45
@dosubot dosubot bot added size:XS This PR changes 0-9 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Mar 10, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/apps/vm-instance/templates/vm.yaml`:
- Around line 37-41: The template is reading the deprecated Kube-OVN field
spec.ipAddress; change the IP selection logic for the
ovn.kubernetes.io/ip_address annotation to prefer spec.v4IpAddress, then
spec.v6IpAddress, and only fall back to spec.ipAddress if neither v4 nor v6 is
present. Locate the variables $ovnIPName and $ovnIP in the vm.yaml template and
update the annotation rendering to choose the first non-empty of
spec.v4IpAddress / spec.v6IpAddress / spec.ipAddress so live-migration preserves
IPs when v4/v6 fields are set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bab8d94d-020a-4e02-a24f-d88fb8beddba

📥 Commits

Reviewing files that changed from the base of the PR and between 71cf230 and 26d0a7a.

📒 Files selected for processing (1)
  • packages/apps/vm-instance/templates/vm.yaml

Comment on lines +37 to +41
{{- $ovnIPName := printf "%s.%s" (include "virtual-machine.fullname" .) .Release.Namespace }}
{{- $ovnIP := lookup "kubeovn.io/v1" "IP" "" $ovnIPName }}
{{- if $ovnIP }}
ovn.kubernetes.io/mac_address: {{ $ovnIP.spec.macAddress | quote }}
ovn.kubernetes.io/ip_address: {{ $ovnIP.spec.ipAddress | quote }}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Kube-OVN IP CRD fields:"
sed -n '2507,2585p' packages/system/kubeovn/charts/kube-ovn/templates/kube-ovn-crd.yaml \
  | rg -n 'ipAddress|v4IpAddress|v6IpAddress|macAddress'

echo
echo "Current template usage:"
sed -n '35,42p' packages/apps/vm-instance/templates/vm.yaml

Repository: cozystack/cozystack

Length of output: 892


Use the canonical Kube-OVN IP field instead of the deprecated ipAddress.

The vendored Kube-OVN CRD marks spec.ipAddress as deprecated and recommends using spec.v4IpAddress or spec.v6IpAddress. Rendering ovn.kubernetes.io/ip_address from the deprecated field risks silently dropping the IP address when ipAddress is empty, even though a valid v4IpAddress exists. This can break IP preservation during VM live migration.

Suggested fix
         {{- $ovnIPName := printf "%s.%s" (include "virtual-machine.fullname" .) .Release.Namespace }}
         {{- $ovnIP := lookup "kubeovn.io/v1" "IP" "" $ovnIPName }}
         {{- if $ovnIP }}
         ovn.kubernetes.io/mac_address: {{ $ovnIP.spec.macAddress | quote }}
-        ovn.kubernetes.io/ip_address: {{ $ovnIP.spec.ipAddress | quote }}
+        {{- $ovnIPAddress := coalesce $ovnIP.spec.v4IpAddress $ovnIP.spec.ipAddress }}
+        ovn.kubernetes.io/ip_address: {{ $ovnIPAddress | quote }}
         {{- end }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- $ovnIPName := printf "%s.%s" (include "virtual-machine.fullname" .) .Release.Namespace }}
{{- $ovnIP := lookup "kubeovn.io/v1" "IP" "" $ovnIPName }}
{{- if $ovnIP }}
ovn.kubernetes.io/mac_address: {{ $ovnIP.spec.macAddress | quote }}
ovn.kubernetes.io/ip_address: {{ $ovnIP.spec.ipAddress | quote }}
{{- $ovnIPName := printf "%s.%s" (include "virtual-machine.fullname" .) .Release.Namespace }}
{{- $ovnIP := lookup "kubeovn.io/v1" "IP" "" $ovnIPName }}
{{- if $ovnIP }}
ovn.kubernetes.io/mac_address: {{ $ovnIP.spec.macAddress | quote }}
{{- $ovnIPAddress := coalesce $ovnIP.spec.v4IpAddress $ovnIP.spec.ipAddress }}
ovn.kubernetes.io/ip_address: {{ $ovnIPAddress | quote }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/apps/vm-instance/templates/vm.yaml` around lines 37 - 41, The
template is reading the deprecated Kube-OVN field spec.ipAddress; change the IP
selection logic for the ovn.kubernetes.io/ip_address annotation to prefer
spec.v4IpAddress, then spec.v6IpAddress, and only fall back to spec.ipAddress if
neither v4 nor v6 is present. Locate the variables $ovnIPName and $ovnIP in the
vm.yaml template and update the annotation rendering to choose the first
non-empty of spec.v4IpAddress / spec.v6IpAddress / spec.ipAddress so
live-migration preserves IPs when v4/v6 fields are set.

…instance migration

Kube-OVN reads MAC address exclusively from the pod annotation
ovn.kubernetes.io/mac_address, not from the IP resource spec.macAddress.
Without pod-level annotations, migrated VMs receive a new random MAC,
breaking OS-level network config that matches by MAC (e.g. netplan).

Add a Helm lookup for the Kube-OVN IP resource in the vm-instance chart
template. When the IP resource exists, its macAddress and ipAddress are
automatically injected as pod annotations. This removes the need for
fragile Flux postRenderers on the HelmRelease — the chart itself handles
MAC/IP preservation based on actual cluster state.

Remove the postRenderers approach from migration 29 since the chart now
handles this natively.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Kirill Ilin <[email protected]>
@kvaps kvaps force-pushed the fix/vm-migration-mac-address branch from 26d0a7a to 9a4f492 Compare March 10, 2026 11:41
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/apps/vm-instance/templates/vm.yaml (1)

37-41: ⚠️ Potential issue | 🟠 Major

Prefer the canonical Kube-OVN IP fields here.

Line 41 still reads the deprecated spec.ipAddress field. If the IP CR only has v4IpAddress/v6IpAddress populated, the annotation stays wrong and the migration fix only preserves MAC, not IP.

Suggested fix
         {{- $ovnIPName := printf "%s.%s" (include "virtual-machine.fullname" .) .Release.Namespace }}
         {{- $ovnIP := lookup "kubeovn.io/v1" "IP" "" $ovnIPName }}
         {{- if $ovnIP }}
         ovn.kubernetes.io/mac_address: {{ $ovnIP.spec.macAddress | quote }}
-        ovn.kubernetes.io/ip_address: {{ $ovnIP.spec.ipAddress | quote }}
+        {{- $ovnIPAddress := coalesce $ovnIP.spec.v4IpAddress $ovnIP.spec.v6IpAddress $ovnIP.spec.ipAddress }}
+        ovn.kubernetes.io/ip_address: {{ $ovnIPAddress | quote }}
         {{- end }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/apps/vm-instance/templates/vm.yaml` around lines 37 - 41, The
annotation generation uses the deprecated spec.ipAddress field; update the
vm.yaml template where $ovnIP is used to set ovn.kubernetes.io/ip_address so it
prefers canonical fields (ovnIP.spec.v4IpAddress or ovnIP.spec.v6IpAddress) and
falls back to ovnIP.spec.ipAddress only if neither v4/v6 is present, ensuring
the annotation is correct for IP CRs that only populate v4IpAddress/v6IpAddress
while maintaining backward compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/apps/vm-instance/templates/vm.yaml`:
- Around line 37-41: The annotation generation uses the deprecated
spec.ipAddress field; update the vm.yaml template where $ovnIP is used to set
ovn.kubernetes.io/ip_address so it prefers canonical fields
(ovnIP.spec.v4IpAddress or ovnIP.spec.v6IpAddress) and falls back to
ovnIP.spec.ipAddress only if neither v4/v6 is present, ensuring the annotation
is correct for IP CRs that only populate v4IpAddress/v6IpAddress while
maintaining backward compatibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5bfb8d2-f868-4e8c-9c8d-62713d321911

📥 Commits

Reviewing files that changed from the base of the PR and between 26d0a7a and 9a4f492.

📒 Files selected for processing (1)
  • packages/apps/vm-instance/templates/vm.yaml

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Mar 10, 2026
@kvaps kvaps merged commit 3606b51 into main Mar 10, 2026
8 checks passed
@kvaps kvaps deleted the fix/vm-migration-mac-address branch March 10, 2026 14:18
@github-actions
Copy link

Successfully created backport PR for release-1.0:

@github-actions
Copy link

Successfully created backport PR for release-1.1:

kvaps added a commit that referenced this pull request Mar 10, 2026
…ring migration (#2190)

# Description
Backport of #2169 to `release-1.1`.
kvaps added a commit that referenced this pull request Mar 10, 2026
…ring migration (#2191)

# Description
Backport of #2169 to `release-1.0`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport Should change be backported on previus release backport-previous bug Something isn't working lgtm This PR has been approved by a maintainer size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(migration): VM MAC address not preserved during virtual-machine → vm-instance migration

2 participants