Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit814b412

Browse files
fix: opt-in tailscale vpn loop prevention (#148)
Co-authored-by: Dean Sheather <dean@deansheather.com>
1 parentd7f4ac8 commit814b412

File tree

10 files changed

+62
-34
lines changed

10 files changed

+62
-34
lines changed

‎App/App.xaml.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public App()
9292

9393
services.AddSingleton<IDispatcherQueueManager>(_=>this);
9494
services.AddSingleton<IDefaultNotificationHandler>(_=>this);
95+
services.AddSingleton<ISettingsManager<CoderConnectSettings>,SettingsManager<CoderConnectSettings>>();
9596
services.AddSingleton<ICredentialBackend>(_=>
9697
newWindowsCredentialBackend(WindowsCredentialBackend.CoderCredentialsTargetName));
9798
services.AddSingleton<ICredentialManager,CredentialManager>();
@@ -120,7 +121,6 @@ public App()
120121
// FileSyncListMainPage is created by FileSyncListWindow.
121122
services.AddTransient<FileSyncListWindow>();
122123

123-
services.AddSingleton<ISettingsManager<CoderConnectSettings>,SettingsManager<CoderConnectSettings>>();
124124
services.AddSingleton<IStartupManager,StartupManager>();
125125
// SettingsWindow views and view models
126126
services.AddTransient<SettingsViewModel>();

‎App/Models/Settings.cs‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public class CoderConnectSettings : ISettings<CoderConnectSettings>
3434
/// </summary>
3535
publicboolConnectOnLaunch{get;set;}
3636

37+
/// <summary>
38+
/// When this is true Coder Connect will not attempt to protect against Tailscale loopback issues.
39+
/// </summary>
40+
publicboolEnableCorporateVpnSupport{get;set;}
41+
3742
/// <summary>
3843
/// CoderConnect current settings version. Increment this when the settings schema changes.
3944
/// In future iterations we will be able to handle migrations when the user has
@@ -46,17 +51,21 @@ public CoderConnectSettings()
4651
Version=VERSION;
4752

4853
ConnectOnLaunch=false;
54+
55+
EnableCorporateVpnSupport=false;
4956
}
5057

51-
publicCoderConnectSettings(int?version,boolconnectOnLaunch)
58+
publicCoderConnectSettings(int?version,boolconnectOnLaunch,boolenableCorporateVpnSupport)
5259
{
5360
Version=version??VERSION;
5461

5562
ConnectOnLaunch=connectOnLaunch;
63+
64+
EnableCorporateVpnSupport=enableCorporateVpnSupport;
5665
}
5766

5867
publicCoderConnectSettingsClone()
5968
{
60-
returnnewCoderConnectSettings(Version,ConnectOnLaunch);
69+
returnnewCoderConnectSettings(Version,ConnectOnLaunch,EnableCorporateVpnSupport);
6170
}
6271
}

‎App/Services/CredentialManager.cs‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,9 @@ private async Task<CredentialModel> LoadCredentialsInner(CancellationToken ct)
223223
};
224224
}
225225

226-
// Grab the lock again so we can update the state.
227-
using(await_opLock.LockAsync(ct))
226+
// Grab the lock again so we can update the state. Don't use the CT
227+
// here as it may have already been canceled.
228+
using(await_opLock.LockAsync(TimeSpan.FromSeconds(5),CancellationToken.None))
228229
{
229230
// Prevent new LoadCredentials calls from returning this task.
230231
if(_loadCts!=null)
@@ -242,11 +243,8 @@ private async Task<CredentialModel> LoadCredentialsInner(CancellationToken ct)
242243
if(latestCredsis notnull)returnlatestCreds;
243244
}
244245

245-
// If there aren't any latest credentials after a cancellation, we
246-
// most likely timed out and should throw.
247-
ct.ThrowIfCancellationRequested();
248-
249246
UpdateState(model);
247+
ct.ThrowIfCancellationRequested();
250248
returnmodel;
251249
}
252250
}

‎App/Services/RpcController.cs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,18 @@ public interface IRpcController : IAsyncDisposable
6969
publicclassRpcController:IRpcController
7070
{
7171
privatereadonlyICredentialManager_credentialManager;
72+
privatereadonlyISettingsManager<CoderConnectSettings>_settingsManager;
7273

7374
privatereadonlyRaiiSemaphoreSlim_operationLock=new(1,1);
7475
privateSpeaker<ClientMessage,ServiceMessage>?_speaker;
7576

7677
privatereadonlyRaiiSemaphoreSlim_stateLock=new(1,1);
7778
privatereadonlyRpcModel_state=new();
7879

79-
publicRpcController(ICredentialManagercredentialManager)
80+
publicRpcController(ICredentialManagercredentialManager,ISettingsManager<CoderConnectSettings>settingsManager)
8081
{
8182
_credentialManager=credentialManager;
83+
_settingsManager=settingsManager;
8284
}
8385

8486
publiceventEventHandler<RpcModel>?StateChanged;
@@ -156,6 +158,7 @@ public async Task StartVpn(CancellationToken ct = default)
156158
usingvar_=awaitAcquireOperationLockNowAsync();
157159
AssertRpcConnected();
158160

161+
varcoderConnectSettings=await_settingsManager.Read(ct);
159162
varcredentials=_credentialManager.GetCachedCredentials();
160163
if(credentials.State!=CredentialState.Valid)
161164
thrownewRpcOperationException(
@@ -175,6 +178,7 @@ public async Task StartVpn(CancellationToken ct = default)
175178
{
176179
CoderUrl=credentials.CoderUrl?.ToString(),
177180
ApiToken=credentials.ApiToken,
181+
TunnelUseSoftNetIsolation=coderConnectSettings.EnableCorporateVpnSupport,
178182
},
179183
},ct);
180184
}

‎App/ViewModels/SettingsViewModel.cs‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public partial class SettingsViewModel : ObservableObject
1313
[ObservableProperty]
1414
publicpartialboolConnectOnLaunch{get;set;}
1515

16+
[ObservableProperty]
17+
publicpartialboolDisableTailscaleLoopProtection{get;set;}
18+
1619
[ObservableProperty]
1720
publicpartialboolStartOnLoginDisabled{get;set;}
1821

@@ -31,6 +34,7 @@ public SettingsViewModel(ILogger<SettingsViewModel> logger, ISettingsManager<Cod
3134
_connectSettings=settingsManager.Read().GetAwaiter().GetResult();
3235
StartOnLogin=startupManager.IsEnabled();
3336
ConnectOnLaunch=_connectSettings.ConnectOnLaunch;
37+
DisableTailscaleLoopProtection=_connectSettings.EnableCorporateVpnSupport;
3438

3539
// Various policies can disable the "Start on login" option.
3640
// We disable the option in the UI if the policy is set.
@@ -43,6 +47,21 @@ public SettingsViewModel(ILogger<SettingsViewModel> logger, ISettingsManager<Cod
4347
}
4448
}
4549

50+
partialvoidOnDisableTailscaleLoopProtectionChanged(boololdValue,boolnewValue)
51+
{
52+
if(oldValue==newValue)
53+
return;
54+
try
55+
{
56+
_connectSettings.EnableCorporateVpnSupport=DisableTailscaleLoopProtection;
57+
_connectSettingsManager.Write(_connectSettings);
58+
}
59+
catch(Exceptionex)
60+
{
61+
_logger.LogError($"Error saving Coder Connect{nameof(DisableTailscaleLoopProtection)} settings:{ex.Message}");
62+
}
63+
}
64+
4665
partialvoidOnConnectOnLaunchChanged(boololdValue,boolnewValue)
4766
{
4867
if(oldValue==newValue)
@@ -54,7 +73,7 @@ partial void OnConnectOnLaunchChanged(bool oldValue, bool newValue)
5473
}
5574
catch(Exceptionex)
5675
{
57-
_logger.LogError($"Error saving Coder Connect settings:{ex.Message}");
76+
_logger.LogError($"Error saving Coder Connect{nameof(ConnectOnLaunch)}settings:{ex.Message}");
5877
}
5978
}
6079

‎App/Views/Pages/SettingsMainPage.xaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@
4444
>
4545
<ToggleSwitchIsOn="{x:Bind ViewModel.ConnectOnLaunch, Mode=TwoWay}" />
4646
</controls:SettingsCard>
47+
<controls:SettingsCardDescription="This setting loosens some VPN loop protection checks in Coder Connect, allowing traffic to flow to a Coder deployment behind a corporate VPN. We only recommend enabling this option if Coder Connect doesn't work with your Coder deployment behind a corporate VPN."
48+
Header="Enable support for corporate VPNs"
49+
HeaderIcon="{ui:FontIcon Glyph=&#xE705;}"
50+
>
51+
<ToggleSwitchIsOn="{x:Bind ViewModel.DisableTailscaleLoopProtection, Mode=TwoWay}" />
52+
</controls:SettingsCard>
4753
</StackPanel>
4854
</Grid>
4955
</ScrollViewer>

‎App/Views/SettingsWindow.xaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
xmlns:winuiex="using:WinUIEx"
1010
mc:Ignorable="d"
1111
Title="Coder Settings"
12-
Width="600"Height="350"
13-
MinWidth="600"MinHeight="350">
12+
Width="600"Height="500"
13+
MinWidth="600"MinHeight="500">
1414

1515
<Window.SystemBackdrop>
1616
<MicaBackdrop/>

‎App/Views/TrayWindow.xaml.cs‎

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
usingCoder.Desktop.App.Views.Pages;
66
usingCommunityToolkit.Mvvm.Input;
77
usingMicrosoft.UI;
8-
usingMicrosoft.UI.Input;
98
usingMicrosoft.UI.Windowing;
109
usingMicrosoft.UI.Xaml;
1110
usingMicrosoft.UI.Xaml.Controls;
12-
usingMicrosoft.UI.Xaml.Documents;
1311
usingMicrosoft.UI.Xaml.Media.Animation;
1412
usingSystem;
1513
usingSystem.Collections.Generic;
@@ -19,6 +17,7 @@
1917
usingWindows.Graphics;
2018
usingWindows.System;
2119
usingWindows.UI.Core;
20+
usingMicrosoft.UI.Input;
2221
usingWinRT.Interop;
2322
usingWindowActivatedEventArgs=Microsoft.UI.Xaml.WindowActivatedEventArgs;
2423

@@ -41,7 +40,6 @@ public sealed partial class TrayWindow : Window
4140

4241
privatereadonlyIRpcController_rpcController;
4342
privatereadonlyICredentialManager_credentialManager;
44-
privatereadonlyISyncSessionController_syncSessionController;
4543
privatereadonlyIUpdateController_updateController;
4644
privatereadonlyIUserNotifier_userNotifier;
4745
privatereadonlyTrayWindowLoadingPage_loadingPage;
@@ -51,15 +49,13 @@ public sealed partial class TrayWindow : Window
5149

5250
publicTrayWindow(
5351
IRpcControllerrpcController,ICredentialManagercredentialManager,
54-
ISyncSessionControllersyncSessionController,IUpdateControllerupdateController,
55-
IUserNotifieruserNotifier,
52+
IUpdateControllerupdateController,IUserNotifieruserNotifier,
5653
TrayWindowLoadingPageloadingPage,
5754
TrayWindowDisconnectedPagedisconnectedPage,TrayWindowLoginRequiredPageloginRequiredPage,
5855
TrayWindowMainPagemainPage)
5956
{
6057
_rpcController=rpcController;
6158
_credentialManager=credentialManager;
62-
_syncSessionController=syncSessionController;
6359
_updateController=updateController;
6460
_userNotifier=userNotifier;
6561
_loadingPage=loadingPage;
@@ -74,9 +70,7 @@ public TrayWindow(
7470

7571
_rpcController.StateChanged+=RpcController_StateChanged;
7672
_credentialManager.CredentialsChanged+=CredentialManager_CredentialsChanged;
77-
_syncSessionController.StateChanged+=SyncSessionController_StateChanged;
78-
SetPageByState(_rpcController.GetState(),_credentialManager.GetCachedCredentials(),
79-
_syncSessionController.GetState());
73+
SetPageByState(_rpcController.GetState(),_credentialManager.GetCachedCredentials());
8074

8175
// Setting these directly in the .xaml doesn't seem to work for whatever reason.
8276
TrayIcon.OpenCommand=Tray_OpenCommand;
@@ -127,8 +121,7 @@ public TrayWindow(
127121
};
128122
}
129123

130-
privatevoidSetPageByState(RpcModelrpcModel,CredentialModelcredentialModel,
131-
SyncSessionControllerStateModelsyncSessionModel)
124+
privatevoidSetPageByState(RpcModelrpcModel,CredentialModelcredentialModel)
132125
{
133126
if(credentialModel.State==CredentialState.Unknown)
134127
{
@@ -201,18 +194,13 @@ private void MaybeNotifyUser(RpcModel rpcModel)
201194

202195
privatevoidRpcController_StateChanged(object?_,RpcModelmodel)
203196
{
204-
SetPageByState(model,_credentialManager.GetCachedCredentials(),_syncSessionController.GetState());
197+
SetPageByState(model,_credentialManager.GetCachedCredentials());
205198
MaybeNotifyUser(model);
206199
}
207200

208201
privatevoidCredentialManager_CredentialsChanged(object?_,CredentialModelmodel)
209202
{
210-
SetPageByState(_rpcController.GetState(),model,_syncSessionController.GetState());
211-
}
212-
213-
privatevoidSyncSessionController_StateChanged(object?_,SyncSessionControllerStateModelmodel)
214-
{
215-
SetPageByState(_rpcController.GetState(),_credentialManager.GetCachedCredentials(),model);
203+
SetPageByState(_rpcController.GetState(),model);
216204
}
217205

218206
// Sadly this is necessary because Window.Content.SizeChanged doesn't

‎Tests.App/Services/CredentialManagerTest.cs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,10 @@ public async Task SetDuringLoad(CancellationToken ct)
317317
varloadTask=manager.LoadCredentials(ct);
318318
// Then fully perform a set.
319319
awaitmanager.SetCredentials(TestServerUrl,TestApiToken,ct).WaitAsync(ct);
320-
// The load should have been cancelled.
321-
Assert.ThrowsAsync<TaskCanceledException>(()=>loadTask);
320+
321+
// The load should complete with the new valid credentials
322+
varresult=awaitloadTask;
323+
Assert.That(result.State,Is.EqualTo(CredentialState.Valid));
324+
Assert.That(result.CoderUrl?.ToString(),Is.EqualTo(TestServerUrl));
322325
}
323326
}

‎Vpn.Proto/vpn.proto‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ message ServiceMessage {
6262
StartResponsestart=2;
6363
StopResponsestop=3;
6464
Statusstatus=4;// either in reply to a StatusRequest or broadcasted
65-
StartProgressstart_progress=5;// broadcasted during startup
65+
StartProgressstart_progress=5;// broadcasted during startup (used exclusively by Windows)
6666
}
6767
}
6868

@@ -214,6 +214,7 @@ message NetworkSettingsResponse {
214214
// StartResponse.
215215
messageStartRequest {
216216
int32tunnel_file_descriptor=1;
217+
booltunnel_use_soft_net_isolation=8;
217218
stringcoder_url=2;
218219
stringapi_token=3;
219220
// Additional HTTP headers added to all requests

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp