{"record":{"id":"1cb841e6e5ad0a75","repo":"kubernetes/kops","slug":"ipv4-ranges-should-not-be-in-a-ipv6-named-rule-fo","errorCode":null,"errorMessage":"ipv4 ranges should not be in a ipv6-named rule (found %s in %s)","messagePattern":"ipv4 ranges should not be in a ipv6-named rule \\(found (.+?) in (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/firewallrule.go","lineNumber":132,"sourceCode":"\n\t// Make sure we've split the ipv4 / ipv6 addresses.\n\t// A single firewall rule can't mix ipv4 and ipv6 addresses, so we split them into two rules.\n\tfor _, sourceRange := range e.SourceRanges {\n\t\t_, cidr, err := net.ParseCIDR(sourceRange)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"sourceRange %q is not valid: %w\", sourceRange, err)\n\t\t}\n\n\t\tif e.Family == \"\" {\n\t\t\t// This is our own requirement, just for consistency checking.\n\t\t\t// Previous we used the name, but that was confused when the cluster name was ipv6.example.com\n\t\t\treturn fmt.Errorf(\"must set Family when using SourceRanges\")\n\t\t}\n\n\t\tif cidr.IP.To4() != nil {\n\t\t\t// IPv4\n\t\t\tif e.Family != AddressFamilyIPv4 {\n\t\t\t\treturn fmt.Errorf(\"ipv4 ranges should not be in a ipv6-named rule (found %s in %s)\", sourceRange, name)\n\t\t\t}\n\t\t} else {\n\t\t\t// IPv6\n\t\t\tif e.Family != AddressFamilyIPv6 {\n\t\t\t\treturn fmt.Errorf(\"ipv6 ranges should be in a ipv6-named rule (found %s in %s)\", sourceRange, name)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (_ *FirewallRule) CheckChanges(a, e, changes *FirewallRule) error {\n\tif e.Network == nil {\n\t\treturn fi.RequiredField(\"Network\")\n\t}\n\treturn nil\n}","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/firewallrule.go#L114-L150","documentation":"A single GCE firewall rule cannot mix IPv4 and IPv6 source ranges, so kOps enforces that each entry matches the rule's declared Family. An IPv4 CIDR inside a rule whose Family is AddressFamilyIPv6 triggers this error (and symmetrically for IPv6).","triggerScenarios":"e.Family is ipv6 but the loop encounters a CIDR whose cidr.IP.To4() != nil (an IPv4 range, e.g. 10.0.0.0/8 in a rule under an ipv6-named rule).","commonSituations":"Adding an IPv4 range to an IPv6 rule when enabling dual-stack; wrong family label after copy-paste; tooling that defaults all rules to ipv6 because the cluster name contains ipv6.","solutions":["Move the IPv4 range into a separate rule with family: ipv4","Or correct family to ipv4 if the rule should be IPv4-only","Remove the mismatched range if it was added by mistake","Re-run kops update"],"exampleFix":"// before\nfamily: ipv6\nsourceRanges: [\"10.0.0.0/8\", \"fd00::/8\"]\n// after\nfamily: ipv6\nsourceRanges: [\"fd00::/8\"]\n# plus a second rule with family: ipv4, sourceRanges: [\"10.0.0.0/8\"]","handlingStrategy":"validation","validationCode":"for _, r := range rule.SourceRanges {\n  _, cidr, _ := net.ParseCIDR(r)\n  isV4 := cidr.IP.To4() != nil\n  if (rule.Family == \"ipv6\" && isV4) || (rule.Family == \"ipv4\" && !isV4) {\n    return fmt.Errorf(\"rule %s: CIDR %q does not match family %s\", rule.Name, r, rule.Family)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never mix v4 and v6 ranges in one GCE rule — GCE forbids it","Split dual-stack sources into two rules with correct families","Check To4() on parsed CIDRs in any manifest generator"],"tags":["gcp","gce","firewall","ipv6","validation"],"backgroundTag":"mixed-ip-family-firewall-rule","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}