Skip to content

Conversation

@david-garcia-garcia
Copy link
Contributor

@david-garcia-garcia david-garcia-garcia commented Nov 12, 2025

What does this PR do?

Fixes #12263

Enables plugins to support TCP middlewares by implementing an optional NewTCP() function. Plugins can now work with both HTTP and TCP protocols using the same configuration and discovery system.

The Serve() implementation for TCP plugins is also changed (not breaking because this is not public) to pass a Context, this change needs to be done before TCP middlewares go public because it would be impossible to introduce later without a breaking change. The middleware support implementatoin would have been so much simpler without this. Adding a context makes sense because it:

  • Allows proper handling the lifetime of whatever happens in the middleware
  • Being able to add metadata to TCP connections so that they are available in other middlewares or to traefik (i.e. future logging). Why? HTTP headers are used extensively by middlewares to store information that goes to logs, or is consumed by other middlewares. As this is not available in TCP, we need an alternate way of doing this, hence passing a Context that contains an attribute designed to hold arbitrary metadata.

Motivation

Traefik currently has only 2 built-in TCP middlewares (InFlightConn and IPAllowList), while the plugin system only supported HTTP. This prevented the community from building custom TCP solutions for rate limiting, geo-blocking, connection filtering, and security enhancements.

The solution: Add a single function to your plugin, and it automatically becomes TCP-compatible, sort of.... if you are not using HTTP request specific stuff adding TPC support to existing plugins should be very easy.

Quick Example

Add one function to your plugin:

import (
	"context"
	"net"
)

// NewTCP - uses callback functions (stdlib types) instead of tcp.Handler
func NewTCP(
	ctx context.Context,
	next func(ctx context.Context, conn net.Conn, closeWrite func() error),
	config *Config,
	name string,
) (func(ctx context.Context, conn net.Conn, closeWrite func() error), error) {
	handler := &tcpMiddleware{
		nextFunc: next,
		config:   config,
	}
	
	// Return function closure
	return func(ctx context.Context, conn net.Conn, closeWrite func() error) {
		handler.ServeTCP(ctx, conn, closeWrite)
	}, nil
}

type tcpMiddleware struct {
	nextFunc func(ctx context.Context, conn net.Conn, closeWrite func() error)
	config   *Config
}

// ServeTCP - implements TCP handler logic
func (t *tcpMiddleware) ServeTCP(ctx context.Context, conn net.Conn, closeWrite func() error) {
	// Your logic here (check IP, set metadata, etc.)
	
	// Call next handler
	t.nextFunc(ctx, conn, closeWrite)
}

Use it in TCP routers:

tcp:
  middlewares:
    my-plugin:
      plugin:
        myplugin:
          maxConnections: 20

Same config format and same discovery system.

Note

NewTCP() and ServeTCP() needed to use stdlib types that's why the signatures are messy and differ from the plugins that come bundled with traefik. I could not come up with a solution that would allow us to use the Traefik custom types that did not involve having Yaegi interpret all dependencies with the plugin again, including Traefik itself and transient dependencies. It might be possible to create a new stand alone library to centralize tcpHandler and closeWriter abstractions, and use both them both from the middleware and from traefik.

More

  • Added/updated tests (50+ TCP router tests passing)
  • Added/updated documentation (example plugin included)

@nmengin
Copy link
Contributor

nmengin commented Nov 17, 2025

Hello @david-garcia-garcia,

Thank you for your contribution.

We've set the status to "design-review" to allow us to check the PR and ensure there is no deep impact on Traefik before moving forward.
We'll keep you updated once the analysis is done.

@lvangool
Copy link

👀 This would be excellent!

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.

Support TCP middleware plugins

4 participants