Skip to content

Commit a13f952

Browse files
committed
fix: opt-in tailscale vpn loop prevention
1 parent efb60ca commit a13f952

File tree

6 files changed

+48
-7
lines changed

6 files changed

+48
-7
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
new WindowsCredentialBackend(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
public bool ConnectOnLaunch { get; set; }
3636

37+
/// <summary>
38+
/// When this is true Coder Connect will not attempt to protect against Tailscale loopback issues.
39+
/// </summary>
40+
public bool DisableTailscaleLoopProtection { 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+
DisableTailscaleLoopProtection = false;
4956
}
5057

51-
public CoderConnectSettings(int? version, bool connectOnLaunch)
58+
public CoderConnectSettings(int? version, bool connectOnLaunch, bool disableTailscaleLoopProtection)
5259
{
5360
Version = version ?? VERSION;
5461

5562
ConnectOnLaunch = connectOnLaunch;
63+
64+
DisableTailscaleLoopProtection = disableTailscaleLoopProtection;
5665
}
5766

5867
public CoderConnectSettings Clone()
5968
{
60-
return new CoderConnectSettings(Version, ConnectOnLaunch);
69+
return new CoderConnectSettings(Version, ConnectOnLaunch, DisableTailscaleLoopProtection);
6170
}
6271
}

App/Services/RpcController.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,18 @@ public interface IRpcController : IAsyncDisposable
6969
public class RpcController : IRpcController
7070
{
7171
private readonly ICredentialManager _credentialManager;
72+
private readonly ISettingsManager<CoderConnectSettings> _settingsManager;
7273

7374
private readonly RaiiSemaphoreSlim _operationLock = new(1, 1);
7475
private Speaker<ClientMessage, ServiceMessage>? _speaker;
7576

7677
private readonly RaiiSemaphoreSlim _stateLock = new(1, 1);
7778
private readonly RpcModel _state = new();
7879

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

8486
public event EventHandler<RpcModel>? StateChanged;
@@ -156,6 +158,11 @@ public async Task StartVpn(CancellationToken ct = default)
156158
using var _ = await AcquireOperationLockNowAsync();
157159
AssertRpcConnected();
158160

161+
var coderConnectSettings = await _settingsManager.Read();
162+
var disableTailscaleLoopProtection = coderConnectSettings.DisableTailscaleLoopProtection;
163+
Debug.WriteLine(
164+
$"Starting VPN with DisableTailscaleLoopProtection={disableTailscaleLoopProtection}");
165+
159166
var credentials = _credentialManager.GetCachedCredentials();
160167
if (credentials.State != CredentialState.Valid)
161168
throw new RpcOperationException(

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
public partial bool ConnectOnLaunch { get; set; }
1515

16+
[ObservableProperty]
17+
public partial bool DisableTailscaleLoopProtection { get; set; }
18+
1619
[ObservableProperty]
1720
public partial bool StartOnLoginDisabled { 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.DisableTailscaleLoopProtection;
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+
partial void OnDisableTailscaleLoopProtectionChanged(bool oldValue, bool newValue)
51+
{
52+
if (oldValue == newValue)
53+
return;
54+
try
55+
{
56+
_connectSettings.DisableTailscaleLoopProtection = DisableTailscaleLoopProtection;
57+
_connectSettingsManager.Write(_connectSettings);
58+
}
59+
catch (Exception ex)
60+
{
61+
_logger.LogError($"Error saving Coder Connect {nameof(DisableTailscaleLoopProtection)} settings: {ex.Message}");
62+
}
63+
}
64+
4665
partial void OnConnectOnLaunchChanged(bool oldValue, bool newValue)
4766
{
4867
if (oldValue == newValue)
@@ -54,7 +73,7 @@ partial void OnConnectOnLaunchChanged(bool oldValue, bool newValue)
5473
}
5574
catch (Exception ex)
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
<ToggleSwitch IsOn="{x:Bind ViewModel.ConnectOnLaunch, Mode=TwoWay}" />
4646
</controls:SettingsCard>
47+
<controls:SettingsCard Description="This setting controls whether Coder Connect should bypass VPN loop preventation mechanism. Turn this ON only when dealing with a Coder deployment behind a corporate VPN."
48+
Header="Disable corporate VPN loop protection"
49+
HeaderIcon="{ui:FontIcon Glyph=&#xE8AF;}"
50+
>
51+
<ToggleSwitch IsOn="{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="450"
13+
MinWidth="600" MinHeight="450">
1414

1515
<Window.SystemBackdrop>
1616
<MicaBackdrop/>

0 commit comments

Comments
 (0)