BrowserRole (Delphi)
用于给 Windows Chrome + Edge 写入/回滚 Local Network Access 相关企业策略,解决公网页面连接局域网/本机 WebSocket 服务时被新版本浏览器限制的问题。
功能
-
`apply <origin>`: 写入策略(Chrome + Edge)
-
`rollback`: 回滚策略(Chrome + Edge)
文件
- `ChromeRole.dpr`:控制台程序源码(你可编译成 `BrowserRole.exe`)
编译
用 Delphi 直接打开 `ChromeRole.dpr` 编译。
使用
-
以管理员权限运行(必须)
-
应用策略:
```bat
BrowserRole.exe apply https://etk.example.com
```
- 回滚策略:
```bat
BrowserRole.exe rollback
```
- 执行后重启浏览器,访问 `chrome://policy` 和 `edge://policy` 点击 `Reload policies` 验证。
写入的策略位置
Chrome:
-
`HKLM\SOFTWARE\Policies\Google\Chrome\LocalNetworkAccessRestrictionsTemporaryOptOut` = `1`
-
`HKLM\SOFTWARE\Policies\Google\Chrome\LocalNetworkAccessAllowedForUrls\1` = `<origin>`
-
`HKLM\SOFTWARE\Policies\Google\Chrome\LocalNetworkAllowedForUrls\1` = `<origin>`
Edge:
-
`HKLM\SOFTWARE\Policies\Microsoft\Edge\LocalNetworkAccessRestrictionsTemporaryOptOut` = `1`
-
`HKLM\SOFTWARE\Policies\Microsoft\Edge\LocalNetworkAccessAllowedForUrls\1` = `<origin>`
-
`HKLM\SOFTWARE\Policies\Microsoft\Edge\LocalNetworkAllowedForUrls\1` = `<origin>`
> 说明:`TemporaryOptOut` 是过渡策略,后续大版本可能移除,建议长期仍推进插件/服务端改造(例如更规范的权限与安全通信方案)。
program ChromeRole;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Win.Registry,
Winapi.Windows;
const
CHROME_POLICY_ROOT = 'SOFTWARE\Policies\Google\Chrome';
EDGE_POLICY_ROOT = 'SOFTWARE\Policies\Microsoft\Edge';
SUBKEY_LNA_ALLOWED_OLD = 'LocalNetworkAccessAllowedForUrls';
SUBKEY_LNA_ALLOWED_NEW = 'LocalNetworkAllowedForUrls';
VALUE_TEMP_OPT_OUT = 'LocalNetworkAccessRestrictionsTemporaryOptOut';
procedure WriteStatus(const S: string);
begin
Writeln(S);
end;
function SetDwordValue(const Root, Name: string; Value: Cardinal): Boolean;
var
R: TRegistry;
begin
Result := False;
R := TRegistry.Create(KEY_WRITE or KEY_WOW64_64KEY);
try
R.RootKey := HKEY_LOCAL_MACHINE;
if R.OpenKey(Root, True) then
begin
R.WriteInteger(Name, Integer(Value));
Result := True;
end;
finally
R.Free;
end;
end;
function SetStringValue(const Root, Name, Value: string): Boolean;
var
R: TRegistry;
begin
Result := False;
R := TRegistry.Create(KEY_WRITE or KEY_WOW64_64KEY);
try
R.RootKey := HKEY_LOCAL_MACHINE;
if R.OpenKey(Root, True) then
begin
R.WriteString(Name, Value);
Result := True;
end;
finally
R.Free;
end;
end;
procedure DeleteValueIfExists(const Root, Name: string);
var
R: TRegistry;
begin
R := TRegistry.Create(KEY_WRITE or KEY_WOW64_64KEY);
try
R.RootKey := HKEY_LOCAL_MACHINE;
if R.OpenKey(Root, False) then
if R.ValueExists(Name) then
R.DeleteValue(Name);
finally
R.Free;
end;
end;
procedure DeleteKeyTreeIfExists(const ParentPath, SubKeyName: string);
var
R: TRegistry;
begin
R := TRegistry.Create(KEY_WRITE or KEY_WOW64_64KEY);
try
R.RootKey := HKEY_LOCAL_MACHINE;
if R.OpenKey(ParentPath, False) then
if R.KeyExists(SubKeyName) then
R.DeleteKey(SubKeyName);
finally
R.Free;
end;
end;
procedure ApplyBrowserPolicies(const BrowserPolicyRoot, BrowserName, Origin: string);
var
KeyOld: string;
KeyNew: string;
begin
KeyOld := BrowserPolicyRoot + '\\' + SUBKEY_LNA_ALLOWED_OLD;
KeyNew := BrowserPolicyRoot + '\\' + SUBKEY_LNA_ALLOWED_NEW;
if not SetDwordValue(BrowserPolicyRoot, VALUE_TEMP_OPT_OUT, 1) then
raise Exception.CreateFmt('Failed to write %s TemporaryOptOut policy. Run as administrator.', [BrowserName]);
if not SetStringValue(KeyOld, '1', Origin) then
raise Exception.CreateFmt('Failed to write %s %s.', [BrowserName, SUBKEY_LNA_ALLOWED_OLD]);
if not SetStringValue(KeyNew, '1', Origin) then
raise Exception.CreateFmt('Failed to write %s %s.', [BrowserName, SUBKEY_LNA_ALLOWED_NEW]);
end;
procedure RollbackBrowserPolicies(const BrowserPolicyRoot: string);
begin
DeleteValueIfExists(BrowserPolicyRoot, VALUE_TEMP_OPT_OUT);
DeleteKeyTreeIfExists(BrowserPolicyRoot, SUBKEY_LNA_ALLOWED_OLD);
DeleteKeyTreeIfExists(BrowserPolicyRoot, SUBKEY_LNA_ALLOWED_NEW);
end;
procedure ApplyPolicies(const Origin: string);
begin
if Origin.Trim.IsEmpty then
raise Exception.Create('origin is empty. Example: https://etk.example.com');
ApplyBrowserPolicies(CHROME_POLICY_ROOT, 'Chrome', Origin);
ApplyBrowserPolicies(EDGE_POLICY_ROOT, 'Edge', Origin);
WriteStatus('Policies applied successfully for Chrome and Edge.');
WriteStatus('Please restart browsers and open chrome://policy / edge://policy -> Reload policies');
end;
procedure RollbackPolicies;
begin
RollbackBrowserPolicies(CHROME_POLICY_ROOT);
RollbackBrowserPolicies(EDGE_POLICY_ROOT);
WriteStatus('Policy rollback finished for Chrome and Edge.');
WriteStatus('Please restart browsers and open chrome://policy / edge://policy -> Reload policies');
end;
procedure PrintUsage;
begin
Writeln('Usage:');
Writeln(' BrowserRole.exe apply <origin>');
Writeln(' BrowserRole.exe rollback');
Writeln('');
Writeln('Examples:');
Writeln(' BrowserRole.exe apply https://etk.example.com');
Writeln(' BrowserRole.exe rollback');
end;
var
Cmd: string;
Origin: string;
begin
try
if ParamCount = 0 then
begin
PrintUsage;
ExitCode := 1;
Exit;
end;
Cmd := LowerCase(Trim(ParamStr(1)));
if Cmd = 'apply' then
begin
if ParamCount < 2 then
raise Exception.Create('Missing origin. Example: BrowserRole.exe apply https://etk.example.com');
Origin := Trim(ParamStr(2));
ApplyPolicies(Origin);
ExitCode := 0;
end
else if Cmd = 'rollback' then
begin
RollbackPolicies;
ExitCode := 0;
end
else
begin
PrintUsage;
ExitCode := 1;
end;
except
on E: Exception do
begin
Writeln('Error: ' + E.Message);
Writeln('Tip: run this program as Administrator.');
ExitCode := 2;
end;
end;
end.