unitlab 2.3.15__py3-none-any.whl → 2.3.17__py3-none-any.whl
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.
- unitlab/cloudflare_api_tunnel.py +68 -26
- {unitlab-2.3.15.dist-info → unitlab-2.3.17.dist-info}/METADATA +1 -1
- {unitlab-2.3.15.dist-info → unitlab-2.3.17.dist-info}/RECORD +7 -7
- {unitlab-2.3.15.dist-info → unitlab-2.3.17.dist-info}/LICENSE.md +0 -0
- {unitlab-2.3.15.dist-info → unitlab-2.3.17.dist-info}/WHEEL +0 -0
- {unitlab-2.3.15.dist-info → unitlab-2.3.17.dist-info}/entry_points.txt +0 -0
- {unitlab-2.3.15.dist-info → unitlab-2.3.17.dist-info}/top_level.txt +0 -0
unitlab/cloudflare_api_tunnel.py
CHANGED
@@ -142,12 +142,12 @@ class CloudflareAPITunnel:
|
|
142
142
|
print(f"🔧 Configuring tunnel routes...")
|
143
143
|
|
144
144
|
# Get current tunnel config first
|
145
|
-
get_url = f"{self.api_base}/accounts/{self.account_id}/
|
145
|
+
get_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{self.tunnel_id}/configurations"
|
146
146
|
|
147
147
|
try:
|
148
148
|
# Get existing config
|
149
149
|
response = requests.get(get_url, headers=self.headers)
|
150
|
-
current_config = response.json()
|
150
|
+
current_config = response.json() if response.status_code == 200 else {}
|
151
151
|
|
152
152
|
# Build new ingress rules
|
153
153
|
new_ingress = [
|
@@ -190,7 +190,7 @@ class CloudflareAPITunnel:
|
|
190
190
|
}
|
191
191
|
}
|
192
192
|
|
193
|
-
put_url = f"{self.api_base}/accounts/{self.account_id}/
|
193
|
+
put_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{self.tunnel_id}/configurations"
|
194
194
|
response = requests.put(put_url, headers=self.headers, json=config_data)
|
195
195
|
|
196
196
|
if response.status_code == 200:
|
@@ -231,9 +231,12 @@ class CloudflareAPITunnel:
|
|
231
231
|
# Create new tunnel
|
232
232
|
print(f"📦 Creating new tunnel: {tunnel_name}")
|
233
233
|
create_url = f"{self.api_base}/accounts/{self.account_id}/tunnels"
|
234
|
+
# Generate secret first
|
235
|
+
tunnel_secret = os.urandom(32).hex()
|
236
|
+
|
234
237
|
create_data = {
|
235
238
|
"name": tunnel_name,
|
236
|
-
"tunnel_secret":
|
239
|
+
"tunnel_secret": tunnel_secret
|
237
240
|
}
|
238
241
|
|
239
242
|
create_response = requests.post(create_url, headers=self.headers, json=create_data)
|
@@ -242,6 +245,9 @@ class CloudflareAPITunnel:
|
|
242
245
|
existing_tunnel = create_response.json()['result']
|
243
246
|
print(f"✅ Created tunnel: {tunnel_name}")
|
244
247
|
|
248
|
+
# Add the secret to the tunnel info (API doesn't return it)
|
249
|
+
existing_tunnel['tunnel_secret'] = tunnel_secret
|
250
|
+
|
245
251
|
# Save credentials for this tunnel
|
246
252
|
self._save_tunnel_credentials(existing_tunnel)
|
247
253
|
|
@@ -254,8 +260,13 @@ class CloudflareAPITunnel:
|
|
254
260
|
print(f"❌ Failed to create tunnel: {create_response.text}")
|
255
261
|
return None
|
256
262
|
else:
|
257
|
-
# Tunnel exists -
|
258
|
-
print(f"♻️
|
263
|
+
# Tunnel exists - but we need to ensure it can be used
|
264
|
+
print(f"♻️ Found existing tunnel, setting up for use")
|
265
|
+
|
266
|
+
# Store tunnel info for later use
|
267
|
+
existing_tunnel['needs_token'] = True # Mark that we'll need to use token
|
268
|
+
|
269
|
+
# Configure tunnel routes (creates config file)
|
259
270
|
self._configure_tunnel_routes(existing_tunnel['id'])
|
260
271
|
|
261
272
|
# Ensure DNS records exist
|
@@ -308,19 +319,32 @@ class CloudflareAPITunnel:
|
|
308
319
|
def _save_tunnel_credentials(self, tunnel_info):
|
309
320
|
"""
|
310
321
|
Save tunnel credentials locally for this device
|
322
|
+
Credentials must be base64 encoded for cloudflared
|
311
323
|
"""
|
324
|
+
import base64
|
325
|
+
import json
|
326
|
+
|
312
327
|
creds_dir = Path.home() / '.cloudflared'
|
313
328
|
creds_dir.mkdir(exist_ok=True)
|
314
329
|
|
315
330
|
creds_file = creds_dir / f"{tunnel_info['id']}.json"
|
316
331
|
|
332
|
+
# Get the secret - it should be hex string
|
333
|
+
secret_hex = tunnel_info.get('tunnel_secret') or tunnel_info.get('secret')
|
334
|
+
if secret_hex:
|
335
|
+
# Convert hex to bytes then to base64
|
336
|
+
secret_bytes = bytes.fromhex(secret_hex)
|
337
|
+
secret_b64 = base64.b64encode(secret_bytes).decode('ascii')
|
338
|
+
else:
|
339
|
+
print("⚠️ No tunnel secret found")
|
340
|
+
return None
|
341
|
+
|
317
342
|
credentials = {
|
318
343
|
"AccountTag": self.account_id,
|
319
|
-
"TunnelSecret":
|
344
|
+
"TunnelSecret": secret_b64, # Must be base64!
|
320
345
|
"TunnelID": tunnel_info['id']
|
321
346
|
}
|
322
347
|
|
323
|
-
import json
|
324
348
|
with open(creds_file, 'w') as f:
|
325
349
|
json.dump(credentials, f)
|
326
350
|
|
@@ -334,11 +358,6 @@ class CloudflareAPITunnel:
|
|
334
358
|
try:
|
335
359
|
print("🚀 Starting Cloudflare tunnel...")
|
336
360
|
|
337
|
-
# First, try to set up DNS and routes via API
|
338
|
-
if self.api_token:
|
339
|
-
self.create_dns_records()
|
340
|
-
self.update_tunnel_config()
|
341
|
-
|
342
361
|
# Ensure cloudflared is available
|
343
362
|
cloudflared_path = self._ensure_cloudflared()
|
344
363
|
if not cloudflared_path:
|
@@ -347,6 +366,12 @@ class CloudflareAPITunnel:
|
|
347
366
|
# Create or get existing tunnel for this device
|
348
367
|
device_tunnel = self.create_device_tunnel()
|
349
368
|
|
369
|
+
# Now set up DNS and routes via API after tunnel is created/found
|
370
|
+
if self.api_token and device_tunnel:
|
371
|
+
self.tunnel_id = device_tunnel['id'] # Ensure tunnel_id is set
|
372
|
+
self.create_dns_records()
|
373
|
+
self.update_tunnel_config()
|
374
|
+
|
350
375
|
if not device_tunnel:
|
351
376
|
print("❌ Could not create/find device tunnel")
|
352
377
|
# Fallback to shared tunnel if API fails
|
@@ -368,34 +393,51 @@ class CloudflareAPITunnel:
|
|
368
393
|
|
369
394
|
# Check if credentials file exists
|
370
395
|
creds_file = Path.home() / '.cloudflared' / f"{tunnel_id}.json"
|
396
|
+
config_file = Path.home() / '.cloudflared' / f'config-{tunnel_id}.yml'
|
397
|
+
cmd = None # Initialize cmd
|
371
398
|
|
372
|
-
if not creds_file.exists():
|
399
|
+
if not creds_file.exists() or device_tunnel.get('needs_token'):
|
373
400
|
# Try to recreate credentials from stored secret
|
374
401
|
if device_tunnel.get('tunnel_secret'):
|
375
402
|
self._save_tunnel_credentials(device_tunnel)
|
403
|
+
# Continue to use credentials below
|
376
404
|
else:
|
377
|
-
print("⚠️ No credentials
|
405
|
+
print("⚠️ No stored credentials, using tunnel token...")
|
378
406
|
# Get token for this tunnel
|
379
407
|
token_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}/token"
|
380
408
|
token_response = requests.get(token_url, headers=self.headers)
|
381
409
|
if token_response.status_code == 200:
|
382
410
|
token = token_response.json()['result']
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
"
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
411
|
+
|
412
|
+
# Check if config file exists with ingress rules
|
413
|
+
if config_file.exists():
|
414
|
+
# Use token with config file for ingress rules
|
415
|
+
print(f" Using token with config file: {config_file}")
|
416
|
+
cmd = [
|
417
|
+
cloudflared_path,
|
418
|
+
"tunnel",
|
419
|
+
"--no-autoupdate",
|
420
|
+
"--config", str(config_file),
|
421
|
+
"run",
|
422
|
+
"--token", token
|
423
|
+
]
|
424
|
+
else:
|
425
|
+
# Use token directly
|
426
|
+
print(f" Using token directly (no config file)")
|
427
|
+
cmd = [
|
428
|
+
cloudflared_path,
|
429
|
+
"tunnel",
|
430
|
+
"--no-autoupdate",
|
431
|
+
"run",
|
432
|
+
"--token", token
|
433
|
+
]
|
392
434
|
else:
|
393
435
|
print("❌ Could not get tunnel token")
|
394
436
|
return None
|
395
437
|
|
396
|
-
if
|
438
|
+
# Only use credentials if we haven't already set cmd with token
|
439
|
+
if cmd is None and creds_file.exists():
|
397
440
|
# Check if config file exists
|
398
|
-
config_file = Path.home() / '.cloudflared' / f'config-{tunnel_id}.yml'
|
399
441
|
if config_file.exists():
|
400
442
|
# Run tunnel with config file (includes routes)
|
401
443
|
cmd = [
|
@@ -2,15 +2,15 @@ unitlab/__init__.py,sha256=Wtk5kQ_MTlxtd3mxJIn2qHVK5URrVcasMMPjD3BtrVM,214
|
|
2
2
|
unitlab/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
|
3
3
|
unitlab/binary_manager.py,sha256=Q1v2Odm0hk_3g7jfDUJQfkjEbUbSjtuyo2JDUyWjDrk,5468
|
4
4
|
unitlab/client.py,sha256=V5fTgbprmMsnMwD_FPn7oZh0KK6hdnqB4BYuY4D-JRw,24558
|
5
|
-
unitlab/cloudflare_api_tunnel.py,sha256=
|
5
|
+
unitlab/cloudflare_api_tunnel.py,sha256=yVovEvLsjNFOv02pv9KU76Q8kZSf-IGe_G9ghkF2n64,25495
|
6
6
|
unitlab/exceptions.py,sha256=68Tr6LreEzjQ3Vns8HAaWdtewtkNUJOvPazbf6NSnXU,950
|
7
7
|
unitlab/main.py,sha256=7gPZ_2n90sxDnq9oGZVKOkuifr-k7w2Tq3ZIldAUE8I,5877
|
8
8
|
unitlab/tunnel_config.py,sha256=7CiAqasfg26YQfJYXapCBQPSoqw4jIx6yR64saybLLo,8312
|
9
9
|
unitlab/tunnel_service_token.py,sha256=ji96a4s4W2cFJrHZle0zBD85Ac_T862-gCKzBUomrxM,3125
|
10
10
|
unitlab/utils.py,sha256=83ekAxxfXecFTg76Z62BGDybC_skKJHYoLyawCD9wGM,1920
|
11
|
-
unitlab-2.3.
|
12
|
-
unitlab-2.3.
|
13
|
-
unitlab-2.3.
|
14
|
-
unitlab-2.3.
|
15
|
-
unitlab-2.3.
|
16
|
-
unitlab-2.3.
|
11
|
+
unitlab-2.3.17.dist-info/LICENSE.md,sha256=Gn7RRvByorAcAaM-WbyUpsgi5ED1-bKFFshbWfYYz2Y,1069
|
12
|
+
unitlab-2.3.17.dist-info/METADATA,sha256=vwinORqbWDCEvNcS-fekSwMoly9rnyhzcKK2OWdAYDA,844
|
13
|
+
unitlab-2.3.17.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
14
|
+
unitlab-2.3.17.dist-info/entry_points.txt,sha256=ig-PjKEqSCj3UTdyANgEi4tsAU84DyXdaOJ02NHX4bY,45
|
15
|
+
unitlab-2.3.17.dist-info/top_level.txt,sha256=Al4ZlTYE3fTJK2o6YLCDMH5_DjuQkffRBMxgmWbKaqQ,8
|
16
|
+
unitlab-2.3.17.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|