Skip to content

Commit 49192b8

Browse files
committed
fix(kubernetes): support kata sidecar on ipv4 pods
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
1 parent ac4211c commit 49192b8

2 files changed

Lines changed: 103 additions & 17 deletions

File tree

crates/openshell-supervisor-process/src/netns/mod.rs

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -485,45 +485,77 @@ fn install_sidecar_nft_bypass_rules(proxy_uid: u32) -> Result<()> {
485485
}
486486

487487
const SIDECAR_IPTABLES_CHAIN: &str = "OPENSHELL_SIDECAR_BYPASS";
488+
const PROC_NET_IF_INET6_PATH: &str = "/proc/net/if_inet6";
488489

489490
fn install_sidecar_iptables_legacy_bypass_rules(proxy_uid: u32) -> Result<()> {
490491
let ipv4_filter_tool = find_iptables_legacy().ok_or_else(|| {
491492
miette::miette!(
492493
"trusted iptables-legacy helper not found; sidecar network enforcement fallback unavailable"
493494
)
494495
})?;
495-
let ipv6_fence_tool = find_ip6tables_legacy().ok_or_else(|| {
496-
miette::miette!(
497-
"trusted ip6tables-legacy helper not found; sidecar network enforcement fallback cannot fence IPv6"
498-
)
499-
})?;
500496

501-
cleanup_sidecar_iptables_legacy_rules(&ipv4_filter_tool);
502-
cleanup_sidecar_iptables_legacy_rules(&ipv6_fence_tool);
497+
let ipv6_fence_tool = if current_namespace_has_non_loopback_ipv6()? {
498+
Some(find_ip6tables_legacy().ok_or_else(|| {
499+
miette::miette!(
500+
"trusted ip6tables-legacy helper not found; sidecar network enforcement fallback cannot fence IPv6"
501+
)
502+
})?)
503+
} else {
504+
warn!(
505+
"Skipping IPv6 sidecar iptables-legacy fallback because the current namespace has no non-loopback IPv6 interface"
506+
);
507+
None
508+
};
509+
510+
cleanup_sidecar_iptables_legacy_rule_families(&ipv4_filter_tool, ipv6_fence_tool.as_deref());
503511

504512
if let Err(e) = install_sidecar_iptables_legacy_family_rules(
505513
&ipv4_filter_tool,
506514
proxy_uid,
507515
"icmp-port-unreachable",
508516
) {
509-
cleanup_sidecar_iptables_legacy_rules(&ipv4_filter_tool);
510-
cleanup_sidecar_iptables_legacy_rules(&ipv6_fence_tool);
517+
cleanup_sidecar_iptables_legacy_rule_families(
518+
&ipv4_filter_tool,
519+
ipv6_fence_tool.as_deref(),
520+
);
511521
return Err(e);
512522
}
513523

514-
if let Err(e) = install_sidecar_iptables_legacy_family_rules(
515-
&ipv6_fence_tool,
516-
proxy_uid,
517-
"icmp6-port-unreachable",
518-
) {
519-
cleanup_sidecar_iptables_legacy_rules(&ipv4_filter_tool);
520-
cleanup_sidecar_iptables_legacy_rules(&ipv6_fence_tool);
521-
return Err(e);
524+
if let Some(ipv6_fence_tool) = ipv6_fence_tool {
525+
if let Err(e) = install_sidecar_iptables_legacy_family_rules(
526+
&ipv6_fence_tool,
527+
proxy_uid,
528+
"icmp6-port-unreachable",
529+
) {
530+
cleanup_sidecar_iptables_legacy_rule_families(
531+
&ipv4_filter_tool,
532+
Some(&ipv6_fence_tool),
533+
);
534+
return Err(e);
535+
}
522536
}
523537

524538
Ok(())
525539
}
526540

541+
fn current_namespace_has_non_loopback_ipv6() -> Result<bool> {
542+
match std::fs::read_to_string(PROC_NET_IF_INET6_PATH) {
543+
Ok(content) => Ok(has_non_loopback_ipv6_interface(&content)),
544+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
545+
Err(e) => Err(miette::miette!(
546+
"failed to inspect {PROC_NET_IF_INET6_PATH} before installing sidecar IPv6 fence: {e}"
547+
)),
548+
}
549+
}
550+
551+
fn has_non_loopback_ipv6_interface(content: &str) -> bool {
552+
content.lines().any(|line| {
553+
line.split_whitespace()
554+
.nth(5)
555+
.is_some_and(|iface| iface != "lo")
556+
})
557+
}
558+
527559
fn install_sidecar_iptables_legacy_family_rules(
528560
cmd: &str,
529561
proxy_uid: u32,
@@ -597,6 +629,13 @@ fn cleanup_sidecar_iptables_legacy_rules(iptables_cmd: &str) {
597629
let _ = run_iptables_legacy_current_namespace(iptables_cmd, &["-X", SIDECAR_IPTABLES_CHAIN]);
598630
}
599631

632+
fn cleanup_sidecar_iptables_legacy_rule_families(ipv4_cmd: &str, ipv6_cmd: Option<&str>) {
633+
cleanup_sidecar_iptables_legacy_rules(ipv4_cmd);
634+
if let Some(ipv6_cmd) = ipv6_cmd {
635+
cleanup_sidecar_iptables_legacy_rules(ipv6_cmd);
636+
}
637+
}
638+
600639
/// Run an `ip` command on the host.
601640
fn run_ip(args: &[&str]) -> Result<()> {
602641
let ip_path = find_trusted_binary("ip", IP_SEARCH_PATHS)?;
@@ -906,6 +945,29 @@ mod tests {
906945
}
907946
}
908947

948+
#[test]
949+
fn non_loopback_ipv6_detector_ignores_empty_input() {
950+
assert!(!has_non_loopback_ipv6_interface(""));
951+
assert!(!has_non_loopback_ipv6_interface("\n\n"));
952+
}
953+
954+
#[test]
955+
fn non_loopback_ipv6_detector_ignores_loopback() {
956+
let content = "00000000000000000000000000000001 01 80 10 80 lo\n";
957+
958+
assert!(!has_non_loopback_ipv6_interface(content));
959+
}
960+
961+
#[test]
962+
fn non_loopback_ipv6_detector_detects_pod_interface() {
963+
let content = "\
964+
00000000000000000000000000000001 01 80 10 80 lo
965+
fe800000000000000000000000000001 02 40 20 80 eth0
966+
";
967+
968+
assert!(has_non_loopback_ipv6_interface(content));
969+
}
970+
909971
#[test]
910972
#[ignore = "requires root privileges"]
911973
fn test_create_and_drop_namespace() {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# CI/dev overlay for exercising the Kubernetes supervisor sidecar topology under
5+
# a Kata RuntimeClass.
6+
#
7+
# Use with e2e/with-kube-gateway.sh by setting:
8+
# OPENSHELL_E2E_KUBE_EXTRA_VALUES=deploy/helm/openshell/ci/values-sidecar-kata.yaml
9+
# The e2e wrapper supplies the image repository and tag through OPENSHELL_REGISTRY
10+
# and IMAGE_TAG for existing-cluster runs.
11+
12+
supervisor:
13+
# Use the sidecar topology under Kata so network enforcement runs in the
14+
# sidecar and the sandbox agent container stays low-privilege.
15+
topology: sidecar
16+
sidecar:
17+
# Keep strict process/binary-aware network policy enabled for the Kata
18+
# validation path. Set this false only when intentionally validating the
19+
# documented endpoint/L7-only downgrade mode.
20+
processBinaryAwareNetworkPolicy: true
21+
22+
# Kata validation clusters normally install this RuntimeClass.
23+
server:
24+
defaultRuntimeClassName: kata-qemu

0 commit comments

Comments
 (0)