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

Add callback API to track allocation lifeycle #419

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rg0now
Copy link
Contributor

@rg0now rg0now commented Nov 7, 2024

Fixes #324, #325, and #402

This PR adds a set of callbacks that can be used to track the lifecycle of TURN allocations in the server. This allows user code to be called whenever an authentication request has been processed (this can be used to mitigate DoS attacks against a server, see #402), when an allocation has been created/removed (e.g., to implement user tracking for adding quota support, see #324), when a permission has been created/removed (e.g., to log the peers a user tries to reach via the server), a channel has been created/removed (this will simplify integrating external TURN accelerators, see #360), or when an allocation ends with an error.

The implementation adds a new EventHandlers struct to the ServerConfig, defined as follows:

type EventHandlers struct {
  OnAuth func(srcAddr, dstAddr net.Addr, protocol, username, realm string, method string, verdict bool)
  OnAllocationCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, requestedPort int)
  OnAllocationDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string)
  OnAllocationError func(srcAddr, dstAddr net.Addr, protocol, message string)
  OnPermissionCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, peer net.IP)
  OnPermissionDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string, peer net.IP)
  OnChannelCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, peer net.Addr, channelNumber uint16)
  OnChannelDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string, peer net.Addr, channelNumber uint16)
}

It is OK to handle only a subset of the events.

Caveats:

  • Event handlers are called synchronously, so long-running callbacks will block the server.
  • Theoretically, allocations are allowed to change credentials during their lifetime, in that any Refresh, CreatePermission or ChannelBind message may use a different credential. Since this does not seem to be easy to implement with the PeerConnection API, credential swaps are currently no tracked.

@rg0now rg0now requested review from stv0g and Sean-Der November 7, 2024 16:42
@rg0now
Copy link
Contributor Author

rg0now commented Nov 7, 2024

CC @renandincer

Copy link

codecov bot commented Nov 7, 2024

Codecov Report

Attention: Patch coverage is 91.23711% with 17 lines in your changes missing coverage. Please review.

Project coverage is 68.73%. Comparing base (3ff9392) to head (bd0b543).

Files with missing lines Patch % Lines
internal/server/util.go 72.00% 5 Missing and 2 partials ⚠️
internal/allocation/five_tuple.go 60.00% 4 Missing ⚠️
server.go 40.00% 2 Missing and 1 partial ⚠️
internal/server/nonce.go 50.00% 0 Missing and 1 partial ⚠️
relay_address_generator_range.go 0.00% 1 Missing ⚠️
server_config.go 97.43% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #419      +/-   ##
==========================================
+ Coverage   66.63%   68.73%   +2.10%     
==========================================
  Files          43       43              
  Lines        2919     3103     +184     
==========================================
+ Hits         1945     2133     +188     
+ Misses        807      803       -4     
  Partials      167      167              
Flag Coverage Δ
go 68.73% <91.23%> (+2.10%) ⬆️
wasm 25.55% <3.09%> (-1.58%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rg0now rg0now force-pushed the lifeycle-callback-api branch from a968c74 to dae4d0c Compare November 7, 2024 20:00
@renandincer
Copy link

I just looked at it briefly @rg0now! Thank you for PRing this in.

I think it might be useful to get a handle to the PacketConn from OnAllocatedCreated as well. This might be useful for rate limiting. What do you think?

@rg0now
Copy link
Contributor Author

rg0now commented Nov 12, 2024

I think a better way to handle rate limiting would be to provide a RelayAddressGenerator.AllocatePacketConn to the server that will emit rate-limited packetconns. This works even today. The only issue with that approach is if we need user context for applying rate limiting (i.e., when different users may get different rate limits), since currently we miss the user context in the RelayAddressGenerator interface handlers. But then again, it would somehow be more adequate to add the user context there. Wdyt?

@renandincer
Copy link

Agreed. Only issue is that would be a breaking API change.

@rg0now
Copy link
Contributor Author

rg0now commented Nov 27, 2024

While working with the lifecycle event handler API it came to our attention that we never report the allocation's relay address in the callbacks. This makes it difficult to track server port allocation status, open firewalls, etc.

The last patch updates the relevant callbacks to also pass the relay address to the caller. The changed event handler signatures are as follows:

type EventHandlers struct {
        ...
	OnAllocationCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, relayAddr net.Addr, requestedPort int)
        ...
	OnChannelCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, relayAddr, peer net.Addr, channelNumber uint16)
	OnChannelDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string, relayAddr, peer net.Addr, channelNumber uint16)
}

@rg0now rg0now force-pushed the lifeycle-callback-api branch 2 times, most recently from 6eefa1c to 37ba0b5 Compare December 5, 2024 11:38
@rg0now rg0now force-pushed the lifeycle-callback-api branch from 37ba0b5 to bd0b543 Compare December 5, 2024 14:16
@rg0now
Copy link
Contributor Author

rg0now commented Dec 5, 2024

Further evolving the PR based on operational experience: It seems the PermissionCreated/PermissionDeleted callbacks could also use the relay-address so we added these to the API. The changes:

type EventHandlers struct {
        ...
	OnPermissionCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, relayAddr net.Addr, peer net.IP)
	OnPermissionDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string, relayAddr net.Addr, peer net.IP)
        ...
}

@rg0now
Copy link
Contributor Author

rg0now commented Dec 16, 2024

We have been deploying this PR into production for a while now, so far all goes as expected. If there are no objections, I'd like to merge this ASAP. Please speak up now if you have concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Add AllocationHandler
2 participants