Featured image of post Load Balancing SSH or Other TCP Connections? Preserve Client IP Addresses with One of These Tools

Load Balancing SSH or Other TCP Connections? Preserve Client IP Addresses with One of These Tools

Learn how to preserve client IP addresses for SSH and other TCP connections with tools like go-mmproxy, mmproxy, and TPROXY without losing them at the load balancer

Introduction

In modern cloud environments, applications often sit behind load balancers or proxies to manage incoming traffic efficiently. While this setup helps distribute connections and scale services, it introduces a challenge: the original client’s IP address gets replaced by the proxy’s IP address. For many applications—particularly those using SSH—this can be problematic, as it obscures the real source of a connection.

Unlike HTTP traffic, which already has mechanisms like the X-Forwarded-For header to pass the client’s IP address to the backend, SSH and other TCP-based services don’t have built-in support for this. This makes it difficult for backend services to log or restrict access based on the original client IP address, which is important for security and monitoring.

To solve this, tools like go-mmproxy, mmproxy, and TPROXY exist to preserve the client’s IP address while forwarding traffic through a proxy. Each tool has unique strengths and is suitable for different environments. In this article, we’ll break down the differences between these solutions and help you choose the right one for handling SSH connections behind a proxy.

Why Preserving Client IP Addresses Matters

When using SSH or similar services to manage servers, it’s common to apply security rules based on the client’s IP address. For example, you might want to allow access only from a specific range of trusted IP addresses or monitor connections based on where they originate. However, when an SSH server sits behind a load balancer or proxy, the server will see the proxy’s IP address instead of the client’s IP address.

This loss of visibility into the real source IP address can create security blind spots. Fortunately, the PROXY protocol solves this issue by passing connection details, like the original client’s IP address, to the backend service. However, many services, including SSHD (the SSH daemon), don’t natively support the PROXY protocol. That’s where go-mmproxy, mmproxy, and TPROXY come into play.

Before diving into the comparison, it’s important to understand how these tools function at a high level. You can use one of them as another proxy that sits betweeen your load balancer and your service. They capture the connection, spoof the original client’s IP address, and then forward the remaining payload to the destination service. This ensures that your backend service can still see and act upon the original client’s IP address while handling the connection.

The request flow looks like this:

Client request > Load balancer > go-mmproxy/mmproxy/TPROXY > Your service (e.g. SSH)

Comparing the Tools: go-mmproxy vs mmproxy vs TPROXY

So how do you decide which one to use? Each tool has its own strengths, weaknesses, and ideal use cases. Choosing the right one depends on factors like performance, ease of setup, resource efficiency, and the complexity of your environment. Below is a comparison table that breaks down the key features and considerations for each tool, helping you make an informed decision based on your specific needs for handling these types of connections.

Feature/Aspect go-mmproxy mmproxy TPROXY
Language Go C Kernel-level feature (uses iptables)
Performance Slightly higher overhead due to Go’s runtime Lower overhead (written in C) High performance, minimal overhead (kernel)
Ease of Use Easy to set up, Go binaries are simple to compile or download Requires compilation or binaries More complex to configure (requires iptables and kernel support)
Configuration Simple CLI options with flags like --allowed-subnets Simple CLI, but requires C and networking knowledge Complex iptables rules and kernel setup
Routing Requires Linux routing (ip rules and iptables) Also requires routing setup Handles routing natively within the kernel
IPv4/IPv6 Support Full support for IPv4 and IPv6 traffic Full support for IPv4 and IPv6 Full support for IPv4 and IPv6
Scalability Scales well with Go’s concurrency, but with a slightly higher memory footprint Lightweight and highly scalable Extremely scalable, as it’s part of the kernel
Resource Efficiency Higher CPU and memory usage due to Go runtime Extremely lightweight (C-based) Extremely efficient (minimal CPU and memory usage)
Concurrency Built-in concurrency via Go goroutines Relies on system-level processes/threads Kernel-level concurrency, highly optimized
Installation Easy with go get or precompiled binaries Requires manual compilation or prebuilt binaries Built into the Linux kernel (but requires iptables setup)
Use Case Fit Ideal for those looking for ease of use with Go Best for lightweight, low-resource environments Ideal for complex, large-scale deployments with advanced networking needs

Detailed Breakdown

Language and Resource Efficiency

  • go-mmproxy: Written in Go, it benefits from Go’s built-in concurrency model (goroutines), which makes handling many simultaneous SSH connections efficient. However, Go’s garbage collector introduces slightly more memory overhead compared to mmproxy or TPROXY.
  • mmproxy: Written in C, mmproxy is extremely lightweight and resource-efficient. Its minimal CPU and memory usage make it ideal for servers where resource efficiency is critical.
  • TPROXY: As a kernel-level feature, TPROXY is the most resource-efficient of the three. Because it operates entirely within the Linux kernel, it avoids the overhead of user-space processes, making it perfect for environments where performance and scalability are essential.

Performance

  • go-mmproxy: Go’s runtime adds some overhead due to garbage collection and memory management. While this doesn’t impact moderate-traffic environments significantly, it may become a factor in large-scale, high-performance setups.
  • mmproxy: mmproxy’s C-based implementation ensures minimal overhead, making it more suitable for environments with high traffic and performance requirements.
  • TPROXY: As a kernel-native solution, TPROXY is the most performant, with almost no overhead. It can handle large volumes of traffic with minimal CPU and memory usage, making it ideal for high-performance and low-latency SSH traffic scenarios.

Ease of Use and Setup

  • go-mmproxy: Installation is straightforward, especially if you are familiar with Go. You can either compile it using go get or download a precompiled binary. The configuration is user-friendly, with intuitive CLI options like --allowed-subnets to filter incoming connections by IP address.
  • mmproxy: Setting up mmproxy can be slightly more involved, particularly if you need to compile it from source. Precompiled binaries are available, but you need to be comfortable working with C-based tools and networking configurations.
  • TPROXY: Setting up TPROXY is more complex, as it requires configuring iptables rules and custom routing tables in the Linux kernel. While this offers maximum flexibility, it demands more networking expertise and time.

Configuration and Features

  • go-mmproxy: Offers a wide range of configuration options, including the ability to restrict access to specific IP address ranges using the --allowed-subnets flag. Its features are well-suited for users who need flexibility in filtering and managing connections.
  • mmproxy: mmproxy provides basic functionality to preserve client IP addresses and forward traffic, without the additional filtering features found in go-mmproxy. It’s designed to be simple, lightweight, and efficient.
  • TPROXY: TPROXY allows for advanced configuration at the kernel level. You can create complex routing and traffic management rules using iptables, making it highly flexible but also more difficult to set up and maintain.

Routing Setup

  • go-mmproxy and mmproxy: Both require additional routing setup using iptables and custom routing tables to forward traffic while preserving the original client IP address. This setup involves creating rules to mark packets and routing them correctly to the backend SSH service.
  • TPROXY: TPROXY natively handles routing within the kernel, eliminating the need for external routing tools. However, the initial setup with iptables can be complex and requires an in-depth understanding of networking.

Scalability and Concurrency

  • go-mmproxy: Thanks to Go’s concurrency model, go-mmproxy scales well in environments that need to handle many SSH connections at once. However, its Go runtime introduces some resource overhead, which can limit scalability in extremely resource-constrained environments.
  • mmproxy: mmproxy scales well due to its lightweight, low-overhead design. It relies on system-level concurrency, which can be fine-tuned for the specific environment.
  • TPROXY: As part of the Linux kernel, TPROXY scales effortlessly, handling large volumes of traffic with minimal overhead. It is the best choice for large-scale, performance-critical deployments where resource efficiency is paramount.

Recommendations

Use go-mmproxy if…

  • You want a simple, easy-to-use solution for preserving client IP addresses in SSH traffic.
  • You are familiar with Go or prefer using precompiled binaries.
  • You need features like IP address filtering (--allowed-subnets) for extra security.
  • Your environment handles moderate traffic and can tolerate the Go runtime’s resource usage.

Use mmproxy if…

  • You need a highly efficient and lightweight solution for handling SSH traffic.
  • Resource constraints are a priority, and you want to minimize CPU and memory usage.
  • You’re comfortable with C-based tools and networking configurations.
  • You prefer a minimalistic approach and don’t require advanced filtering features.

Use TPROXY if…

  • You need the highest possible performance and scalability with minimal resource overhead.
  • You’re working in a large-scale environment with complex networking requirements.
  • You have the expertise to configure iptables and manage routing tables in the Linux kernel.
  • You want full control over how traffic is handled at the network level, with the ability to create custom rules and manage routing efficiently.

Conclusion

When it comes to preserving client IP addresses for TCP connections behind proxies, the choice between go-mmproxy, mmproxy, and TPROXY depends on your specific needs and environment. go-mmproxy offers ease of use and flexibility, while mmproxy is a highly efficient, resource-light option for more constrained setups. TPROXY provides unmatched performance and scalability but requires advanced configuration.

By understanding the strengths of each tool, you can choose the right solution for your traffic needs and ensure your systems maintain the security and visibility needed to manage connections effectively.

Built with Hugo
Theme Stack designed by Jimmy