├── .circleci └── config.yml ├── .github ├── CODEOWNERS └── workflows │ ├── archive.yml │ ├── ghpages.yml │ ├── publish.yml │ └── update.yml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── archive ├── draft-schinazi-masque-obfuscation.md └── draft-schinazi-masque-protocol.md └── draft-schinazi-masque-proxy.md /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: martinthomson/i-d-template:latest 6 | working_directory: ~/draft 7 | 8 | steps: 9 | - checkout 10 | 11 | # Build txt and html versions of drafts 12 | - run: 13 | name: "Build Drafts" 14 | command: "make 'CLONE_ARGS=--reference ~/git-reference'" 15 | 16 | # Update editor's copy on gh-pages 17 | - run: 18 | name: "Update GitHub Pages" 19 | command: | 20 | if [ "${CIRCLE_TAG#draft-}" == "${CIRCLE_TAG}" ]; then 21 | make gh-pages 22 | fi 23 | 24 | # For tagged builds, upload to the datatracker. 25 | - deploy: 26 | name: "Upload to Datatracker" 27 | command: | 28 | if [ "${CIRCLE_TAG#draft-}" != "${CIRCLE_TAG}" ]; then 29 | make upload 30 | fi 31 | 32 | # Save GitHub issues 33 | - run: 34 | name: "Save GitHub Issues" 35 | command: "make issues || make issues DISABLE_ISSUE_FETCH=true && make gh-issues" 36 | 37 | # Create and store artifacts 38 | - run: 39 | name: "Create Artifacts" 40 | command: "make artifacts CI_ARTIFACTS=/tmp/artifacts" 41 | 42 | - store_artifacts: 43 | path: /tmp/artifacts 44 | 45 | 46 | workflows: 47 | version: 2 48 | build: 49 | jobs: 50 | - build: 51 | filters: 52 | tags: 53 | only: /.*?/ 54 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Automatically generated CODEOWNERS 2 | # Regenerate with `make update-codeowners` 3 | draft-schinazi-masque-proxy.md dschinazi.ietf@gmail.com 4 | -------------------------------------------------------------------------------- /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: "Archive Issues and Pull Requests" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0,2,4' 6 | repository_dispatch: 7 | types: [archive] 8 | workflow_dispatch: 9 | inputs: 10 | archive_full: 11 | description: 'Recreate the archive from scratch' 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build: 17 | name: "Archive Issues and Pull Requests" 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: "Checkout" 23 | uses: actions/checkout@v4 24 | 25 | # Note: No caching for this build! 26 | 27 | - name: "Update Archive" 28 | uses: martinthomson/i-d-template@v1 29 | env: 30 | ARCHIVE_FULL: ${{ inputs.archive_full }} 31 | with: 32 | make: archive 33 | token: ${{ github.token }} 34 | 35 | - name: "Update GitHub Pages" 36 | uses: martinthomson/i-d-template@v1 37 | with: 38 | make: gh-archive 39 | token: ${{ github.token }} 40 | 41 | - name: "Save Archive" 42 | uses: actions/upload-artifact@v4 43 | with: 44 | path: archive.json 45 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: "Update Editor's Copy" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | - CONTRIBUTING.md 8 | - LICENSE.md 9 | - .gitignore 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - CONTRIBUTING.md 14 | - LICENSE.md 15 | - .gitignore 16 | 17 | jobs: 18 | build: 19 | name: "Update Editor's Copy" 20 | runs-on: ubuntu-latest 21 | permissions: 22 | contents: write 23 | steps: 24 | - name: "Checkout" 25 | uses: actions/checkout@v4 26 | 27 | - name: "Setup" 28 | id: setup 29 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 30 | 31 | - name: "Caching" 32 | uses: actions/cache@v4 33 | with: 34 | path: | 35 | .refcache 36 | .venv 37 | .gems 38 | node_modules 39 | .targets.mk 40 | key: i-d-${{ steps.setup.outputs.date }} 41 | restore-keys: i-d- 42 | 43 | - name: "Build Drafts" 44 | uses: martinthomson/i-d-template@v1 45 | with: 46 | token: ${{ github.token }} 47 | 48 | - name: "Update GitHub Pages" 49 | uses: martinthomson/i-d-template@v1 50 | if: ${{ github.event_name == 'push' }} 51 | with: 52 | make: gh-pages 53 | token: ${{ github.token }} 54 | 55 | - name: "Archive Built Drafts" 56 | uses: actions/upload-artifact@v4 57 | with: 58 | path: | 59 | draft-*.html 60 | draft-*.txt 61 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish New Draft Version" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "draft-*" 7 | workflow_dispatch: 8 | inputs: 9 | email: 10 | description: "Submitter email" 11 | default: "" 12 | type: string 13 | 14 | jobs: 15 | build: 16 | name: "Publish New Draft Version" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: "Checkout" 20 | uses: actions/checkout@v4 21 | 22 | # See https://github.com/actions/checkout/issues/290 23 | - name: "Get Tag Annotations" 24 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} 25 | 26 | - name: "Setup" 27 | id: setup 28 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 29 | 30 | - name: "Caching" 31 | uses: actions/cache@v4 32 | with: 33 | path: | 34 | .refcache 35 | .venv 36 | .gems 37 | node_modules 38 | .targets.mk 39 | key: i-d-${{ steps.setup.outputs.date }} 40 | restore-keys: i-d- 41 | 42 | - name: "Build Drafts" 43 | uses: martinthomson/i-d-template@v1 44 | with: 45 | token: ${{ github.token }} 46 | 47 | - name: "Upload to Datatracker" 48 | uses: martinthomson/i-d-template@v1 49 | with: 50 | make: upload 51 | env: 52 | UPLOAD_EMAIL: ${{ inputs.email }} 53 | 54 | - name: "Archive Submitted Drafts" 55 | uses: actions/upload-artifact@v4 56 | with: 57 | path: "versioned/draft-*-[0-9][0-9].*" 58 | -------------------------------------------------------------------------------- /.github/workflows/update.yml: -------------------------------------------------------------------------------- 1 | name: "Update Generated Files" 2 | # This rule is not run automatically. 3 | # It can be run manually to update all of the files that are part 4 | # of the template, specifically: 5 | # - README.md 6 | # - CONTRIBUTING.md 7 | # - .note.xml 8 | # - .github/CODEOWNERS 9 | # - Makefile 10 | # 11 | # 12 | # This might be useful if you have: 13 | # - added, removed, or renamed drafts (including after adoption) 14 | # - added, removed, or changed draft editors 15 | # - changed the title of drafts 16 | # 17 | # Note that this removes any customizations you have made to 18 | # the affected files. 19 | on: workflow_dispatch 20 | 21 | jobs: 22 | build: 23 | name: "Update Files" 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: "Checkout" 27 | uses: actions/checkout@v4 28 | 29 | - name: "Update Generated Files" 30 | uses: martinthomson/i-d-template@v1 31 | with: 32 | make: update-files 33 | token: ${{ github.token }} 34 | 35 | - name: "Push Update" 36 | run: git push 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.redxml 4 | *.swp 5 | *.txt 6 | *.upload 7 | *~ 8 | .tags 9 | /*-[0-9][0-9].xml 10 | /.*.mk 11 | /.gems/ 12 | /.refcache 13 | /.venv/ 14 | /.vscode/ 15 | /lib 16 | /node_modules/ 17 | /versioned/ 18 | Gemfile.lock 19 | archive.json 20 | draft-schinazi-masque-proxy.xml 21 | package-lock.json 22 | report.xml 23 | !requirements.txt 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | dist: xenial 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - python-pip 9 | - xsltproc 10 | 11 | env: 12 | global: 13 | - GOPATH="${TRAVIS_BUILD_DIR}/.go_workspace" 14 | - mmark_src=github.com/miekg/mmark/mmark 15 | - mmark=./mmark 16 | 17 | install: 18 | - pip install xml2rfc 19 | - if head -1 -q *.md | grep '^\-\-\-' >/dev/null 2>&1; then gem install --no-doc kramdown-rfc2629; fi 20 | - if head -1 -q *.md | grep '^%%%' >/dev/null 2>&1; then go get "$mmark_src" && go build "$mmark_src"; fi 21 | 22 | script: 23 | - make 24 | - make issues || make issues DISABLE_ISSUE_FETCH=true && make gh-issues 25 | - make gh-pages 26 | 27 | deploy: 28 | provider: script 29 | script: make upload 30 | skip_cleanup: true 31 | on: 32 | tags: true 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository relates to activities in the Internet Engineering Task Force 4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered 5 | Contributions to the IETF Standards Process, as defined in the intellectual 6 | property policies of IETF currently designated as 7 | [BCP 78](https://www.rfc-editor.org/info/bcp78), 8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the 9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html). 10 | 11 | Any edit, commit, pull request, issue, comment or other change made to this 12 | repository constitutes Contributions to the IETF Standards Process 13 | (https://www.ietf.org/). 14 | 15 | You agree to comply with all applicable IETF policies and procedures, including, 16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being 17 | subject to a Simplified BSD License) in Contributions. 18 | 19 | 20 | ## Working Group Information 21 | 22 | Discussion of this work occurs on the [Multiplexed Application Substrate over QUIC Encryption 23 | Working Group mailing list](mailto:masque@ietf.org) 24 | ([archive](https://mailarchive.ietf.org/arch/browse/masque/), 25 | [subscribe](https://www.ietf.org/mailman/listinfo/masque)). 26 | In addition to contributions in GitHub, you are encouraged to participate in 27 | discussions there. 28 | 29 | **Note**: Some working groups adopt a policy whereby substantive discussion of 30 | technical issues needs to occur on the mailing list. 31 | 32 | You might also like to familiarize yourself with other 33 | [Working Group documents](https://datatracker.ietf.org/wg/masque/documents/). 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | See the 4 | [guidelines for contributions](https://github.com/DavidSchinazi/masque-drafts/blob/master/CONTRIBUTING.md). 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR := lib 2 | include $(LIBDIR)/main.mk 3 | 4 | $(LIBDIR)/main.mk: 5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) 6 | git submodule sync 7 | git submodule update --init 8 | else 9 | ifneq (,$(wildcard $(ID_TEMPLATE_HOME))) 10 | ln -s "$(ID_TEMPLATE_HOME)" $(LIBDIR) 11 | else 12 | git clone -q --depth 10 -b main \ 13 | https://github.com/martinthomson/i-d-template $(LIBDIR) 14 | endif 15 | endif 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The MASQUE Proxy 2 | 3 | This is the working area for the individual Internet-Draft, "The MASQUE Proxy". 4 | 5 | * [Editor's Copy](https://DavidSchinazi.github.io/masque-drafts/#go.draft-schinazi-masque-proxy.html) 6 | * [Datatracker Page](https://datatracker.ietf.org/doc/draft-schinazi-masque-proxy) 7 | * [Individual Draft](https://datatracker.ietf.org/doc/html/draft-schinazi-masque-proxy) 8 | * [Compare Editor's Copy to Individual Draft](https://DavidSchinazi.github.io/masque-drafts/#go.draft-schinazi-masque-proxy.diff) 9 | 10 | # Previous Work 11 | 12 | Multiple drafts were moved from this repository after adoption by the 13 | [MASQUE Working Group](https://datatracker.ietf.org/wg/masque/documents/). 14 | 15 | Most of the protocol work in this repository has since been published as RFCs: 16 | * [RFC 9297: HTTP Datagrams and the Capsule Protocol](https://www.rfc-editor.org/rfc/rfc9297) 17 | * [RFC 9298: Proxying UDP in HTTP](https://www.rfc-editor.org/rfc/rfc9298) 18 | * [RFC 9484: Proxying IP in HTTP](https://www.rfc-editor.org/rfc/rfc9484) 19 | * [RFC 9729: The Concealed HTTP Authentication Scheme](https://www.rfc-editor.org/rfc/rfc9729) 20 | 21 | ## Contributing 22 | 23 | See the 24 | [guidelines for contributions](https://github.com/DavidSchinazi/masque-drafts/blob/main/CONTRIBUTING.md). 25 | 26 | Contributions can be made by creating pull requests. 27 | The GitHub interface supports creating pull requests using the Edit (✏) button. 28 | 29 | 30 | ## Command Line Usage 31 | 32 | Formatted text and HTML versions of the draft can be built using `make`. 33 | 34 | ```sh 35 | $ make 36 | ``` 37 | 38 | Command line usage requires that you have the necessary software installed. See 39 | [the instructions](https://github.com/martinthomson/i-d-template/blob/main/doc/SETUP.md). 40 | 41 | -------------------------------------------------------------------------------- /archive/draft-schinazi-masque-obfuscation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MASQUE Obfuscation 3 | abbrev: MASQUE Obfuscation 4 | docname: draft-schinazi-masque-obfuscation-latest 5 | category: exp 6 | wg: MASQUE 7 | 8 | ipr: trust200902 9 | keyword: Internet-Draft 10 | 11 | stand_alone: yes 12 | pi: [toc, sortrefs, symrefs] 13 | 14 | author: 15 | - 16 | ins: "D. Schinazi" 17 | name: "David Schinazi" 18 | organization: "Google LLC" 19 | street: "1600 Amphitheatre Parkway" 20 | city: "Mountain View, California 94043" 21 | country: "United States of America" 22 | email: dschinazi.ietf@gmail.com 23 | 24 | 25 | --- abstract 26 | 27 | This document describes MASQUE Obfuscation. MASQUE Obfuscation is a mechanism 28 | that allows co-locating and obfuscating networking applications behind an 29 | HTTPS web server. The currently prevalent use-case is to allow running a proxy 30 | or VPN server that is indistinguishable from an HTTPS server to any 31 | unauthenticated observer. We do not expect major providers and CDNs to deploy 32 | this behind their main TLS certificate, as they are not willing to take the 33 | risk of getting blocked, as shown when domain fronting was blocked. An 34 | expected use would be for individuals to enable this behind their personal 35 | websites via easy to configure open-source software. 36 | 37 | This document is a straw-man proposal. It does not contain enough details to 38 | implement the protocol, and is currently intended to spark discussions on 39 | the approach it is taking. Discussion of this work is encouraged to happen on 40 | the MASQUE IETF mailing list [](masque@ietf.org) or on the GitHub repository 41 | which contains the draft: [](https://github.com/DavidSchinazi/masque-drafts). 42 | 43 | 44 | --- middle 45 | 46 | # Introduction {#introduction} 47 | 48 | This document describes MASQUE Obfuscation. MASQUE Obfuscation is a mechanism 49 | that allows co-locating and obfuscating networking applications behind an 50 | HTTPS web server. The currently prevalent use-case is to allow running a proxy 51 | or VPN server that is indistinguishable from an HTTPS server to any 52 | unauthenticated observer. We do not expect major providers and CDNs to deploy 53 | this behind their main TLS certificate, as they are not willing to take the 54 | risk of getting blocked, as shown when domain fronting was blocked. An 55 | expected use would be for individuals to enable this behind their personal 56 | websites via easy to configure open-source software. 57 | 58 | This document is a straw-man proposal. It does not contain enough details to 59 | implement the protocol, and is currently intended to spark discussions on 60 | the approach it is taking. Discussion of this work is encouraged to happen on 61 | the MASQUE IETF mailing list [](masque@ietf.org) or on the GitHub repository 62 | which contains the draft: [](https://github.com/DavidSchinazi/masque-drafts). 63 | 64 | MASQUE Obfuscation is built upon the MASQUE protocol 65 | {{!MASQUE=I-D.schinazi-masque-protocol}}. MASQUE Obfuscation leverages the efficient 66 | head-of-line blocking prevention features of the QUIC transport protocol 67 | {{!QUIC=I-D.ietf-quic-transport}} when MASQUE Obfuscation is used in an HTTP/3 68 | {{!HTTP3=I-D.ietf-quic-http}} server. MASQUE Obfuscation can also run in an 69 | HTTP/2 server {{!HTTP2=RFC7540}} but at a performance cost. 70 | 71 | 72 | ## Conventions and Definitions {#conventions} 73 | 74 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 75 | "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this 76 | document are to be interpreted as described in BCP 14 {{!RFC2119}} {{!RFC8174}} 77 | when, and only when, they appear in all capitals, as shown here. 78 | 79 | 80 | # Usage Scenarios {#usage-scenarios} 81 | 82 | There are currently multiple usage scenarios that can benefit from MASQUE 83 | Obfuscation. 84 | 85 | 86 | ## Protection from Network Providers {#protection-providers} 87 | 88 | Some users may wish to obfuscate the destination of their network traffic from 89 | their network provider. This prevents network providers from using data 90 | harvested from this network traffic in ways the user did not intend. 91 | 92 | 93 | ## Protection from Web Servers {#protection-servers} 94 | 95 | There are many clients who would rather not establish a direct connection to 96 | web servers, for example to avoid location tracking. The clients can do that 97 | by running their traffic through a MASQUE Obfuscation server. The web server 98 | will only see the IP address of the MASQUE Obfuscation server, not that of the 99 | client. 100 | 101 | 102 | ## Onion Routing {#onion} 103 | 104 | Routing traffic through a MASQUE Obfuscation server only provides partial 105 | protection against tracking, because the MASQUE Obfuscation server knows the 106 | address of the client. Onion routing as it exists today mitigates this issue 107 | for TCP/TLS. A MASQUE Obfuscation server could allow onion routing over QUIC. 108 | 109 | In this scenario, the client establishes a connection to the MASQUE 110 | Obfuscation server, then through that to another MASQUE Obfuscation server, 111 | etc. This creates a tree of MASQUE servers rooted at the client. QUIC 112 | connections are mapped to a specific branch of the tree. The first MASQUE 113 | Obfuscation server knows the actual address of the client, but the other 114 | MASQUE Obfuscation servers only know the address of the previous server. 115 | To assure reasonable privacy, the path should include at least 3 MASQUE 116 | Obfuscation servers. 117 | 118 | 119 | # Requirements {#requirements} 120 | 121 | This section describes the goals and requirements chosen for MASQUE 122 | Obfuscation. 123 | 124 | 125 | ## Invisibility of Usage {#invisibility-usage} 126 | 127 | An authenticated client using MASQUE Obfuscation appears to observers as a 128 | regular HTTPS client. Observers only see that HTTP/3 or HTTP/2 is being used 129 | over an encrypted channel. No part of the exchanges between client and server 130 | may stick out. Note that traffic analysis is discussed in {{traffic-analysis}}. 131 | 132 | 133 | ## Invisibility of the Server {#invisibility-server} 134 | 135 | To anyone without private keys, the server is indistinguishable from a regular 136 | web server. It is impossible to send an unauthenticated probe that the server 137 | would reply to differently than if it were a normal web server. 138 | 139 | 140 | ## Fallback to HTTP/2 over TLS over TCP {#fallback-h2} 141 | 142 | When QUIC is blocked, MASQUE Obfuscation can run over TCP and still satisfy 143 | previous requirements. Note that in this scenario performance may be 144 | negatively impacted. 145 | 146 | 147 | # Overview of the Mechanism {#overview} 148 | 149 | The server runs an HTTPS server on port 443, and has a valid TLS certificate 150 | for its domain. The client has a public/private key pair, and the server 151 | maintains a list of authorized MASQUE Obfuscation clients, and their public 152 | key. (Alternatively, clients can also be authenticated using a shared secret.) 153 | The client starts by establishing a regular HTTPS connection to the server 154 | (HTTP/3 over QUIC or HTTP/2 over TLS 1.3 {{!TLS13=RFC8446}} over TCP), and 155 | validates the server's TLS certificate as it normally would for HTTPS. If 156 | validation fails, the connection is aborted. At this point the client can send 157 | regular unauthenticated HTTP requests to the server. When it wishes to start 158 | MASQUE Obfuscation, the client uses HTTP Transport Authentication 159 | {{!TRANSPORT-AUTH=I-D.schinazi-httpbis-transport-auth}} to prove its possession 160 | of its associated key. The client sends the Transport-Authentication header 161 | alongside its MASQUE Negotiation request. 162 | 163 | When the server receives the MASQUE Negotiation request, it authenticates the 164 | client and if that fails responds with code "404 Not Found", making sure its 165 | response is the same as what it would return for any unexpected POST 166 | request. If authentication succeeds, the server sends its list of supported 167 | MASQUE applications and the client can start using them. 168 | 169 | 170 | # Connection Resumption {#resumption} 171 | 172 | Clients MUST NOT attempt to "resume" MASQUE Obfuscation state similarly to how 173 | TLS sessions can be resumed. Every new QUIC or TLS connection requires fully 174 | authenticating the client and server. QUIC 0-RTT and TLS early data MUST 175 | NOT be used with MASQUE Obfuscation as they are not forward secure. 176 | 177 | 178 | # Path MTU Discovery {#pmtud} 179 | 180 | In the main deployment of this mechanism, QUIC will be used between client 181 | and server, and that will most likely be the smallest MTU link in the path 182 | due to QUIC header and authentication tag overhead. The client is responsible 183 | for not sending overly large UDP packets and notifying the server of the low 184 | MTU. Therefore PMTUD is currently seen as out of scope of this document. 185 | 186 | 187 | # Operation over HTTP/2 {#http2} 188 | 189 | We will need to define the details of how to run MASQUE over HTTP/2. 190 | When running over HTTP/2, MASQUE uses the Extended CONNECT method to negotiate 191 | the use of datagrams over an HTTP/2 stream 192 | {{!HTTP2-TRANSPORT=I-D.kinnear-httpbis-http2-transport}}. 193 | 194 | MASQUE Obfuscation implementations SHOULD discover that HTTP/3 is available 195 | (as opposed to only HTTP/2) using the same mechanism as regular HTTP traffic. 196 | This current standardized mechanism for this is HTTP Alternative Services 197 | {{!ALT-SVC=RFC7838}}, but future mechanisms such as 198 | {{?HTTPSSVC=I-D.ietf-dnsop-svcb-httpssvc}} can be used if they become 199 | widespread. 200 | 201 | MASQUE Obfuscation implementations using HTTP/3 MUST support the fallback to 202 | HTTP/2 to avoid incentivizing censors to block HTTP/3 or QUIC. 203 | 204 | When the client wishes to use the "UDP Proxying" MASQUE application over 205 | HTTP/2, the client opens a new stream with a CONNECT request to the 206 | "masque-udp-proxy" protocol and then sends datagrams encapsulated inside the 207 | stream with a two-byte length prefix in network byte order. The target IP and 208 | port are sent as part of the URL query. Resetting that stream instructs the 209 | server to release any associated resources. 210 | 211 | When the client wishes to use the "IP Proxying" MASQUE application over 212 | HTTP/2, the client opens a new stream with a CONNECT request to the 213 | "masque-ip-proxy" protocol and then sends IP datagrams with a two byte length 214 | prefix. The server can inspect the IP datagram to look for the destination 215 | address in the IP header. 216 | 217 | 218 | # Security Considerations {#security} 219 | 220 | Here be dragons. TODO: slay the dragons. 221 | 222 | ## Traffic Analysis {#traffic-analysis} 223 | 224 | While MASQUE Obfuscation ensures that proxied traffic appears similar to 225 | regular HTTP traffic, it doesn't inherently defeat traffic analysis. However, 226 | the fact that MASQUE leverages QUIC allows it to segment STREAM frames over 227 | multiple packets and add PADDING frames to change the observable 228 | characteristics of its encrypted traffic. The exact details of how to change 229 | traffic patterns to defeat traffic analysis is considered an open research 230 | question and is out of scope for this document. 231 | 232 | When multiple MASQUE Obfuscation servers are available, a client can leverage 233 | QUIC connection migration to seamlessly transition its end-to-end QUIC 234 | connections by treating separate MASQUE Obfuscation servers as different paths. 235 | This could afford an additional level of obfuscation in hopes of rendering 236 | traffic analysis less effective. 237 | 238 | 239 | ## Untrusted Servers {#untrusted-servers} 240 | 241 | As with any proxy or VPN technology, MASQUE Obfuscation hides some of the 242 | client's private information (such as who they are communicating with) from 243 | their network provider by transferring that information to the MASQUE server. 244 | It is paramount that clients only use MASQUE Obfuscation servers that they 245 | trust, as a malicious actor could easily setup a MASQUE Obfuscation server and 246 | advertise it as a privacy solution in hopes of attracting users to send it 247 | their traffic. 248 | 249 | 250 | # IANA Considerations {#iana} 251 | 252 | We will need to register the "masque-udp-proxy" and "masque-ip-proxy" extended 253 | HTTP CONNECT protocols. 254 | 255 | 256 | --- back 257 | 258 | # Acknowledgments {#acknowledgments} 259 | {:numbered="false"} 260 | 261 | This proposal was inspired directly or indirectly by prior work from many 262 | people. In particular, this work is related to 263 | {{?I-D.schwartz-httpbis-helium}} and 264 | {{?I-D.pardue-httpbis-http-network-tunnelling}}. The mechanism used to 265 | run the MASQUE protocol over HTTP/2 streams was inspired by {{?RFC8441}}. 266 | Brendan Moran is to thank for the idea of leveraging connection migration 267 | across MASQUE servers. The author would also like to thank 268 | Nick Harper, 269 | Christian Huitema, 270 | Marcus Ihlar, 271 | Eric Kinnear, 272 | Mirja Kuehlewind, 273 | Lucas Pardue, 274 | Tommy Pauly, 275 | Zaheduzzaman Sarker, 276 | Ben Schwartz, 277 | and 278 | Christopher A. Wood 279 | for their input. 280 | 281 | The author would like to express immense gratitude to Christophe A., an 282 | inspiration and true leader of VPNs. 283 | 284 | 285 | # Design Justifications {#design} 286 | {:numbered="false"} 287 | 288 | Using an exported key as a nonce allows us to prevent replay attacks (since it 289 | depends on randomness from both endpoints of the TLS connection) without 290 | requiring the server to send an explicit nonce before it has authenticated the 291 | client. Adding an explicit nonce mechanism would expose the server as it would 292 | need to send these nonces to clients that have not been authenticated yet. 293 | 294 | The rationale for a separate MASQUE protocol stream is to allow 295 | server-initiated messages. If we were to use HTTP semantics, we would only be 296 | able to support the client-initiated request-response model. We could have used 297 | WebSocket for this purpose but that would have added wire overhead and 298 | dependencies without providing useful features. 299 | 300 | There are many other ways to authenticate HTTP, however the authentication 301 | used here needs to work in a single client-initiated message to meet the 302 | requirement of not exposing the server. 303 | 304 | The current proposal would also work with TLS 1.2, but in that case TLS false 305 | start and renegotiation must be disabled, and the extended master secret and 306 | renegotiation indication TLS extensions must be enabled. 307 | 308 | If the server or client want to hide that HTTP/2 is used, the client can set 309 | its ALPN to an older version of HTTP and then use the Upgrade header to 310 | upgrade to HTTP/2 inside the TLS encryption. 311 | 312 | The client authentication used here is similar to how Token 313 | Binding {{?RFC8471}} operates, but it has very different goals. MASQUE does 314 | not use token binding directly because using token binding requires sending 315 | the token_binding TLS extension in the TLS ClientHello, and that would stick 316 | out compared to a regular TLS connection. 317 | 318 | TLS post-handshake authentication {{?I-D.sullivan-tls-post-handshake-auth}} 319 | is not used by this proposal because that requires sending the 320 | "post_handshake_auth" extension in the TLS ClientHello, and that would stick 321 | out from a regular HTTPS connection. 322 | 323 | Client authentication could have benefited from Secondary Certificate 324 | Authentication in HTTP/2 {{?I-D.ietf-httpbis-http2-secondary-certs}}, 325 | however that has two downsides: it requires the server advertising that 326 | it supports it in its SETTINGS, and it cannot be sent unprompted by the 327 | client, so the server would have to request authentication. 328 | Both of these would make the server stick out from regular HTTP/2 servers. 329 | 330 | MASQUE proposes a new client authentication method (as opposed to reusing 331 | something like HTTP basic authentication) because HTTP authentication methods 332 | are conceptually per-request (they need to be repeated on each request) 333 | whereas the new method is bound to the underlying connection (be it QUIC or 334 | TLS). In particular, this allows sending QUIC DATAGRAM frames without 335 | authenticating every frame individually. Additionally, HMAC and asymmetric 336 | keying are preferred to sending a password for client authentication since 337 | they have a tighter security bound. Going into the design rationale, HMACs 338 | (and signatures) need some data to sign, and to avoid replay attacks that 339 | should be a fresh nonce provided by the remote peer. Having the server 340 | provide an explicit nonce would leak the existence of the server so we use 341 | TLS keying material exporters as they provide us with a nonce that contains 342 | entropy from the server without requiring explicit communication. 343 | 344 | -------------------------------------------------------------------------------- /archive/draft-schinazi-masque-protocol.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The MASQUE Protocol 3 | abbrev: MASQUE 4 | docname: draft-schinazi-masque-protocol-latest 5 | category: exp 6 | wg: MASQUE 7 | 8 | ipr: trust200902 9 | keyword: Internet-Draft 10 | 11 | stand_alone: yes 12 | pi: [toc, sortrefs, symrefs] 13 | 14 | author: 15 | - 16 | ins: "D. Schinazi" 17 | name: "David Schinazi" 18 | organization: "Google LLC" 19 | street: "1600 Amphitheatre Parkway" 20 | city: "Mountain View, California 94043" 21 | country: "United States of America" 22 | email: dschinazi.ietf@gmail.com 23 | 24 | 25 | --- abstract 26 | 27 | This document describes MASQUE (Multiplexed Application Substrate over QUIC 28 | Encryption). MASQUE is a framework that allows concurrently running multiple 29 | networking applications inside an HTTP/3 connection. For example, MASQUE can 30 | allow a QUIC client to negotiate proxying capability with an HTTP/3 server, 31 | and subsequently make use of this functionality while concurrently processing 32 | HTTP/3 requests and responses. 33 | 34 | Discussion of this work is encouraged to happen on the MASQUE IETF mailing 35 | list [](masque@ietf.org) or on the GitHub repository which contains the draft: 36 | [](https://github.com/DavidSchinazi/masque-drafts). 37 | 38 | 39 | --- middle 40 | 41 | # Introduction {#introduction} 42 | 43 | This document describes MASQUE (Multiplexed Application Substrate over QUIC 44 | Encryption). MASQUE is a framework that allows concurrently running multiple 45 | networking applications inside an HTTP/3 connection (see 46 | {{!HTTP3=I-D.ietf-quic-http}}). For example, MASQUE can allow a QUIC client to 47 | negotiate proxying capability with an HTTP/3 server, and subsequently make use 48 | of this functionality while concurrently processing HTTP/3 requests and 49 | responses. 50 | 51 | MASQUE Negotiation is performed using HTTP mechanisms, but MASQUE applications 52 | can subsequently leverage QUIC {{!QUIC=I-D.ietf-quic-transport}} features 53 | without using HTTP. 54 | 55 | Discussion of this work is encouraged to happen on the MASQUE IETF mailing 56 | list [](masque@ietf.org) or on the GitHub repository which contains the draft: 57 | [](https://github.com/DavidSchinazi/masque-drafts). 58 | 59 | 60 | ## Conventions and Definitions {#conventions} 61 | 62 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 63 | "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this 64 | document are to be interpreted as described in BCP 14 {{!RFC2119}} {{!RFC8174}} 65 | when, and only when, they appear in all capitals, as shown here. 66 | 67 | 68 | # MASQUE Negotiation {#negotiation} 69 | 70 | In order to negotiate the use of the MASQUE protocol, the client starts by 71 | sending a MASQUE request in the HTTP data of an HTTP POST request to 72 | "/.well-known/masque/initial". The client can use this to request specific 73 | MASQUE applications and advertise support for MASQUE extensions. The MASQUE 74 | server indicates support for MASQUE by sending an HTTP status code 200 response, 75 | and can use the data to inform the client of which MASQUE applications are now 76 | in use, and various configuration parameters. 77 | 78 | Both the MASQUE negotiation initial request and its response carry a sequence 79 | of MASQUE applications, as shown in {{fig-masque-app-sequence}}: 80 | 81 | ~~~ 82 | 0 1 2 3 83 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 84 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 85 | | MASQUE Application 1 (*) ... 86 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 87 | | MASQUE Application 2 (*) ... 88 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 89 | ... 90 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 91 | | MASQUE Application N (*) ... 92 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 93 | ~~~ 94 | {: #fig-masque-app-sequence title="Sequence of MASQUE Applications"} 95 | 96 | Each MASQUE Application is encoded as an (identifier, length, value) tuple, 97 | as shown in {{fig-masque-app-encoding}}: 98 | 99 | ~~~ 100 | 0 1 2 3 101 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 102 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 103 | | MASQUE Application ID (i) ... 104 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 105 | | MASQUE Application Length (i) ... 106 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 107 | | MASQUE Application Value (*) ... 108 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 109 | ~~~ 110 | {: #fig-masque-app-encoding title="MASQUE Application Encoding"} 111 | 112 | The MASQUE Application Length field contains the length of the MASQUE 113 | Application Value field. The contents of the MASQUE Application Value 114 | field are defined by its corresponding MASQUE application. When parsing, 115 | endpoints MUST ignore unknown MASQUE applications. A given MASQUE application 116 | ID MUST NOT appear twice in a given sequence of MASQUE applications. 117 | 118 | 119 | # MASQUE Applications {#applications} 120 | 121 | When the MASQUE server accepts the client's MASQUE initial request, it 122 | advertises support for MASQUE Applications, which will be multiplexed over this 123 | HTTP/3 connection. 124 | 125 | 126 | ## HTTP Proxy {#http-proxy} 127 | 128 | The client can make proxied HTTP requests through the server to other 129 | servers. In practice this will mean using the HTTP CONNECT method to establish 130 | a stream over which to run TLS to a different remote destination. The proxy 131 | applies back-pressure to streams in both directions. 132 | 133 | 134 | ### HTTP Proxy Negotiation {#http-proxy-negotiation} 135 | 136 | Use of the HTTP Proxying MASQUE application is negotiated by sending the 137 | `http_proxying` (type 0x00) type-length-value during MASQUE negotiation. 138 | The length MUST be zero. 139 | 140 | 141 | ## DNS over HTTPS {#doh} 142 | 143 | The client can send DNS queries using DNS over HTTPS {{!DOH=RFC8484}} to the 144 | MASQUE server. 145 | 146 | 147 | ### DNS over HTTPS Negotiation {#doh-negotiation} 148 | 149 | Use of the DNS over HTTPS MASQUE application is negotiated by sending the 150 | `dns_over_https` (type 0x01) type-length-value during MASQUE negotiation. 151 | When sent by the client, the length MUST be zero. When sent by the server, 152 | the value contains the DoH URI Template encoded as a non-null-terminated 153 | UTF-8 string. 154 | 155 | 156 | ## QUIC Proxying {#quic-proxy} 157 | 158 | By leveraging QUIC client connection IDs, a MASQUE server can act as a QUIC 159 | proxy while only using one UDP port. To allow this, the MASQUE server informs 160 | the client of a required client connection ID length during negotiation. The 161 | client is then able to send proxied packets to the MASQUE server who will 162 | forward them on to the desired IP address and UDP port. Return packets are 163 | similarly forwarded in the opposite direction. 164 | 165 | Compared to UDP proxying, this mode has the advantage of only requiring one UDP 166 | port to be open on the MASQUE server, and can lower the overhead on the link 167 | between client and MASQUE server by compressing connection IDs. 168 | 169 | 170 | ### QUIC Proxy Compression {#quic-proxy-compression} 171 | 172 | To reduce the overhead of proxying, QUIC Proxying leverages compression to 173 | elide the connection IDs on the link between the client and MASQUE server. 174 | This uses the concept of a compression context. Compression contexts are 175 | indexed using a datagram flow identifiers 176 | {{!H3DGRAM=I-D.schinazi-quic-h3-datagram}}, and contain the tuple 177 | (client connection ID, server connection ID, server IP address, server port). 178 | 179 | Any time and endpoint wants to send a proxied packet to its peer, it searches 180 | its list of compression contexts looking for one that matches the address, port 181 | and connection IDs from the proxied packet. If there was no match, the endpoint 182 | creates a new compression context and adds it to the list. 183 | 184 | Compression contexts also carry a boolean value representing whether the 185 | context has been validated, which means that this endpoint is confident 186 | that its peer is aware if the given compression context. Compression contexts 187 | that were created by the peer start off validated, whereas locally-created 188 | ones are not validated until the endpoint receives a packet using that 189 | compression context, or an acknowledgement for a sent packet that uses the 190 | context. 191 | 192 | The DATAGRAM frame {{!DGRAM=I-D.ietf-quic-datagram}} format below allows both 193 | endpoints to immediately start sending proxied QUIC packets using unvalidated 194 | compression contexts. Once contexts are vaidated, the server IP address, 195 | server port and the connection IDs can be ommited. 196 | 197 | TODO: garbage collect obsolete compression contexts. 198 | 199 | 200 | ### QUIC Proxy Negotiation {#quic-proxy-negotiation} 201 | 202 | Use of the QUIC Proxying MASQUE application is negotiated by sending the 203 | `quic_proxying` (type 0x02) type-length-value during MASQUE negotiation. 204 | 205 | When sent by the client, the value contains a single variable-length 206 | integer, called `new_quic_proxy_compression_context_flow_id`, that represents 207 | the DATAGRAM flow ID used to negotiate compression contexts. 208 | 209 | When sent by the server, the value contains two variable-length integers, 210 | the DATAGRAM flow ID used to negotiate compression contexts (called 211 | `new_quic_proxy_compression_context_flow_id`), followed by the required 212 | connection ID length. 213 | 214 | 215 | ### QUIC Proxy Encoding {#quic-proxy-encoding} 216 | 217 | Once negotiated, the QUIC Proxying MASQUE Application only uses DATAGRAM 218 | frames, whose content is shown in {{fig-quic-proxy-dgram-unval}} and 219 | {{fig-quic-proxy-dgram-val}}. 220 | 221 | ~~~ 222 | 0 1 2 3 223 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 224 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 225 | | Flow Identifier (i) ... 226 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 227 | | New Compression Context ID (i) ... 228 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 229 | | CCIDL (8) | | 230 | +-+-+-+-+-+-+-+-+ Client Connection ID (0..160) + 231 | | ... 232 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 233 | | SCIDL (8) | | 234 | +-+-+-+-+-+-+-+-+ Server Connection ID (0..160) + 235 | | ... 236 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 237 | | Server Port Number (16) | 238 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 239 | | Family (8) | 240 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 241 | + Server IP Address (32/128) ... 242 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 243 | | Byte 1 (8) | 244 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 245 | + [Version (32)] | 246 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 247 | + QUIC Payload (*) ... 248 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 249 | ~~~ 250 | {: #fig-quic-proxy-dgram-unval title="Unvalidated QUIC Proxy Datagram"} 251 | 252 | Unvalidated QUIC Proxy DATAGRAM frames contain the following fields: 253 | 254 | Flow Identifier: 255 | 256 | : The flow identifier represents the compression context used 257 | by this frame. Unvalidated compression contexts use the 258 | `new_quic_proxy_compression_context_flow_id` value received during 259 | negotiation. 260 | 261 | New Compression Context ID: 262 | 263 | : The new Compression Context ID that this frame uses. The 264 | connection IDs and server IP address family, address, and port 265 | are associated with this context. 266 | 267 | CCIDL: 268 | 269 | : Length of the Client Connection ID field. 270 | 271 | Client Connection ID: 272 | 273 | : The client connection ID associated with this compression context. 274 | 275 | SCIDL: 276 | 277 | : Length of the Server Connection ID field. 278 | 279 | Server Connection ID: 280 | 281 | : The server connection ID associated with this compression context. 282 | 283 | Server Port Number: 284 | 285 | : The UDP port number used by the server. 286 | 287 | Family: 288 | 289 | : The IP address family of the Server IP Address field. Can be either 290 | 4 or 6. 291 | 292 | Server IP Address: 293 | 294 | : The IP address of the server. This field is 32 bits long if Family is 4 295 | and 128 bits long if Family is 6. 296 | 297 | Byte 1: 298 | 299 | : The first byte of the QUIC packet being transferred. 300 | 301 | Version: 302 | 303 | : The QUIC version in the long header of the QUIC packet being transferred. 304 | This field is present if and only if the first bit of the Byte 1 field is set. 305 | 306 | QUIC Payload: 307 | 308 | : The contents of the QUIC packet being transmitted, starting after the last 309 | byte of last connection ID field. 310 | 311 | ~~~ 312 | 0 1 2 3 313 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 314 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 315 | | Flow Identifier (i) ... 316 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 317 | | Byte 1 (8) | 318 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 319 | + [Version (32)] | 320 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 321 | + QUIC Payload (*) ... 322 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 323 | ~~~ 324 | {: #fig-quic-proxy-dgram-val title="Validated QUIC Proxy Datagram"} 325 | 326 | Validated QUIC Proxy DATAGRAM frames contain the following fields: 327 | 328 | Flow Identifier: 329 | 330 | : The flow identifier represents the compression context used 331 | by this frame. Validated compression contexts MUST NOT use the 332 | `new_quic_proxy_compression_context_flow_id` value received during 333 | negotiation. 334 | 335 | Byte 1: 336 | 337 | : The first byte of the QUIC packet being transferred. 338 | 339 | Version: 340 | 341 | : The QUIC version in the long header of the QUIC packet being transferred. 342 | This field is present if and only if the first bit of the Byte 1 field is set. 343 | 344 | QUIC Payload: 345 | 346 | : The contents of the QUIC packet being transmitted, starting after the last 347 | byte of last connection ID field. 348 | 349 | 350 | ### QUIC Proxy Routing {#quic-proxy-routing} 351 | 352 | A MASQUE server keeps a mapping from client connection IDs to MASQUE clients, 353 | so that it can correctly route return packets. When a MASQUE server receives a 354 | QUIC Proxy DATAGRAM frame, it forwards it to the IP address and UDP port from 355 | the corresponding compression context. Additionally, the MASQUE server will 356 | ensure that the client connection ID from that compression context maps to 357 | that MASQUE client. 358 | 359 | TODO: garbage collect this mapping from client connection ID to MASQUE client. 360 | 361 | 362 | ## UDP Proxying {#udp-proxy} 363 | 364 | In order to support WebRTC to further servers, clients need a way to 365 | relay UDP onwards to a remote server. In practice for most widely deployed 366 | protocols other than DNS, this involves many datagrams over the same ports. 367 | Therefore this mechanism can compress the server's IP address and UDP port 368 | to reduce overhead. 369 | 370 | 371 | ### UDP Proxy Compression {#udp-proxy-compression} 372 | 373 | UDP Proxy leverages compression similarly to QUIC proxying, except that it 374 | only compresses the IP address and port, not QUIC connection IDs. 375 | 376 | 377 | ### UDP Proxy Negotiation {#udp-proxy-negotiation} 378 | 379 | Use of the UDP Proxying MASQUE application is negotiated by sending the 380 | `udp_proxying` (type 0x03) type-length-value during MASQUE negotiation. 381 | 382 | The value contains a single variable-length integer, called 383 | `new_udp_proxy_compression_context_flow_id`, that represents 384 | the DATAGRAM flow ID used to negotiate compression contexts. 385 | 386 | 387 | ### UDP Proxy Encoding {#udp-proxy-encoding} 388 | 389 | Once negotiated, the UDP Proxying MASQUE Application only uses DATAGRAM 390 | frames, whose content is shown in {{fig-udp-proxy-dgram-unval}} and 391 | {{fig-udp-proxy-dgram-val}}. 392 | 393 | ~~~ 394 | 0 1 2 3 395 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 396 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 397 | | Flow Identifier (i) ... 398 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 399 | | New Compression Context ID (i) ... 400 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 401 | | Server Port Number (16) | 402 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 403 | | Family (8) | 404 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 405 | + Server IP Address (32/128) ... 406 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 407 | + UDP Payload (*) ... 408 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 409 | ~~~ 410 | {: #fig-udp-proxy-dgram-unval title="Unvalidated UDP Proxy Datagram"} 411 | 412 | Unvalidated UDP Proxy DATAGRAM frames contain the following fields: 413 | 414 | Flow Identifier: 415 | 416 | : The flow identifier represents the compression context used 417 | by this frame. Unvalidated compression contexts use the 418 | `new_udp_proxy_compression_context_flow_id` value received during 419 | negotiation. 420 | 421 | New Compression Context ID: 422 | 423 | : The new Compression Context ID that this frame uses. The 424 | server IP address family, address, and port are associated with this context. 425 | 426 | Server Port Number: 427 | 428 | : The UDP port number used by the server. 429 | 430 | Family: 431 | 432 | : The IP address family of the Server IP Address field. Can be either 433 | 4 or 6. 434 | 435 | Server IP Address: 436 | 437 | : The IP address of the server. This field is 32 bits long if Family is 4 438 | and 128 bits long if Family is 6. 439 | 440 | UDP Payload: 441 | 442 | : The contents of the UDP packet being transmitted, starting after the last 443 | byte of the UDP checksum field. 444 | 445 | ~~~ 446 | 0 1 2 3 447 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 448 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 449 | | Flow Identifier (i) ... 450 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 451 | + UDP Payload (*) ... 452 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 453 | ~~~ 454 | {: #fig-udp-proxy-dgram-val title="Validated UDP Proxy Datagram"} 455 | 456 | Validated UDP Proxy DATAGRAM frames contain the following fields: 457 | 458 | Flow Identifier: 459 | 460 | : The flow identifier represents the compression context used 461 | by this frame. Validated compression contexts MUST NOT use the 462 | `new_udp_proxy_compression_context_flow_id` value received during 463 | negotiation. 464 | 465 | UDP Payload: 466 | 467 | : The contents of the UDP packet being transmitted, starting after the last 468 | byte of the UDP checksum field. 469 | 470 | 471 | ## IP Proxying {#ip-proxy} 472 | 473 | For the rare cases where the previous mechanisms are not sufficient, proxying 474 | can be performed at the IP layer. This would use a different DATAGRAM_ID and 475 | IP datagrams would be encoded inside it without framing. 476 | 477 | 478 | ### IP Proxy Negotiation {#ip-proxy-negotiation} 479 | 480 | Use of the IP Proxying MASQUE application is negotiated by sending the 481 | `ip_proxying` (type 0x04) type-length-value during MASQUE negotiation. 482 | 483 | The value contains a single variable-length integer, called 484 | `ip_proxy_flow_id`, that represents the DATAGRAM flow ID used by IP Proxying 485 | DATAGRAM frames. 486 | 487 | 488 | ### IP Proxy Encoding {#ip-proxy-encoding} 489 | 490 | Once negotiated, the IP Proxying MASQUE Application only uses DATAGRAM 491 | frames, whose content is shown in {{fig-ip-proxy-dgram}}. 492 | 493 | ~~~ 494 | 0 1 2 3 495 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 496 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 497 | | Flow Identifier (i) ... 498 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 499 | + IP Packet (*) ... 500 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 501 | ~~~ 502 | {: #fig-ip-proxy-dgram title="IP Proxy Datagram"} 503 | 504 | IP Proxy DATAGRAM frames contain the following fields: 505 | 506 | Flow Identifier: 507 | 508 | : The flow identifier MUST be set to the `ip_proxy_flow_id` value received 509 | during negotiation. 510 | 511 | IP Packet: 512 | 513 | : The full IP packet, starting from the IP Version field. 514 | 515 | 516 | ## Service Registration {#service-registration} 517 | 518 | MASQUE can be used to make a home server accessible on the wide area. The home 519 | server authenticates to the MASQUE server and registers a domain name it wishes 520 | to serve. The MASQUE server can then forward any traffic it receives for that 521 | domain name (by inspecting the TLS Server Name Indication (SNI) extension) to 522 | the home server. This received traffic is not authenticated and it allows 523 | non-modified clients to communicate with the home server without knowing it is 524 | not colocated with the MASQUE server. 525 | 526 | To help obfuscate the home server, deployments can use Encrypted Server Name 527 | Indication {{?ESNI=I-D.ietf-tls-esni}}. That will require the MASQUE server 528 | sending the cleartext SNI to the home server. 529 | 530 | TODO: define the wire format for Service Registration. 531 | 532 | 533 | # Security Considerations {#security} 534 | 535 | Here be dragons. TODO: slay the dragons. 536 | 537 | 538 | # IANA Considerations {#iana} 539 | 540 | ## MASQUE Well-Known URI {#iana-uri} 541 | 542 | This document will request IANA to register the "/.well-known/masque/" URI 543 | (expert review) 544 | . 545 | 546 | 547 | ## MASQUE Applications Registry {#iana-apps} 548 | 549 | This document will request IANA to create a new MASQUE Applications registry 550 | which governs a 62-bit space of MASQUE application types. This registry 551 | follows the same registration policy as the QUIC Transport Parameter Registry; 552 | see the IANA Considerations section of {{QUIC}}. 553 | 554 | The initial contents of this registry are shown in {{iana-apps-table}}: 555 | 556 | | Value| Parameter Name | Specification | 557 | |:-----|:----------------------------|:------------------------------------| 558 | | 0x00 | http_proxying | {{http-proxy}} | 559 | | 0x01 | dns_over_https | {{doh}} | 560 | | 0x02 | quic_proxying | {{quic-proxy}} | 561 | | 0x03 | udp_proxying | {{udp-proxy}} | 562 | | 0x04 | ip_proxying | {{ip-proxy}} | 563 | {: #iana-apps-table title="Initial MASQUE Applications Entries"} 564 | 565 | 566 | --- back 567 | 568 | # Acknowledgments {#acknowledgments} 569 | {:numbered="false"} 570 | 571 | This proposal was inspired directly or indirectly by prior work from many 572 | people. The author would like to thank 573 | Nick Harper, 574 | Christian Huitema, 575 | Marcus Ihlar, 576 | Eric Kinnear, 577 | Mirja Kuehlewind, 578 | Brendan Moran, 579 | Lucas Pardue, 580 | Tommy Pauly, 581 | Zaheduzzaman Sarker, 582 | Ben Schwartz, 583 | and 584 | Christopher A. Wood 585 | for their input. 586 | 587 | -------------------------------------------------------------------------------- /draft-schinazi-masque-proxy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The MASQUE Proxy 3 | docname: draft-schinazi-masque-proxy-latest 4 | submissiontype: independent 5 | number: 6 | date: 7 | v: 3 8 | category: info 9 | keyword: 10 | - masque 11 | - proxy 12 | - kuzh 13 | venue: 14 | repo: https://github.com/DavidSchinazi/masque-drafts 15 | latest: https://davidschinazi.github.io/masque-drafts/draft-schinazi-masque-proxy.html 16 | author: 17 | - 18 | ins: D. Schinazi 19 | name: David Schinazi 20 | org: Google LLC 21 | street: 1600 Amphitheatre Parkway 22 | city: Mountain View 23 | region: CA 24 | code: 94043 25 | country: United States of America 26 | email: dschinazi.ietf@gmail.com 27 | informative: 28 | H3: 29 | =: RFC9114 30 | display: HTTP/3 31 | TODO: 32 | title: find that 20 year old email about using nested CONNECT tunnels with SSL to improve privacy 33 | date: false 34 | 35 | --- abstract 36 | 37 | MASQUE (Multiplexed Application Substrate over QUIC Encryption) is a set of 38 | protocols and extensions to HTTP that allow proxying all kinds of Internet 39 | traffic over HTTP. This document defines the concept of a "MASQUE Proxy", an 40 | Internet-accessible node that can relay client traffic in order to provide 41 | privacy guarantees. 42 | 43 | --- middle 44 | 45 | # Introduction 46 | 47 | In the early days of HTTP, requests and responses weren't encrypted. In order 48 | to add features such as caching, HTTP proxies were developed to parse HTTP 49 | requests from clients and forward them on to other HTTP servers. As SSL/TLS 50 | became more common, the CONNECT method was introduced {{?CONNECT=RFC2817}} to 51 | allow proxying SSL/TLS over HTTP. That gave HTTP the ability to create tunnels 52 | that allow proxying any TCP-based protocol. While non-TCP-based protocols were 53 | always prevalent on the Internet, the large-scale deployment of QUIC 54 | {{?QUIC=RFC9000}} meant that TCP no longer represented the majority of Internet 55 | traffic. Simultaneously, the creation of HTTP/3 {{H3}} allowed running HTTP 56 | over a non-TCP-based protocol. In particular, QUIC allows disabling loss 57 | recovery {{?DGRAM=RFC9221}} and that can then be used in HTTP 58 | {{?HTTP-DGRAM=RFC9297}}. This confluence of events created both the possibility 59 | and the necessity for new proxying technologies in HTTP. 60 | 61 | This led to the creation of MASQUE (Multiplexed Application Substrate over QUIC 62 | Encryption). MASQUE allows proxying both UDP ({{?CONNECT-UDP=RFC9298}}) and IP 63 | ({{?CONNECT-IP=RFC9484}}) over HTTP. While MASQUE has uses beyond improving 64 | user privacy, its focus and design are best suited for protecting sensitive 65 | information. 66 | 67 | # Privacy Protections 68 | 69 | There are currently multiple usage scenarios that can benefit from using a 70 | MASQUE Proxy. 71 | 72 | ## Protection from Web Servers 73 | 74 | Connecting directly to Web servers allows them to access the public IP address 75 | of the user. There are many privacy concerns relating to user IP addresses 76 | {{?IP-PRIVACY=I-D.irtf-pearg-ip-address-privacy-considerations}}. Because of 77 | these, many user agents would rather not establish a direct connection to Web 78 | servers. They can do that by running their traffic through a MASQUE Proxy. The 79 | Web server will only see the IP address of the MASQUE Proxy, not that of the 80 | client. 81 | 82 | ## Protection from Network Providers 83 | 84 | Some users may wish to obfuscate the destination of their network traffic from 85 | their network provider. This prevents network providers from using data 86 | harvested from this network traffic in ways the user did not intend. 87 | 88 | ## Partitioning 89 | 90 | While routing traffic through a MASQUE proxy reduces the network provider's 91 | ability to observe traffic, that information is transfered to the proxy 92 | operator. This can be suitable for some threat models, but for the majority of 93 | users transferring trust from their network provider to their proxy (or VPN) 94 | provider is not a meaningful security improvement. 95 | 96 | There is a technical solution that allows resolving this issue: it is possible 97 | to nest MASQUE tunnels such that traffic flows through multiple MASQUE proxies. 98 | This has the advantage of partitioning sensitive information to prevent 99 | correlation {{?PARTITION=RFC9614}}. 100 | 101 | Though the idea of nested tunnels dates back decades {{TODO}}, MASQUE now 102 | allows running HTTP/3 end-to-end from a user agent to an origin via multiple 103 | nested CONNECT-UDP tunnels. The proxy closest to the user can see the user's IP 104 | address but not the origin, whereas the other proxy can see the origin without 105 | knowing the user's IP address. If the two proxies are operated by non-colluding 106 | entities, this allows hiding the user's IP address from the origin without the 107 | proxies knowing the user's browsing history. 108 | 109 | ## Obfuscation 110 | 111 | The fact that MASQUE is layered over HTTP makes it much more resilient to 112 | detection. To network observers, the unencrypted bits in a QUIC connection used 113 | for MASQUE are indistinguishable from those of a regular Web browsing 114 | connection. Separately, if paired with a non-probeable HTTP authentication 115 | scheme {{?CONCEALED-AUTH=RFC9729}}, any Web server can also become a MASQUE 116 | proxy while remaining indistinguishable from a regular Web server. This defeats 117 | detection tools that operate solely on packet formats. 118 | 119 | However, it is still possible to perform statitiscal analyses on the encrypted 120 | data. There exist commercially available products that are able to identify 121 | visited websites based solely on the timing and size of encrypted packets. 122 | While MASQUE increases the cost of such traffic analysis efforts, it doesn't 123 | prevent them from being used at scale. 124 | 125 | MASQUE implementations can leverage the ability for HTTP/2, HTTP/3, TLS, or 126 | QUIC to introduce padding inside the encryption. That enables many defensive 127 | strategies such as ensuring that all packets are the same size, or introducing 128 | variable amounts of cover traffic. From a theoretical perspective, sending data 129 | at a constant bitrate is the only way to fully prevent statistical analysis, 130 | but it likely introduces too much overhead to be deployable at scale. Finding 131 | padding strategies that balance resistance against statistical analysis with 132 | overheads remains an open research problem. 133 | 134 | # Related Technologies 135 | 136 | This section discusses how MASQUE fits in with other contemporary 137 | privacy-focused IETF protocols. 138 | 139 | ## OHTTP 140 | 141 | Oblivious HTTP {{?OHTTP=RFC9458}} uses a cryptographic primitive 142 | {{?HPKE=RFC9180}} that is more lightweight than TLS {{?TLS=RFC8446}}, making it 143 | a great fit for decorrelating HTTP requests. In traditional Web browsing, the 144 | user agent will often make many requests to the same origin (e.g., to load 145 | HTML, style sheets, images, scripts) and those requests are correlatable since 146 | the origin can include identifying query parameters to join separate requests. 147 | In such scenarios, MASQUE is a better fit since it operates at the granularity 148 | of a connection. However, there are scenarios where a user agent might want to 149 | make non-correlatable requests (e.g., to anonymously report telemetry); for 150 | those, OHTTP provides better efficiency than using MASQUE with a separate 151 | connection per request. While OHTTP and MASQUE are separate technologies that 152 | serve different use cases, they can be colocated on the same HTTP server that 153 | acts as both a MASQUE Proxy and an OHTTP Relay. 154 | 155 | ## DoH 156 | 157 | DNS over HTTPS {{?DoH=RFC8484}} allows encrypting DNS traffic by sending it 158 | through an encrypted HTTP connection. Colocating a DoH server with a MASQUE IP 159 | proxy provides better performance than using DNS over port 53 inside the 160 | encrypted tunnel. 161 | 162 | # Security Considerations 163 | 164 | Implementers of a MASQUE proxy need to review the Security Considerations of 165 | the documents referenced by this one. 166 | 167 | # IANA Considerations 168 | 169 | This document has no IANA actions. 170 | 171 | --- back 172 | 173 | # Acknowledgments 174 | {:numbered="false"} 175 | 176 | MASQUE was originally inspired directly or indirectly by prior work from many 177 | people. The author would like to thank {{{Nick Harper}}}, 178 | {{{Christian Huitema}}}, {{{Marcus Ihlar}}}, {{{Eric Kinnear}}}, 179 | {{{Mirja Kuehlewind}}}, {{{Brendan Moran}}}, {{{Lucas Pardue}}}, 180 | {{{Tommy Pauly}}}, {{{Zaheduzzaman Sarker}}} and {{{Ben Schwartz}}} for their 181 | input. 182 | 183 | In particular, the probing resistance component of MASQUE came from a 184 | conversation with {{{Chris A. Wood}}} as we were preparing a draft for an 185 | upcoming Thursday evening BoF. 186 | 187 | All of the MASQUE enthusiasts and other contributors to the MASQUE working 188 | group are to thank for the successful standardization of {{HTTP-DGRAM}}, 189 | {{CONNECT-UDP}}, and {{CONNECT-IP}}. 190 | 191 | The author would like to express immense gratitude to Christophe A., an 192 | inspiration and true leader of VPNs. 193 | --------------------------------------------------------------------------------