Skip to content

Commit a857e07

Browse files
committed
docs: [#272] Add subtask 7.5 for use_tls_proxy configuration refactor
- Add subtask 7.5 to fix on_reverse_proxy tracker config bug - Replace misleading 'tls' object with 'domain' + 'use_tls_proxy' fields - Document incremental implementation plan (one service at a time) - Add before/after examples using full manual-https-test.json - Document dependency rule: use_tls_proxy implies on_reverse_proxy - Document future compatibility with per-tracker on_reverse_proxy
1 parent 5334429 commit a857e07

1 file changed

Lines changed: 302 additions & 0 deletions

File tree

docs/issues/272-add-https-support-with-caddy.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,308 @@ Health Check:
11511151
- [x] Display "Internal only" message for internal-only services
11521152
- [x] Apply to: health check API, HTTP API, HTTP trackers (Grafana excluded - hardcoded port)
11531153

1154+
#### 7.5: Fix `on_reverse_proxy` Tracker Configuration Bug
1155+
1156+
**Problem**:
1157+
1158+
The Torrust Tracker has a configuration option `[core.net].on_reverse_proxy` that tells the tracker whether it's running behind a reverse proxy. When `true`, the tracker expects the `X-Forwarded-For` HTTP header to get the real client IP instead of the proxy's IP. This is critical for HTTP trackers to correctly identify peers.
1159+
1160+
Currently, in `templates/tracker/tracker.toml.tera`, this option is **hardcoded to `true`**:
1161+
1162+
```toml
1163+
[core.net]
1164+
on_reverse_proxy = true
1165+
```
1166+
1167+
This is wrong because:
1168+
1169+
1. When an HTTP tracker is exposed directly (no Caddy proxy), the tracker expects `X-Forwarded-For` headers that won't exist, causing incorrect peer identification
1170+
2. The current implementation assumes all HTTP trackers with TLS go through Caddy, but users might want to use the tracker's built-in TLS support without a proxy
1171+
1172+
**Tracker Configuration Limitation**:
1173+
1174+
The `on_reverse_proxy` option is **global** (in `[core.net]`), not per-tracker. This means:
1175+
1176+
- ALL HTTP trackers share the same setting
1177+
- You cannot have some trackers behind a proxy and others direct in the same deployment
1178+
- If ANY tracker uses a proxy, ALL trackers must be configured for proxy mode
1179+
1180+
This is a limitation in the Torrust Tracker itself (not the deployer). A proper fix would require the tracker to support per-tracker `on_reverse_proxy` settings.
1181+
1182+
**Solution**:
1183+
1184+
Rename `tls` to a clearer structure with `domain` at the top level and `use_tls_proxy` as a separate boolean. The `tls` name was misleading because it doesn't map to the tracker's TLS config - the domain is only used for Caddy proxy configuration.
1185+
1186+
**Before** (current - using `tls` object):
1187+
1188+
```json
1189+
{
1190+
"environment": {
1191+
"name": "manual-https-test"
1192+
},
1193+
"ssh_credentials": {
1194+
"private_key_path": "/path/to/fixtures/testing_rsa",
1195+
"public_key_path": "/path/to/fixtures/testing_rsa.pub"
1196+
},
1197+
"provider": {
1198+
"provider": "lxd",
1199+
"profile_name": "torrust-profile-manual-https-test"
1200+
},
1201+
"tracker": {
1202+
"core": {
1203+
"database": {
1204+
"driver": "sqlite3",
1205+
"database_name": "tracker.db"
1206+
},
1207+
"private": false
1208+
},
1209+
"udp_trackers": [
1210+
{
1211+
"bind_address": "0.0.0.0:6969"
1212+
}
1213+
],
1214+
"http_trackers": [
1215+
{
1216+
"bind_address": "0.0.0.0:7070",
1217+
"tls": {
1218+
"domain": "http1.tracker.local"
1219+
}
1220+
},
1221+
{
1222+
"bind_address": "0.0.0.0:7071",
1223+
"tls": {
1224+
"domain": "http2.tracker.local"
1225+
}
1226+
},
1227+
{
1228+
"bind_address": "0.0.0.0:7072"
1229+
}
1230+
],
1231+
"http_api": {
1232+
"bind_address": "0.0.0.0:1212",
1233+
"admin_token": "MyAccessToken",
1234+
"tls": {
1235+
"domain": "api.tracker.local"
1236+
}
1237+
},
1238+
"health_check_api": {
1239+
"bind_address": "0.0.0.0:1313",
1240+
"tls": {
1241+
"domain": "health.tracker.local"
1242+
}
1243+
}
1244+
},
1245+
"grafana": {
1246+
"admin_user": "admin",
1247+
"admin_password": "admin-password",
1248+
"tls": {
1249+
"domain": "grafana.tracker.local"
1250+
}
1251+
},
1252+
"prometheus": {
1253+
"scrape_interval_in_secs": 15
1254+
},
1255+
"https": {
1256+
"admin_email": "admin@tracker.local",
1257+
"use_staging": true
1258+
}
1259+
}
1260+
```
1261+
1262+
**After** (proposed - using `domain` + `use_tls_proxy`):
1263+
1264+
```json
1265+
{
1266+
"environment": {
1267+
"name": "manual-https-test"
1268+
},
1269+
"ssh_credentials": {
1270+
"private_key_path": "/path/to/fixtures/testing_rsa",
1271+
"public_key_path": "/path/to/fixtures/testing_rsa.pub"
1272+
},
1273+
"provider": {
1274+
"provider": "lxd",
1275+
"profile_name": "torrust-profile-manual-https-test"
1276+
},
1277+
"tracker": {
1278+
"core": {
1279+
"database": {
1280+
"driver": "sqlite3",
1281+
"database_name": "tracker.db"
1282+
},
1283+
"private": false
1284+
},
1285+
"udp_trackers": [
1286+
{
1287+
"bind_address": "0.0.0.0:6969"
1288+
}
1289+
],
1290+
"http_trackers": [
1291+
{
1292+
"bind_address": "0.0.0.0:7070",
1293+
"domain": "http1.tracker.local",
1294+
"use_tls_proxy": true
1295+
},
1296+
{
1297+
"bind_address": "0.0.0.0:7071",
1298+
"domain": "http2.tracker.local",
1299+
"use_tls_proxy": true
1300+
},
1301+
{
1302+
"bind_address": "0.0.0.0:7072"
1303+
}
1304+
],
1305+
"http_api": {
1306+
"bind_address": "0.0.0.0:1212",
1307+
"admin_token": "MyAccessToken",
1308+
"domain": "api.tracker.local",
1309+
"use_tls_proxy": true
1310+
},
1311+
"health_check_api": {
1312+
"bind_address": "0.0.0.0:1313",
1313+
"domain": "health.tracker.local",
1314+
"use_tls_proxy": true
1315+
}
1316+
},
1317+
"grafana": {
1318+
"admin_user": "admin",
1319+
"admin_password": "admin-password",
1320+
"domain": "grafana.tracker.local",
1321+
"use_tls_proxy": true
1322+
},
1323+
"prometheus": {
1324+
"scrape_interval_in_secs": 15
1325+
},
1326+
"https": {
1327+
"admin_email": "admin@tracker.local",
1328+
"use_staging": true
1329+
}
1330+
}
1331+
```
1332+
1333+
**Configuration Semantics**:
1334+
1335+
| `domain` | `use_tls_proxy` | Meaning |
1336+
| -------- | --------------- | --------------------------------------------------------- |
1337+
| absent | absent | Direct HTTP, no proxy |
1338+
| present | absent | HTTP with domain (for future use, e.g., DNS-based access) |
1339+
| present | `true` | HTTPS via Caddy proxy (TLS termination) |
1340+
| absent | `true` | **INVALID** - TLS proxy needs domain for virtual host |
1341+
1342+
**Why `use_tls_proxy` (not `on_reverse_proxy`)?**:
1343+
1344+
The name `use_tls_proxy` accurately describes what our Caddy proxy does: **TLS termination**. This naming choice is intentional for future compatibility:
1345+
1346+
1. **Current state**: The tracker has a global `[core.net].on_reverse_proxy` option
1347+
2. **Future state**: The tracker may add per-tracker `on_reverse_proxy` support
1348+
3. **No conflict**: When that happens, we can expose both options without ambiguity:
1349+
1350+
```json
1351+
{
1352+
"bind_address": "0.0.0.0:7071",
1353+
"domain": "http2.tracker.local",
1354+
"use_tls_proxy": true,
1355+
"on_reverse_proxy": true
1356+
}
1357+
```
1358+
1359+
**Dependency Rule**: `use_tls_proxy: true` → tracker's `on_reverse_proxy` MUST be `true`. This is enforced automatically:
1360+
1361+
- When `use_tls_proxy: true`, the deployer sets the tracker's `[core.net].on_reverse_proxy = true`
1362+
- This is because Caddy sends `X-Forwarded-For` headers that the tracker must read
1363+
1364+
**Future Compatibility**: If the tracker adds per-tracker `on_reverse_proxy`:
1365+
1366+
- `use_tls_proxy` controls Caddy inclusion and implies `on_reverse_proxy: true`
1367+
- `on_reverse_proxy` could be explicitly set for edge cases (non-TLS reverse proxy)
1368+
- Validation: `use_tls_proxy: true` + `on_reverse_proxy: false` = **INVALID**
1369+
1370+
**Behavior**:
1371+
1372+
1. **Tracker config** (`[core.net].on_reverse_proxy`):
1373+
1374+
- Set to `true` if ANY HTTP tracker has `use_tls_proxy: true`
1375+
- Set to `false` otherwise
1376+
- Note: This only affects HTTP trackers; other services ignore it
1377+
1378+
2. **Caddy config** (Caddyfile):
1379+
1380+
- Include service in Caddy config only if `use_tls_proxy: true`
1381+
- Requires `domain` to be present for the virtual host configuration
1382+
1383+
3. **Validation rules**:
1384+
- `use_tls_proxy: true` requires `domain` to be present
1385+
- Localhost bind addresses with `use_tls_proxy: true` should be rejected (proxy can't reach localhost)
1386+
1387+
**Known Limitation** (due to tracker's global setting):
1388+
1389+
If you have multiple HTTP trackers where some use `use_tls_proxy` and others don't, the ones without it will still receive the global `on_reverse_proxy = true` setting and may fail if they receive direct requests without `X-Forwarded-For` headers.
1390+
1391+
**Workaround**: Ensure all HTTP trackers in a deployment either ALL use the TLS proxy or NONE use it.
1392+
1393+
**Reference**: [Torrust Tracker Network Configuration](https://docs.rs/torrust-tracker-configuration/latest/torrust_tracker_configuration/v2_0_0/network/struct.Network.html)
1394+
1395+
**Implementation Scope**:
1396+
1397+
The implementation is split into incremental steps, one service type at a time, to minimize risk and simplify review.
1398+
1399+
##### Step 7.5.1: HTTP Trackers
1400+
1401+
- [ ] Add `domain: Option<String>` and `use_tls_proxy: Option<bool>` to `HttpTrackerSection` DTO
1402+
- [ ] Update `HttpTrackerConfig` domain type to include `use_tls_proxy` and `domain`
1403+
- [ ] Add validation: `use_tls_proxy: true` requires `domain` to be present
1404+
- [ ] Add validation: `use_tls_proxy: true` with localhost bind address → reject
1405+
- [ ] Update tracker config template (`templates/tracker/tracker.toml.tera`) to conditionally set `on_reverse_proxy` based on ANY HTTP tracker having `use_tls_proxy: true`
1406+
- [ ] Update Caddy template (`templates/caddy/Caddyfile.tera`) to check `use_tls_proxy` for HTTP trackers
1407+
- [ ] Update show command `ServiceInfo` for HTTP trackers
1408+
- [ ] Update `envs/manual-https-test.json` for HTTP trackers only
1409+
- [ ] Remove `TlsSection` from HTTP trackers (keep in other services temporarily)
1410+
- [ ] Add unit tests for HTTP tracker validation
1411+
- [ ] Run E2E tests to verify HTTP trackers work
1412+
1413+
##### Step 7.5.2: Tracker REST API
1414+
1415+
- [ ] Add `domain: Option<String>` and `use_tls_proxy: Option<bool>` to `HttpApiSection` DTO
1416+
- [ ] Update `HttpApiConfig` domain type
1417+
- [ ] Add validation rules (same as HTTP trackers)
1418+
- [ ] Update Caddy template for API
1419+
- [ ] Update show command `ServiceInfo` for API
1420+
- [ ] Update `envs/manual-https-test.json` for API
1421+
- [ ] Remove `TlsSection` from API
1422+
- [ ] Add unit tests for API validation
1423+
- [ ] Run E2E tests
1424+
1425+
##### Step 7.5.3: Tracker Health Check API
1426+
1427+
- [ ] Add `domain: Option<String>` and `use_tls_proxy: Option<bool>` to `HealthCheckApiSection` DTO
1428+
- [ ] Update `HealthCheckApiConfig` domain type
1429+
- [ ] Add validation rules
1430+
- [ ] Update Caddy template for health check
1431+
- [ ] Update show command `ServiceInfo` for health check
1432+
- [ ] Update `envs/manual-https-test.json` for health check
1433+
- [ ] Remove `TlsSection` from health check
1434+
- [ ] Add unit tests
1435+
- [ ] Run E2E tests
1436+
1437+
##### Step 7.5.4: Grafana
1438+
1439+
- [ ] Add `domain: Option<String>` and `use_tls_proxy: Option<bool>` to `GrafanaSection` DTO
1440+
- [ ] Update `GrafanaConfig` domain type
1441+
- [ ] Add validation rules (note: Grafana has no configurable bind address, so localhost validation not needed)
1442+
- [ ] Update Caddy template for Grafana
1443+
- [ ] Update show command `ServiceInfo` for Grafana
1444+
- [ ] Update `envs/manual-https-test.json` for Grafana
1445+
- [ ] Remove `TlsSection` from Grafana
1446+
- [ ] Add unit tests
1447+
- [ ] Run E2E tests
1448+
1449+
##### Step 7.5.5: Cleanup and Final Verification
1450+
1451+
- [ ] Remove `TlsSection` type completely (should be unused after all services migrated)
1452+
- [ ] Run full E2E test suite
1453+
- [ ] Run all linters
1454+
- [ ] Manual verification with `envs/manual-https-test.json`
1455+
11541456
### Phase 8: Schema Generation (30 minutes)
11551457

11561458
- [ ] Regenerate JSON schema from Rust DTOs:

0 commit comments

Comments
 (0)