Skip to content
This repository has been archived by the owner on Dec 19, 2024. It is now read-only.

Commit

Permalink
ProxyGuard: Update to latest common integration
Browse files Browse the repository at this point in the history
  • Loading branch information
jwijenbergh committed Oct 29, 2024
1 parent 24ec736 commit 4f74567
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 54 deletions.
38 changes: 13 additions & 25 deletions eduvpn/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from eduvpn_common.main import EduVPN, ServerType, WrappedError
from eduvpn_common.state import State, StateType
from eduvpn_common.types import ProxyReady, ProxySetup, ReadRxBytes, RefreshList # type: ignore[attr-defined]
from eduvpn_common.types import ProxySetup, ReadRxBytes, RefreshList # type: ignore[attr-defined]

from eduvpn import nm
from eduvpn.config import Configuration
Expand All @@ -21,6 +21,7 @@
parse_tokens,
)
from eduvpn.keyring import DBusKeyring, InsecureFileKeyring, TokenKeyring
from eduvpn.proxy import Proxy
from eduvpn.server import ServerDatabase, parse_profiles, parse_required_transition
from eduvpn.utils import (
handle_exception,
Expand Down Expand Up @@ -144,8 +145,8 @@ def __init__(
self.nm_manager = nm_manager
self._was_tcp = False
self._should_failover = False
self._peer_ips_proxy = None
self._refresh_list_handler = RefreshList(self.refresh_list)
self._proxy_setup_handler = ProxySetup(self.on_proxy_setup)

@property
def keyring(self):
Expand Down Expand Up @@ -327,22 +328,8 @@ def save_tokens(self, server_id: str, server_type: int, tokens: str):
logger.error("Failed saving tokens with exception:")
logger.error(e, exc_info=True)

def on_proxy_setup(self, fd, peer_ips):
logger.debug(f"got proxy fd: {fd}, peer_ips: {peer_ips}")
self._peer_ips_proxy = json.loads(peer_ips)

@run_in_background_thread("start-proxy")
def start_proxy(self, proxy, callback):
try:
self.common.start_proxyguard(
proxy.listen,
proxy.source_port,
proxy.peer,
ProxySetup(self.on_proxy_setup),
ProxyReady(lambda: callback()),
)
except Exception as e:
handle_exception(self.common, e)
def on_proxy_setup(self, fd):
logger.debug(f"got proxy fd: {fd}")

def connect(
self,
Expand Down Expand Up @@ -424,21 +411,22 @@ def connect(config):
if not self.common.in_state(State.CONNECTING):
self.common.set_state(State.CONNECTING)
connection = Connection.parse(config)
proxy = None
if config.proxy is not None:
wrapper_proxy = self.common.new_proxyguard(
config.proxy.listen_port, config.proxy.source_port, config.proxy.peer, self._proxy_setup_handler
)
proxy = Proxy(self.common, config.proxy, wrapper_proxy)
connection.connect(
self.nm_manager,
config.default_gateway,
self.config.allow_wg_lan,
config.dns_search_domains,
config.proxy,
self._peer_ips_proxy,
proxy,
on_connect,
)
self._peer_ips_proxy = None

if config.proxy:
self.start_proxy(config.proxy, lambda: connect(config))
else:
connect(config)
connect(config)

def reconnect(self, callback: Optional[Callable] = None, prefer_tcp: bool = False):
def on_disconnected(success: bool):
Expand Down
31 changes: 7 additions & 24 deletions eduvpn/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from datetime import datetime, timedelta
from enum import IntEnum
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse

from eduvpn.ovpn import Ovpn

Expand Down Expand Up @@ -38,35 +37,22 @@ class Protocol(IntEnum):
WIREGUARDTCP = 3


class Proxy:
class ProxyConfig:
"""The class that represents a proxyguard instance
:param: peer: str: The remote peer string
:param: listen: str: The listen proxy string
:param source_port: int: The source port for the TCP connection
:param: listen_port: str: The listen port for the proxy
"""

def __init__(
self,
peer: str,
source_port: int,
listen: str,
listen_port: int,
):
self.peer = peer
self.source_port = source_port
self.listen = listen

@property
def peer_scheme(self) -> str:
try:
parsed = urlparse(self.peer)
return parsed.scheme
except Exception:
return ""

@property
def peer_port(self):
if self.peer_scheme == "http":
return 80
return 443
self.listen_port = listen_port


class Config:
Expand All @@ -82,7 +68,7 @@ def __init__(
protocol: Protocol,
default_gateway: bool,
dns_search_domains: List[str],
proxy: Proxy,
proxy: ProxyConfig,
should_failover: bool,
):
self.config = config
Expand All @@ -105,7 +91,7 @@ def parse_config(config_json: str) -> Config:
d = json.loads(config_json)
proxy = d.get("proxy", None)
if proxy:
proxy = Proxy(proxy["peer"], proxy["source_port"], proxy["listen"])
proxy = ProxyConfig(proxy["peer"], proxy["source_port"], proxy["listen_port"])
cfg = Config(
d["config"],
Protocol(d["protocol"]),
Expand Down Expand Up @@ -202,7 +188,6 @@ def connect(
allow_lan,
dns_search_domains,
proxy,
proxy_peer_ips,
callback,
):
manager.start_openvpn_connection(
Expand Down Expand Up @@ -231,14 +216,12 @@ def connect(
allow_lan,
dns_search_domains,
proxy,
proxy_peer_ips,
callback,
):
manager.start_wireguard_connection(
self.config,
default_gateway,
allow_wg_lan=allow_lan,
callback=callback,
proxy_peer_ips=proxy_peer_ips,
proxy=proxy,
)
38 changes: 33 additions & 5 deletions eduvpn/nm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import time
import uuid
from configparser import ConfigParser
from contextlib import closing
from ipaddress import ip_address, ip_interface
from pathlib import Path
from shutil import rmtree
from socket import AF_INET, AF_INET6, IPPROTO_TCP
from socket import AF_INET, AF_INET6, IPPROTO_TCP, SOCK_DGRAM, socket
from tempfile import mkdtemp
from typing import Any, Callable, Optional, TextIO, Tuple

Expand All @@ -16,7 +17,7 @@

from eduvpn.ovpn import Ovpn
from eduvpn.storage import get_uuid, set_uuid, write_ovpn
from eduvpn.utils import run_in_glib_thread
from eduvpn.utils import run_in_background_thread, run_in_glib_thread
from eduvpn.variants import ApplicationVariant

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -78,6 +79,12 @@ def from_active_state(cls, state: "NM.ActiveConnectionState") -> "ConnectionStat
raise ValueError(state)


def find_free_udp_port():
with closing(socket(AF_INET, SOCK_DGRAM)) as s:
s.bind(("", 0))
return s.getsockname()[1]


# A manager for a manager :-)
class NMManager:
def __init__(self, variant: ApplicationVariant, common_lib: EduVPN):
Expand All @@ -101,6 +108,15 @@ def client(self) -> "NM.Client":
def available(self) -> bool:
return self._client is not None

@run_in_background_thread("proxyguard-tunnel")
def proxy_tunnel(self, listen_port):
assert self.proxy is not None
_logger.debug(f"tunneling proxyguard with listen port: {listen_port}")
try:
self.proxy.tunnel(listen_port)
except Exception as e:
self.proxy.forward_exception(e)

# TODO: Move this somewhere else?
def open_stats_file(self, filename: str) -> Optional[TextIO]:
"""
Expand Down Expand Up @@ -441,7 +457,6 @@ def start_wireguard_connection( # noqa: C901
*,
allow_wg_lan=False,
proxy=None,
proxy_peer_ips=None,
callback=None,
) -> None:
_logger.debug("writing wireguard configuration to Network Manager")
Expand Down Expand Up @@ -547,6 +562,13 @@ def start_wireguard_connection( # noqa: C901
fwmark = int(os.environ.get("EDUVPN_WG_FWMARK", 51860))
listen_port = int(os.environ.get("EDUVPN_WG_LISTEN_PORT", 0))

# if we are using proxyguard we need to know the port beforehand
# just get an available port
# Yes, this can race but there is not other option right now
# the NM API just gives port 0 from device.get_listen_port()
if proxy is not None and listen_port == 0:
listen_port = find_free_udp_port()

s_ip4.set_property(NM.SETTING_IP_CONFIG_ROUTE_TABLE, fwmark)
s_ip6.set_property(NM.SETTING_IP_CONFIG_ROUTE_TABLE, fwmark)
w_con.set_property(NM.DEVICE_WIREGUARD_FWMARK, fwmark)
Expand All @@ -572,7 +594,7 @@ def start_wireguard_connection( # noqa: C901

if proxy:
dport_proxy = proxy.peer_port
for proxy_peer_ip in proxy_peer_ips:
for proxy_peer_ip in proxy.peer_ips:
address = ip_address(proxy_peer_ip)
if address.version != ipver:
continue
Expand Down Expand Up @@ -613,7 +635,13 @@ def start_wireguard_connection( # noqa: C901

self.proxy = proxy

self.set_connection(profile, callback) # type: ignore
def set_callback(success: bool):
if success and proxy is not None:
self.proxy_tunnel(listen_port)
if callback is not None:
callback(success)

self.set_connection(profile, set_callback) # type: ignore

def new_cancellable(self):
c = Cancellable.new()
Expand Down
58 changes: 58 additions & 0 deletions eduvpn/proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from typing import List
from urllib.parse import urlparse

from eduvpn.utils import handle_exception


class Proxy:
"""The class that represents a proxyguard instance
:param: common: The common library
:param: peer: str: The remote peer string
:param: listen: str: The listen proxy string
"""

def __init__(
self,
common,
config,
wrapper,
):
self.common = common
self.config = config
self.wrapper = wrapper

def forward_exception(self, error):
handle_exception(self.common, error)

def tunnel(self, wgport):
self.wrapper.tunnel(wgport)

@property
def peer(self) -> str:
return self.config.peer

@property
def source_port(self) -> int:
return self.config.source_port

@property
def listen_port(self) -> int:
return self.config.listen_port

@property
def peer_ips(self) -> List[str]:
return self.wrapper.peer_ips

@property
def peer_scheme(self) -> str:
try:
parsed = urlparse(self.config.peer)
return parsed.scheme
except Exception:
return ""

@property
def peer_port(self):
if self.peer_scheme == "http":
return 80
return 443

0 comments on commit 4f74567

Please sign in to comment.