1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
<#
.SYNOPSIS
Add (or remove) the inbound Windows Firewall rules MeshBay needs.
.DESCRIPTION
Two independent things need an inbound allow, and Windows prompts
"Allow access" for each the first time it happens if there is no rule:
1. WebRTC (MeshBay.exe and meshbay-node.exe). It binds an ephemeral UDP
port per connection, and the browser always dials the node -- aioice
cannot resolve the peer's mDNS `.local` candidate to call back -- so the
node (and, during ICE connectivity checks, the client) must accept
unsolicited inbound UDP. There is no fixed port, so these rules are
scoped by PROGRAM with no LocalPort restriction, which is the Windows-
native way to say "this binary, any port it happens to bind" -- the
Linux packaging (packaging/firewall/) has to approximate the same thing
with a broad 1024-65535/udp port range because netfilter has no
equivalent program scoping.
2. LAN casting (MeshBay.exe only): the HTTP relay to a Chromecast/Smart TV
(src/cast-relay.js, fixed TCP 19550-19553) and mDNS device discovery
(src/cast-chromecast.js via bonjour-service, UDP 5353). Both scoped by
program AND port -- narrower than the WebRTC rules, since these ports
are fixed and known. Matches packaging/firewall/*/meshbay-cast.xml.
The installer runs this once, elevated, so the user answers one UAC prompt
instead of up to four dialogs spread across first use of chat, downloads
and casting. Declining the installer's offer is fine -- the dialogs are
the fallback, one per program/port combination as each is first used.
Shipped as an extraResource at <install>\resources\firewall.ps1, so it
locates the two executables from its own path and takes no arguments
beyond the action. add/remove run elevated and windowless, leaving a
trace at %TEMP%\meshbay-firewall.log; check is a plain read (no admin
needed -- Get-NetFirewallRule does not require it, only New/Remove do)
that the installer runs first, unelevated, so re-running setup with the
rules already in place skips the prompt and the UAC entirely.
.PARAMETER Action
add (default) create/replace the rules
remove delete them
check exit 0 if all rules already exist, 1 otherwise -- no admin needed
#>
[CmdletBinding()]
param(
[ValidateSet("add", "remove", "check")]
[string]$Action = "add"
)
$log = Join-Path $env:TEMP "meshbay-firewall.log"
"[{0}] {1}" -f (Get-Date -Format s), $Action | Add-Content $log
# This script sits at <install>\resources\firewall.ps1.
$resources = $PSScriptRoot
$install = Split-Path -Parent $resources
$GROUP = "MeshBay"
$client = Join-Path $install "MeshBay.exe"
$node = Join-Path $resources "node-runtime\meshbay-node.exe"
# Program-scoped, any port: the ephemeral-UDP-port problem WebRTC always has.
# Port-scoped as well: the two fixed, known ports LAN casting actually uses.
$rules = @(
@{ Name = "MeshBay"; Path = $client; Protocol = "UDP"; LocalPort = "Any" }
@{ Name = "MeshBay Node"; Path = $node; Protocol = "UDP"; LocalPort = "Any" }
@{ Name = "MeshBay Cast"; Path = $client; Protocol = "TCP"; LocalPort = "19550-19553" }
@{ Name = "MeshBay Cast Discovery"; Path = $client; Protocol = "UDP"; LocalPort = "5353" }
)
if ($Action -eq "check") {
$missing = $rules | Where-Object { -not (Get-NetFirewallRule -DisplayName $_.Name -ErrorAction SilentlyContinue) }
if ($missing) {
" check: missing $(($missing | ForEach-Object { $_.Name }) -join ', ')" | Add-Content $log
exit 1
}
" check: all rules present" | Add-Content $log
exit 0
}
$ErrorActionPreference = "Stop"
try {
foreach ($r in $rules) {
# Idempotent: clear any existing rule of this name first.
Remove-NetFirewallRule -DisplayName $r.Name -ErrorAction SilentlyContinue
if ($Action -eq "add") {
if (-not (Test-Path $r.Path)) {
" skip $($r.Name): $($r.Path) not found" | Add-Content $log
continue
}
New-NetFirewallRule -DisplayName $r.Name -Group $GROUP `
-Direction Inbound -Action Allow `
-Program $r.Path -Protocol $r.Protocol -LocalPort $r.LocalPort `
-Profile Any | Out-Null
" allowed $($r.Name) ($($r.Protocol) $($r.LocalPort), $($r.Path))" | Add-Content $log
}
else {
" removed $($r.Name)" | Add-Content $log
}
}
}
catch {
" ERROR: $_" | Add-Content $log
throw
}
|