suxitunnel 0.1.0__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.
- suxitunnel-0.1.0/.gitignore +50 -0
- suxitunnel-0.1.0/LICENSE +21 -0
- suxitunnel-0.1.0/PKG-INFO +370 -0
- suxitunnel-0.1.0/README.md +347 -0
- suxitunnel-0.1.0/__init__.py +9 -0
- suxitunnel-0.1.0/examples/basic_example.py +40 -0
- suxitunnel-0.1.0/examples/context_manager.py +106 -0
- suxitunnel-0.1.0/examples/fastapi_example.py +60 -0
- suxitunnel-0.1.0/examples/flask_example.py +55 -0
- suxitunnel-0.1.0/examples/multiple_tunnels.py +104 -0
- suxitunnel-0.1.0/pyproject.toml +51 -0
- suxitunnel-0.1.0/suxitunnel.py +296 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# Virtual Environment
|
|
25
|
+
venv/
|
|
26
|
+
env/
|
|
27
|
+
ENV/
|
|
28
|
+
.venv
|
|
29
|
+
|
|
30
|
+
# IDE
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
*~
|
|
36
|
+
.DS_Store
|
|
37
|
+
|
|
38
|
+
# Testing
|
|
39
|
+
.pytest_cache/
|
|
40
|
+
.coverage
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
*.cover
|
|
44
|
+
|
|
45
|
+
# Distribution
|
|
46
|
+
dist/
|
|
47
|
+
build/
|
|
48
|
+
|
|
49
|
+
# Local cloudflared downloads
|
|
50
|
+
.cloudflare-tunnel/
|
suxitunnel-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: suxitunnel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple Python library to expose local servers using Cloudflare Tunnel
|
|
5
|
+
Project-URL: Homepage, https://github.com/sadwx/cloudflare-tunnel
|
|
6
|
+
Project-URL: Repository, https://github.com/sadwx/cloudflare-tunnel
|
|
7
|
+
Project-URL: Issues, https://github.com/sadwx/cloudflare-tunnel/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/sadwx/cloudflare-tunnel#readme
|
|
9
|
+
Author-email: Simon <simon@suxi.world>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cloudflare,development,expose,localhost,tunnel
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.14
|
|
21
|
+
Requires-Dist: requests>=2.32.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# cloudflare-tunnel
|
|
25
|
+
|
|
26
|
+
A simple Python library to expose your local server to the internet using Cloudflare's infrastructure.
|
|
27
|
+
|
|
28
|
+
Inspired by [localtunnel-py](https://github.com/gweidart/localtunnel-py) but using Cloudflare's global network for better performance and DDoS protection.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install cloudflare-tunnel
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from cloudflare_tunnel import Tunnel
|
|
40
|
+
|
|
41
|
+
# Expose local port 8000
|
|
42
|
+
tunnel = Tunnel(8000)
|
|
43
|
+
print(f"Your URL: {tunnel.url}")
|
|
44
|
+
|
|
45
|
+
# Keep your app running...
|
|
46
|
+
input("Press Enter to close tunnel...")
|
|
47
|
+
|
|
48
|
+
tunnel.close()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That's it! No configuration, no authentication, no manual setup.
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- 🚀 **Zero Configuration** - Just pass a port number
|
|
56
|
+
- 🔒 **Secure** - Uses Cloudflare's infrastructure with DDoS protection
|
|
57
|
+
- 🌍 **Global CDN** - Fast access from anywhere in the world
|
|
58
|
+
- 📦 **Auto-install** - Automatically downloads cloudflared if needed
|
|
59
|
+
- 🐍 **Simple API** - Clean, Pythonic interface
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Basic Usage
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from cloudflare_tunnel import Tunnel
|
|
67
|
+
|
|
68
|
+
tunnel = Tunnel(8000)
|
|
69
|
+
print(tunnel.url) # https://random-name.trycloudflare.com
|
|
70
|
+
|
|
71
|
+
# Your local server is now public!
|
|
72
|
+
# Keep tunnel alive...
|
|
73
|
+
tunnel.close() # Close when done
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Context Manager (Recommended)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from cloudflare_tunnel import Tunnel
|
|
80
|
+
|
|
81
|
+
with Tunnel(8000) as tunnel:
|
|
82
|
+
print(f"Public URL: {tunnel.url}")
|
|
83
|
+
# Do your work...
|
|
84
|
+
# Tunnel automatically closes when exiting the block
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### With Flask
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from flask import Flask
|
|
91
|
+
from cloudflare_tunnel import Tunnel
|
|
92
|
+
import threading
|
|
93
|
+
|
|
94
|
+
app = Flask(__name__)
|
|
95
|
+
|
|
96
|
+
@app.route('/')
|
|
97
|
+
def index():
|
|
98
|
+
return "Hello from Flask!"
|
|
99
|
+
|
|
100
|
+
if __name__ == '__main__':
|
|
101
|
+
# Start tunnel in background
|
|
102
|
+
tunnel = Tunnel(5000)
|
|
103
|
+
print(f"🌐 Public URL: {tunnel.url}")
|
|
104
|
+
|
|
105
|
+
# Start Flask
|
|
106
|
+
app.run(port=5000)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With FastAPI
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from fastapi import FastAPI
|
|
113
|
+
from cloudflare_tunnel import Tunnel
|
|
114
|
+
import uvicorn
|
|
115
|
+
|
|
116
|
+
app = FastAPI()
|
|
117
|
+
|
|
118
|
+
@app.get("/")
|
|
119
|
+
def read_root():
|
|
120
|
+
return {"message": "Hello from FastAPI!"}
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
# Start tunnel
|
|
124
|
+
tunnel = Tunnel(8000)
|
|
125
|
+
print(f"🌐 Public URL: {tunnel.url}")
|
|
126
|
+
|
|
127
|
+
# Start FastAPI
|
|
128
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Check Tunnel Status
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
tunnel = Tunnel(8000)
|
|
135
|
+
|
|
136
|
+
print(tunnel.url) # Public URL
|
|
137
|
+
print(tunnel.is_alive()) # True if tunnel is running
|
|
138
|
+
print(tunnel.port) # 8000
|
|
139
|
+
print(tunnel) # <Tunnel port=8000 url=https://... status=active>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Custom Host
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# Tunnel a service running on a different host
|
|
146
|
+
tunnel = Tunnel(8000, host="192.168.1.100")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Manual cloudflared Path
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Use specific cloudflared binary
|
|
153
|
+
tunnel = Tunnel(8000, cloudflared_path="/usr/local/bin/cloudflared")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Disable Auto-download
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
# Don't automatically download cloudflared
|
|
160
|
+
tunnel = Tunnel(8000, auto_download=False)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### `Tunnel(port, host="localhost", cloudflared_path=None, auto_download=True)`
|
|
166
|
+
|
|
167
|
+
Create a tunnel to expose a local port.
|
|
168
|
+
|
|
169
|
+
**Parameters:**
|
|
170
|
+
- `port` (int): Local port to expose (required)
|
|
171
|
+
- `host` (str): Local hostname (default: "localhost")
|
|
172
|
+
- `cloudflared_path` (str): Path to cloudflared binary (auto-detected if None)
|
|
173
|
+
- `auto_download` (bool): Auto-download cloudflared if not found (default: True)
|
|
174
|
+
|
|
175
|
+
**Attributes:**
|
|
176
|
+
- `url` (str): Public HTTPS URL for the tunnel
|
|
177
|
+
- `port` (int): Local port being exposed
|
|
178
|
+
- `host` (str): Local hostname
|
|
179
|
+
- `is_alive()` (bool): Check if tunnel is running
|
|
180
|
+
- `close()`: Close the tunnel
|
|
181
|
+
|
|
182
|
+
**Example:**
|
|
183
|
+
```python
|
|
184
|
+
tunnel = Tunnel(8000)
|
|
185
|
+
print(tunnel.url) # https://abc123.trycloudflare.com
|
|
186
|
+
tunnel.close()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### `open_tunnel(port, host="localhost")`
|
|
190
|
+
|
|
191
|
+
Convenience function to create a tunnel.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from cloudflare_tunnel import open_tunnel
|
|
195
|
+
|
|
196
|
+
tunnel = open_tunnel(8000)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## How It Works
|
|
200
|
+
|
|
201
|
+
### Technical Overview
|
|
202
|
+
|
|
203
|
+
This library uses **Cloudflare Quick Tunnels** (also called TryCloudflare), which:
|
|
204
|
+
|
|
205
|
+
1. Creates an outbound HTTP/2 connection from your machine to Cloudflare's edge
|
|
206
|
+
2. No inbound ports are opened on your machine (firewall-friendly!)
|
|
207
|
+
3. No authentication or account required
|
|
208
|
+
4. Traffic is routed through Cloudflare's global network
|
|
209
|
+
5. Automatic HTTPS with valid certificate
|
|
210
|
+
|
|
211
|
+
**Architecture:**
|
|
212
|
+
```
|
|
213
|
+
Your App (localhost:8000) → cloudflared → Cloudflare Edge → Internet
|
|
214
|
+
(HTTP/2) (Global CDN) (HTTPS)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Cloudflared Binary
|
|
218
|
+
|
|
219
|
+
The library automatically downloads the `cloudflared` binary on first use if it's not already installed. The binary is saved to `~/.cloudflare-tunnel/`.
|
|
220
|
+
|
|
221
|
+
Supported platforms:
|
|
222
|
+
- Linux (x64, ARM64)
|
|
223
|
+
- macOS (x64, ARM64/M1)
|
|
224
|
+
- Windows (x64)
|
|
225
|
+
|
|
226
|
+
## Comparison with Alternatives
|
|
227
|
+
|
|
228
|
+
| Feature | cloudflare-tunnel | localtunnel | ngrok |
|
|
229
|
+
|---------|------------------|-------------|-------|
|
|
230
|
+
| Free tier | ✅ Unlimited | ✅ Yes | ⚠️ Limited |
|
|
231
|
+
| Custom domains | ❌ Random | ❌ Random | 💰 Paid |
|
|
232
|
+
| DDoS protection | ✅ Included | ❌ No | ⚠️ Limited |
|
|
233
|
+
| Global CDN | ✅ Yes | ❌ No | ⚠️ Limited |
|
|
234
|
+
| Auth required | ✅ No | ✅ No | ⚠️ Yes |
|
|
235
|
+
| Connection limits | ✅ None | ⚠️ Yes | ⚠️ Yes |
|
|
236
|
+
| Rate limits | ✅ Generous | ⚠️ Yes | ⚠️ Yes |
|
|
237
|
+
|
|
238
|
+
## Limitations
|
|
239
|
+
|
|
240
|
+
1. **Random URLs**: Each tunnel gets a random `*.trycloudflare.com` URL (no custom domains)
|
|
241
|
+
2. **Temporary**: Tunnels are meant for development/testing, not production
|
|
242
|
+
3. **No persistence**: URL changes each time you create a new tunnel
|
|
243
|
+
4. **Rate limits**: Subject to Cloudflare's fair use policy
|
|
244
|
+
|
|
245
|
+
For production use with custom domains, see the full [cloudflared documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/).
|
|
246
|
+
|
|
247
|
+
## Security Considerations
|
|
248
|
+
|
|
249
|
+
### What This Tunnel Provides
|
|
250
|
+
|
|
251
|
+
✅ **DDoS protection** - Cloudflare's network absorbs attacks
|
|
252
|
+
✅ **HTTPS encryption** - Valid SSL certificate included
|
|
253
|
+
✅ **No exposed ports** - Outbound connection only
|
|
254
|
+
✅ **Firewall-friendly** - Works behind NAT/firewalls
|
|
255
|
+
|
|
256
|
+
### What You Still Need
|
|
257
|
+
|
|
258
|
+
⚠️ **Application security** - Implement authentication/authorization
|
|
259
|
+
⚠️ **Input validation** - Protect against malicious input
|
|
260
|
+
⚠️ **Rate limiting** - Prevent abuse of your endpoints
|
|
261
|
+
⚠️ **Access control** - Not everyone should access your tunnel
|
|
262
|
+
|
|
263
|
+
**Important:** Once your local server is exposed via a tunnel, anyone with the URL can access it. Make sure your application has proper authentication!
|
|
264
|
+
|
|
265
|
+
### Best Practices
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
# ❌ DON'T: Expose services without authentication
|
|
269
|
+
tunnel = Tunnel(8000) # If port 8000 has no auth, anyone can access it!
|
|
270
|
+
|
|
271
|
+
# ✅ DO: Use authentication in your application
|
|
272
|
+
from flask import Flask, request, abort
|
|
273
|
+
from functools import wraps
|
|
274
|
+
|
|
275
|
+
def require_auth(f):
|
|
276
|
+
@wraps(f)
|
|
277
|
+
def decorated(*args, **kwargs):
|
|
278
|
+
auth = request.headers.get('Authorization')
|
|
279
|
+
if auth != 'Bearer YOUR_SECRET_TOKEN':
|
|
280
|
+
abort(401)
|
|
281
|
+
return f(*args, **kwargs)
|
|
282
|
+
return decorated
|
|
283
|
+
|
|
284
|
+
@app.route('/api/data')
|
|
285
|
+
@require_auth # Protected!
|
|
286
|
+
def get_data():
|
|
287
|
+
return {"data": "sensitive"}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Troubleshooting
|
|
291
|
+
|
|
292
|
+
### "cloudflared not found"
|
|
293
|
+
|
|
294
|
+
The library should auto-download cloudflared. If it fails:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Manual install
|
|
298
|
+
# macOS
|
|
299
|
+
brew install cloudflare/cloudflare/cloudflared
|
|
300
|
+
|
|
301
|
+
# Linux
|
|
302
|
+
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
|
|
303
|
+
chmod +x cloudflared-linux-amd64
|
|
304
|
+
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
|
|
305
|
+
|
|
306
|
+
# Or disable auto-download and specify path
|
|
307
|
+
tunnel = Tunnel(8000, cloudflared_path="/path/to/cloudflared", auto_download=False)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### "Tunnel failed to start"
|
|
311
|
+
|
|
312
|
+
Check that:
|
|
313
|
+
1. Your local service is running: `curl http://localhost:8000`
|
|
314
|
+
2. The port is not already in use
|
|
315
|
+
3. You have internet connectivity
|
|
316
|
+
4. Firewall allows outbound HTTPS (port 443)
|
|
317
|
+
|
|
318
|
+
### Tunnel URL not appearing
|
|
319
|
+
|
|
320
|
+
The library waits up to 30 seconds for the URL. If it times out:
|
|
321
|
+
1. Check your internet connection
|
|
322
|
+
2. Verify cloudflared is working: `cloudflared --version`
|
|
323
|
+
3. Try running manually: `cloudflared tunnel --url http://localhost:8000`
|
|
324
|
+
|
|
325
|
+
### Random disconnections
|
|
326
|
+
|
|
327
|
+
Quick Tunnels are designed for temporary use. For stable connections:
|
|
328
|
+
1. Restart the tunnel when disconnected
|
|
329
|
+
2. For production, use authenticated Cloudflare Tunnels with named tunnels
|
|
330
|
+
3. Implement automatic reconnection logic
|
|
331
|
+
|
|
332
|
+
## Examples
|
|
333
|
+
|
|
334
|
+
See the `examples/` directory for complete working examples:
|
|
335
|
+
|
|
336
|
+
- `basic_example.py` - Simplest usage
|
|
337
|
+
- `flask_example.py` - Flask integration
|
|
338
|
+
- `fastapi_example.py` - FastAPI integration
|
|
339
|
+
- `context_manager.py` - Using with context manager
|
|
340
|
+
- `multiple_tunnels.py` - Running multiple tunnels
|
|
341
|
+
|
|
342
|
+
## Development
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
# Clone the repo
|
|
346
|
+
git clone https://github.com/sadwx/cloudflare-tunnel.git
|
|
347
|
+
cd cloudflare-tunnel
|
|
348
|
+
|
|
349
|
+
# Install in development mode
|
|
350
|
+
pip install -e .
|
|
351
|
+
|
|
352
|
+
# Run tests
|
|
353
|
+
python -m pytest tests/
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT License - see LICENSE file for details.
|
|
359
|
+
|
|
360
|
+
## Related Projects
|
|
361
|
+
|
|
362
|
+
- [cloudflared](https://github.com/cloudflare/cloudflared) - Official Cloudflare Tunnel client
|
|
363
|
+
- [localtunnel-py](https://github.com/gweidart/localtunnel-py) - Similar library using localtunnel
|
|
364
|
+
- [pyngrok](https://github.com/alexdlaird/pyngrok) - Python wrapper for ngrok
|
|
365
|
+
|
|
366
|
+
## Credits
|
|
367
|
+
|
|
368
|
+
This library is a Python wrapper around [cloudflared](https://github.com/cloudflare/cloudflared), the official Cloudflare Tunnel client.
|
|
369
|
+
|
|
370
|
+
Inspired by the simplicity of [localtunnel-py](https://github.com/gweidart/localtunnel-py).
|