exposr 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exposr-0.5.2/PKG-INFO +862 -0
- exposr-0.5.2/README.md +851 -0
- exposr-0.5.2/client/__init__.py +1 -0
- exposr-0.5.2/client/config.py +43 -0
- exposr-0.5.2/client/main.py +75 -0
- exposr-0.5.2/client/tcp/__init__.py +1 -0
- exposr-0.5.2/client/tcp/connection.py +86 -0
- exposr-0.5.2/client/tcp/tunnel.py +44 -0
- exposr-0.5.2/client/udp/__init__.py +1 -0
- exposr-0.5.2/client/udp/connection.py +76 -0
- exposr-0.5.2/client/udp/tunnel.py +68 -0
- exposr-0.5.2/common/__init__.py +1 -0
- exposr-0.5.2/common/cli.py +60 -0
- exposr-0.5.2/common/logger.py +21 -0
- exposr-0.5.2/common/protocol.py +10 -0
- exposr-0.5.2/exposr.egg-info/PKG-INFO +862 -0
- exposr-0.5.2/exposr.egg-info/SOURCES.txt +32 -0
- exposr-0.5.2/exposr.egg-info/dependency_links.txt +1 -0
- exposr-0.5.2/exposr.egg-info/entry_points.txt +2 -0
- exposr-0.5.2/exposr.egg-info/top_level.txt +3 -0
- exposr-0.5.2/server/__init__.py +1 -0
- exposr-0.5.2/server/config.py +34 -0
- exposr-0.5.2/server/control.py +96 -0
- exposr-0.5.2/server/data.py +29 -0
- exposr-0.5.2/server/main.py +33 -0
- exposr-0.5.2/server/ports.py +26 -0
- exposr-0.5.2/server/tcp/__init__.py +1 -0
- exposr-0.5.2/server/tcp/ports.py +24 -0
- exposr-0.5.2/server/tcp/tunnel.py +58 -0
- exposr-0.5.2/server/udp/__init__.py +1 -0
- exposr-0.5.2/server/udp/ports.py +24 -0
- exposr-0.5.2/server/udp/tunnel.py +72 -0
- exposr-0.5.2/setup.cfg +4 -0
- exposr-0.5.2/setup.py +23 -0
exposr-0.5.2/PKG-INFO
ADDED
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: exposr
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: Expose local TCP and UDP services to the public internet
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Dynamic: description
|
|
8
|
+
Dynamic: description-content-type
|
|
9
|
+
Dynamic: requires-python
|
|
10
|
+
Dynamic: summary
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
_____
|
|
14
|
+
| ____|_ ___ __ ___ ___ _ __
|
|
15
|
+
| _| \ \/ / '_ \ / _ \/ __| '__|
|
|
16
|
+
| |___ > <| |_) | (_) \__ \ |
|
|
17
|
+
|_____/_/\_\ .__/ \___/|___/_|
|
|
18
|
+
|_|
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
# Exposr
|
|
22
|
+
|
|
23
|
+
Exposr is a reverse TCP and UDP tunneling project that exposes services running
|
|
24
|
+
on a user's local machine to the public internet through a remote relay server.
|
|
25
|
+
|
|
26
|
+
The project is built with Python and uses a public server as the relay. The agent maintains a persistent control connection and creates a dedicated data connection for every incoming public connection.
|
|
27
|
+
|
|
28
|
+
## Current Version
|
|
29
|
+
|
|
30
|
+
**Exposr v0.5 - Experimental / Proof of Concept**
|
|
31
|
+
|
|
32
|
+
### Current capabilities
|
|
33
|
+
|
|
34
|
+
- Reverse TCP tunneling
|
|
35
|
+
- Reverse UDP tunneling
|
|
36
|
+
- Localhost service exposure
|
|
37
|
+
- Dynamic public port registration
|
|
38
|
+
- TCP and UDP port availability checking through server registration
|
|
39
|
+
- Persistent agent connection
|
|
40
|
+
- Automatic agent reconnection
|
|
41
|
+
- Multiple simultaneous public connections
|
|
42
|
+
- Dedicated data tunnel per connection
|
|
43
|
+
- UUID-based connection identification
|
|
44
|
+
- Async networking using Python `asyncio`
|
|
45
|
+
- Colored logs for connected, trying, error, and info events
|
|
46
|
+
- Command-line interface
|
|
47
|
+
|
|
48
|
+
Simple TCP tunnel syntax:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
exposr tcp 3000 25565
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Simple UDP tunnel syntax:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
exposr udp 3000 25565
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
# How It Works
|
|
63
|
+
|
|
64
|
+
Suppose an application is running locally:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
127.0.0.1:3000
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Start Exposr:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
exposr tcp 3000 25565
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The agent creates an outbound connection to the Exposr relay server.
|
|
77
|
+
|
|
78
|
+
UDP tunnels use the same control connection and public-port selection as TCP.
|
|
79
|
+
The public UDP listener forwards each datagram to the local UDP service. UDP
|
|
80
|
+
payloads travel through the existing TCP data channel using length-prefixed
|
|
81
|
+
frames, then are sent back as UDP datagrams.
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
Your PC
|
|
85
|
+
127.0.0.1:3000
|
|
86
|
+
|
|
|
87
|
+
v
|
|
88
|
+
Exposr Agent
|
|
89
|
+
|
|
|
90
|
+
| Persistent control connection
|
|
91
|
+
v
|
|
92
|
+
+--------------------------+
|
|
93
|
+
| Exposr Server |
|
|
94
|
+
| |
|
|
95
|
+
| Control Port: 9000 |
|
|
96
|
+
| Data Port: 9001 |
|
|
97
|
+
| |
|
|
98
|
+
| Public TCP/UDP Ports: |
|
|
99
|
+
| 25565 |
|
|
100
|
+
| 20000-30000 |
|
|
101
|
+
+-------------+------------+
|
|
102
|
+
|
|
|
103
|
+
v
|
|
104
|
+
Internet Users
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Example forwarding path:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
Internet User
|
|
111
|
+
|
|
|
112
|
+
v
|
|
113
|
+
SERVER_IP:25565
|
|
114
|
+
|
|
|
115
|
+
v
|
|
116
|
+
Exposr Server
|
|
117
|
+
|
|
|
118
|
+
v
|
|
119
|
+
Exposr Agent
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
127.0.0.1:3000
|
|
123
|
+
|
|
|
124
|
+
v
|
|
125
|
+
Your Application
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The application continues running on the user's computer. The relay server only forwards traffic.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
# Project Structure
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
Exposr/
|
|
136
|
+
|
|
|
137
|
+
+-- client/
|
|
138
|
+
| +-- __init__.py
|
|
139
|
+
| +-- config.py
|
|
140
|
+
| +-- main.py
|
|
141
|
+
| +-- tcp/
|
|
142
|
+
| | +-- __init__.py
|
|
143
|
+
| | +-- connection.py
|
|
144
|
+
| | +-- tunnel.py
|
|
145
|
+
| +-- udp/
|
|
146
|
+
| +-- __init__.py
|
|
147
|
+
| +-- connection.py
|
|
148
|
+
| +-- tunnel.py
|
|
149
|
+
|
|
|
150
|
+
+-- common/
|
|
151
|
+
| +-- __init__.py
|
|
152
|
+
| +-- logger.py
|
|
153
|
+
| +-- protocol.py
|
|
154
|
+
|
|
|
155
|
+
+-- server/
|
|
156
|
+
| +-- __init__.py
|
|
157
|
+
| +-- control.py
|
|
158
|
+
| +-- data.py
|
|
159
|
+
| +-- ports.py
|
|
160
|
+
| +-- main.py
|
|
161
|
+
| +-- tcp/
|
|
162
|
+
| | +-- __init__.py
|
|
163
|
+
| | +-- ports.py
|
|
164
|
+
| | +-- tunnel.py
|
|
165
|
+
| +-- udp/
|
|
166
|
+
| +-- __init__.py
|
|
167
|
+
| +-- ports.py
|
|
168
|
+
| +-- tunnel.py
|
|
169
|
+
|
|
|
170
|
+
+-- setup.py
|
|
171
|
+
+-- README.MD
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
TCP- and UDP-specific client and server logic lives in their respective
|
|
175
|
+
transport packages. Shared client and server coordination stays in the
|
|
176
|
+
top-level packages, while shared logging and protocol messages live in
|
|
177
|
+
`common/`.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
# CLI Installation
|
|
182
|
+
|
|
183
|
+
Exposr can be installed as a command-line tool.
|
|
184
|
+
|
|
185
|
+
Clone the repository and navigate into the project:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
git clone YOUR_REPOSITORY_URL
|
|
189
|
+
cd Exposr
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Install Exposr:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
python -m pip install .
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For development, use an editable installation:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
python -m pip install -e .
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The console command is provided by the `client.main:main` entry point as `exposr`. The editable installation means source changes are immediately used without reinstalling the package.
|
|
205
|
+
|
|
206
|
+
## Configure the Relay Server
|
|
207
|
+
|
|
208
|
+
The server address is blank when Exposr is first installed. Before using
|
|
209
|
+
`tcp` or `udp`, save the public IP address or hostname of the relay VM:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
exposr config set-server YOUR_SERVER_IP
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
For example:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
exposr config set-server 12.345.67.890
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This generates a random agent token and saves it in:
|
|
222
|
+
|
|
223
|
+
```text
|
|
224
|
+
~/.exposr/agent_token.txt
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Copy the contents of that file into the server's
|
|
228
|
+
`~/.exposr/config.json`:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"server_host": "",
|
|
233
|
+
"agent_token": "PASTE_TOKEN_HERE"
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The token is sent with every control registration request. The server closes
|
|
238
|
+
connections whose token does not match its configured token before accepting
|
|
239
|
+
the agent or opening a public tunnel.
|
|
240
|
+
|
|
241
|
+
On the relay server, initialize the token by pasting the generated value:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
exposr server init-token PASTE_TOKEN_HERE
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
This writes the token to the server's `~/.exposr/config.json` while preserving
|
|
248
|
+
other configuration values.
|
|
249
|
+
|
|
250
|
+
Start the relay server with:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
exposr server start
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The server must be initialized with `exposr server init-token` before it can
|
|
257
|
+
start accepting authenticated agents.
|
|
258
|
+
|
|
259
|
+
The value is saved in:
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
~/.exposr/config.json
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
If you run `exposr tcp 3000 25565` before configuring the server, Exposr
|
|
266
|
+
stops and displays:
|
|
267
|
+
|
|
268
|
+
```text
|
|
269
|
+
[ERROR] Server IP is not configured. Run: exposr config set-server <server-ip>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The `--server-host` option can be used to override the saved address for one
|
|
273
|
+
run:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
exposr tcp 3000 25565 --server-host YOUR_SERVER_IP
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
# Windows PATH Setup
|
|
282
|
+
|
|
283
|
+
Depending on the Python installation, the Exposr executable may be installed in a Python `Scripts` directory that is not automatically added to `PATH`.
|
|
284
|
+
|
|
285
|
+
If this happens:
|
|
286
|
+
|
|
287
|
+
```text
|
|
288
|
+
'exposr' is not recognized as an internal or external command
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Find the Python user base directory:
|
|
292
|
+
|
|
293
|
+
```cmd
|
|
294
|
+
python -m site --user-base
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Then add the `Scripts` directory inside that location to the Windows `PATH`. To check where `exposr.exe` exists, run:
|
|
298
|
+
|
|
299
|
+
```cmd
|
|
300
|
+
where exposr
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
After adding the correct directory to `PATH`, close existing terminals and open a new terminal. Verify with `where exposr`, then run:
|
|
304
|
+
|
|
305
|
+
```cmd
|
|
306
|
+
exposr tcp 3000 25565
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
# Using the CLI
|
|
312
|
+
|
|
313
|
+
## Basic Usage
|
|
314
|
+
|
|
315
|
+
Expose a local TCP service running on port `3000` through public port `25565`:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
exposr tcp 3000 25565
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Exposr requests the specified public port and reports an error if it is unavailable.
|
|
322
|
+
When the public port is omitted, Exposr tries `25565` first and then selects
|
|
323
|
+
random ports from `20000-30000` until it finds one that the server accepts.
|
|
324
|
+
|
|
325
|
+
The same syntax and port-selection behavior apply to UDP:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
exposr udp 3000
|
|
329
|
+
exposr udp 3000 21342
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Optional connection settings can be supplied with:
|
|
333
|
+
|
|
334
|
+
```text
|
|
335
|
+
--server-host
|
|
336
|
+
--control-port
|
|
337
|
+
--data-port
|
|
338
|
+
--local-host
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The saved server address is used when `--server-host` is omitted. The control
|
|
342
|
+
port defaults to `9000`, the data port defaults to `9001`, and the local host
|
|
343
|
+
defaults to `127.0.0.1`.
|
|
344
|
+
|
|
345
|
+
## TCP Tunnels
|
|
346
|
+
|
|
347
|
+
The TCP command accepts an optional public port:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
exposr tcp 3000 21342
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
This forwards:
|
|
354
|
+
|
|
355
|
+
```text
|
|
356
|
+
127.0.0.1:3000 -> SERVER_IP:21342
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The syntax is:
|
|
360
|
+
|
|
361
|
+
```text
|
|
362
|
+
exposr tcp <local-port> [public-port]
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Examples:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
exposr tcp 3000 25565
|
|
369
|
+
exposr tcp 8080 28080
|
|
370
|
+
exposr tcp 5000 25000
|
|
371
|
+
exposr tcp 25565 25565
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
When a public port is supplied, Exposr requests that exact port and reports an
|
|
375
|
+
error if it is unavailable. When omitted, it uses the fallback described above.
|
|
376
|
+
|
|
377
|
+
## UDP Tunnels
|
|
378
|
+
|
|
379
|
+
UDP exposes a local UDP service through a public UDP port:
|
|
380
|
+
|
|
381
|
+
```text
|
|
382
|
+
exposr udp <local-port> [public-port]
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Examples:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
exposr udp 3000
|
|
389
|
+
exposr udp 5000 25000
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
With no public port, Exposr tries `25565`, then random ports from `20000-30000`.
|
|
393
|
+
With a public port, it requests that exact port. Each incoming public datagram
|
|
394
|
+
gets a temporary tunnel session to the local UDP service, and responses are
|
|
395
|
+
returned to the original sender.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
# Port Assignment
|
|
400
|
+
|
|
401
|
+
For either TCP or UDP, Exposr requests the public port supplied on the command line:
|
|
402
|
+
|
|
403
|
+
```text
|
|
404
|
+
requested public port
|
|
405
|
+
|
|
|
406
|
+
v
|
|
407
|
+
available?
|
|
408
|
+
|
|
|
409
|
+
+-- yes -> register tunnel
|
|
410
|
+
|
|
|
411
|
+
+-- no -> try another random port from 20000-30000
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
When no public port is supplied, the agent tries `25565` first. The server
|
|
415
|
+
tracks ownership and releases public ports when an agent disconnects.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
# Ports
|
|
420
|
+
|
|
421
|
+
| Port | Purpose |
|
|
422
|
+
|---|---|
|
|
423
|
+
| `9000` | Persistent agent control channel |
|
|
424
|
+
| `9001` | Dedicated TCP data tunnel connections, including UDP payload frames |
|
|
425
|
+
| `25565` | Default preferred public tunnel port |
|
|
426
|
+
| `20000-30000` | Random fallback public tunnel range |
|
|
427
|
+
|
|
428
|
+
The relay host or Azure firewall must allow inbound TCP and UDP traffic for the
|
|
429
|
+
public tunnel ports, and inbound TCP traffic for ports `9000` and `9001`.
|
|
430
|
+
|
|
431
|
+
## Port 9000 - Control Channel
|
|
432
|
+
|
|
433
|
+
The agent maintains a persistent connection to:
|
|
434
|
+
|
|
435
|
+
```text
|
|
436
|
+
SERVER_IP:9000
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The agent registers a public port:
|
|
440
|
+
|
|
441
|
+
```text
|
|
442
|
+
REGISTER 25565
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
For UDP, the registration includes the transport marker:
|
|
446
|
+
|
|
447
|
+
```text
|
|
448
|
+
REGISTER 25565 <agent-token> UDP
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
When an internet user connects to the public port, the server sends the agent:
|
|
452
|
+
|
|
453
|
+
```text
|
|
454
|
+
CONNECT <connection-id>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Example:
|
|
458
|
+
|
|
459
|
+
```text
|
|
460
|
+
CONNECT 8bab2f9a-b0e2-4db2-8fed-9a8dda8e3aed
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Port 9001 - Data Channel
|
|
464
|
+
|
|
465
|
+
For each incoming public connection:
|
|
466
|
+
|
|
467
|
+
1. The server generates a UUID.
|
|
468
|
+
2. The server tells the correct agent to handle it.
|
|
469
|
+
3. The agent connects to the local application.
|
|
470
|
+
4. The agent opens a new connection to port `9001`.
|
|
471
|
+
5. The agent identifies that connection with:
|
|
472
|
+
|
|
473
|
+
```text
|
|
474
|
+
DATA <connection-id>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
6. The server matches the data connection with the waiting public client.
|
|
478
|
+
7. Traffic is forwarded in both directions.
|
|
479
|
+
|
|
480
|
+
Each public client receives a separate data connection.
|
|
481
|
+
|
|
482
|
+
For UDP, port `9001` carries length-prefixed datagram frames over a temporary
|
|
483
|
+
TCP data connection. The public and local service endpoints remain UDP sockets.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
# Requirements
|
|
488
|
+
|
|
489
|
+
## Server
|
|
490
|
+
|
|
491
|
+
- Python 3.10+
|
|
492
|
+
- Linux server, VPS, Azure VM, or another machine with a reachable public IP
|
|
493
|
+
- Open inbound TCP ports `9000` and `9001`
|
|
494
|
+
- Open inbound UDP port `25565` and the UDP range `20000-30000`
|
|
495
|
+
|
|
496
|
+
## Client
|
|
497
|
+
|
|
498
|
+
- Python 3.10+
|
|
499
|
+
- Internet connection
|
|
500
|
+
- A local TCP or UDP service running on the desired port
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
# Server Setup
|
|
505
|
+
|
|
506
|
+
On the relay machine, clone the project and enter its directory:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
git clone YOUR_REPOSITORY_URL
|
|
510
|
+
cd Exposr
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Install Exposr:
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
python3 -m pip install .
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Initialize the server with the agent token generated by the client setup:
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
exposr server init-token PASTE_TOKEN_HERE
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
Before starting the relay, allow inbound TCP traffic on ports `9000` and
|
|
526
|
+
`9001`, and allow inbound TCP and UDP traffic on public tunnel ports `25565`
|
|
527
|
+
and `20000-30000`. The public port protocol must match the tunnel command:
|
|
528
|
+
|
|
529
|
+
```text
|
|
530
|
+
exposr tcp 3000 # public TCP port
|
|
531
|
+
exposr udp 3000 # public UDP port
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Start the relay server with:
|
|
535
|
+
|
|
536
|
+
```bash
|
|
537
|
+
exposr server start
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
The server listens on TCP control port `9000` and TCP data port `9001`. It
|
|
541
|
+
creates a TCP or UDP public listener when an authenticated agent registers a
|
|
542
|
+
tunnel. Keep this process running while clients use the relay.
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
# Example: FastAPI
|
|
547
|
+
|
|
548
|
+
Suppose FastAPI runs locally on `127.0.0.1:3000`:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
uvicorn main:app --host 127.0.0.1 --port 3000
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Start Exposr:
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
exposr tcp 3000 25565
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
If Exposr assigns `25565`, visiting `http://SERVER_IP:25565` forwards traffic to `http://127.0.0.1:3000`. Swagger documentation is available through `http://SERVER_IP:25565/docs` when that public port is assigned.
|
|
561
|
+
|
|
562
|
+
For a local UDP service listening on port `3000`, run:
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
exposr udp 3000
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Send UDP datagrams to `SERVER_IP:25565`. If `25565` is unavailable, the agent
|
|
569
|
+
selects and registers an available port from `20000-30000`.
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
# Multiple Agents
|
|
574
|
+
|
|
575
|
+
The server supports multiple agents. Each agent can own a different public port, while the server tracks the owner of each tunnel:
|
|
576
|
+
|
|
577
|
+
```text
|
|
578
|
+
Agent A: 127.0.0.1:3000 -> SERVER_IP:25565
|
|
579
|
+
Agent B: 127.0.0.1:8080 -> SERVER_IP:28061
|
|
580
|
+
Agent C: 127.0.0.1:5000 -> SERVER_IP:29040
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
# Multiple Simultaneous Connections
|
|
584
|
+
|
|
585
|
+
Multiple users can connect to the same public port simultaneously. Every connection receives a unique UUID and a dedicated data connection:
|
|
586
|
+
|
|
587
|
+
```text
|
|
588
|
+
Client A --+
|
|
589
|
+
|
|
|
590
|
+
Client B --+----> Exposr Server
|
|
591
|
+
| |
|
|
592
|
+
Client C --+ +-- Tunnel A --> Local Service
|
|
593
|
+
+-- Tunnel B --> Local Service
|
|
594
|
+
+-- Tunnel C --> Local Service
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
# Logging
|
|
600
|
+
|
|
601
|
+
Exposr uses colored status logs.
|
|
602
|
+
|
|
603
|
+
## Green - `[CONNECTED]`
|
|
604
|
+
|
|
605
|
+
Used for successful connections and active tunnels.
|
|
606
|
+
|
|
607
|
+
## Yellow - `[TRYING]`
|
|
608
|
+
|
|
609
|
+
Used while connecting, registering ports, and creating tunnels.
|
|
610
|
+
|
|
611
|
+
## Red - `[ERROR]`
|
|
612
|
+
|
|
613
|
+
Used for failures, timeouts, disconnections, and cleanup.
|
|
614
|
+
|
|
615
|
+
## Blue - `[INFO]`
|
|
616
|
+
|
|
617
|
+
Used for informational messages such as clean shutdown.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
# Azure / Firewall Configuration
|
|
622
|
+
|
|
623
|
+
The relay server firewall or cloud security rules must allow inbound traffic for:
|
|
624
|
+
|
|
625
|
+
| Port / Range | Protocol | Purpose |
|
|
626
|
+
|---|---|---|
|
|
627
|
+
| `22` | TCP | SSH, if required for administration |
|
|
628
|
+
| `9000` | TCP | Exposr control channel |
|
|
629
|
+
| `9001` | TCP | Exposr data channel |
|
|
630
|
+
| `25565` | TCP/UDP | Default public tunnel port |
|
|
631
|
+
| `20000-30000` | TCP/UDP | Random fallback public tunnel range |
|
|
632
|
+
|
|
633
|
+
The requested public port must be allowed for the matching protocol through the
|
|
634
|
+
cloud firewall or Network Security Group. TCP tunnels need TCP access; UDP
|
|
635
|
+
tunnels need UDP access. Ports `9000` and `9001` always use TCP.
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
# Current Architecture
|
|
640
|
+
|
|
641
|
+
```text
|
|
642
|
+
+---------------------+
|
|
643
|
+
| Internet User |
|
|
644
|
+
+----------+----------+
|
|
645
|
+
|
|
|
646
|
+
v
|
|
647
|
+
SERVER_IP:PUBLIC_PORT
|
|
648
|
+
|
|
|
649
|
+
v
|
|
650
|
+
+---------------------+
|
|
651
|
+
| Exposr Server |
|
|
652
|
+
| |
|
|
653
|
+
| Control -> 9000 |
|
|
654
|
+
| Data -> 9001 |
|
|
655
|
+
| |
|
|
656
|
+
| Public TCP/UDP |
|
|
657
|
+
| 25565 |
|
|
658
|
+
| 20000-30000 |
|
|
659
|
+
+----------+----------+
|
|
660
|
+
|
|
|
661
|
+
| Persistent outbound
|
|
662
|
+
| control connection
|
|
663
|
+
v
|
|
664
|
+
+---------------------+
|
|
665
|
+
| Exposr Agent |
|
|
666
|
+
+----------+----------+
|
|
667
|
+
|
|
|
668
|
+
v
|
|
669
|
+
+---------------------+
|
|
670
|
+
| Local Service |
|
|
671
|
+
| 127.0.0.1:LOCAL_PORT|
|
|
672
|
+
+---------------------+
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
# Benchmarks
|
|
680
|
+
|
|
681
|
+
Exposr v0.4 was benchmarked against a direct (non-tunneled) baseline to measure protocol overhead.
|
|
682
|
+
|
|
683
|
+
## Test Environment
|
|
684
|
+
|
|
685
|
+
**Relay server:**
|
|
686
|
+
- Azure Standard_B1s (1 vCPU, 1 GiB RAM, burstable)
|
|
687
|
+
- Region: Central India
|
|
688
|
+
- OS: Ubuntu 24.04
|
|
689
|
+
|
|
690
|
+
**Client:** Windows, local network connection to Azure
|
|
691
|
+
|
|
692
|
+
**Method:** 100 sequential HTTP GET requests per run, measured with an async
|
|
693
|
+
benchmark harness (`aiohttp`). Direct requests hit the local service on
|
|
694
|
+
`127.0.0.1`; tunneled requests hit the same service through the public
|
|
695
|
+
Exposr port.
|
|
696
|
+
|
|
697
|
+
## Results
|
|
698
|
+
|
|
699
|
+
| Metric | Direct | Tunneled | Overhead |
|
|
700
|
+
|---|---|---|---|
|
|
701
|
+
| Mean latency | 80.80 ms | 547.77 ms | +466.97 ms |
|
|
702
|
+
| Median latency | 78.59 ms | 541.00 ms | +462.41 ms |
|
|
703
|
+
| p95 latency | 110.20 ms | 587.42 ms | +477.22 ms |
|
|
704
|
+
| p99 latency | 124.50 ms | 623.29 ms | +498.79 ms |
|
|
705
|
+
| Throughput | 12.4 req/s | 1.8 req/s | -85.5% |
|
|
706
|
+
|
|
707
|
+
Raw TCP connect time to the relay server (`curl -w "%{time_connect}"`)
|
|
708
|
+
measured **113 ms**, isolating pure network RTT from protocol-level cost.
|
|
709
|
+
|
|
710
|
+
## Overhead Breakdown
|
|
711
|
+
|
|
712
|
+
```text
|
|
713
|
+
Total tunneled latency: 547.77 ms
|
|
714
|
+
Network RTT (TCP connect): -113.00 ms
|
|
715
|
+
--------------------------------------
|
|
716
|
+
Exposr protocol overhead: ~435 ms
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
The majority of tunneled latency is not raw network distance but overhead
|
|
720
|
+
introduced by Exposr's connection lifecycle:
|
|
721
|
+
|
|
722
|
+
- A fresh TCP handshake for the **data channel** (port `9001`) on every
|
|
723
|
+
request, since each public connection gets a dedicated data tunnel
|
|
724
|
+
rather than a reused/pooled connection
|
|
725
|
+
- A control-channel round trip (`CONNECT <uuid>` → agent dial-back with
|
|
726
|
+
`DATA <uuid>`) that must complete before any payload is forwarded
|
|
727
|
+
- No connection keep-alive or pooling on the tunnel path, so this cost
|
|
728
|
+
repeats on every single request instead of being amortized
|
|
729
|
+
|
|
730
|
+
## Known Confounds
|
|
731
|
+
|
|
732
|
+
- The relay server runs on the cheapest available Azure tier
|
|
733
|
+
(Standard_B1s), which is CPU-credit throttled under sustained load.
|
|
734
|
+
Some of the measured overhead is plausibly hardware-imposed rather than
|
|
735
|
+
protocol-imposed.
|
|
736
|
+
- Direct-baseline latency (80 ms on `127.0.0.1`) is higher than a typical
|
|
737
|
+
loopback benchmark, likely due to the local test server used
|
|
738
|
+
(`python -m http.server` is single-threaded/blocking). A faster local
|
|
739
|
+
server would tighten the baseline and slightly increase the reported
|
|
740
|
+
overhead percentage.
|
|
741
|
+
|
|
742
|
+
## Reproducing
|
|
743
|
+
|
|
744
|
+
```bash
|
|
745
|
+
pip install aiohttp
|
|
746
|
+
python exposr_benchmark.py \
|
|
747
|
+
--direct-url http://127.0.0.1:3000/ \
|
|
748
|
+
--tunnel-url http://YOUR_SERVER_IP:25565/ \
|
|
749
|
+
--requests 100
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
Concurrency sweep:
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
python exposr_benchmark.py \
|
|
756
|
+
--direct-url http://127.0.0.1:3000/ \
|
|
757
|
+
--tunnel-url http://YOUR_SERVER_IP:25565/ \
|
|
758
|
+
--concurrency 1 10 50 100 \
|
|
759
|
+
--requests 200
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
This overhead is the primary target for the connection-reuse and
|
|
763
|
+
persistent-tunnel work listed under **Planned Features**.
|
|
764
|
+
|
|
765
|
+
# Current Limitations
|
|
766
|
+
|
|
767
|
+
Exposr is currently an experimental proof of concept.
|
|
768
|
+
|
|
769
|
+
Known limitations:
|
|
770
|
+
|
|
771
|
+
- TCP and UDP forwarding use separate public sockets
|
|
772
|
+
- No encryption or TLS
|
|
773
|
+
- Data connections are not separately authenticated
|
|
774
|
+
- No domain or subdomain routing
|
|
775
|
+
- No persistent tunnel configuration
|
|
776
|
+
- No user accounts or dashboard
|
|
777
|
+
- No rate limiting or abuse protection
|
|
778
|
+
- Public port ranges must be explicitly allowed by the server firewall
|
|
779
|
+
- Random port allocation does not bypass firewall or cloud security rules
|
|
780
|
+
- UDP forwarding uses temporary TCP data connections for payload transport
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
# Security Warning
|
|
785
|
+
|
|
786
|
+
The current version is not production-ready. The control port uses the
|
|
787
|
+
configured agent token, but the data port does not use separate
|
|
788
|
+
authentication or encryption. Do not expose the control and data ports
|
|
789
|
+
publicly in a production deployment without appropriate security controls.
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
# Planned Features
|
|
794
|
+
|
|
795
|
+
Possible future improvements include:
|
|
796
|
+
|
|
797
|
+
- Server-assigned ports
|
|
798
|
+
- Agent heartbeat and stale-agent detection
|
|
799
|
+
- Improved tunnel registration
|
|
800
|
+
- Persistent server operation using `systemd`
|
|
801
|
+
- Agent authentication tokens
|
|
802
|
+
- TLS encryption
|
|
803
|
+
- Domain support
|
|
804
|
+
- CLI status and tunnel management commands
|
|
805
|
+
|
|
806
|
+
These are not part of the current protocol or implementation.
|
|
807
|
+
|
|
808
|
+
---
|
|
809
|
+
|
|
810
|
+
# Development Status
|
|
811
|
+
|
|
812
|
+
```text
|
|
813
|
+
Exposr v0.4
|
|
814
|
+
Experimental / Proof of Concept
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
The current version demonstrates the core functionality of Exposr: exposing
|
|
818
|
+
local TCP and UDP services through a publicly accessible relay server with
|
|
819
|
+
dynamic port registration, automatic fallback allocation, dedicated TCP data
|
|
820
|
+
tunnels, UDP datagram forwarding, and a command-line interface.
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
# CLI Quick Reference
|
|
825
|
+
|
|
826
|
+
```bash
|
|
827
|
+
# Install Exposr
|
|
828
|
+
python -m pip install .
|
|
829
|
+
|
|
830
|
+
# Development installation
|
|
831
|
+
python -m pip install -e .
|
|
832
|
+
|
|
833
|
+
# Configure the relay server once
|
|
834
|
+
exposr config set-server YOUR_SERVER_IP
|
|
835
|
+
|
|
836
|
+
# Expose a local TCP service using the default public port
|
|
837
|
+
exposr tcp 3000 25565
|
|
838
|
+
|
|
839
|
+
# Expose a local service using a specific public port
|
|
840
|
+
exposr tcp 3000 21342
|
|
841
|
+
|
|
842
|
+
# Expose another local service
|
|
843
|
+
exposr tcp 8080 28080
|
|
844
|
+
|
|
845
|
+
# Expose a Minecraft Java server on its default local port
|
|
846
|
+
exposr tcp 25565 25565
|
|
847
|
+
|
|
848
|
+
# Expose a local UDP service using the default public port
|
|
849
|
+
exposr udp 3000
|
|
850
|
+
|
|
851
|
+
# Expose a local UDP service using a specific public port
|
|
852
|
+
exposr udp 3000 21342
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
The general TCP syntax is:
|
|
856
|
+
|
|
857
|
+
```text
|
|
858
|
+
exposr tcp <local-port> [public-port]
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
UDP uses the parallel syntax `exposr udp <local-port> [public-port]` and follows
|
|
862
|
+
the same `25565` then `20000-30000` fallback behavior as TCP.
|