@@ -138,27 +138,49 @@ try {
138138 # Continue despite firewall error
139139}
140140
141- function Grant-FirewallManagement {
142- param ([string ]$Account )
141+ # Add an ACE to a Windows service's DACL for the given account.
142+ # Used to grant non-SYSTEM accounts specific service permissions.
143+ function Add-ServiceDaclAce {
144+ param ([string ]$ServiceName , [string ]$Account , [string ]$AccessMask )
143145
144- # Grant the service account permission to add firewall rules at runtime.
145- # The agent downloads binaries (viam-server, subsystems) and creates firewall
146- # exceptions for each via netsh. netsh goes through the BFE (Base Filtering Engine)
147- # service, which enforces admin-only access by default.
148- #
149- # We add an ACE to the BFE service DACL granting the service account
150- # CCLCRPRC (connect, query status, start, read control) + WP (write property,
151- # needed to add filter rules).
152- if (-not $Silent ) { Write-Host " Granting $Account firewall management rights (BFE service)..." }
153146 $sid = (New-Object System.Security.Principal.NTAccount($Account )).Translate(
154147 [System.Security.Principal.SecurityIdentifier ]).Value
155- $currentSD = ((& sc.exe sdshow BFE) | Where-Object { $_ -match ' ^D:' })
156- $ace = " (A;;CCLCRPWPRC;;;$sid )"
148+ $currentSD = ((& sc.exe sdshow $ServiceName ) | Where-Object { $_ -match ' ^D:' })
149+ if (-not $currentSD ) {
150+ Write-Warning " Failed to read DACL for service $ServiceName "
151+ return
152+ }
153+ $ace = " (A;;$AccessMask ;;;$sid )"
154+ if ($currentSD -match [regex ]::Escape($ace )) { return } # already present
157155 $newSD = $currentSD -replace ' (S:)' , " $ace `$ 1"
158156 if ($newSD -eq $currentSD ) {
157+ # No SACL present, append to end
159158 $newSD = $currentSD + $ace
160159 }
161- & sc.exe sdset BFE $newSD | Out-Null
160+ & sc.exe sdset $ServiceName $newSD | Out-Null
161+ if ($LASTEXITCODE -ne 0 ) {
162+ Write-Warning " Failed to set DACL on service $ServiceName "
163+ }
164+ }
165+
166+ # Remove a previously-added ACE from a Windows service's DACL.
167+ function Remove-ServiceDaclAce {
168+ param ([string ]$ServiceName , [string ]$Account , [string ]$AccessMask )
169+
170+ $sid = (New-Object System.Security.Principal.NTAccount($Account )).Translate(
171+ [System.Security.Principal.SecurityIdentifier ]).Value
172+ $currentSD = ((& sc.exe sdshow $ServiceName ) | Where-Object { $_ -match ' ^D:' })
173+ if (-not $currentSD ) {
174+ Write-Warning " Failed to read DACL for service $ServiceName "
175+ return
176+ }
177+ $ace = " (A;;$AccessMask ;;;$sid )"
178+ if ($currentSD -notmatch [regex ]::Escape($ace )) { return } # not present
179+ $newSD = $currentSD -replace [regex ]::Escape($ace ), " "
180+ & sc.exe sdset $ServiceName $newSD | Out-Null
181+ if ($LASTEXITCODE -ne 0 ) {
182+ Write-Warning " Failed to set DACL on service $ServiceName "
183+ }
162184}
163185
164186# If a user account is specified, set up permissions for non-SYSTEM operation
@@ -187,7 +209,12 @@ if ($UserAccount -ne "") {
187209 # Register event log source (so the non-admin account can write events)
188210 New-EventLog - LogName Application - Source " viam-agent" - ErrorAction SilentlyContinue
189211
190- Grant-FirewallManagement - Account $UserAccount
212+ # Grant the service account permission to add firewall rules at runtime.
213+ # The agent downloads binaries (viam-server, subsystems) and creates firewall
214+ # exceptions for each via netsh, which goes through the BFE service.
215+ # CCLCRPWPRC = connect, query status, start, read control, write property
216+ if (-not $Silent ) { Write-Host " Granting $UserAccount firewall management rights (BFE service)..." }
217+ Add-ServiceDaclAce - ServiceName " BFE" - Account $UserAccount - AccessMask " CCLCRPWPRC"
191218}
192219
193220# Configure and start service
@@ -230,21 +257,11 @@ try {
230257 & sc.exe failure " viam-agent" reset= 0 actions= restart/ 5000 / restart/ 5000 / restart/ 5000 | Out-Null
231258 & sc.exe failureflag " viam-agent" 1 | Out-Null
232259
233- # If using a non-SYSTEM account, grant it permission to query/start/stop its own service.
234- # SDDL rights: LC= query status, RP= start, WP= stop, LO= interrogate, RC= read control
260+ # Grant the service account permission to query/start/stop its own service.
261+ # LCRPWPLORC = query status, start, stop, interrogate, read control
235262 if ($UserAccount -ne " " ) {
236- $sid = (New-Object System.Security.Principal.NTAccount($UserAccount )).Translate(
237- [System.Security.Principal.SecurityIdentifier ]).Value
238- $currentSD = ((& sc.exe sdshow " viam-agent" ) | Where-Object { $_ -match ' ^D:' })
239- $ace = " (A;;LCRPWPLORC;;;$sid )"
240- # Insert the new ACE before the final closing paren of the DACL
241- $newSD = $currentSD -replace ' (S:)' , " $ace `$ 1"
242- if ($newSD -eq $currentSD ) {
243- # No SACL present, append before end
244- $newSD = $currentSD + $ace
245- }
246- & sc.exe sdset " viam-agent" $newSD | Out-Null
247- if (-not $Silent ) { Write-Host " Granted $UserAccount service self-management rights" }
263+ if (-not $Silent ) { Write-Host " Granting $UserAccount service self-management rights..." }
264+ Add-ServiceDaclAce - ServiceName " viam-agent" - Account $UserAccount - AccessMask " LCRPWPLORC"
248265 }
249266
250267 # Start service
0 commit comments