transmitm
transmitm is a Twisted-based Python module that provides transparent intercepting proxying at transport level.
Transports supported as of the latest version:
- TCP
- UDP
Install
transmitm requires a 3.5 minimum version of Python
pip install transmitm
Operation
Transparent proxying requires traffic redirection to the app using a third party utility such as iptables or nftables.
iptables example to redirect TCP traffic targeting server port 80 to proxy port 8080
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
transmitm uses the concept of taps. Tap is a class whose instances receive transport SDUs (Service Data Units) from transitioning packets and perform some sort of operation on them; multiple taps are chained.
Taps get attached to Proxy objects (TCPProxy, UDPProxy) that handle packets on two arms - both from the client and the server.
Dispatcher class holds a list of proxy instances; it cannot be instantiated.
+---------------------------------------------+
| proxy |
| +--------+ +--------+ +-------+ |
| | | | | | | |
SDU | | | | | | |
client ------>-----+ tap 1 +---> tap 2 |...| tap n +---------> server
| | | | | | | |
| | | | | | | |
| +--------+ +--------+ +-------+ |
+---------------------------------------------+
gateway
Usage / API
The following script illustrate the module's API
#!/usr/bin/env python3
from transmitm import Tap, Dispatcher, TCPProxy, UDPProxy
# Define Tap classes that handle data (SDUs)
# At minimum, they must implement the 'handle' method
# The returned value gets passed to the next tap in chain
class PktLogger(Tap):
"""Prints packet size to stdout
"""
def handle(self, data, ip_tuple):
"""Not altering data parameter causes returning
the same object reference"""
peer, proxy = ip_tuple
print(f"Got {len(data)} bytes from {peer} on {proxy}")
return data
class Mangler(Tap):
"""Do a search and replace in packet bytes
"""
def __init__(self, search, replace):
self.search = search
self.replace = replace
def handle(self, data, ip_tuple):
return data.replace(self.search, self.replace)
# Create proxy instances
# A Proxy object requires at least a destination server's IP and port number
# Listen on TCP 8081 and forward to 127.0.0.1:8080
tcp_proxy_8080 = TCPProxy("127.0.0.1", 8080, bind_port=8081)
# Bind port may be omitted for getting a random one
# You can also specify a bind interface; by default all proxies are bind to localhost
udp_proxy_53 = UDPProxy("1.1.1.1", 53, bind_port=53)
# The proxy can be used as a connector between IPv4 and IPv6 endpoints
udp_proxy_rnd = UDPProxy("1.1.1.1", 53, interface='::0')
# Create tap instances that will process packets
logger = PktLogger()
path_mangler = Mangler(
search=b'/api',
replace=b'/forbidden'
)
# Attach taps instances to the proxies
# The order in which the taps are added defines the tap chaining
tcp_proxy_8080.add_tap(path_mangler)
tcp_proxy_8080.add_tap(logger)
# Just logging for DNS packets
udp_proxy_53.add_tap(logger)
# When registering multiple proxies make sure you add those with a specified
# bind_port first, to avoid collision with randomly assigned ones
Dispatcher.add_proxies([
tcp_proxy_8080,
udp_proxy_53,
udp_proxy_rnd
])
# If not provided, bind port is randomly assigned and can be retrieved
# after adding the proxy to the Dispatcher
print("Registered proxies:")
for proxy in Dispatcher.proxies:
print(
proxy.__class__.__name__,
proxy.interface,
proxy.bind_port,
'->',
proxy.server_ip,
proxy.server_port
)
# Blocking method, should be called last
Dispatcher.run()
TODO
- Add UNIX Domain sockets support
- Add packet routing capabilities
Bug reporting
- Open a new issue
- Explain expected vs actual behavior
- Add code snippet that reproduces the issue
Contributing
This project uses poetry for package management during development. Development dependencies require a >=3.7 version of Python. To get a working environment use the following commands
# Install poetry
pip install poetry
# or...
# curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
# Install module's development dependencies
poetry install --no-root
# Check the setup by running the test
pytest
- Fork the repo
- Check out a feature or bug branch
- Add your changes
- Add test cases
- Update README when needed
- Ensure tests are passing
- Submit a pull request to upstream repo
- Add description of your changes
- Ensure branch is mergeable
MIT License, 2020 @tim17d
Metadata
Release files for transmitm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| transmitm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / transmitm-0.1.0-py3-none-any.whl
| Download URL | transmitm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
00e4ab7d5aff40169128a22b77d7c469c659de74fef539d350f4b3112a6bcbbd
|
|
BLAKE2b-256 checksum How to use checksums |
0c45f47a12537f92f5e45435b787ef7f7ce23f1d895273d581fb38c433299047
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/39.0.1 requests-toolbelt/0.9.1 tqdm/4.48.0 CPython/3.7.0
|
Security for transmitm 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.