Reserve an explicit Lazaret network topology
Launch slirp4netns on 10.137.54.0/24, update its gateway and DNS routes, and refuse configured allows or specific denies that collide with the enclosing Lazaret /23 while preserving broad RFC 1918 denial.
This commit is contained in:
parent
73e3e9fdee
commit
1c86e1aa0e
2 changed files with 87 additions and 7 deletions
|
|
@ -384,6 +384,36 @@ pub(super) fn resolve_network(
|
|||
configuration: &TrustedHostConfigurationV1,
|
||||
selected: &ProfileConfigurationV1,
|
||||
) -> Result<(Vec<Ipv4Prefix>, Vec<Ipv4Prefix>), ConfigurationError> {
|
||||
let reserved = parse_ipv4_prefix("10.137.54.0/23")?;
|
||||
for declared in configuration
|
||||
.network()
|
||||
.default_allow
|
||||
.iter()
|
||||
.chain(&selected.allow_egress)
|
||||
{
|
||||
if ipv4_prefixes_overlap(parse_ipv4_prefix(declared)?, reserved) {
|
||||
return Err(ConfigurationError::new(
|
||||
ConfigurationErrorKind::InvalidNetwork,
|
||||
"configured allow overlaps Lazaret's reserved topology",
|
||||
));
|
||||
}
|
||||
}
|
||||
for declared in configuration
|
||||
.network()
|
||||
.default_deny
|
||||
.iter()
|
||||
.chain(&selected.deny_egress)
|
||||
{
|
||||
let denied = parse_ipv4_prefix(declared)?;
|
||||
if denied.prefix_length() >= reserved.prefix_length()
|
||||
&& ipv4_prefixes_overlap(denied, reserved)
|
||||
{
|
||||
return Err(ConfigurationError::new(
|
||||
ConfigurationErrorKind::InvalidNetwork,
|
||||
"configured deny conflicts with Lazaret's reserved topology",
|
||||
));
|
||||
}
|
||||
}
|
||||
let mut allow = BTreeSet::new();
|
||||
let mut deny = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
||||
.into_iter()
|
||||
|
|
@ -414,6 +444,16 @@ pub(super) fn resolve_network(
|
|||
Ok((allow.into_iter().collect(), deny.into_iter().collect()))
|
||||
}
|
||||
|
||||
fn ipv4_prefixes_overlap(left: Ipv4Prefix, right: Ipv4Prefix) -> bool {
|
||||
let prefix_length = left.prefix_length().min(right.prefix_length());
|
||||
let mask = if prefix_length == 0 {
|
||||
0
|
||||
} else {
|
||||
u32::MAX << (32 - prefix_length)
|
||||
};
|
||||
u32::from_be_bytes(left.address()) & mask == u32::from_be_bytes(right.address()) & mask
|
||||
}
|
||||
|
||||
const MAX_READINESS_TARGETS: usize = 32;
|
||||
pub(super) fn resolve_network_readiness(
|
||||
readiness: &NetworkReadinessConfigurationV1,
|
||||
|
|
|
|||
|
|
@ -535,6 +535,20 @@ fn path_network_environment_and_volume_conflicts_refuse_loading() {
|
|||
),
|
||||
ConfigurationErrorKind::InvalidNetwork,
|
||||
),
|
||||
(
|
||||
base.replace(
|
||||
"allow_egress = [\"10.1.2.3/24\"]",
|
||||
"allow_egress = [\"10.137.55.2/32\"]",
|
||||
),
|
||||
ConfigurationErrorKind::InvalidNetwork,
|
||||
),
|
||||
(
|
||||
base.replace(
|
||||
"allow_egress = [\"10.1.2.3/24\"]",
|
||||
"allow_egress = [\"10.1.2.3/24\"]\ndeny_egress = [\"10.137.54.3/32\"]",
|
||||
),
|
||||
ConfigurationErrorKind::InvalidNetwork,
|
||||
),
|
||||
(
|
||||
base.replace("/root/.local/state/agent", "/root"),
|
||||
ConfigurationErrorKind::InvalidVolume,
|
||||
|
|
@ -583,6 +597,19 @@ fn path_network_environment_and_volume_conflicts_refuse_loading() {
|
|||
assert_eq!(error.kind(), ConfigurationErrorKind::MissingEnvironment);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broad_private_deny_can_contain_the_reserved_topology() {
|
||||
let tree = TestTree::new();
|
||||
let body = valid_configuration(&tree).replace(
|
||||
"default_allow = [\"192.0.2.4/24\", \"198.51.100.53/32\"]",
|
||||
"default_allow = [\"192.0.2.4/24\", \"198.51.100.53/32\"]\ndefault_deny = [\"10.0.0.0/8\"]",
|
||||
);
|
||||
let config = write_configuration(&tree, &body);
|
||||
let mut environment = BTreeMap::new();
|
||||
environment.insert("HOST_TOKEN".into(), "value".into());
|
||||
load_trusted_configuration(request(&tree, config, environment)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_environment_resolution_occurs_after_authority_validation() {
|
||||
let volume_tree = TestTree::new();
|
||||
|
|
|
|||
Loading…
Reference in a new issue