protox-gatekeeper 0.1.2__py3-none-any.whl → 0.2.1__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.
- protox_gatekeeper/core.py +36 -2
- protox_gatekeeper/geo.py +1 -1
- {protox_gatekeeper-0.1.2.dist-info → protox_gatekeeper-0.2.1.dist-info}/METADATA +58 -15
- protox_gatekeeper-0.2.1.dist-info/RECORD +11 -0
- {protox_gatekeeper-0.1.2.dist-info → protox_gatekeeper-0.2.1.dist-info}/WHEEL +1 -1
- protox_gatekeeper-0.1.2.dist-info/RECORD +0 -11
- {protox_gatekeeper-0.1.2.dist-info → protox_gatekeeper-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {protox_gatekeeper-0.1.2.dist-info → protox_gatekeeper-0.2.1.dist-info}/top_level.txt +0 -0
protox_gatekeeper/core.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
import requests
|
|
4
|
+
from requests.exceptions import RequestException
|
|
4
5
|
|
|
5
6
|
from protox_gatekeeper.session import make_tor_session
|
|
6
7
|
from protox_gatekeeper.verify import is_tor_exit, get_public_ip
|
|
@@ -33,8 +34,16 @@ class GateKeeper:
|
|
|
33
34
|
self._session = make_tor_session(port=socks_port)
|
|
34
35
|
|
|
35
36
|
# 3) Verify Tor routing
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
try:
|
|
38
|
+
if not is_tor_exit(session=self._session, timeout=timeout):
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
'Tor verification failed. Execution aborted.')
|
|
41
|
+
except RequestException as e:
|
|
42
|
+
logger.debug('Underlying Tor connection error', exc_info=e)
|
|
43
|
+
raise RuntimeError(
|
|
44
|
+
f'Tor SOCKS proxy is not reachable on 127.0.0.1:{socks_port}. '
|
|
45
|
+
'Start Tor or Tor Browser and try again.'
|
|
46
|
+
) from None
|
|
38
47
|
|
|
39
48
|
# 4) Measure Tor exit IP
|
|
40
49
|
self.exit_ip = get_public_ip(session=self._session, timeout=timeout)
|
|
@@ -69,6 +78,31 @@ class GateKeeper:
|
|
|
69
78
|
""" Returns the Tor exit IP address. """
|
|
70
79
|
return self.exit_ip
|
|
71
80
|
|
|
81
|
+
def get(self, url: str, **kwargs) -> requests.Response:
|
|
82
|
+
"""
|
|
83
|
+
Passes a GET request to the Tor Gatekeeper.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
url (str): The url to request.
|
|
87
|
+
**kwargs: Additional parameters to pass to the GET request.
|
|
88
|
+
"""
|
|
89
|
+
logger.info(f'[Tor {self.tor_exit}] GET {url}')
|
|
90
|
+
return self._session.get(url=url, **kwargs)
|
|
91
|
+
|
|
92
|
+
def post(self, url: str, data=None, json=None,
|
|
93
|
+
**kwargs) -> requests.Response:
|
|
94
|
+
"""
|
|
95
|
+
Passes a POST request to the Tor Gatekeeper.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
url (str): The url to post to.
|
|
99
|
+
data (dict, optional): The data to post.
|
|
100
|
+
json (dict, optional): The json data to post.
|
|
101
|
+
**kwargs: Additional parameters to pass to the POST request.
|
|
102
|
+
"""
|
|
103
|
+
logger.info(f'[Tor {self.tor_exit}] POST {url}')
|
|
104
|
+
return self._session.post(url=url, data=data, json=json, **kwargs)
|
|
105
|
+
|
|
72
106
|
def download(self, url: str, target_path: str, timeout: int = 30,
|
|
73
107
|
chunk_size: int = 8192):
|
|
74
108
|
"""
|
protox_gatekeeper/geo.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: protox-gatekeeper
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Fail-closed Tor session enforcement for Python HTTP(S) traffic
|
|
5
5
|
Author: Tom Erik Harnes
|
|
6
6
|
License: MIT License
|
|
@@ -33,6 +33,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
33
33
|
Classifier: Programming Language :: Python :: 3.10
|
|
34
34
|
Classifier: Programming Language :: Python :: 3.11
|
|
35
35
|
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
36
37
|
Classifier: License :: OSI Approved :: MIT License
|
|
37
38
|
Classifier: Operating System :: OS Independent
|
|
38
39
|
Requires-Python: >=3.10
|
|
@@ -46,25 +47,24 @@ Dynamic: license-file
|
|
|
46
47
|
[](https://pypi.org/project/protox-gatekeeper/)
|
|
47
48
|
[](https://pypi.org/project/protox-gatekeeper/)
|
|
48
49
|
|
|
49
|
-
|
|
50
50
|
# ProtoX GateKeeper
|
|
51
51
|
|
|
52
52
|
**ProtoX GateKeeper** is a small, opinionated Python library that enforces
|
|
53
|
-
**fail
|
|
53
|
+
**fail-closed Tor routing** for HTTP(S) traffic.
|
|
54
54
|
|
|
55
55
|
The goal is simple:
|
|
56
56
|
|
|
57
57
|
> If Tor is not active and verified, **nothing runs**.
|
|
58
58
|
|
|
59
|
-
GateKeeper is designed to be *fire
|
|
59
|
+
GateKeeper is designed to be *fire-and-forget*: create a client once, then perform network operations with a hard guarantee that traffic exits through the Tor network.
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
63
63
|
## What GateKeeper Is
|
|
64
64
|
|
|
65
|
-
- A **Tor
|
|
66
|
-
- A thin wrapper around `requests.Session`
|
|
67
|
-
- Fail
|
|
65
|
+
- A **Tor-verified HTTP client**
|
|
66
|
+
- A thin wrapper around `requests.Session` with safe helpers
|
|
67
|
+
- Fail-closed by default (no silent clearnet fallback)
|
|
68
68
|
- Observable (exit IP, optional geo info)
|
|
69
69
|
- Suitable for scripts, tooling, and automation
|
|
70
70
|
|
|
@@ -92,6 +92,12 @@ On Windows this usually means **Tor Browser** running in the background.
|
|
|
92
92
|
|
|
93
93
|
## Installation
|
|
94
94
|
|
|
95
|
+
### From PyPI
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install protox-gatekeeper
|
|
99
|
+
```
|
|
100
|
+
|
|
95
101
|
### From source (development)
|
|
96
102
|
|
|
97
103
|
```bash
|
|
@@ -136,6 +142,20 @@ This confirms:
|
|
|
136
142
|
|
|
137
143
|
---
|
|
138
144
|
|
|
145
|
+
### HTTP requests
|
|
146
|
+
|
|
147
|
+
GateKeeper can also be used as a Tor-verified HTTP client:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
with GateKeeper() as gk:
|
|
151
|
+
response = gk.get("https://httpbin.org/ip")
|
|
152
|
+
print(response.json())
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
All requests are guaranteed to use the verified Tor session.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
139
159
|
## API Overview
|
|
140
160
|
|
|
141
161
|
### `GateKeeper(...)`
|
|
@@ -149,7 +169,7 @@ gk = GateKeeper(
|
|
|
149
169
|
|
|
150
170
|
**Parameters**:
|
|
151
171
|
- `socks_port` *(int)* – Tor SOCKS port (default: `9150`)
|
|
152
|
-
- `geo` *(bool)* – Enable best
|
|
172
|
+
- `geo` *(bool)* – Enable best-effort Tor exit geolocation (optional)
|
|
153
173
|
|
|
154
174
|
Raises `RuntimeError` if Tor routing cannot be verified.
|
|
155
175
|
|
|
@@ -168,13 +188,37 @@ gk.download(url, target_path)
|
|
|
168
188
|
|
|
169
189
|
---
|
|
170
190
|
|
|
191
|
+
### `get(url, **kwargs)`
|
|
192
|
+
|
|
193
|
+
Performs a Tor-verified HTTP GET request.
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
response = gk.get(url, timeout=10)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Returns a standard `requests.Response`.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### `post(url, data=None, json=None, **kwargs)`
|
|
204
|
+
|
|
205
|
+
Performs a Tor-verified HTTP POST request.
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
response = gk.post(url, json={"key": "value"})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Returns a standard `requests.Response`.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
171
215
|
## Design Principles
|
|
172
216
|
|
|
173
217
|
- **Fail closed**: no Tor → no execution
|
|
174
218
|
- **Single verification point** (during construction)
|
|
175
219
|
- **No global state**
|
|
176
220
|
- **No logging configuration inside the library**
|
|
177
|
-
- **Session reuse without re
|
|
221
|
+
- **Session reuse without re-verification**
|
|
178
222
|
|
|
179
223
|
Logging is emitted by the library, but **configured by the application**.
|
|
180
224
|
|
|
@@ -196,8 +240,8 @@ The library does **not** call `logging.basicConfig()` internally.
|
|
|
196
240
|
## Security Notes
|
|
197
241
|
|
|
198
242
|
- Tor exit IPs may rotate over time
|
|
199
|
-
- Geo information is best
|
|
200
|
-
- GateKeeper guarantees routing, not anonymity
|
|
243
|
+
- Geo information is best-effort and may be unavailable (rate-limits, CAPTCHAs)
|
|
244
|
+
- GateKeeper guarantees transport routing, not anonymity
|
|
201
245
|
|
|
202
246
|
---
|
|
203
247
|
|
|
@@ -209,14 +253,13 @@ MIT License
|
|
|
209
253
|
|
|
210
254
|
## Status
|
|
211
255
|
|
|
212
|
-
- Version: **v0.1
|
|
213
|
-
- Phase
|
|
256
|
+
- Version: **v0.2.1**
|
|
257
|
+
- Phase 2 in progress
|
|
214
258
|
- API intentionally minimal
|
|
215
259
|
|
|
216
260
|
Future versions may add optional features such as:
|
|
217
261
|
- circuit rotation
|
|
218
262
|
- ControlPort support
|
|
219
|
-
- higher
|
|
263
|
+
- higher-level request helpers
|
|
220
264
|
|
|
221
265
|
Without breaking the core contract.
|
|
222
|
-
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
protox_gatekeeper/__init__.py,sha256=HisDC-NCbMlkrNkrLRdANILoBqw03ssqFp6BTsXm6Us,75
|
|
2
|
+
protox_gatekeeper/core.py,sha256=oMTTlSbzZ3OxpJ7DFY5sKjbIsffeQhV2bzjt5KLArAM,4414
|
|
3
|
+
protox_gatekeeper/geo.py,sha256=j1TY88OavTx5rqhWd-3hbhZzD51Gjtl726bqvnApA1Q,641
|
|
4
|
+
protox_gatekeeper/ops.py,sha256=Kmr8lU8Ohe81URHHNUlUJ0S71aZ1uGgyiztLBPsOUO4,690
|
|
5
|
+
protox_gatekeeper/session.py,sha256=0qGZF44EHr7CP-bSM0awtq4F-5LWiIb4w87mV7Yocfk,235
|
|
6
|
+
protox_gatekeeper/verify.py,sha256=bsHK3f16sRV8cHo30AY82SB1vb_Svu-US7IhghTSQ6E,452
|
|
7
|
+
protox_gatekeeper-0.2.1.dist-info/licenses/LICENSE,sha256=EE75Vy9_csDDBRXBF4uVQWxYu1YpzXTcmaeuVcPOjl4,1093
|
|
8
|
+
protox_gatekeeper-0.2.1.dist-info/METADATA,sha256=Is8hyTIs6kRhGdhLsRrw86rxI9PCtxMu6ZUetxVNZ6s,6942
|
|
9
|
+
protox_gatekeeper-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
10
|
+
protox_gatekeeper-0.2.1.dist-info/top_level.txt,sha256=KOVL4YUpiWQLGjCvx2SmP8VSsA6hPcEvKCd4k6kPl8s,18
|
|
11
|
+
protox_gatekeeper-0.2.1.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
protox_gatekeeper/__init__.py,sha256=HisDC-NCbMlkrNkrLRdANILoBqw03ssqFp6BTsXm6Us,75
|
|
2
|
-
protox_gatekeeper/core.py,sha256=Z4LEY1WcSbjMDU3OS1rf7kPOTry9NJq5-quzACxPvKg,3062
|
|
3
|
-
protox_gatekeeper/geo.py,sha256=AdjHfZqjP0w5ujHFI_WV7c5XWnDTzqkVL32KeJQojYo,641
|
|
4
|
-
protox_gatekeeper/ops.py,sha256=Kmr8lU8Ohe81URHHNUlUJ0S71aZ1uGgyiztLBPsOUO4,690
|
|
5
|
-
protox_gatekeeper/session.py,sha256=0qGZF44EHr7CP-bSM0awtq4F-5LWiIb4w87mV7Yocfk,235
|
|
6
|
-
protox_gatekeeper/verify.py,sha256=bsHK3f16sRV8cHo30AY82SB1vb_Svu-US7IhghTSQ6E,452
|
|
7
|
-
protox_gatekeeper-0.1.2.dist-info/licenses/LICENSE,sha256=EE75Vy9_csDDBRXBF4uVQWxYu1YpzXTcmaeuVcPOjl4,1093
|
|
8
|
-
protox_gatekeeper-0.1.2.dist-info/METADATA,sha256=MmHERPyR-Jx8UnAM8BhmpIe6IKrgk7-k_44fN7l8Jh8,6154
|
|
9
|
-
protox_gatekeeper-0.1.2.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
10
|
-
protox_gatekeeper-0.1.2.dist-info/top_level.txt,sha256=KOVL4YUpiWQLGjCvx2SmP8VSsA6hPcEvKCd4k6kPl8s,18
|
|
11
|
-
protox_gatekeeper-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|