8000 T52 — Add the dynamic_ingress setting to the box's ingress policy by minimal-foundry[bot] · Pull Request #1699 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content
Merged
4 changes: 4 additions & 0 deletions crates/minimal-tui/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ fn policy_lines(model: &Model, key: &SessionKey) -> Vec<Line<'static>> {
Some(ingress) => {
if ingress.port_mappings.is_empty()
&& ingress.dynamic_allowed_range.is_none()
&& ingress.dynamic_ingress.is_none()
{
lines.push(Line::raw(" deny all"));
}
Expand All @@ -557,6 +558,9 @@ fn policy_lines(model: &Model, key: &SessionKey) -> Vec<Line<'static>> {
if let Some((lo, hi)) = ingress.dynamic_allowed_range {
lines.push(Line::raw(format!(" dynamic ports {lo}–{hi}")));
}
if let Some(mode) = ingress.dynamic_ingress {
lines.push(Line::raw(format!(" dynamic ingress {mode}")));
}
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions crates/minimal-tui/tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,45 @@ fn detail_pane_with_policy() {
proto: sessions::IpProto::Tcp,
}],
dynamic_allowed_range: None,
dynamic_ingress: None,
}),
)),
},
);
// Focus the session.
model.cursor = 1;
insta::assert_snapshot!(render(&mut model));
}

#[test]
fn detail_pane_shows_dynamic_ingress() {
let mut model = fixed_model(vec![provider(
"host",
vec![entry(1, Some("api-staging"), "/src/api")],
)]);
let key = SessionKey {
provider: "host".to_string(),
id: id(1),
};
model.details.insert(
key,
Detail {
record: Some(record(Some("api-staging"), NetworkMode::OwnIp)),
policy: Some(sessions::SessionPolicy::new(
Some(sessions::EgressPolicy {
allow_subnets: Some(vec!["10.0.0.0/8".to_string()]),
allow_dns_hosts: None,
allow_protocols: None,
deny_subnets: Some(vec!["192.168.0.0/16".to_string()]),
}),
Some(sessions::IngressPolicy {
port_mappings: vec![sessions::PortMapping {
external_port: 8080,
internal_port: 80,
proto: sessions::IpProto::Tcp,
}],
dynamic_allowed_range: None,
dynamic_ingress: Some(sessions::DynamicIngress::Ask),
}),
)),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
source: crates/minimal-tui/tests/snapshots.rs
assertion_line: 231
expression: render(&mut model)
---
"┌──────────────────────────────┐┌ api-staging · 00000000 ──────────────────────────────────────────┐"
"│ ▃🭕🭏🭕🭏 M I N I M A L ││ project /src/api net OwnIp │"
"│ ──────────────────────────── ││ user chroma idle - │"
"│ / ││ title - │"
"│ ──────────────────────────── ││ bells - │"
"│ ▼ host v0.1 ● ││ ─ Policy ─────────────────────────────────────────────────────── │"
"│ ▸ api-staging OwnIp ││ egress │"
"│ ││ subnets 10.0.0.0/8 │"
"│ ││ dns hosts allow all │"
"│ ││ protocols allow all │"
"│ ││ deny subnets 192.168.0.0/16 │"
"│ ││ ingress │"
"│ ││ tcp :8080 → :80 │"
"│ ││ dynamic ingress ask │"
"│ ││ ─ Preview (live screen snapshot) ─────────────────────────────── │"
"│ ││ loading preview… │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"└──────────────────────────────┘└──────────────────────────────────────────────────────────────────┘"
" ↑↓ move · / filter · enter attach · d destroy · r rename · n new · q quit "
79 changes: 78 additions & 1 deletion crates/minimal/src/cmd/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub(crate) async fn activate_session(
ingress: (!port_mappings.is_empty()).then_some(sessions::IngressPolicy {
port_mappings,
dynamic_allowed_range: None,
dynamic_ingress: None,
}),
};

Expand Down Expand Up @@ -971,7 +972,10 @@ pub fn format_policy(
match &policy.ingress {
None => writeln!(out, " deny all")?,
Some(ingress) => {
if ingress.port_mappings.is_empty() && ingress.dynamic_allowed_range.is_none() {
if ingress.port_mappings.is_empty()
&& ingress.dynamic_allowed_range.is_none()
&& ingress.dynamic_ingress.is_none()
{
writeln!(out, " deny all")?;
}
for mapping in &ingress.port_mappings {
Expand All @@ -984,6 +988,9 @@ pub fn format_policy(
if let Some((lo, hi)) = ingress.dynamic_allowed_range {
writeln!(out, " dynamic ports {lo}–{hi}")?;
}
if let Some(mode) = ingress.dynamic_ingress {
writeln!(out, " dynamic ingress {mode}")?;
}
}
}
}
Expand Down Expand Up @@ -1647,3 +1654,73 @@ pub async fn cmd_rename(global: &GlobalArgs, args: RenameArgs) -> Result<(), any
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use sessions::{
DynamicIngress, IngressPolicy, IpProto, NetworkMode, PortMapping, SessionPolicy,
};

#[test]
fn format_policy_dynamic_ingress_allow_prints_row_not_deny_all() {
let policy = SessionPolicy::new(
None,
Some(IngressPolicy {
port_mappings: vec![],
dynamic_allowed_range: None,
dynamic_ingress: Some(DynamicIngress::Allow),
}),
);
let mut out = Vec::new();
format_policy(&mut out, &policy, NetworkMode::OwnIp).unwrap();
let rendered = String::from_utf8(out).unwrap();
assert!(
rendered.contains(" dynamic ingress allow"),
"dynamic ingress row must be printed: {rendered}"
);
assert!(
!rendered.contains(" deny all"),
"a policy with an explicit dynamic ingress setting must not print 'deny all': {rendered}"
);
}

#[test]
fn format_policy_dynamic_ingress_prints_beside_static_mapping() {
let policy = SessionPolicy::new(
None,
Some(IngressPolicy {
port_mappings: vec![PortMapping {
external_port: 8080,
internal_port: 80,
proto: IpProto::Tcp,
}],
dynamic_allowed_range: None,
dynamic_ingress: Some(DynamicIngress::Ask),
}),
);
let mut out = Vec::new();
format_policy(&mut out, &policy, NetworkMode::OwnIp).unwrap();
let rendered = String::from_utf8(out).unwrap();
assert!(
rendered.contains(" tcp :8080 → :80"),
"static mapping must still render: {rendered}"
);
assert!(
rendered.contains(" dynamic ingress ask"),
"dynamic ingress row must render alongside mapping: {rendered}"
);
}

#[test]
fn format_policy_egress_rows_default_when_unset() {
let policy = SessionPolicy::default();
let mut out = Vec::new();
format_policy(&mut out, &policy, NetworkMode::OwnIp).unwrap();
let rendered = String::from_utf8(out).unwrap();
assert!(rendered.contains("egress\n"), "{rendered}");
assert!(rendered.contains(" allow all\n"), "{rendered}");
assert!(rendered.contains("ingress\n"), "{rendered}");
assert!(rendered.contains(" deny all\n"), "{rendered}");
}
}
6 changes: 5 additions & 1 deletion crates/minimald-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ pub mod exec;
pub mod taskenv;
pub mod trace;

pub use sessions::{EgressPolicy, IngressPolicy, IpProto, NetworkMode, PortMapping, SessionPolicy};
pub use sessions::{
DynamicIngress, EgressPolicy, IngressPolicy, IpProto, NetworkMode, PortMapping, SessionPolicy,
};

pub const RPC_SUBSYSTEM_PREFIX: &str = "minimald-v1-";

Expand Down Expand Up @@ -1208,6 +1210,7 @@ mod tests {
let ingress = IngressPolicy {
port_mappings: vec![mapping],
dynamic_allowed_range: Some((10000, 20000)),
dynamic_ingress: Some(sessions::DynamicIngress::Ask),
};
let json = serde_json_lenient::to_string(&ingress).unwrap();
let rt: IngressPolicy = serde_json_lenient::from_str(&json).unwrap();
Expand All @@ -1230,6 +1233,7 @@ mod tests {
json.contains("\"dynamic_allowed_range\":null"),
"got: {json}"
);
assert!(json.contains("\"dynamic_ingress\":null"), "got: {json}");
}

fn round_trip<T>(value: &T) -> T
Expand Down
2 changes: 2 additions & 0 deletions crates/minimald/src/net/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,7 @@ mod tests {
},
],
dynamic_allowed_range: None,
dynamic_ingress: None,
};
let gate = IngressGate::for_session("100.64.0.9".into(), Some(&ingress));
assert!(gate.allowed.contains(&80)); // TCP internal port
Expand Down Expand Up @@ -1050,6 +1051,7 @@ mod tests {
proto: sessions::IpProto::Tcp,
}],
dynamic_allowed_range: None,
dynamic_ingress: None,
};
let gate = IngressGate::for_session(LEASE.to_string(), Some(&ingress));
// New TCP connection to an undeclared port -> dropped, tagged Tcp.
Expand Down
1 change: 1 addition & 0 deletions crates/minimald/tests/netns_root_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ async fn netns_ingress_static_port_mapping_exposes_then_unexposes() {
proto: IpProto::Tcp,
}],
dynamic_allowed_range: None,
dynamic_ingress: None,
};
let control = ControlChannel::Unix(sock.clone());
let exposed = apply_ingress(&control, lease.ip, &ingress)
Expand Down
Loading
Loading
0