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

refactor(connlib): remove concept of "allowed IPs" from Peer #5077

Closed
wants to merge 1 commit into from

Conversation

thomaseizinger
Copy link
Member

In firezone, a wireguard tunnel between two nodes is always ephemeral. Thus, any packet coming out of a tunnel always comes from the same IP.

There is no need to double check that the packets do indeed come from the TUN device of the other peer. If the remote has the correct WG key to send us packets, we should accept them. Checking the IP does not give us any additional security benefit.

Copy link

vercel bot commented May 22, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Ignored Deployment
Name Status Preview Comments Updated (UTC)
firezone ⬜️ Ignored (Inspect) Visit Preview May 22, 2024 1:34am

@thomaseizinger
Copy link
Member Author

I'd like to merge #4728 before we merge this.

@github-actions github-actions bot added the kind/refactor Code refactoring label May 22, 2024
Copy link

github-actions bot commented May 22, 2024

Terraform Cloud Plan Output

Plan: 15 to add, 15 to change, 15 to destroy.

Terraform Cloud Plan

In firezone, a wireguard tunnel between two nodes is always ephemeral.
Thus, any packet coming out of a tunnel always comes from the same IP.

There is no need to double check that the packets do indeed come from the TUN device of the other peer.
If the remote has the correct WG key to send us packets, we should accept them.
Checking the IP does not give us any additional security benefit.
@thomaseizinger thomaseizinger force-pushed the chore/connlib/remove-allowed-ips branch from 07e1803 to 182f0fe Compare May 22, 2024 01:34
Comment on lines -1025 to -1054
let Some(peer) = self.peers.get_mut(&gateway_id) else {
continue;
};

// First we remove the id from all allowed ips
for (network, resources) in peer
.allowed_ips
.iter_mut()
.filter(|(_, resources)| resources.contains(id))
{
resources.remove(id);

if !resources.is_empty() {
continue;
}

// If the allowed_ips doesn't correspond to any resource anymore we
// clean up any related translation.
peer.translations.remove_by_left(&network.network_address());
}

// We remove all empty allowed ips entry since there's no resource that corresponds to it
peer.allowed_ips.retain(|_, r| !r.is_empty());

// If there's no allowed ip left we remove the whole peer because there's no point on keeping it around
if peer.allowed_ips.is_empty() {
self.peers.remove(&gateway_id);
self.update_site_status_by_gateway(&gateway_id, Status::Unknown);
// TODO: should we have a Node::remove_connection?
}
Copy link
Member Author

Choose a reason for hiding this comment

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

I think this is fine. I think what ends up happening here is that we have a connection to a gateway that we no longer need. We can clean up unused connections at a later point and I think it is probably a feature we should add to snownet and clean them up based on how often we see traffic going through even if we still have a resource mapping.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we keep track of gateways to resources ids like suggested here we can cleanup peers when no resource is around that corresponds to a gateway.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good. I am planning to work on #5054 next.

Copy link

Performance Test Results

TCP

Test Name Received/s Sent/s Retransmits
direct-tcp-client2server 239.9 MiB (-0%) 241.4 MiB (-0%) 351 (+79%)
direct-tcp-server2client 241.9 MiB (-1%) 243.4 MiB (-1%) 313 (-4%)
relayed-tcp-client2server 230.1 MiB (+1%) 231.0 MiB (+1%) 264 (+36%)
relayed-tcp-server2client 239.2 MiB (-1%) 239.6 MiB (-1%) 397 (+12%)

UDP

Test Name Total/s Jitter Lost
direct-udp-client2server 500.0 MiB (+0%) 0.02ms (-36%) 39.10% (-7%)
direct-udp-server2client 500.0 MiB (+0%) 0.02ms (+19%) 20.61% (-2%)
relayed-udp-client2server 500.0 MiB (+0%) 0.01ms (+220%) 55.54% (+2%)
relayed-udp-server2client 500.0 MiB (+0%) 0.01ms (+16%) 40.34% (-2%)


// If the allowed_ips doesn't correspond to any resource anymore we
// clean up any related translation.
peer.translations.remove_by_left(&network.network_address());
Copy link
Collaborator

Choose a reason for hiding this comment

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

ah, I see this is why we keep the ResourceId in allowed_ips, I think it's a good to keep translation clean but we won't need this anymore after #5049

Copy link
Member Author

Choose a reason for hiding this comment

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

Fair. We can defer merging this until after #5049 is in.

Comment on lines -271 to -274
if self.allowed_ips.longest_match(packet.source()).is_none() {
return Err(connlib_shared::Error::UnallowedPacket(packet.source()));
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

@thomaseizinger I was just thinking about this, and removing this means that another user, could use your ip as a source to send packets to a resource.

This means they could send something like a RST packet to kill your conneciton.

For this reason it might be better to still have the allowed_ips

Copy link
Collaborator

Choose a reason for hiding this comment

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

We could however implement this as just 2 allowed_ipv4 and allowed_ipv6.

Which I might already use for #5049 to convert between ipv4 and ipv6 source ips

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think since this is on the gateway it wouldn't affect #5080 too much?

Copy link
Member Author

Choose a reason for hiding this comment

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

another user, could use your ip as a source to send packets to a resource.

Another user where? Is the packet coming out of the tunnel?

I'd agree that it is somewhat of a defense-in-depth thing. Technically, the source IP could be anything when coming out of the tunnel. But also, if somebody has access to the tunnel, they can also just mangle the IP to the one of the TUN device, no?

Copy link
Collaborator

@conectado conectado May 29, 2024

Choose a reason for hiding this comment

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

Well, I mean this:

Say you have User A with public Key A and IP A and user B with public key B and IP B.

Without allowed_ips, user A could maliciously:

  1. Mangle their encapsulated packet to have IP B
  2. It's sent through the tunnel
  3. Packet is successfully decapuselated using public key A
  4. This associates the decapsulated packet to the conn-id of the corresponding public key A
  5. We get the peer based on the conn-id
  6. Now, w/o the allowed_ips the packets just gets forwarded to the resource as long as they have access to it
  7. Resource believes the packet comes from IP B (sans nat-table)(*)

(*) IP A could make the source port collide with whatever user B is using and interrupt their connection or something else

If we have the allowed_ips step 6 would prevent always that user A uses anything but their assigned source ips since this is ensured by the portal

Copy link
Member Author

Choose a reason for hiding this comment

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

Makes sense! I'll add a test for this to ensure we don't break it! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/refactor Code refactoring
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants