tobaru 0.9.6

Port forwarding CLI utility written in Rust with allowlists, TLS SNI and ALPN routing, HTTP forwarding, load balancing, IP-to-destination mapping, and iptables support.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
# tobaru

Advanced port forwarding tool written in Rust with powerful routing and TLS features:

## Key Features

- **Multiple routing strategies**: Route connections based on IP address, TLS SNI (with wildcard support), ALPN protocol, or HTTP path
- **Flexible TLS handling**:
  - **Passthrough mode**: Route TLS by SNI/ALPN without decryption (zero overhead, no private keys needed)
  - **Terminate mode**: Decrypt TLS and route based on SNI/ALPN or HTTP content
  - Mix both modes on the same port
  - Client certificate pinning (SHA256 fingerprint validation)
  - Server certificate pinning (SHA256 fingerprint validation)
- **HTTP proxy features**:
  - Path-based routing with prefix matching
  - Serve static files from directories
  - Serve custom responses with configurable status codes
  - Header manipulation (add/remove/modify headers)
  - WebSocket support with automatic upgrade handling
  - Connection keep-alive support
- **Hot reloading**: Config changes are automatically detected and applied
- **iptables integration**: Automatically configure firewall rules for IP allowlists
- **IP groups**: Reusable named groups of IP ranges
- **High performance**: Async I/O with Tokio, minimal allocations

## Quick Example

```yaml
# Simple TLS passthrough routing by SNI
- address: 0.0.0.0:443
  transport: tcp
  targets:
    # Route api.example.com without decryption
    - location: api-backend:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: api.example.com

    # Route www.example.com to different backend
    - location: web-backend:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: www.example.com
```

## Installation

### Pre-compiled Binaries

Download from [GitHub Releases](https://github.com/cfal/tobaru/releases) for:
- Linux x86_64
- macOS Apple Silicon (aarch64)

### Build from Source

Requires Rust 1.88+ and cargo:

```bash
cargo install tobaru
```

## Usage

```
USAGE:
    tobaru [OPTIONS] <CONFIG PATH or CONFIG URL> [CONFIG PATH or CONFIG URL] [..]

OPTIONS:
    -t, --threads NUM           Number of worker threads (default: auto-detected)
    --clear-iptables-all        Clear all tobaru iptables rules and exit
    --clear-iptables-matching   Clear iptables rules for specified configs and exit
    -h, --help                  Show help

EXAMPLES:
    # Run with config file
    tobaru config.yaml

    # Run with multiple configs
    tobaru servers.yaml ip_groups.yaml

    # Simple TCP forwarding via URL
    tobaru tcp://127.0.0.1:8080?target=192.168.1.10:80

    # Clear iptables rules
    sudo tobaru --clear-iptables-matching config.yaml
```

## Configuration

See [CONFIG.md](./CONFIG.md) for the complete YAML configuration reference.

### TLS Passthrough Mode

Route TLS connections by SNI/ALPN without decryption - no private keys needed on the proxy:

```yaml
- address: 0.0.0.0:443
  transport: tcp
  targets:
    # Route api.example.com to backend1 (passthrough - no cert/key needed!)
    - location: backend1:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: api.example.com
        alpn_protocols:
          - h2
          - http/1.1

    # Route www.example.com to backend2
    - location: backend2:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: www.example.com
```

**Benefits:**
- No decryption/re-encryption overhead
- No private keys needed on proxy (improved security)
- Near-zero latency routing
- Full end-to-end encryption preserved

### TLS Terminate Mode

Decrypt TLS and route based on content:

```yaml
- address: 0.0.0.0:443
  transport: tcp
  targets:
    - location: backend:8080
      allowlist: 0.0.0.0/0
      server_tls:
        mode: terminate  # or omit mode (terminate is default)
        cert: app.crt
        key: app.key
        sni_hostnames: app.example.com
        alpn_protocols:
          - h2
          - http/1.1
```

### HTTP Proxy with Path Routing

```yaml
- address: 0.0.0.0:80
  transport: tcp
  target:
    allowlist: 0.0.0.0/0
    http_paths:
      # Serve static files
      /static/:
        http_action:
          type: serve-directory
          path: /var/www/static

      # Custom redirect
      /redirect:
        http_action:
          type: serve-message
          status_code: 302
          response_headers:
            Location: https://example.com

      # Forward to backend
      /api/:
        http_action:
          type: forward
          addresses:
            - backend:8080

    # Default for unmatched paths
    default_http_action:
      type: forward
      addresses:
        - default-backend:8080
```

### Mixed TLS Modes on Same Port

```yaml
- address: 0.0.0.0:443
  transport: tcp
  targets:
    # Passthrough: public API (no keys needed)
    - location: api-backend:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: api.example.com

    # Terminate: admin panel (decrypt and inspect)
    - location: admin-backend:8080
      allowlist:
        - 10.0.0.0/8      # Internal network only
      server_tls:
        mode: terminate
        cert: admin.crt
        key: admin.key
        sni_hostnames: admin.example.com
```

### Wildcard SNI Matching

Route all subdomains of a domain to a single backend:

```yaml
- address: 0.0.0.0:443
  transport: tcp
  targets:
    # *.example.com matches foo.example.com, bar.example.com, etc.
    # but NOT example.com itself
    - location: wildcard-backend:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: "*.example.com"

    # .example.com matches example.com AND all subdomains
    - location: dot-backend:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: ".other.com"

    # Exact match takes priority over wildcards
    - location: api-backend:443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: passthrough
        sni_hostnames: api.example.com
```

Matching priority: exact match > deepest wildcard > shallower wildcard > no match.

### Host Header Routing

Route HTTP requests by the `Host` header, with the same wildcard pattern support as SNI matching. When the `host` key is used in `required_request_headers`, values are automatically treated as hostname patterns:

```yaml
- address: 0.0.0.0:80
  transport: tcp
  target:
    allowlist: 0.0.0.0/0
    http_paths:
      /:
        # Route app.example.com to the app backend
        - required_request_headers:
            host: app.example.com
          http_action:
            type: forward
            addresses:
              - app-backend:8080

        # Route all *.api.example.com subdomains to the API backend
        - required_request_headers:
            host: "*.api.example.com"
          http_action:
            type: forward
            addresses:
              - api-backend:8080

    default_http_action:
      type: serve-message
      status_code: 404
```

The same patterns are supported: `example.com` (exact), `*.example.com` (subdomains only), `.example.com` (base domain + subdomains), and `*` (catch-all). Port suffixes in the `Host` header (e.g. `example.com:8080`) are stripped before matching.

**Note:** When possible, prefer routing by SNI (`sni_hostnames`) over `Host` header matching. SNI routing operates at the TLS layer before any HTTP parsing, making it more efficient. Host header routing is useful for plain HTTP, or when multiple virtual hosts share the same TLS certificate.

### Client Certificate Pinning

Authenticate clients using SHA256 certificate fingerprints (no CA needed):

```yaml
- address: 0.0.0.0:8443
  transport: tcp
  targets:
    - location: secure-backend:8443
      allowlist: 0.0.0.0/0
      server_tls:
        mode: terminate
        cert: server.crt
        key: server.key
        sni_hostnames: secure.example.com
        # Only allow these client certificate fingerprints
        client_fingerprints:
          - "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
          - "1122334455667788990011223344556677889900112233445566778899001122" # colons optional
```

**Generate client certificate and get fingerprint:**
```bash
# Generate key
openssl ecparam -genkey -name prime256v1 -out client.key

# Create self-signed certificate
openssl req -new -x509 -nodes -key client.key -out client.crt -days 365 -subj "/CN=Client"

# Get SHA256 fingerprint
openssl x509 -in client.crt -noout -fingerprint -sha256
```

### Outgoing TLS with Server Certificate Pinning

Connect to upstream TLS servers and pin their certificates:

```yaml
- address: 0.0.0.0:8080
  transport: tcp
  targets:
    - allowlist: 0.0.0.0/0
      locations:
        - address: upstream.example.com:443
          client_tls:
            # Verify server certificate via WebPKI (default: true)
            verify: true

            # Pin server certificate by SHA256 fingerprint
            server_fingerprints:
              - "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"

            # Present client certificate for authentication
            key: client.key
            cert: client.crt

            # Custom SNI hostname (default: derive from address)
            sni_hostname: custom.example.com
            # Or disable SNI: sni_hostname: null

            # ALPN protocols to negotiate
            alpn_protocols:
              - h2
              - http/1.1
```

**Get server certificate fingerprint:**
```bash
# Fetch certificate
openssl s_client -connect example.com:443 < /dev/null 2>/dev/null | openssl x509 -outform PEM > server.crt

# Get SHA256 fingerprint
openssl x509 -in server.crt -noout -fingerprint -sha256
```

### IP-Based Routing

```yaml
- address: 0.0.0.0:8080
  transport: tcp
  targets:
    # Internal network → backend1
    - location: backend1:8080
      allowlist:
        - 192.168.1.0/24
        - 10.0.0.0/8

    # Specific IPs → backend2
    - location: backend2:8080
      allowlist:
        - 1.2.3.4
        - 2001:db8::1

    # Everyone else → backend3
    - location: backend3:8080
      allowlist: 0.0.0.0/0
```

### IP Groups

Define reusable IP groups:

```yaml
# Define IP groups
- group: internal
  ip_masks:
    - 192.168.0.0/16
    - 10.0.0.0/8

- group: trusted
  ip_masks:
    - 1.2.3.4
    - 5.6.7.8

# Use IP groups in servers
- address: 0.0.0.0:8080
  transport: tcp
  target:
    location: backend:8080
    allowlist:
      - internal
      - trusted
```

### Load Balancing (Round-Robin)

```yaml
- address: 0.0.0.0:8080
  transport: tcp
  target:
    # Distribute across multiple backends
    locations:
      - backend1:8080
      - backend2:8080
      - backend3:8080
      - backend4:8080
    allowlist: 0.0.0.0/0
```

### iptables Integration

Automatically configure firewall rules:

```yaml
- address: 0.0.0.0:8080
  transport: tcp
  use_iptables: true  # Enable iptables auto-configuration
  target:
    location: backend:8080
    allowlist:
      - 192.168.1.0/24
      - 10.0.0.0/8
    # Packets from other IPs will be dropped by iptables
```

**Note:** Requires root or `CAP_NET_RAW` and `CAP_NET_ADMIN` capabilities.

### UDP Forwarding

```yaml
- address: 0.0.0.0:53
  transport: udp
  target:
    addresses:
      - 8.8.8.8:53
      - 8.8.4.4:53
    allowlist: 0.0.0.0/0
    # Optional: association timeout in seconds (default: 200)
    association_timeout_secs: 300
```

### UNIX Domain Sockets

```yaml
- address: 0.0.0.0:8080
  transport: tcp
  target:
    # Forward to UNIX socket
    location:
      path: /run/app.sock
    allowlist: 0.0.0.0/0
```

## Configuration Format

Supports both **YAML** and **JSON** formats. Config is an array of objects, where each object is either:
- A server configuration (`address` + `transport` + `target`/`targets`)
- An IP group definition (`group` + `ip_masks`)

### Server Configuration Fields

**Required fields:**
- `address`: The address to listen on (e.g., `0.0.0.0:443`)
- `transport`: Either `tcp` or `udp`
- `target` or `targets`: Single target or array of targets

**Optional fields:**
- `use_iptables`: Enable iptables rules (default: `false`)
- `tcp_nodelay`: Disable Nagle's algorithm (default: `true`, TCP only)

### Target Configuration Fields

**For TCP targets:**

**Location** (one of):
- `location`: Single address string (e.g., `backend:8080`)
- `locations`: Array of addresses for round-robin
- `location` with object form:
  - `address`: TCP address
  - `path`: UNIX socket path
  - `client_tls`: Outgoing TLS config (see below)

**Required:**
- `allowlist`: IP mask, IP group name, or array of either (e.g., `0.0.0.0/0`, `["internal", "1.2.3.4"]`)

**Optional TLS:**
- `server_tls`: Incoming TLS configuration
  - `mode`: `passthrough` or `terminate` (default: `terminate`)
  - `cert`: Path to certificate file (required for `terminate`)
  - `key`: Path to private key file (required for `terminate`)
  - `sni_hostnames`: Single hostname, wildcard pattern, or array (or `any`, `none`)
    - `example.com` -- exact match only
    - `*.example.com` -- matches any subdomain, but not `example.com` itself
    - `.example.com` -- matches both `example.com` and any subdomain
  - `alpn_protocols`: Single protocol or array (or `any`, `none`)
  - `client_fingerprints`: Array of SHA256 fingerprints for client certificate pinning

**Optional HTTP:**
- `http_paths`: Map of path prefixes to HTTP actions
  - Each path can have one or more configs with `required_request_headers` and `http_action`
  - `required_request_headers`: Map of header names to expected values (keys are case-insensitive)
    - The `host` key supports wildcard patterns: `*.example.com`, `.example.com`, `*`
- `default_http_action`: Fallback HTTP action

**Client TLS configuration (`client_tls`):**
- `verify`: Verify server certificate (default: `true`)
- `key`: Path to client private key (for client certificate auth)
- `cert`: Path to client certificate (for client certificate auth)
- `sni_hostname`: SNI hostname to send (default: derive from address, or `null` to disable)
- `alpn_protocols`: Array of ALPN protocols to negotiate
- `server_fingerprints`: Array of SHA256 fingerprints for server certificate pinning

## URL-Based Configuration

For simple TCP forwarding, use URL format:

```bash
# TCP forwarding
tobaru tcp://127.0.0.1:8080?target=192.168.1.10:80

# Forward to UNIX socket
tobaru tcp://127.0.0.1:8080?target-path=/run/app.sock
```

## Hot Reload

Config files are automatically watched and reloaded when changed. No restart needed!

## Advanced Examples

See the [examples](examples/) directory for complete working configurations:
- [`sni_passthrough.yml`]examples/sni_passthrough.yml - TLS passthrough routing examples
- [`wildcard_sni.yml`]examples/wildcard_sni.yml - Wildcard SNI hostname matching
- [`host_header_routing.yml`]examples/host_header_routing.yml - HTTP Host header virtual hosting
- [`bookmarks.yml`]examples/bookmarks.yml - HTTP path-based routing for URL bookmarks

## Performance

- **Async I/O**: Built on Tokio for high concurrency
- **Zero-copy**: Efficient buffer management with minimal allocations
- **Passthrough mode**: Near-zero overhead TLS routing
- **Connection pooling**: HTTP keep-alive support

## Upgrading

See [UPGRADING.md](UPGRADING.md) for migration guides from older versions.

## License

MIT