Skip to main content

Python relay/proxy server and library to build reliable encrypted TCP/UDP tunnels with entropy control for TCP traffic

Project description

Python relay/proxy server and library to build reliable encrypted TCP/UDP tunnels with entropy control for TCP traffic

pypi version

Features

  • Easy to install, configure and use

  • TCP/UDP tunneling

  • Pluggable traffic ciphers

  • Pluggable traffic entropy control

Hides TCP traffic in encrypted TCP/UDP tunnel between relay and proxy servers

TCP/UDP tunneling

Requirements

  • Python 3.11

  • pbjson 1.18.0

  • cryptography 41.0.2

  • numpy 1.25.2

Install

python3.11 -m venv .env
source .env/bin/activate
pip install ouija

Usage

Generate cipher_key/token secrets:

ouija_secret

Run relay/proxy server:

ouija <config.json>

tcp-relay.json - TCP relay server - HTTP/HTTPS proxy server interface with TCP connectors:

{
  "protocol": "TCP",
  "mode": "RELAY",
  "debug": true,
  "monitor": true,
  "relay_host": "127.0.0.1",
  "relay_port": 9000,
  "proxy_host": "127.0.0.1",
  "proxy_port": 50000,
  "cipher_key": "bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI=",
  "entropy_rate": 5,
  "token": "395f249c-343a-4f92-9129-68c6d83b5f55",
  "serving_timeout": 20.0,
  "tcp_buffer": 1024,
  "tcp_timeout": 1.0,
  "message_timeout": 5.0
}

tcp-proxy.json - TCP-relayed proxy server:

{
  "protocol": "TCP",
  "mode": "PROXY",
  "debug": true,
  "monitor": true,
  "proxy_host": "0.0.0.0",
  "proxy_port": 50000,
  "cipher_key": "bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI=",
  "entropy_rate": 5,
  "token": "395f249c-343a-4f92-9129-68c6d83b5f55",
  "serving_timeout": 20.0,
  "tcp_buffer": 1024,
  "tcp_timeout": 1.0,
  "message_timeout": 5.0
}

udp-relay.json - UDP relay server - HTTP/HTTPS proxy server interface with UDP connectors:

{
  "protocol": "UDP",
  "mode": "RELAY",
  "debug": true,
  "monitor": true,
  "relay_host": "127.0.0.1",
  "relay_port": 9000,
  "proxy_host": "127.0.0.1",
  "proxy_port": 50000,
  "cipher_key": "bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI=",
  "entropy_rate": 5,
  "token": "395f249c-343a-4f92-9129-68c6d83b5f55",
  "serving_timeout": 20.0,
  "tcp_buffer": 1024,
  "tcp_timeout": 1.0,
  "udp_min_payload": 512,
  "udp_max_payload": 1024,
  "udp_timeout": 2.0,
  "udp_retries": 5,
  "udp_capacity": 10000,
  "udp_resend_sleep": 0.25
}

udp-proxy.json - UDP-relayed proxy server:

{
  "protocol": "UDP",
  "mode": "PROXY",
  "debug": true,
  "monitor": true,
  "proxy_host": "0.0.0.0",
  "proxy_port": 50000,
  "cipher_key": "bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI=",
  "entropy_rate": 5,
  "token": "395f249c-343a-4f92-9129-68c6d83b5f55",
  "serving_timeout": 20.0,
  "tcp_buffer": 1024,
  "tcp_timeout": 1.0,
  "udp_min_payload": 512,
  "udp_max_payload": 1024,
  "udp_timeout": 2.0,
  "udp_retries": 5,
  "udp_capacity": 10000,
  "udp_resend_sleep": 0.25
}

Relay and proxy setup configuration with supervisord - ouija-config

Cipher and entropy

  • cipher_key - FernetCipher key - use ouija_secret to generate key

  • entropy_rate - SimpleEntropy rate, when rate=N every Nth byte will be generated and payload size will increase, rate=5 means 20% traffic overhead

Protocols

  • Stream - TCP

  • Datagram - UDP

Entities

  • Cipher - cipher implementation - FernetCipher out of the box

  • Entropy - entropy control implementation - SimpleEntropy out of the box

  • Tuning - relay/proxy and connector/link interaction settings

  • Relay - HTTPS proxy server interface

  • Connector - relay connector, which communicates with proxy link

  • Proxy - proxy server, which gets requests from relay and sends back responses from remote servers

  • Link - proxy link with relay connector

Tuning - TCP

  • cipher - cipher instance, if None then no encryption will be applied

  • entropy - entropy instance, if None then no entropy control will be applied

  • token - your secret token - UUID4 or anything else - use ouija_secret to generate token

  • serving_timeout - timeout for serve/resend workers, 2X for handlers, seconds

  • tcp_buffer - TCP buffer size, bytes

  • tcp_timeout - TCP awaiting timeout, seconds

  • message_timeout - TCP service message timeout, seconds

Tuning - UDP

  • cipher - cipher instance, if None then no encryption will be applied

  • entropy - entropy instance, if None then no entropy control will be applied

  • token - your secret token - UUID4 or anything else - use ouija_secret to generate token

  • serving_timeout - timeout for serve/resend workers, 2X for handlers, seconds

  • tcp_buffer - TCP buffer size, bytes

  • tcp_timeout - TCP awaiting timeout, seconds

  • udp_min_payload - UDP min payload size, bytes

  • udp_max_payload - UDP max payload size, bytes

  • udp_timeout - UDP awaiting timeout, seconds

  • udp_retries - UDP max retry count per interaction

  • udp_capacity - UDP send/receive buffer capacity - max packet count

  • udp_resend_sleep - UDP resend sleep between retries, seconds

Library usage

stream-relay.py - TCP relay server - HTTP/HTTPS proxy server interface with TCP connectors:

import asyncio
import logging

from ouija import StreamRelay as Relay, StreamTuning as Tuning, Telemetry, SimpleEntropy, FernetCipher


async def main() -> None:
    tuning = Tuning(
        cipher=FernetCipher(key='bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI='),
        entropy=SimpleEntropy(rate=5),
        token='395f249c-343a-4f92-9129-68c6d83b5f55',
        serving_timeout=20.0,
        tcp_buffer=1024,
        tcp_timeout=1.0,
        message_timeout=5.0,
    )
    relay = Relay(
        telemetry=Telemetry(),
        tuning=tuning,
        relay_host='127.0.0.1',
        relay_port=9000,
        proxy_host='127.0.0.1',
        proxy_port=50000,
    )
    asyncio.create_task(relay.debug())
    await relay.serve()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.run_forever()

stream-proxy.py - TCP-relayed proxy server:

import asyncio
import logging

from ouija import StreamProxy as Proxy, Telemetry, StreamTuning as Tuning, SimpleEntropy, FernetCipher


async def main() -> None:
    tuning = Tuning(
        cipher=FernetCipher(key='bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI='),
        entropy=SimpleEntropy(rate=5),
        token='395f249c-343a-4f92-9129-68c6d83b5f55',
        serving_timeout=20.0,
        tcp_buffer=1024,
        tcp_timeout=1.0,
        message_timeout=5.0,
    )
    proxy = Proxy(
        telemetry=Telemetry(),
        tuning=tuning,
        proxy_host='0.0.0.0',
        proxy_port=50000,
    )
    asyncio.create_task(proxy.debug())
    await proxy.serve()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.run_forever()

datagram-relay.py - UDP relay server - HTTPS proxy server interface with UDP connectors:

import asyncio
import logging

from ouija import DatagramRelay as Relay, DatagramTuning as Tuning, Telemetry, SimpleEntropy, FernetCipher


async def main() -> None:
    tuning = Tuning(
        cipher=FernetCipher(key='bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI='),
        entropy=SimpleEntropy(rate=5),
        token='395f249c-343a-4f92-9129-68c6d83b5f55',
        serving_timeout=20.0,
        tcp_buffer=1024,
        tcp_timeout=1.0,
        udp_min_payload=512,
        udp_max_payload=1024,
        udp_timeout=2.0,
        udp_retries=5,
        udp_capacity=10000,
        udp_resend_sleep=0.25,
    )
    relay = Relay(
        telemetry=Telemetry(),
        tuning=tuning,
        relay_host='127.0.0.1',
        relay_port=9000,
        proxy_host='127.0.0.1',
        proxy_port=50000,
    )
    asyncio.create_task(relay.debug())
    await relay.serve()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.run_forever()

datagram-proxy.py - UDP-relayed proxy server:

import asyncio
import logging

from ouija import DatagramProxy as Proxy, Telemetry, DatagramTuning as Tuning, SimpleEntropy, FernetCipher


async def main() -> None:
    tuning = Tuning(
        cipher=FernetCipher(key='bdDmN4VexpDvTrs6gw8xTzaFvIBobFg1Cx2McFB1RmI='),
        entropy=SimpleEntropy(rate=5),
        token='395f249c-343a-4f92-9129-68c6d83b5f55',
        serving_timeout=20.0,
        tcp_buffer=1024,
        tcp_timeout=1.0,
        udp_min_payload=512,
        udp_max_payload=1024,
        udp_timeout=2.0,
        udp_retries=5,
        udp_capacity=10000,
        udp_resend_sleep=0.25,
    )
    proxy = Proxy(
        telemetry=Telemetry(),
        tuning=tuning,
        proxy_host='0.0.0.0',
        proxy_port=50000,
    )
    asyncio.create_task(proxy.debug())
    await proxy.serve()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.run_forever()

Tests

pytest --cov-report html:htmlcov --cov=ouija tests/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ouija-1.3.1.tar.gz (22.6 kB view hashes)

Uploaded Source

Built Distribution

ouija-1.3.1-py3-none-any.whl (18.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page