From 555d30721fa125bbaf80aed43c5c7b278a8f8905 Mon Sep 17 00:00:00 2001 From: Alex <256235273+alekspop26@users.noreply.github.com> Date: Fri, 8 May 2026 12:32:21 -0400 Subject: [PATCH] [VPN 2.0] BoringTun 3p dependency: Rust crate and vendoring script (#36229) [VPN 2.0] BoringTun 3p dependency: Rust crate and vendoring script. This is a multi-part change to add BoringTun dependency to the brave-core, to be used later in the VPN 2.0 architecture by a privileged helper on desktop OSes. The dependency is added to the existing VPN to ensure it actually gets built with the browser, but not yet distributed. Part 4: the actual Rust crate for BoringTun dependency, and an update script used to generate and manually update BoringTun's vendored dependencies. --- .gitignore | 2 + third_party/boringtun/.cargo/config.toml | 15 ++ third_party/boringtun/Cargo.toml | 23 +++ third_party/boringtun/src/lib.rs | 11 ++ third_party/boringtun/update.py | 212 +++++++++++++++++++++++ 5 files changed, 263 insertions(+) create mode 100644 third_party/boringtun/.cargo/config.toml create mode 100644 third_party/boringtun/Cargo.toml create mode 100644 third_party/boringtun/src/lib.rs create mode 100755 third_party/boringtun/update.py diff --git a/.gitignore b/.gitignore index 463c2345c9a..e41ad22e92a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ vendor/omaha/ node_modules/ patches/**/*.patchinfo /third_party/argon2/src +/third_party/boringtun/.cargo-home/ +/third_party/boringtun/target/ /third_party/brave-vpn-wireguard-nt-dlls/ /third_party/brave-vpn-wireguard-tunnel-dlls/ /third_party/bip39wally-core-native/ diff --git a/third_party/boringtun/.cargo/config.toml b/third_party/boringtun/.cargo/config.toml new file mode 100644 index 00000000000..8337b0d749a --- /dev/null +++ b/third_party/boringtun/.cargo/config.toml @@ -0,0 +1,15 @@ +# Cargo configuration for the BoringTun vendored build. +# +# Build environment is isolated; the build never reads +# or writes Chromium's shared Rust caches. + +[source.crates-io] +replace-with = "vendored-sources" + +[source."git+https://github.com/cloudflare/boringtun.git?rev=cdf3b24558441ff4dc62bf5992484c5457a36760"] +git = "https://github.com/cloudflare/boringtun.git" +rev = "cdf3b24558441ff4dc62bf5992484c5457a36760" +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor/" diff --git a/third_party/boringtun/Cargo.toml b/third_party/boringtun/Cargo.toml new file mode 100644 index 00000000000..921ab4ecfbc --- /dev/null +++ b/third_party/boringtun/Cargo.toml @@ -0,0 +1,23 @@ +# Copyright (c) 2026 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. +# +# This crate exists ONLY to pin BoringTun as a git dependency at a specific +# commit. We never build this crate ourselves -- the actual cdylib shipped +# to consumers is `boringtun` itself. + +[package] +name = "boringtun_pin" +version = "0.0.0" +edition = "2021" +publish = false +license = "MPL-2.0" +description = "Internal pin of cloudflare/boringtun for Brave's vendored cdylib build." + +[lib] +path = "src/lib.rs" +crate-type = ["rlib"] + +[dependencies] +boringtun = { git = "https://github.com/cloudflare/boringtun.git", rev = "cdf3b24558441ff4dc62bf5992484c5457a36760", default-features = false, features = ["ffi-bindings"] } diff --git a/third_party/boringtun/src/lib.rs b/third_party/boringtun/src/lib.rs new file mode 100644 index 00000000000..1f681c356ab --- /dev/null +++ b/third_party/boringtun/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2026 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Empty stub. This crate exists solely so Cargo accepts the manifest at +//! ../Cargo.toml as a valid package, which in turn lets us pin `boringtun` +//! as a git dependency in Cargo.lock. +//! +//! We never build this crate. The actual library shipped to consumers is +//! the `boringtun` cdylib produced by `cargo build -p boringtun --release`. diff --git a/third_party/boringtun/update.py b/third_party/boringtun/update.py new file mode 100755 index 00000000000..2f4107da2fa --- /dev/null +++ b/third_party/boringtun/update.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. +""" +Update the vendored sources under brave/third_party/boringtun/vendor/. + +Run the update after bumping the pinned BoringTun rev in Cargo.toml +(and any explicit `cargo update` you want for transitive deps). It: + + 1. Backs up reviewer-owned files (per-crate README.chromium, + the vendor .clang-format). + 2. Wipes and re-runs `cargo vendor --locked`, capturing the source + replacement snippet cargo prints. + 3. Compares the captured snippet against .cargo/config.toml so a SHA + bump that would desync the two is caught explicitly. + 4. Restores the backed-up files. + 5. Removes known-safe stray binaries. + 6. Prints a list of any files in vendor/ that are excluded by git, + which should be fixed before committing. + +The script does NOT regenerate per-crate README.chromium files. Run +create_licenses.py after this when you want that. +""" + +import difflib +import os +import shutil +import subprocess +import sys +from pathlib import Path + +# Add script directory to path for brave_chromium_utils import +_SCRIPT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', 'script') +sys.path.insert(0, os.path.abspath(_SCRIPT_DIR)) + +import brave_chromium_utils + +PRESERVE_PATTERNS = [ + 'vendor/.clang-format', + 'vendor/*/README.chromium', +] + + +def back_up_files(patterns): + """Read text files matching `patterns` into memory. + + Text-only; if a binary pattern is ever added, switch to shutil.copy2 + to a temp dir. + """ + backed_up_files = {} + for pattern in patterns: + for path in Path().glob(pattern): + print(f'Backing up: {path}') + backed_up_files[str(path)] = path.read_text(encoding='utf-8') + return backed_up_files + + +def restore_files(backed_up_files): + for path_str, content in backed_up_files.items(): + path = Path(path_str) + print(f'Restoring: {path}') + path.parent.mkdir(parents=True, exist_ok=True) + # newline='\n' keeps line endings stable across Windows re-runs. + with open(path, 'w', encoding='utf-8', newline='\n') as f: + f.write(content) + + +def clean_up_files(patterns): + for pattern in patterns: + for path in Path().glob(pattern): + print(f'Removing: {path}') + path.unlink() + + +def compare_cargo_snippet(captured: str) -> None: + """Compare cargo vendor's printed config snippet against config.toml. + + cargo vendor prints a [source.crates-io] / [source."git+..."] block + that must be present in .cargo/config.toml for offline builds to + resolve vendored sources. When the pinned git rev changes, the + "git+..." key changes, and config.toml silently goes stale unless + someone updates it. + + We don't attempt to auto-patch config.toml (its structure varies + across edits). Instead we print a unified diff and exit non-zero if + any [source...] section from the snippet isn't already present in + config.toml verbatim. A human applies the diff. + """ + config_path = Path('.cargo/config.toml') + if not config_path.is_file(): + print(f'warning: {config_path} not found; cannot verify source ' + 'replacement. Paste this snippet into it manually:\n') + print(captured) + sys.exit(1) + + current = config_path.read_text(encoding='utf-8') + + # Coarse but effective: every non-empty line of the snippet should + # appear somewhere in the current config. We look for [source...] + # section headers specifically since those are the load-bearing + # parts. + missing = [] + for line in captured.splitlines(): + stripped = line.strip() + if stripped.startswith('[source') and stripped not in current: + missing.append(stripped) + + if not missing: + return + + print('\nERROR: .cargo/config.toml is out of sync with what cargo ' + 'vendor just produced. The following [source...] stanzas are ' + 'missing from config.toml:\n') + for line in missing: + print(f' {line}') + print('\nUnified diff hint (section headers only):\n') + diff = difflib.unified_diff( + current.splitlines(keepends=True), + (current + '\n' + captured).splitlines(keepends=True), + fromfile='.cargo/config.toml (current)', + tofile='.cargo/config.toml (with snippet appended)', + n=1) + sys.stdout.writelines(diff) + print('\nUpdate .cargo/config.toml to match what cargo vendor printed, ' + 'then re-run this script.') + sys.exit(1) + + +def get_excluded_files(vendor_dir: str) -> list[str]: + """ + Returns the list of files inside `vendor` that git would exclude + on commit. + """ + result = subprocess.run( + [ + "git", + "-C", + str(vendor_dir), + "ls-files", + "--cached", + "--others", + "--ignored", + "--exclude-standard", + ], + capture_output=True, + text=True, + check=True, + ) + return result.stdout.splitlines() + + +with brave_chromium_utils.sys_path('//tools/rust'): + import update_rust + CARGO = os.path.join(update_rust.RUST_TOOLCHAIN_OUT_DIR, 'bin', + 'cargo' + ('.exe' if sys.platform == 'win32' else '')) + + +def main(): + os.chdir(os.path.dirname(os.path.realpath(__file__))) + + backed_up_files = back_up_files(PRESERVE_PATTERNS) + + # Isolated env. Mirrors build.py's isolation so re-vendor + # and re-build behave consistently. `cargo vendor` doesn't compile + # anything, but rustup's shim on PATH can still try to manage + # toolchains when invoked, so we blind it. + env = os.environ.copy() + env['CARGO_HOME'] = str(Path('.cargo-home').resolve()) + for v in ('RUSTFLAGS', 'CARGO_BUILD_RUSTFLAGS', 'CARGO_BUILD_TARGET', + 'RUSTC_WRAPPER', 'RUSTC_WORKSPACE_WRAPPER', 'RUSTUP_HOME', + 'RUSTUP_TOOLCHAIN', 'RUSTUP_DIST_SERVER', 'RUSTUP_UPDATE_ROOT'): + env.pop(v, None) + + shutil.rmtree('vendor', ignore_errors=True) + + # --locked makes lockfile drift an error rather than a silent update. + # If you're re-vendoring because you intentionally changed deps, run + # `cargo update` (or edit Cargo.toml rev) beforehand -- the new + # Cargo.lock needs to be consistent before this script runs. + print(f'Running cargo vendor...') + result = subprocess.run([CARGO, 'vendor', '--locked'], + env=env, + check=True, + capture_output=True, + text=True) + + restore_files(backed_up_files) + shutil.rmtree('.cargo-home', ignore_errors=True) + + # After restoring files and cleaning up, confirm the config is in + # sync with what vendor printed. This runs last so a config mismatch + # doesn't leave the tree half-updated -- vendor/ is fully written + # and reviewer files are restored before we check. + compare_cargo_snippet(result.stdout) + + excluded_files = get_excluded_files('vendor') + if excluded_files: + print('\nWARNING: The following files in "vendor" are excluded by git ' + 'and will not be committed. Please fix the exclusion rules ' + 'before committing changes.') + for f in excluded_files: + print(f' {f}') + print(f'Total: {len(excluded_files)} file(s).') + + print('Update complete. Now run `create_licenses.py` to re-generate ' + 'Chromium licensing information.') + + +if __name__ == '__main__': + sys.exit(main())