169 lines
5.7 KiB
Markdown
169 lines
5.7 KiB
Markdown
# SmartProxy Byte Counting Audit Report
|
|
|
|
## Executive Summary
|
|
|
|
After a comprehensive audit of the SmartProxy codebase, I can confirm that **byte counting is implemented correctly** with no instances of double counting. Each byte transferred through the proxy is counted exactly once in each direction.
|
|
|
|
## Byte Counting Implementation
|
|
|
|
### 1. Core Tracking Mechanisms
|
|
|
|
SmartProxy uses two complementary tracking systems:
|
|
|
|
1. **Connection Records** (`IConnectionRecord`):
|
|
- `bytesReceived`: Total bytes received from client
|
|
- `bytesSent`: Total bytes sent to client
|
|
|
|
2. **MetricsCollector**:
|
|
- Global throughput tracking via `ThroughputTracker`
|
|
- Per-connection byte tracking for route/IP metrics
|
|
- Called via `recordBytes(connectionId, bytesIn, bytesOut)`
|
|
|
|
### 2. Where Bytes Are Counted
|
|
|
|
Bytes are counted in only two files:
|
|
|
|
#### a) `route-connection-handler.ts`
|
|
- **Line 351**: TLS alert bytes when no SNI is provided
|
|
- **Lines 1286-1301**: Data forwarding callbacks in `setupBidirectionalForwarding()`
|
|
|
|
#### b) `http-proxy-bridge.ts`
|
|
- **Line 127**: Initial TLS chunk for HttpProxy connections
|
|
- **Lines 142-154**: Data forwarding callbacks in `setupBidirectionalForwarding()`
|
|
|
|
## Connection Flow Analysis
|
|
|
|
### 1. Direct TCP Connection (No TLS)
|
|
|
|
```
|
|
Client → SmartProxy → Target Server
|
|
```
|
|
|
|
1. Connection arrives at `RouteConnectionHandler.handleConnection()`
|
|
2. For non-TLS ports, immediately routes via `routeConnection()`
|
|
3. `setupDirectConnection()` creates target connection
|
|
4. `setupBidirectionalForwarding()` handles all data transfer:
|
|
- `onClientData`: `bytesReceived += chunk.length` + `recordBytes(chunk.length, 0)`
|
|
- `onServerData`: `bytesSent += chunk.length` + `recordBytes(0, chunk.length)`
|
|
|
|
**Result**: ✅ Each byte counted exactly once
|
|
|
|
### 2. TLS Passthrough Connection
|
|
|
|
```
|
|
Client (TLS) → SmartProxy → Target Server (TLS)
|
|
```
|
|
|
|
1. Connection waits for initial data to detect TLS
|
|
2. TLS handshake detected, SNI extracted
|
|
3. Route matched, `setupDirectConnection()` called
|
|
4. Initial chunk stored in `pendingData` (NOT counted yet)
|
|
5. On target connect, `pendingData` written to target (still not counted)
|
|
6. `setupBidirectionalForwarding()` counts ALL bytes including initial chunk
|
|
|
|
**Result**: ✅ Each byte counted exactly once
|
|
|
|
### 3. TLS Termination via HttpProxy
|
|
|
|
```
|
|
Client (TLS) → SmartProxy → HttpProxy (localhost) → Target Server
|
|
```
|
|
|
|
1. TLS connection detected with `tls.mode = "terminate"`
|
|
2. `forwardToHttpProxy()` called:
|
|
- Initial chunk: `bytesReceived += chunk.length` + `recordBytes(chunk.length, 0)`
|
|
3. Proxy connection created to HttpProxy on localhost
|
|
4. `setupBidirectionalForwarding()` handles subsequent data
|
|
|
|
**Result**: ✅ Each byte counted exactly once
|
|
|
|
### 4. HTTP Connection via HttpProxy
|
|
|
|
```
|
|
Client (HTTP) → SmartProxy → HttpProxy (localhost) → Target Server
|
|
```
|
|
|
|
1. Connection on configured HTTP port (`useHttpProxy` ports)
|
|
2. Same flow as TLS termination
|
|
3. All byte counting identical to TLS termination
|
|
|
|
**Result**: ✅ Each byte counted exactly once
|
|
|
|
### 5. NFTables Forwarding
|
|
|
|
```
|
|
Client → [Kernel NFTables] → Target Server
|
|
```
|
|
|
|
1. Connection detected, route matched with `forwardingEngine: 'nftables'`
|
|
2. Connection marked as `usingNetworkProxy = true`
|
|
3. NO application-level forwarding (kernel handles packet routing)
|
|
4. NO byte counting in application layer
|
|
|
|
**Result**: ✅ No counting (correct - kernel handles everything)
|
|
|
|
## Special Cases
|
|
|
|
### PROXY Protocol
|
|
- PROXY protocol headers sent to backend servers are NOT counted in client metrics
|
|
- Only actual client data is counted
|
|
- **Correct behavior**: Protocol overhead is not client data
|
|
|
|
### TLS Alerts
|
|
- TLS alerts (e.g., for missing SNI) are counted as sent bytes
|
|
- **Correct behavior**: Alerts are actual data sent to the client
|
|
|
|
### Initial Chunks
|
|
- **Direct connections**: Stored in `pendingData`, counted when forwarded
|
|
- **HttpProxy connections**: Counted immediately upon receipt
|
|
- **Both approaches**: Count each byte exactly once
|
|
|
|
## Verification Methodology
|
|
|
|
1. **Code Analysis**: Searched for all instances of:
|
|
- `bytesReceived +=` and `bytesSent +=`
|
|
- `recordBytes()` calls
|
|
- Data forwarding implementations
|
|
|
|
2. **Flow Tracing**: Followed data path for each connection type from entry to exit
|
|
|
|
3. **Handler Review**: Examined all forwarding handlers to ensure no additional counting
|
|
|
|
## Findings
|
|
|
|
### ✅ No Double Counting Detected
|
|
|
|
- Each byte is counted exactly once in the direction it flows
|
|
- Connection records and metrics are updated consistently
|
|
- No overlapping or duplicate counting logic found
|
|
|
|
### Areas of Excellence
|
|
|
|
1. **Centralized Counting**: All byte counting happens in just two files
|
|
2. **Consistent Pattern**: Uses `setupBidirectionalForwarding()` with callbacks
|
|
3. **Clear Separation**: Forwarding handlers don't interfere with proxy metrics
|
|
|
|
## Recommendations
|
|
|
|
1. **Debug Logging**: Add optional debug logging to verify byte counts in production:
|
|
```typescript
|
|
if (settings.debugByteCount) {
|
|
logger.log('debug', `Bytes counted: ${connectionId} +${bytes} (total: ${record.bytesReceived})`);
|
|
}
|
|
```
|
|
|
|
2. **Unit Tests**: Create specific tests to ensure byte counting accuracy:
|
|
- Test initial chunk handling
|
|
- Test PROXY protocol overhead exclusion
|
|
- Test HttpProxy forwarding accuracy
|
|
|
|
3. **Protocol Overhead Tracking**: Consider separately tracking:
|
|
- PROXY protocol headers
|
|
- TLS handshake bytes
|
|
- HTTP headers vs body
|
|
|
|
4. **NFTables Documentation**: Clearly document that NFTables-forwarded connections are not included in application metrics
|
|
|
|
## Conclusion
|
|
|
|
SmartProxy's byte counting implementation is **robust and accurate**. The design ensures that each byte is counted exactly once, with clear separation between connection tracking and metrics collection. No remediation is required. |