|
| 1 | +# Tracker `on_reverse_proxy` is Global Instead of Per-HTTP-Tracker |
| 2 | + |
| 3 | +**Issue Date**: January 16, 2026 |
| 4 | +**Affected Component**: Torrust Tracker Configuration |
| 5 | +**Status**: Documented - Issue filed in tracker repository |
| 6 | +**Upstream Issue**: [torrust/torrust-tracker#1640](https://github.com/torrust/torrust-tracker/issues/1640) |
| 7 | + |
| 8 | +## Problem Description |
| 9 | + |
| 10 | +The Torrust Tracker has a configuration option `[core.net].on_reverse_proxy` that controls whether the tracker expects `X-Forwarded-For` HTTP headers to determine the real client IP address. This setting is **global** and applies to **all HTTP trackers** in the deployment. |
| 11 | + |
| 12 | +This creates a limitation: you cannot have some HTTP trackers behind a reverse proxy while others are accessed directly in the same deployment. |
| 13 | + |
| 14 | +## How We Discovered This |
| 15 | + |
| 16 | +While implementing HTTPS support with Caddy as a TLS-terminating reverse proxy in the [Torrust Tracker Deployer](https://github.com/torrust/torrust-tracker-deployer), we needed to configure the tracker to work behind Caddy. |
| 17 | + |
| 18 | +Our use case: |
| 19 | + |
| 20 | +- Multiple HTTP trackers on different ports (e.g., 7070, 7071, 7072) |
| 21 | +- Some trackers exposed via HTTPS through Caddy (TLS termination) |
| 22 | +- Some trackers exposed directly via HTTP (no proxy) |
| 23 | + |
| 24 | +**Example configuration intent**: |
| 25 | + |
| 26 | +```json |
| 27 | +{ |
| 28 | + "http_trackers": [ |
| 29 | + { |
| 30 | + "bind_address": "0.0.0.0:7070", |
| 31 | + "domain": "http1.tracker.local", |
| 32 | + "use_tls_proxy": true |
| 33 | + }, |
| 34 | + { |
| 35 | + "bind_address": "0.0.0.0:7071", |
| 36 | + "domain": "http2.tracker.local", |
| 37 | + "use_tls_proxy": true |
| 38 | + }, |
| 39 | + { |
| 40 | + "bind_address": "0.0.0.0:7072" |
| 41 | + } |
| 42 | + ] |
| 43 | +} |
| 44 | +``` |
| 45 | + |
| 46 | +In this scenario: |
| 47 | + |
| 48 | +- Trackers on ports 7070 and 7071 are behind Caddy (need `on_reverse_proxy = true`) |
| 49 | +- Tracker on port 7072 is direct (needs `on_reverse_proxy = false`) |
| 50 | + |
| 51 | +However, the current tracker configuration only allows: |
| 52 | + |
| 53 | +```toml |
| 54 | +[core.net] |
| 55 | +on_reverse_proxy = true # Applies to ALL HTTP trackers |
| 56 | +``` |
| 57 | + |
| 58 | +## Root Cause |
| 59 | + |
| 60 | +The `on_reverse_proxy` setting is defined in `[core.net]` which is a global network configuration section, not per-tracker. Looking at the tracker's network configuration structure: |
| 61 | + |
| 62 | +**Reference**: [Torrust Tracker Network Configuration](https://docs.rs/torrust-tracker-configuration/latest/torrust_tracker_configuration/v2_0_0/network/struct.Network.html) |
| 63 | + |
| 64 | +```rust |
| 65 | +pub struct Network { |
| 66 | + // ... |
| 67 | + pub on_reverse_proxy: bool, // Global setting |
| 68 | + // ... |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +Each HTTP tracker configuration does not have its own `on_reverse_proxy` field. |
| 73 | + |
| 74 | +## Impact |
| 75 | + |
| 76 | +### For Deployer Users |
| 77 | + |
| 78 | +When `on_reverse_proxy = true` is set globally: |
| 79 | + |
| 80 | +1. **All HTTP trackers expect `X-Forwarded-For` headers** |
| 81 | +2. Trackers accessed directly (without proxy) will **fail to identify client IPs correctly** |
| 82 | +3. The tracker will see the absence of `X-Forwarded-For` and may log warnings or behave unexpectedly |
| 83 | + |
| 84 | +When `on_reverse_proxy = false` is set globally: |
| 85 | + |
| 86 | +1. **All HTTP trackers ignore `X-Forwarded-For` headers** |
| 87 | +2. Trackers behind a reverse proxy will **see the proxy's IP as the client IP** |
| 88 | +3. All peers from different clients will appear to come from the same IP (the proxy) |
| 89 | +4. This breaks peer identification in swarms |
| 90 | + |
| 91 | +### Current Workaround in Deployer |
| 92 | + |
| 93 | +We enforce a rule in the deployer: |
| 94 | + |
| 95 | +> **If ANY HTTP tracker uses a TLS proxy, ALL HTTP trackers must use the TLS proxy.** |
| 96 | +
|
| 97 | +This is documented as a known limitation and validated during environment creation: |
| 98 | + |
| 99 | +```text |
| 100 | +Known Limitation (due to tracker's global setting): |
| 101 | +
|
| 102 | +If you have multiple HTTP trackers where some use use_tls_proxy and others don't, |
| 103 | +the ones without it will still receive the global on_reverse_proxy = true setting |
| 104 | +and may fail if they receive direct requests without X-Forwarded-For headers. |
| 105 | +
|
| 106 | +Workaround: Ensure all HTTP trackers in a deployment either ALL use the TLS proxy |
| 107 | +or NONE use it. |
| 108 | +``` |
| 109 | + |
| 110 | +This limitation reduces deployment flexibility and forces users into an all-or-nothing approach. |
| 111 | + |
| 112 | +## Recommended Solution |
| 113 | + |
| 114 | +Add an optional `on_reverse_proxy` field to each HTTP tracker configuration, allowing per-tracker control: |
| 115 | + |
| 116 | +### Proposed Configuration Structure |
| 117 | + |
| 118 | +```toml |
| 119 | +[core.net] |
| 120 | +on_reverse_proxy = false # Default for trackers without explicit setting |
| 121 | + |
| 122 | +[[http_trackers]] |
| 123 | +bind_address = "0.0.0.0:7070" |
| 124 | +on_reverse_proxy = true # Override: this tracker is behind a proxy |
| 125 | + |
| 126 | +[[http_trackers]] |
| 127 | +bind_address = "0.0.0.0:7071" |
| 128 | +on_reverse_proxy = true # Override: this tracker is behind a proxy |
| 129 | + |
| 130 | +[[http_trackers]] |
| 131 | +bind_address = "0.0.0.0:7072" |
| 132 | +# No override: uses global default (false) - direct access |
| 133 | +``` |
| 134 | + |
| 135 | +### Behavior |
| 136 | + |
| 137 | +1. If `on_reverse_proxy` is specified on an HTTP tracker, use that value |
| 138 | +2. If not specified, fall back to `[core.net].on_reverse_proxy` (backward compatible) |
| 139 | +3. Each HTTP tracker independently decides whether to read `X-Forwarded-For` |
| 140 | + |
| 141 | +### Implementation Considerations |
| 142 | + |
| 143 | +The HTTP tracker request handler would need to check its own `on_reverse_proxy` setting when extracting the client IP, rather than checking the global setting. |
| 144 | + |
| 145 | +**Pseudocode change**: |
| 146 | + |
| 147 | +```rust |
| 148 | +// Before (global check) |
| 149 | +fn get_client_ip(request: &Request, config: &Config) -> IpAddr { |
| 150 | + if config.core.net.on_reverse_proxy { |
| 151 | + extract_from_x_forwarded_for(request) |
| 152 | + } else { |
| 153 | + request.peer_addr() |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +// After (per-tracker check) |
| 158 | +fn get_client_ip(request: &Request, tracker_config: &HttpTrackerConfig) -> IpAddr { |
| 159 | + let on_reverse_proxy = tracker_config.on_reverse_proxy |
| 160 | + .unwrap_or(config.core.net.on_reverse_proxy); |
| 161 | + |
| 162 | + if on_reverse_proxy { |
| 163 | + extract_from_x_forwarded_for(request) |
| 164 | + } else { |
| 165 | + request.peer_addr() |
| 166 | + } |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +## Benefits of This Change |
| 171 | + |
| 172 | +1. **Flexible deployments**: Mix proxied and direct HTTP trackers in one deployment |
| 173 | +2. **Backward compatible**: Global setting remains the default |
| 174 | +3. **Clearer intent**: Each tracker explicitly declares its network topology |
| 175 | +4. **Better for edge cases**: Internal trackers (localhost) vs external (behind proxy) |
| 176 | + |
| 177 | +## Use Cases Enabled |
| 178 | + |
| 179 | +1. **Mixed TLS/non-TLS deployment**: Some trackers via HTTPS (Caddy), some via direct HTTP |
| 180 | +2. **Internal monitoring**: Direct localhost tracker for Prometheus, proxied trackers for public access |
| 181 | +3. **Gradual migration**: Move trackers behind proxy one at a time during migration |
| 182 | +4. **Multi-tenant**: Different trackers for different networks with different proxy configurations |
| 183 | + |
| 184 | +## References |
| 185 | + |
| 186 | +- [Torrust Tracker Network Configuration Docs](https://docs.rs/torrust-tracker-configuration/latest/torrust_tracker_configuration/v2_0_0/network/struct.Network.html) |
| 187 | +- [Torrust Tracker Repository](https://github.com/torrust/torrust-tracker) |
| 188 | +- [Deployer Issue #272 - Add HTTPS Support](https://github.com/torrust/torrust-tracker-deployer/issues/272) |
| 189 | +- [Deployer PR #273 - HTTPS Implementation](https://github.com/torrust/torrust-tracker-deployer/pull/273) |
0 commit comments