Skip to content

DRAFT EXPERIMENT: Multi protocol client / MASQUE integration#11140

Draft
encrypt94 wants to merge 9 commits intomainfrom
experiment/masque-integration
Draft

DRAFT EXPERIMENT: Multi protocol client / MASQUE integration#11140
encrypt94 wants to merge 9 commits intomainfrom
experiment/masque-integration

Conversation

@encrypt94
Copy link
Copy Markdown
Collaborator

@encrypt94 encrypt94 commented Mar 19, 2026

Description

Caution

  • this experiment is Linux only, other platforms are broken
  • UDP is currently supported in the daemon but does not work properly
  • since UDP is broken DNS requests are routed to clear internet
  • masque daemon is experimental autogenerated code, it works but need a complete rewrite
  • server switch between different protocols works with deactivate / activate cycle

The scope of this experiment is :

  • Provide a flexible architecture for adding multiple protocols (i.e. MASQUE, obfuscated WireGuard)
  • Enable support for MASQUE based tunnels
  • Support different authentication mechanisms key-pair and token for the current experiment (in the future maybe support different auth for different protocols-providers pairs: token generated with different flows, client certificates)

Architectural changes

Daemon

  • Protocol abstraction layer add the new Tunnel base class that defines a protocol-agnostic interface for tunnel management (check tunnel.h for documentation).
  • Base protocols implementations, MasqueTunnel and WireGuardTunnel
  • Platform specific protocol implementations
    • MasqueTunnelLinux wraps an external daemon using QProcess (like we do in MacOS)
    • WireGuardTunnelLinux wraps WireGuard utils
  • Added abstract methods in Daemon, will be defined in platform specific implementation
    • initializeTunnels will be called during daemon initialization, instantiate long living tunnel objects, clean interfaces if exists (done by tunnel constructor in platform specific implementation) and connect signals to the daemon
    • selectTunnel select the right tunnel on activate request

Controller / Models

  • Add ProtocolType (currently WireGuard and Masque) and AuthType (currently KeyPair and Token) to Server model. Those fields should be provided by guardian, currenly if empty it will gracefully defaults to 'WireGuardandKeyPair`
  • Add ProtocolType and m_hostname to InterfaceConfig
  • Add m_token to MozillaVPN and TaskToken that will be called by Controller in case auth is Token. Currently gets a token from an environment variable, in a future implementation can call guardian to issue a temporary token

How to reproduce

Setup a MASQUE server

Use h2o with the following configuration.

num-threads: 2
listen:
  port: 80
listen: &ssl
  port: 443
  ssl:
    key-file: /path/to/keyfile.key
    certificate-file: /path/to/fullchain.cer
listen:
  <<: *ssl
  type: quic
hosts:
  default:
    paths:
      /.well-known/self-trace:
        self-trace: ON
        compress: ON
      /:
        mruby.handler: |
          acl {
             token = "insertyourtokenhere"
             respond(401, {}, ["Unauthorized"]) { header('Authorization') != "Bearer #{token}"  }
          }
        proxy.connect:
          - "+*"
        proxy.connect-udp:
          - "+*"
        proxy.connect.masque-draft-03: ON
        proxy.timeout.io: 100000
        proxy.max-buffer-size: 65536
        proxy.tunnel: ON
      /status:
        status: ON
access-log:
  path: /dev/stdout
  format: "%h %l %u %t \"%r\" \"%V\" %s %b"

Build and install the client

Follow the usual Linux procedure but remember to set the prefix on configure so the masque-vpn tool will be installed in the right place (because yes, i hardcoded the path :))

cmake -S . -B build -GNinja -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" -DCMAKE_INSTALL_PREFIX="/"

Set your token as env var and launch the VPN client

export MOZVPN_MASQUE_TOKEN=insertyourtokenhere

Add a MASQUE server to the server list

Add a new server to the list using inspector's append_servers command, follow the following JSON template (note: this is the easy way to reproduce it but i use a patched guardian instance running locally)

{
  "countries": [
    {
      "code": "fk",
      "name": "Fake country",
      "cities": [
        {
          "name": "",
          "code": "tau",
          "servers": [
            {
              "stboot": true,
              "weight": 100,
              "provider": "MyProvider",
              "protocol": "Masque",
              "public_key": "masque-test-key",
              "hostname": "my.masque.server.xyz",
              "socks5_fqdn": "my.masque.server.xyz",
              "socks5_ipv4": "",
              "socks5_name": "my.masque.server.xyz",
              "socks5_port": 1080,
              "ipv4_addr_in": "1.2.3.4",
              "ipv4_gateway": "1.2.3.4",
              "ipv6_addr_in": "",
              "ipv6_gateway": "",
              "multihop_port": 1312,
              "include_in_country": true,
              "port_ranges": [
                [
                  443,
                  443
                ],
                [
                  53,
                  53
                ]
              ]
            }
          ],
          "latitude": 0.0000,
          "longitude": 0.000
        }
      ]
    }
  ]
}

Choose the masque server from the list and check your ip

Important

  • now the VPN should work but since connection health uses DNS ping a no signal warning will appear
  • due to the previous point after 5 minutes you will be switched to a different server

Reference

Jira Issue

Checklist

  • My code follows the style guidelines for this project
  • I have not added any packages that contain high risk or unknown licenses (GPL, LGPL, MPL, etc. consult with DevOps if in question)
  • I have performed a self review of my own code
  • I have commented my code PARTICULARLY in hard to understand areas
  • I have added thorough tests where needed

@encrypt94 encrypt94 requested a review from oskirby March 19, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant