Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

network: bridge: add support for unmanaged mode #1126

Merged
merged 2 commits into from
Nov 22, 2024

Conversation

M1cha
Copy link
Contributor

@M1cha M1cha commented Nov 13, 2024

While Linux doesn't support modes on bridges, we use this concept to let the user tell us if they want podman/netavark to own the bridge or not. L3 behaves the same way as before this commit. L2 requires the bridge to exist already, will not setup any sysctls or firewall rules on the host and will not delete the bridge once all containers left.

Fixes #1090

Related: containers/common#2247

Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

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

So this is something I did not thought about. What does it mean to be a internal network with l2? There seems to be fair bit of overlap between internal l3 and l2 (except for a few sysctls and the delete the bridge thing).

My understanding is that routing would not be able to blocked without using the firewall in the l2 case normally you would have your l2 ethX interface connected to the bridge and your LAN so there is no ip routing happing on the host system as all the packages will be send to the routed normally vie the LAN.

I guess there is no good reason to support this but I wonder if we should make podman (rather c/common) error out if internal and l2 is used

Comment on lines 40 to 41
L2,
L3,
Copy link
Member

Choose a reason for hiding this comment

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

Can you add comments to the types what they do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

setup_ipv4_fw_sysctl()?;
if data.ipam.ipv6_enabled {
setup_ipv6_fw_sysctl()?;
if let BridgeMode::L3 = data.mode {
Copy link
Member

Choose a reason for hiding this comment

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

mhh using if let means it will not fail to compile if we add a new enum variant, right?
If we ever were to add a third mode I think it would be cleaner to fail so we have to make a explicit decision

Also now that I look at this we should really not set the global sysctl for internal networks here (feel free to fix this in a second commit or I put it on my list)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If podman passed an unsupported mode it would fail in get_bridge_mode_from_string. In case you mean "in case we add support for the new mode, but forget to handle it in all places", we need to use match everywhere. It might make some conditions more complicated but it would cause compile-errors.

Copy link
Member

Choose a reason for hiding this comment

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

yes I meant we forget to handle it. However if we go unmanaged/managed for now I think it is safe enough that we do not add new modes often enough. And if we do we need to check what mode should do what anyway so let's keep the readable form.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

makes sense.

Also now that I look at this we should really not set the global sysctl for internal networks here (feel free to fix this in a second commit or I put it on my list)

done

@@ -225,7 +237,7 @@ impl driver::NetworkDriver for Bridge<'_> {
};

// if the network is internal block routing and do not setup firewall rules
if self.info.network.internal {
if self.info.network.internal && data.mode == BridgeMode::L3 {
Copy link
Member

Choose a reason for hiding this comment

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

if we wrap this entire part in if let BridgeMode::L3 = data.mode { up until the setup_firewall call we would only need to match once

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea, done

error_list.push(err);
}
};
if let BridgeMode::L3 = mode {
Copy link
Member

Choose a reason for hiding this comment

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

Can we simplify this together with the self.info.network.internal condition into one and then remove the early return from internal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea, done

Comment on lines 573 to 571
if let BridgeMode::L2 = data.mode {
return Err(err).wrap("l2 bridge interface not found");
}
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if user will be running into 730e1bf if we do not create the bridge. There seems to be bad things going on while/adding removing new interfaces.

I think the error here should say l2 requires the bridge to already exist on the host

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but for an unmanaged bridge I'd let netavark move that responsibility to the creator of the bridge. The workaround is not needed if you manually set a mac address on the bridge, because it'll then keep that address forever.

core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?;
}
let enable_arp_notify = format!(
"/proc/sys/net/ipv4/conf/{}/arp_notify",
Copy link
Member

Choose a reason for hiding this comment

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

I think we may want to keep arp_notify even on l2?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know, all of these can actually be useful for both managed and unmanaged mode. The question is just if we want to enforce our own defaults or rely on the linux kernel defaults. If we want to make this decision differently for each sysctl, we have to document why.

@M1cha
Copy link
Contributor Author

M1cha commented Nov 14, 2024

So this is something I did not thought about. What does it mean to be a internal network with l2? There seems to be fair bit of overlap between internal l3 and l2 (except for a few sysctls and the delete the bridge thing).

It may just be me, but I always had trouble understanding what --internal does by just reading the documentation (or even just by it's name). The code is very simple though: Disable forwarding via sysctls, don't setup a firewall and don't add a default route. (1)

I guess there is no good reason to support this but I wonder if we should make podman (rather c/common) error out if internal and l2 is used

No matter if podman "manages" the bridge or not, having podman configure addresses and routes can be useful. You can compare that with static IPs and routes inside the network-config of a physical host.

Also, podmans firewall is currently external to the container, so it can be compared to a managed l3 switch with an integrated firewall. That is definitely needed for both managed and external bridges, the question is just if there is a need to have podman setup the firewall for external bridges. My guess is: no. Either way, podman currently does not have an option to disable the firewall for a single network, that's why I disabled it for L2 in this PR.

My understanding is that routing would not be able to blocked without using the firewall in the l2 case normally you would have your l2 ethX interface connected to the bridge and your LAN so there is no ip routing happing on the host system as all the packages will be send to the routed normally vie the LAN.

Without a firewall, the only way to block traffic going out the bridge to a different interface is by setting the forwarding sysctls to 0, yes. However, you could control traffic between ports on a bridge using an nftables bridge filter(or ebtables). This is like a managed L2-switch with a firewall. Currently, netavark does not support this and it's one of many reasons why I needed configurable veth names, so I can do this outside of podman. It might make sense to add this in future though, especially since this is kinda what the isolate option sounds like - just more fine-grained.

Also, there doesn't always have to be a physical port on an external bridge. For example, you could simply want to put both containers and libvirt VMs on the same bridge so they can communicate directly, but you don't want them to access the internet or the physical local network. Or your containers are running on the router itself and you have to forward packets to the wan interface instead of l2-switching them through the bridge to a different port.

Given my arguments above, implementing the external bridge feature as l2 vs l3 mode might be confusing. In the end it's an API choice and will not affect the functionality, but you might want to reconsider it.

(1) Not setting up a firewall might be wrong if netavark/podman would ever support firewalling between containers on the bridge.

@Luap99
Copy link
Member

Luap99 commented Nov 14, 2024

Yeah I am to sure about the naming either. My think was l3 is routed while 2 is local LAN but l2 is just whatever the user configured so you are right that that naming does not really reflect what is going on.

Would something mode=managed/unmanaged or type=managed/unmanaged better? Where managed is what we to today and unmanaged is what you do here. Then it is easy to documented that unmanaged means no firewall and sysctl settings AND ONLY veth pair + ip assignment to the container interface.

@M1cha
Copy link
Contributor Author

M1cha commented Nov 14, 2024

mode=managed/unmanaged sounds good to me, yes. We're already using mode for both macvlan and ipvlan (with different possible values) so reusing it makes sense to me. I also can't think of anything else we'd want to use mode for in future.

Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

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

code seems good, two minor nits on the test then LGTM

assert "$output" "==" 'Device "podman0" does not exist.'
}

@test bridge - l2 mode {
Copy link
Member

Choose a reason for hiding this comment

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

test need to be changed to unmanged


run_netavark --file ${TESTSDIR}/testfiles/bridge-unmanaged.json teardown $(get_container_netns_path)

# check if the interface gets removed
Copy link
Member

Choose a reason for hiding this comment

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

this is a bit confusion, it should say that we ensure the bridge is not removed.

While Linux doesn't support modes on bridges, we use this concept to let
the user tell us if they want podman/netavark to own the bridge or not.
Managed behaves the same way as before this commit. Unmanaged requires
the bridge to exist already, will not setup any sysctls or firewall
rules on the host and will not delete the bridge once all containers
left.

Fixes containers#1090

Signed-off-by: Michael Zimmermann <[email protected]>
That's simplfy not neccessary, because the whole point of internal
networks is to not have any forwardings or firewall rules.

Signed-off-by: Michael Zimmermann <[email protected]>
@M1cha
Copy link
Contributor Author

M1cha commented Nov 19, 2024

thanks, fixed

Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

openshift-ci bot commented Nov 22, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Luap99, M1cha

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Luap99
Copy link
Member

Luap99 commented Nov 22, 2024

@mheon PTAL

@M1cha M1cha changed the title network: bridge: add support for l2 mode network: bridge: add support for unmanaged mode Nov 22, 2024
@mheon
Copy link
Member

mheon commented Nov 22, 2024

/lgtm

@openshift-ci openshift-ci bot added the lgtm label Nov 22, 2024
@openshift-merge-bot openshift-merge-bot bot merged commit d5358e4 into containers:main Nov 22, 2024
28 checks passed
@M1cha M1cha deleted the l2-bridge branch November 22, 2024 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[RFE] Support bridges created outside of netavark
3 participants