Use tun mode without running with sudo in linux

pull/6079/head
2dust 2024-11-13 16:22:56 +08:00
parent f921ff7d77
commit 9d638968a9
15 changed files with 190 additions and 19 deletions

View File

@ -68,6 +68,7 @@ namespace ServiceLib.Handler
{ {
ShowMsg(true, $"{node.GetSummary()}"); ShowMsg(true, $"{node.GetSummary()}");
await CoreStop(); await CoreStop();
await Task.Delay(100);
await CoreStart(node); await CoreStart(node);
//In tun mode, do a delay check and restart the core //In tun mode, do a delay check and restart the core
@ -169,21 +170,12 @@ namespace ServiceLib.Handler
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
//ECoreType coreType;
//if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
//{
// coreType = ECoreType.sing_box;
//}
//else
//{
// coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
//}
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
_config.RunningCoreType = coreType; _config.RunningCoreType = coreType;
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog); var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
if (proc is null) if (proc is null)
{ {
return; return;
@ -225,7 +217,7 @@ namespace ServiceLib.Handler
if (result.Success) if (result.Success)
{ {
var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true); var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true, true);
if (proc2 is not null) if (proc2 is not null)
{ {
_processPre = proc2; _processPre = proc2;
@ -243,7 +235,7 @@ namespace ServiceLib.Handler
try try
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true); var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
if (proc is null) if (proc is null)
{ {
return -1; return -1;
@ -264,17 +256,28 @@ namespace ServiceLib.Handler
_updateFunc?.Invoke(notify, msg); _updateFunc?.Invoke(notify, msg);
} }
private bool IsNeedSudo(ECoreType eCoreType)
{
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& Utils.IsLinux()
&& _config.TunModeItem.LinuxSudoPassword.IsNotEmpty()
;
}
#endregion Private #endregion Private
#region Process #region Process
private async Task<Process?> RunProcess(CoreInfo coreInfo, string configPath, bool displayLog) private async Task<Process?> RunProcess(CoreInfo coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{ {
var fileName = CoreFindExe(coreInfo); var fileName = CoreFindExe(coreInfo);
if (Utils.IsNullOrEmpty(fileName)) if (Utils.IsNullOrEmpty(fileName))
{ {
return null; return null;
} }
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
try try
{ {
Process proc = new() Process proc = new()
@ -292,6 +295,15 @@ namespace ServiceLib.Handler
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
} }
}; };
if (isNeedSudo)
{
proc.StartInfo.FileName = $"/bin/sudo";
proc.StartInfo.Arguments = $"-S {fileName} {string.Format(coreInfo.Arguments, configPath)}";
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
var startUpErrorMessage = new StringBuilder(); var startUpErrorMessage = new StringBuilder();
var startUpSuccessful = false; var startUpSuccessful = false;
if (displayLog) if (displayLog)
@ -313,6 +325,15 @@ namespace ServiceLib.Handler
}; };
} }
proc.Start(); proc.Start();
if (isNeedSudo)
{
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword);
}
if (displayLog) if (displayLog)
{ {
proc.BeginOutputReadLine(); proc.BeginOutputReadLine();

View File

@ -161,6 +161,7 @@
public int Mtu { get; set; } public int Mtu { get; set; }
public bool EnableExInbound { get; set; } public bool EnableExInbound { get; set; }
public bool EnableIPv6Address { get; set; } public bool EnableIPv6Address { get; set; }
public string? LinuxSudoPassword { get; set; }
} }
[Serializable] [Serializable]

View File

@ -3148,6 +3148,42 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Linux system sudo password 的本地化字符串。
/// </summary>
public static string TbSettingsLinuxSudoPassword {
get {
return ResourceManager.GetString("TbSettingsLinuxSudoPassword", resourceCulture);
}
}
/// <summary>
/// 查找类似 Please set the sudo password in Tun mode settings first 的本地化字符串。
/// </summary>
public static string TbSettingsLinuxSudoPasswordIsEmpty {
get {
return ResourceManager.GetString("TbSettingsLinuxSudoPasswordIsEmpty", resourceCulture);
}
}
/// <summary>
/// 查找类似 Please do not run this app with sudo 的本地化字符串。
/// </summary>
public static string TbSettingsLinuxSudoPasswordNotSudoRunApp {
get {
return ResourceManager.GetString("TbSettingsLinuxSudoPasswordNotSudoRunApp", resourceCulture);
}
}
/// <summary>
/// 查找类似 The password will only be stored in the local file. 的本地化字符串。
/// </summary>
public static string TbSettingsLinuxSudoPasswordTip {
get {
return ResourceManager.GetString("TbSettingsLinuxSudoPasswordTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Enable Log 的本地化字符串。 /// 查找类似 Enable Log 的本地化字符串。
/// </summary> /// </summary>

View File

@ -1369,4 +1369,16 @@
<data name="TbRuleMatchingTips" xml:space="preserve"> <data name="TbRuleMatchingTips" xml:space="preserve">
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value> <value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password will only be stored in the local file.</value>
</data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value>
</data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>Please do not run this app with sudo</value>
</data>
</root> </root>

View File

@ -1369,4 +1369,16 @@
<data name="LvMemo" xml:space="preserve"> <data name="LvMemo" xml:space="preserve">
<value>Remarks Memo</value> <value>Remarks Memo</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password will only be stored in the local file.</value>
</data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value>
</data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>Please do not run this app with sudo</value>
</data>
</root> </root>

View File

@ -1369,4 +1369,16 @@
<data name="menuRemoteBackupAndRestore" xml:space="preserve"> <data name="menuRemoteBackupAndRestore" xml:space="preserve">
<value>Remote (WebDAV)</value> <value>Remote (WebDAV)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password will only be stored in the local file.</value>
</data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value>
</data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>Please do not run this app with sudo</value>
</data>
</root> </root>

View File

@ -1366,4 +1366,16 @@
<data name="LvMemo" xml:space="preserve"> <data name="LvMemo" xml:space="preserve">
<value>备注备忘</value> <value>备注备忘</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux系统的sudo密码</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密码只会存储在本地文件中没有密码无法开启Tun</value>
</data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>请先在Tun模式设置中设置sudo密码</value>
</data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>请不要用sudo运行本app</value>
</data>
</root> </root>

View File

@ -1366,4 +1366,16 @@
<data name="TbPath7" xml:space="preserve"> <data name="TbPath7" xml:space="preserve">
<value>混淆密碼(obfs password)</value> <value>混淆密碼(obfs password)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux系統的sudo密碼</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密碼只會儲存在本機檔案中沒有密碼無法開啟Tun</value>
</data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>請先在Tun模式設定中設定sudo密碼</value>
</data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>請不要用sudo來運行本app</value>
</data>
</root> </root>

View File

@ -14,7 +14,7 @@
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" /> <PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="Splat.NLog" Version="15.2.22" /> <PackageReference Include="Splat.NLog" Version="15.2.22" />
<PackageReference Include="WebDav.Client" Version="2.8.0" /> <PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="16.1.3" /> <PackageReference Include="YamlDotNet" Version="16.2.0" />
<PackageReference Include="QRCoder" Version="1.6.0" /> <PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="CliWrap" Version="3.6.7" /> <PackageReference Include="CliWrap" Version="3.6.7" />
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" /> <PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />

View File

@ -85,6 +85,7 @@ namespace ServiceLib.ViewModels
[Reactive] public int TunMtu { get; set; } [Reactive] public int TunMtu { get; set; }
[Reactive] public bool TunEnableExInbound { get; set; } [Reactive] public bool TunEnableExInbound { get; set; }
[Reactive] public bool TunEnableIPv6Address { get; set; } [Reactive] public bool TunEnableIPv6Address { get; set; }
[Reactive] public string TunLinuxSudoPassword { get; set; }
#endregion Tun mode #endregion Tun mode
@ -197,6 +198,7 @@ namespace ServiceLib.ViewModels
TunMtu = _config.TunModeItem.Mtu; TunMtu = _config.TunModeItem.Mtu;
TunEnableExInbound = _config.TunModeItem.EnableExInbound; TunEnableExInbound = _config.TunModeItem.EnableExInbound;
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPassword;
#endregion Tun mode #endregion Tun mode
@ -340,6 +342,7 @@ namespace ServiceLib.ViewModels
_config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.Mtu = TunMtu;
_config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableExInbound = TunEnableExInbound;
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
_config.TunModeItem.LinuxSudoPassword = TunLinuxSudoPassword;
//coreType //coreType
await SaveCoreType(); await SaveCoreType();

View File

@ -108,7 +108,7 @@ namespace ServiceLib.ViewModels
SelectedServer = new(); SelectedServer = new();
RunningServerToolTipText = "-"; RunningServerToolTipText = "-";
if (_config.TunModeItem.EnableTun && AppHandler.Instance.IsAdministrator) if (_config.TunModeItem.EnableTun && AllowEnableTun())
{ {
EnableTun = true; EnableTun = true;
} }
@ -414,10 +414,17 @@ namespace ServiceLib.ViewModels
{ {
_config.TunModeItem.EnableTun = EnableTun; _config.TunModeItem.EnableTun = EnableTun;
// When running as a non-administrator, reboot to administrator mode // When running as a non-administrator, reboot to administrator mode
if (EnableTun && !AppHandler.Instance.IsAdministrator) if (EnableTun && AllowEnableTun() == false)
{ {
_config.TunModeItem.EnableTun = false; _config.TunModeItem.EnableTun = false;
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin(); if (Utils.IsWindows())
{
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
}
else if (Utils.IsLinux())
{
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty);
}
return; return;
} }
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
@ -425,6 +432,19 @@ namespace ServiceLib.ViewModels
} }
} }
private bool AllowEnableTun()
{
if (Utils.IsWindows())
{
return AppHandler.Instance.IsAdministrator;
}
else if (Utils.IsLinux())
{
return _config.TunModeItem.LinuxSudoPassword.IsNotEmpty();
}
return false;
}
#endregion System proxy and Routings #endregion System proxy and Routings
#region UI #region UI

View File

@ -117,6 +117,11 @@ namespace v2rayN.Desktop.Views
} }
else else
{ {
if (AppHandler.Instance.IsAdministrator)
{
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
}
menuRebootAsAdmin.IsVisible = false; menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false; menuSettingsSetUWP.IsVisible = false;
menuGlobalHotkeySetting.IsVisible = false; menuGlobalHotkeySetting.IsVisible = false;
@ -282,6 +287,7 @@ namespace v2rayN.Desktop.Views
e.Cancel = true; e.Cancel = true;
ShowHideWindow(false); ShowHideWindow(false);
break; break;
case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown: case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown:
await ViewModel?.MyAppExitAsync(true); await ViewModel?.MyAppExitAsync(true);
break; break;

View File

@ -721,6 +721,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -794,6 +795,28 @@
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Classes="Margin8" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsLinuxSudoPassword}" />
<TextBox
x:Name="txtLinuxSudoPassword"
Grid.Row="7"
Grid.Column="1"
Width="200"
HorizontalAlignment="Left"
Classes="Margin8"
PasswordChar="*" />
<TextBlock
Grid.Row="7"
Grid.Column="2"
VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsLinuxSudoPasswordTip}"
TextWrapping="Wrap" />
</Grid> </Grid>
</DockPanel> </DockPanel>
</TabItem> </TabItem>

View File

@ -152,6 +152,7 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunLinuxSudoPassword, v => v.txtLinuxSudoPassword.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);

View File

@ -41,7 +41,7 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
}); });
spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator); //spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator);
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)