Merge pull request #358 from openziti/release-next

release 1.9.3 merge to main
This commit is contained in:
dovholuknf
2021-04-12 13:30:02 -04:00
committed by GitHub
32 changed files with 780 additions and 455 deletions
+18
View File
@@ -149,5 +149,23 @@
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ComboBoxTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Border CornerRadius="5,0,0,5"
BorderThickness="1"
Background="{TemplateBinding Background}"
BorderBrush="Black">
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
+81 -43
View File
@@ -68,8 +68,8 @@ namespace ZitiDesktopEdge {
ExpectedLogPathServices = Path.Combine(ExpectedLogPathRoot, "service", $"ziti-tunneler.log");
}
private void IdentityMenu_OnMessage(string message) {
ShowBlurbAsync(message, "");
async private void IdentityMenu_OnMessage(string message) {
await ShowBlurbAsync(message, "");
}
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) {
@@ -105,14 +105,14 @@ namespace ZitiDesktopEdge {
/// <param name="e">The MFA Event</param>
private void ServiceClient_OnMfaEvent(object sender, MfaEvent mfa) {
HideLoad();
this.Dispatcher.Invoke(() => {
this.Dispatcher.Invoke(async () => {
if (mfa.Action == "enrollment_challenge") {
string url = HttpUtility.UrlDecode(mfa.ProvisioningUrl);
string secret = HttpUtility.ParseQueryString(url)["secret"];
this.IdentityMenu.Identity.MFAInfo.RecoveryCodes = mfa?.RecoveryCodes?.ToArray();
SetupMFA(this.IdentityMenu.Identity, url, secret);
} else if (mfa.Action == "auth_challenge") {
ShowBlurbAsync("Setting Up auth_challenge", "");
await ShowBlurbAsync("Setting Up auth_challenge", "");
} else if (mfa.Action == "enrollment_verification") {
if (mfa.Successful) {
ShowMFARecoveryCodes(this.IdentityMenu.Identity);
@@ -127,7 +127,7 @@ namespace ZitiDesktopEdge {
} else if (mfa.Action == "enrollment_remove") {
// ShowBlurb("removed mfa: " + mfa.Successful, "");
} else {
ShowBlurbAsync("Unexpected error when processing MFA", "");
await ShowBlurbAsync ("Unexpected error when processing MFA", "");
logger.Error("unexpected action: " + mfa.Action);
}
LoadIdentities(true);
@@ -172,9 +172,9 @@ namespace ZitiDesktopEdge {
/// Show the MFA Recovery Codes
/// </summary>
/// <param name="identity">The Ziti Identity to Authenticate</param>
public void ShowMFARecoveryCodes(ZitiIdentity identity) {
async public void ShowMFARecoveryCodes(ZitiIdentity identity) {
if (identity.MFAInfo!=null) {
if (identity.MFAInfo.RecoveryCodes?.Length > 0) {
if (identity.MFAInfo.IsAuthenticated&& identity.MFAInfo.RecoveryCodes!=null) {
MFASetup.Opacity = 0;
MFASetup.Visibility = Visibility.Visible;
MFASetup.Margin = new Thickness(0, 0, 0, 0);
@@ -188,7 +188,7 @@ namespace ZitiDesktopEdge {
this.ShowMFA(IdentityMenu.Identity, 2);
}
} else {
ShowBlurbAsync("MFA is not setup on this Identity", "");
await ShowBlurbAsync("MFA is not setup on this Identity", "");
}
}
@@ -232,7 +232,7 @@ namespace ZitiDesktopEdge {
/// <summary>
/// Close the MFA Screen with animation
/// </summary>
private void DoClose(bool isComplete) {
async private void DoClose(bool isComplete) {
DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3));
ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3));
animation.Completed += CloseComplete;
@@ -252,15 +252,11 @@ namespace ZitiDesktopEdge {
if (IdentityMenu.IsVisible) {
if (isComplete) {
if (MFASetup.Type == 2) {
if (IdentityMenu.Identity.MFAInfo?.RecoveryCodes?.Length > 0) {
ShowRecovery(IdentityMenu.Identity);
} else {
ShowBlurbAsync("You do not have anymore recovery codes", this.RECOVER);
}
ShowRecovery(IdentityMenu.Identity);
} else if (MFASetup.Type == 3) {
IdentityMenu.Identity.IsMFAEnabled = false;
IdentityMenu.Identity.MFAInfo.IsAuthenticated = false;
ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", "");
await ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", "");
} else if (MFASetup.Type == 4) {
ShowRecovery(IdentityMenu.Identity);
}
@@ -346,9 +342,15 @@ namespace ZitiDesktopEdge {
this.IdentityMenu.MainWindow = this;
SetNotifyIcon("white");
MFASetup.OnLoad += MFASetup_OnLoad;
IdentityMenu.OnMessage += IdentityMenu_OnMessage;
}
private void MFASetup_OnLoad(bool isComplete, string title, string message) {
if (isComplete) HideLoad();
else ShowLoad(title, message);
}
private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e) {
if (notifyIcon != null) {
notifyIcon.Visible = false;
@@ -487,7 +489,8 @@ namespace ZitiDesktopEdge {
serviceClient.OnServiceEvent += ServiceClient_OnServiceEvent;
serviceClient.OnTunnelStatusEvent += ServiceClient_OnTunnelStatusEvent;
serviceClient.OnMfaEvent += ServiceClient_OnMfaEvent;
serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent;
serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent;
serviceClient.OnBulkServiceEvent += ServiceClient_OnBulkServiceEvent;
Application.Current.Properties.Add("ServiceClient", serviceClient);
monitorClient = new MonitorClient();
@@ -521,6 +524,28 @@ namespace ZitiDesktopEdge {
Placement();
}
private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) {
var found = identities.Find(id => id.Fingerprint == e.Fingerprint);
if (found == null) {
logger.Warn($"{e.Action} service event for {e.Fingerprint} but the provided identity fingerprint was not found!");
return;
} else {
foreach (var removed in e.RemovedServices) {
removeService(found, removed);
}
foreach (var added in e.AddedServices) {
addService(found, added);
}
LoadIdentities(false);
this.Dispatcher.Invoke(() => {
IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu;
if (deets.IsVisible) {
deets.UpdateView();
}
});
}
}
string nextVersionStr = null;
private void MonitorClient_OnReconnectFailure(object sender, object e) {
if (nextVersionStr == null) {
@@ -699,6 +724,9 @@ namespace ZitiDesktopEdge {
private void ServiceClient_OnClientDisconnected(object sender, object e) {
this.Dispatcher.Invoke(() => {
IdentityMenu.Visibility = Visibility.Collapsed;
MFASetup.Visibility = Visibility.Collapsed;
HideModal();
IdList.Children.Clear();
if (e != null) {
logger.Debug(e.ToString());
@@ -770,34 +798,20 @@ namespace ZitiDesktopEdge {
private void ServiceClient_OnServiceEvent(object sender, ServiceEvent e) {
if (e == null) return;
Debug.WriteLine($"==== ServiceEvent : action:{e.Action} fingerprint:{e.Fingerprint} name:{e.Service.Name} ");
logger.Debug($"==== ServiceEvent : action:{e.Action} fingerprint:{e.Fingerprint} name:{e.Service.Name} ");
var found = identities.Find(id => id.Fingerprint == e.Fingerprint);
if (found == null) {
logger.Debug($"{e.Action} service event for {e.Service.Name} but the provided identity fingerprint {e.Fingerprint} is not found!");
return;
}
if (e.Action == "added") {
addService(found, e.Service);
} else {
removeService(found, e.Service);
}
LoadIdentities(false);
this.Dispatcher.Invoke(() => {
var found = identities.Find(id => id.Fingerprint == e.Fingerprint);
if (found == null) {
Debug.WriteLine($"{e.Action} service event for {e.Service.Name} but the provided identity fingerprint {e.Fingerprint} is not found!");
return;
}
if (e.Action == "added") {
ZitiService zs = new ZitiService(e.Service);
var svc = found.Services.Find(s => s.Name == zs.Name);
if (svc == null) {
found.Services.Add(zs);
if (zs.HasFailingPostureCheck()) {
found.HasServiceFailingPostureCheck = true;
if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) {
found.MFAInfo.IsAuthenticated = false;
}
}
} else {
logger.Debug("the service named " + zs.Name + " is already accounted for on this identity.");
}
} else {
logger.Debug("removing the service named: " + e.Service.Name);
found.Services.RemoveAll(s => s.Name == e.Service.Name);
}
LoadIdentities(false);
IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu;
if (deets.IsVisible) {
deets.UpdateView();
@@ -805,6 +819,27 @@ namespace ZitiDesktopEdge {
});
}
private void addService(ZitiIdentity found, Service added) {
ZitiService zs = new ZitiService(added);
var svc = found.Services.Find(s => s.Name == zs.Name);
if (svc == null) {
found.Services.Add(zs);
if (zs.HasFailingPostureCheck()) {
found.HasServiceFailingPostureCheck = true;
if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) {
found.MFAInfo.IsAuthenticated = false;
}
}
} else {
logger.Debug("the service named " + zs.Name + " is already accounted for on this identity.");
}
}
private void removeService(ZitiIdentity found, Service removed) {
logger.Debug("removing the service named: {0}", removed.Name);
found.Services.RemoveAll(s => s.Name == removed.Name);
}
private void ServiceClient_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) {
if (e == null) return; //just skip it for now...
logger.Debug($"==== TunnelStatusEvent: ");
@@ -889,6 +924,9 @@ namespace ZitiDesktopEdge {
if (!Application.Current.Properties.Contains("dns")) {
Application.Current.Properties.Add("dns", status?.IpInfo?.DNS);
}
if (!Application.Current.Properties.Contains("dnsenabled")) {
Application.Current.Properties.Add("dnsenabled", status?.AddDns);
}
foreach (var id in status.Identities) {
updateViewWithIdentity(id);
+1 -1
View File
@@ -9,7 +9,7 @@ namespace ZitiDesktopEdge.Models {
public int Total { get; set; }
public string Message { get; set; }
public string ToString() {
public override string ToString() {
return Total + " " + Message;
}
}
+3 -3
View File
@@ -50,6 +50,6 @@ using System.Windows;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.9.2.0")]
[assembly: AssemblyVersion("1.9.2.0")]
[assembly: AssemblyFileVersion("1.9.2.0")]
// [assembly: AssemblyVersion("1.9.3.0")]
[assembly: AssemblyVersion("1.9.3.0")]
[assembly: AssemblyFileVersion("1.9.3.0")]
@@ -54,6 +54,13 @@ namespace ZitiDesktopEdge {
}
}
}
public void Toggle() {
Enabled = !Enabled;
if (OnToggled != null) {
OnToggled(Enabled);
}
}
private void OnToggle(object sender, RoutedEventArgs e) {
e.Handled = true;
@@ -12,7 +12,7 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30" Cursor="Hand" MouseUp="OpenDetails"/>
</Grid.ColumnDefinitions>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
@@ -24,22 +24,31 @@
<RowDefinition Height="40"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<local:Toggler x:Name="ToggleSwitch" Grid.Column="0" Grid.Row="0" Margin="0,10,0,0"></local:Toggler>
<Label Name="ToggleStatus" Grid.Column="0" Grid.Row="1" Margin="0,-5,0,0" Content="Enabled" Foreground="White" FontSize="11" Typography.Capitals="AllSmallCaps" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"></Label>
<Label Name="IdName" Cursor="Hand" MouseUp="OpenDetails" MaxWidth="230" VerticalAlignment="Bottom" Grid.Column="1" Margin="0,6,0,0" FontFamily="pack://application:,,,/Assets/Fonts/#Open Sans" FontSize="16" Foreground="White" HorizontalAlignment="Left">
<Rectangle Fill="Transparent" Grid.Column="0" Grid.RowSpan="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Cursor="Hand" MouseUp="ToggledSwitch"></Rectangle>
<Label Name="IdName" MaxWidth="230" Grid.Row="0" Grid.Column="1" VerticalAlignment="Bottom" Margin="0,6,0,0" FontFamily="pack://application:,,,/Assets/Fonts/#Open Sans" FontSize="16" Foreground="White" HorizontalAlignment="Left">
<TextBlock TextTrimming="CharacterEllipsis">Identity Name</TextBlock>
</Label>
<Rectangle Fill="Transparent" Grid.RowSpan="2" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Cursor="Hand" MouseUp="OpenDetails"></Rectangle>
<Label Name="IdUrl" Cursor="Hand" MouseUp="OpenDetails" MaxWidth="180" MinWidth="230" Grid.Column="1" Grid.Row="1" FontSize="11" Margin="0,-5,0,0" Typography.Capitals="AllSmallCaps" FontFamily="pack://application:,,,/Assets/Fonts/#Open Sans" Foreground="White" HorizontalAlignment="Left">
<TextBlock TextTrimming="CharacterEllipsis">https://serverUrlThisShouldTrimIfItsLong:8080</TextBlock>
</Label>
<Image Name="MfaRequired" Source="/Assets/Images/mfaoff.png" Grid.Column="2" Cursor="Hand" Width="50" Height="20" VerticalAlignment="Bottom" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" Visibility="Visible" MouseUp="MFAAuthenticate"></Image>
<Canvas Name="ServiceCountArea" Grid.Column="2" Grid.Row="0" Width="50" Height="40" MouseUp="OpenDetails" Cursor="Hand" Visibility="Collapsed">
<Image Name="MfaRequired" Source="/Assets/Images/mfaoff.png" Grid.Column="2" Cursor="Hand" Width="50" Height="26" VerticalAlignment="Bottom" RenderOptions.BitmapScalingMode="Fant" Stretch="Uniform" Visibility="Visible" MouseUp="MFAAuthenticate"></Image>
<Canvas Name="ServiceCountArea" Grid.Column="2" Grid.Row="0" Width="50" Height="40" Cursor="Hand" Visibility="Collapsed">
<Border Width="44" Height="26" Background="#0068F9" CornerRadius="12" Canvas.Top="10" Canvas.Left="3"></Border>
<Label Name="ServiceCount" Width="50" Height="26" Canvas.Top="9" Padding="0,0,0,0" FontSize="17" Content="23" Foreground="White" HorizontalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"></Label>
<Label Name="ServiceCount" Width="50" Height="26" Canvas.Top="9" Padding="0,0,0,0" FontSize="14" Content="23" Foreground="White" HorizontalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"></Label>
</Canvas>
<Label Name="ServiceCountAreaLabel" Grid.Column="3" MouseUp="OpenDetails" Grid.Row="1" Margin="0,-5,0,0" Content="authorize" Cursor="Hand" Foreground="White" FontSize="11" HorizontalAlignment="Stretch" Typography.Capitals="AllSmallCaps" HorizontalContentAlignment="Center"></Label>
<Rectangle Fill="Transparent" Grid.Column="2" Grid.RowSpan="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Cursor="Hand" MouseUp="DoMFAOrOpen"></Rectangle>
</Grid>
<Image Grid.Column="1" Source="/Assets/Images/next.png" Width="12" MouseUp="OpenDetails" Cursor="Hand"></Image>
<Rectangle Fill="Transparent" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Cursor="Hand" MouseUp="OpenDetails"></Rectangle>
</Grid>
</Canvas>
</UserControl>
@@ -115,5 +115,17 @@ namespace ZitiDesktopEdge {
private void MFAAuthenticate(object sender, MouseButtonEventArgs e) {
this.Authenticate?.Invoke(_identity);
}
private void ToggledSwitch(object sender, MouseButtonEventArgs e) {
ToggleSwitch.Toggle();
}
private void DoMFAOrOpen(object sender, MouseButtonEventArgs e) {
if (MfaRequired.Visibility==Visibility.Visible) {
MFAAuthenticate(sender, e);
} else {
OpenDetails(sender, e);
}
}
}
}
+35 -4
View File
@@ -50,7 +50,7 @@
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition x:Name="ServiceRow" Height="140"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition x:Name="IdImage" Height="0"></RowDefinition>
</Grid.RowDefinitions>
@@ -78,10 +78,41 @@
</Rectangle>
<Label x:Name="AuthMessageLabel" HorizontalAlignment="Center" Content="Authenticate" IsHitTestVisible="False" Foreground="#FFFFFF" Grid.Row="6"></Label>
<!-- Forget Identity Button -->
<Label Name="ForgetIdentityButton" Grid.Column="0" Grid.Row="7" Padding="0,10,0,10" Margin="0,0,0,0" Content="Forget This Identity" Cursor="Hand" Foreground="#F4044D" HorizontalAlignment="Center" Mouse.MouseUp="ForgetIdentity"></Label>
<!-- Sorting and Paging -->
<Grid x:Name="PageSortArea" Grid.Column="0" Grid.Row="7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="90"></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Label Content="Page" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center"></Label>
<ComboBox Name="Pages" Grid.Column="1" Grid.Row="0" SelectionChanged="Pages_SelectionChanged" Width="60" Height="28" VerticalAlignment="Top" HorizontalAlignment="Left" BorderThickness="0" Background="#FFD4CECE" Foreground="#FF292626" FontFamily="/ZitiDesktopEdge;component/Assets/Fonts/#Open Sans" FontSize="14"></ComboBox>
<Image Name="ForgetIdentityImage" Visibility="Collapsed" Source="/Assets/Images/identity.png" Grid.Column="0" Grid.Row="8" Height="80" Width="80" Opacity="0.3" HorizontalAlignment="Stretch"></Image>
<Label Content="Sort" Grid.Column="2" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center"></Label>
<ComboBox Name="SortByField" Grid.Column="3" Grid.Row="0" SelectionChanged="SortByField_SelectionChanged" Height="28" VerticalAlignment="Top" HorizontalAlignment="Stretch" BorderThickness="0" Background="#FFD4CECE" Foreground="#FF292626" FontFamily="/ZitiDesktopEdge;component/Assets/Fonts/#Open Sans" FontSize="14">
<ComboBoxItem IsSelected="True">Name</ComboBoxItem>
<ComboBoxItem>Address</ComboBoxItem>
<ComboBoxItem>Protocol</ComboBoxItem>
<ComboBoxItem>Port</ComboBoxItem>
</ComboBox>
<ComboBox Name="SortWayField" Grid.Column="4" Grid.Row="0" Margin="5,0,0,0" SelectionChanged="SortWayField_SelectionChanged" Height="28" VerticalAlignment="Top" HorizontalAlignment="Stretch" BorderThickness="0" Foreground="#FF292626" FontFamily="/ZitiDesktopEdge;component/Assets/Fonts/#Open Sans" FontSize="14">
<ComboBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.WindowBrushKey}" Color="Yellow" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
</ComboBox.Resources>
<ComboBoxItem IsSelected="True">Asc</ComboBoxItem>
<ComboBoxItem>Desc</ComboBoxItem>
</ComboBox>
</Grid>
<!-- Forget Identity Button -->
<Label Name="ForgetIdentityButton" Grid.Column="0" Grid.Row="8" Padding="0,10,0,10" Margin="0,0,0,0" Content="Forget This Identity" Cursor="Hand" Foreground="#F4044D" HorizontalAlignment="Center" Mouse.MouseUp="ForgetIdentity"></Label>
</Grid>
</StackPanel>
</Grid>
@@ -35,6 +35,10 @@ namespace ZitiDesktopEdge {
public event OnAuthenticate Authenticate;
public delegate void OnRecovery(ZitiIdentity identity);
public event OnRecovery Recovery;
public int Page = 1;
public int PerPage = 50;
public string SortBy = "Name";
public string SortWay = "Asc";
internal MainWindow MainWindow { get; set; }
@@ -53,6 +57,11 @@ namespace ZitiDesktopEdge {
set {
_identity = value;
this.IdDetailToggle.Enabled = _identity.IsEnabled;
Page = 1;
SortBy = "Name";
SortWay = "Asc";
SortByField.SelectedIndex = 0;
SortWayField.SelectedIndex = 0;
UpdateView();
IdentityArea.Opacity = 1.0;
IdentityArea.Visibility = Visibility.Visible;
@@ -117,17 +126,45 @@ namespace ZitiDesktopEdge {
IdentityMFA.RecoveryButton.Visibility = Visibility.Collapsed;
}
ServiceList.Children.Clear();
PageSortArea.Visibility = Visibility.Collapsed;
if (_identity.Services.Count>0) {
foreach(var zitiSvc in _identity.Services.OrderBy(s => s.Name.ToLower())) {
if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >=0||zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >=0) {
Logger.Trace("painting: " + zitiSvc.Name);
ServiceInfo info = new ServiceInfo();
info.Info = zitiSvc;
info.OnMessage += Info_OnMessage;
info.OnDetails += ShowDetails;
ServiceList.Children.Add(info);
PageSortArea.Visibility = Visibility.Visible;
int index = 0;
int total = 0;
ZitiService[] services = new ZitiService[0];
if (SortBy == "Name") services = _identity.Services.OrderBy(s => s.Name.ToLower()).ToArray();
else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray();
else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray();
else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray();
if (SortWay == "Desc") services = services.Reverse().ToArray();
int startIndex = (Page - 1) * PerPage;
for (int i= startIndex; i<services.Length; i++) {
ZitiService zitiSvc = services[i];
total++;
if (index<100) {
if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) {
Logger.Trace("painting: " + zitiSvc.Name);
ServiceInfo info = new ServiceInfo();
info.Info = zitiSvc;
info.OnMessage += Info_OnMessage;
info.OnDetails += ShowDetails;
ServiceList.Children.Add(info);
index++;
}
}
}
if (Page==1) {
Pages.Items.Clear();
int totalPages = (total / PerPage) + 1;
for (int i = 1; i <= totalPages; i++) {
ComboBoxItem item = new ComboBoxItem();
if (i == Page) item.IsSelected = true;
item.Content = i.ToString();
Pages.Items.Add(item);
}
}
double newHeight = MainHeight - 330;
ServiceRow.Height = new GridLength((double)newHeight);
MainDetailScroll.MaxHeight = newHeight;
@@ -326,5 +363,36 @@ namespace ZitiDesktopEdge {
private void AuthFromMessage(object sender, MouseButtonEventArgs e) {
this.Authenticate.Invoke(this.Identity);
}
private void Pages_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBoxItem selected = (ComboBoxItem)Pages.SelectedValue;
if (selected!=null) {
int value = Int32.Parse(selected.Content.ToString());
if (value!=Page) {
Page = value;
UpdateView();
}
}
}
private void SortByField_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBoxItem selected = (ComboBoxItem)SortByField.SelectedValue;
if (selected != null && selected.Content!=null) {
if (selected.Content.ToString()!=SortBy) {
SortBy = selected.Content.ToString();
UpdateView();
}
}
}
private void SortWayField_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBoxItem selected = (ComboBoxItem)SortWayField.SelectedValue;
if (selected != null && selected.Content != null) {
if (selected.Content.ToString() != SortWay) {
SortWay = selected.Content.ToString();
UpdateView();
}
}
}
}
}
+19 -15
View File
@@ -13,11 +13,19 @@
<DropShadowEffect BlurRadius="20" Direction="1" RenderingBias="Quality" ShadowDepth="1" Opacity="1"/>
</Border.Effect>
</Border>
<Rectangle Margin="10,10,10,10" RadiusX="12" RadiusY="12" ClipToBounds="True">
<Rectangle x:Name="MainBrush" Margin="10,10,10,10" RadiusX="12" RadiusY="12" ClipToBounds="True">
<Rectangle.Fill>
<SolidColorBrush Color="#FFFFFF" Opacity="0.97"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="AuthBrush" Margin="10,10,10,10" RadiusX="12" RadiusY="12" ClipToBounds="True">
<Rectangle.Fill>
<SolidColorBrush Color="#131429" Opacity="0.97"/>
</Rectangle.Fill>
<Rectangle.Stroke>
<SolidColorBrush Color="White" Opacity="0.4"></SolidColorBrush>
</Rectangle.Stroke>
</Rectangle>
<Rectangle x:Name="SeperationColor" Margin="16,10,16,16" RadiusX="10" RadiusY="10" ClipToBounds="True" Height="160" VerticalAlignment="Bottom">
<Rectangle.Fill>
<SolidColorBrush Color="#131429" Opacity="0.97"/>
@@ -51,7 +59,7 @@
</StackPanel>
<Label Content="enter the code below to finish setup" Grid.Row="4" FontSize="11" Foreground="DarkGray" HorizontalAlignment="Center"></Label>
<Label Content="Authentication Code" Grid.Row="5" FontSize="18" Foreground="#FFFFFF" HorizontalAlignment="Center" FontWeight="Bold" FontFamily="/ZitiDesktopEdge;component/Assets/Fonts/#Russo One"></Label>
<TextBox x:Name="SetupCode" MaxLength="6" Padding="0,5,0,0" TextBlock.LineHeight="80" Grid.Row="6" Keyboard.KeyUp="HandleKey" BorderThickness="0 0 0 0" TextAlignment="Center" FontSize="50">
<TextBox x:Name="SetupCode" MaxLength="6" Padding="0,5,0,0" Grid.Row="6" Keyboard.KeyUp="HandleKey" BorderThickness="0 0 0 0" TextAlignment="Center" FontSize="50">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="7"/>
@@ -73,6 +81,8 @@
</Grid.RowDefinitions>
<Label Content="MFA Recovery Codes" Grid.Row="0" FontSize="16" HorizontalAlignment="Center" FontWeight="Bold"></Label>
<UniformGrid x:Name="RecoveryList" Grid.Row="1" Columns="3"></UniformGrid>
<TextBlock TextWrapping="WrapWithOverflow" x:Name="NoRecovery" Visibility="Collapsed" Text="No Recovery Codes Available" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20,0,20,0" FontSize="16"></TextBlock>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"></ColumnDefinition>
@@ -91,28 +101,22 @@
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Label Content="Authorize MFA" Grid.Row="0" FontSize="16" HorizontalAlignment="Center" FontWeight="Bold"></Label>
<Label x:Name="AuthSubTitle" Content="Enter your authentication code" Grid.Row="1" FontSize="12" HorizontalAlignment="Center"></Label>
<TextBox x:Name="AuthCode" MaxLength="6" FontSize="50" Padding="0,5,0,0" Grid.Row="2" Keyboard.KeyUp="AuthCode_KeyUp" TextAlignment="Center" BorderThickness="0 0 0 2">
<Label Content="Authorize MFA" Foreground="White" Grid.Row="0" FontSize="16" HorizontalAlignment="Center" FontWeight="Bold"></Label>
<Label x:Name="AuthSubTitle" Foreground="White" Opacity="0.7" Content="Enter Authorization or Recovery Code" Grid.Row="1" FontSize="12" HorizontalAlignment="Center"></Label>
<TextBox x:Name="AuthCode" MaxLength="8" FontSize="50" Padding="0,5,0,0" Grid.Row="2" Keyboard.KeyUp="AuthCode_KeyUp" TextAlignment="Center" BorderThickness="0 0 0 0" Margin="0,0,0,5">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="7"/>
</Style>
</TextBox.Resources>
</TextBox>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label x:Name="ToggleType" HorizontalAlignment="Center" Content="Use Recovery Code" Foreground="#0069FF" Cursor="Hand" Grid.Column="0" MouseUp="ToggleRecovery"></Label>
<Label HorizontalAlignment="Center" Content="Authenticate" Foreground="#0069FF" Cursor="Hand" Grid.Column="1" MouseUp="DoAuthenticate"></Label>
</Grid>
<local:StyledButton x:Name="AuthButton" OnClick="DoAuthenticate" Label="Authenticate" Grid.Row="7"></local:StyledButton>
</Grid>
<Image Source="/Assets/Images/closeBlack.png" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,40,40,0" Width="10" Height="10" Cursor="Hand" MouseUp="ExecuteClose"></Image>
<Image x:Name="CloseBlack" Source="/Assets/Images/closeBlack.png" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,40,40,0" Width="10" Height="10" Cursor="Hand" MouseUp="ExecuteClose"></Image>
<Image x:Name="CloseWhite" Source="/Assets/Images/close.png" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,40,40,0" Width="10" Height="10" Cursor="Hand" MouseUp="ExecuteClose"></Image>
</Grid>
</UserControl>
+41 -34
View File
@@ -31,6 +31,8 @@ namespace ZitiDesktopEdge {
public partial class MFAScreen : UserControl {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public delegate void LoadEvent(bool isComplete, string title, string message);
public event LoadEvent OnLoad;
public delegate void CloseAction(bool isComplete);
public event CloseAction OnClose;
private string _url = "";
@@ -83,10 +85,14 @@ namespace ZitiDesktopEdge {
this._identity = identity;
MFAImage.Visibility = Visibility.Visible;
SecretCode.Visibility = Visibility.Collapsed;
CloseBlack.Visibility = Visibility.Visible;
CloseWhite.Visibility = Visibility.Collapsed;
SecretButton.Content = "Show Secret";
IdName.Content = identity.Name;
Logger.Debug($"MFA Url: {url}");
AuthBrush.Visibility = Visibility.Collapsed;
MainBrush.Visibility = Visibility.Visible;
MFAImage.Source = CreateQRFromUrl(url);
SecretCode.Text = secret;
@@ -107,24 +113,39 @@ namespace ZitiDesktopEdge {
MFARecoveryArea.Visibility = Visibility.Visible;
RecoveryList.Children.Clear();
_codes = codes;
AuthBrush.Visibility = Visibility.Collapsed;
MainBrush.Visibility = Visibility.Visible;
CloseBlack.Visibility = Visibility.Visible;
CloseWhite.Visibility = Visibility.Collapsed;
MFAArea.Height = 380;
for (int i=0; i<codes.Length; i++) {
TextBox label = new TextBox();
label.Text = codes[i];
label.BorderThickness = new Thickness(0);
label.Background = new SolidColorBrush();
label.Background.Opacity = 0;
label.HorizontalAlignment = HorizontalAlignment.Center;
label.HorizontalContentAlignment = HorizontalAlignment.Center;
label.VerticalAlignment = VerticalAlignment.Center;
label.VerticalContentAlignment = VerticalAlignment.Center;
RecoveryList.Children.Add(label);
if (codes.Length>0) {
for (int i = 0; i < codes.Length; i++) {
TextBox label = new TextBox();
label.Text = codes[i];
label.BorderThickness = new Thickness(0);
label.Background = new SolidColorBrush();
label.Background.Opacity = 0;
label.HorizontalAlignment = HorizontalAlignment.Center;
label.HorizontalContentAlignment = HorizontalAlignment.Center;
label.VerticalAlignment = VerticalAlignment.Center;
label.VerticalContentAlignment = VerticalAlignment.Center;
RecoveryList.Children.Add(label);
}
RecoveryList.Visibility = Visibility.Visible;
NoRecovery.Visibility = Visibility.Collapsed;
} else {
RecoveryList.Visibility = Visibility.Collapsed;
NoRecovery.Visibility = Visibility.Visible;
}
}
public void ShowMFA(ZitiIdentity identity, int type) {
this.Type = type;
AuthCode.Text = "";
AuthBrush.Visibility = Visibility.Visible;
MainBrush.Visibility = Visibility.Collapsed;
CloseBlack.Visibility = Visibility.Collapsed;
CloseWhite.Visibility = Visibility.Visible;
this._identity = identity;
MFASetupArea.Visibility = Visibility.Collapsed;
MFARecoveryArea.Visibility = Visibility.Collapsed;
@@ -152,21 +173,6 @@ namespace ZitiDesktopEdge {
}
}
private void ToggleRecovery(object sender, MouseButtonEventArgs e) {
AuthCode.Text = "";
if (AuthCode.MaxLength==6) {
GenerateMFACodes(null, null);
AuthCode.MaxLength = 8;
ToggleType.Content = "Use Auth Code";
AuthSubTitle.Content = "Enter a recovery code";
AuthCode.Focus();
} else {
AuthCode.MaxLength = 6;
ToggleType.Content = "Use Recovery Code";
AuthSubTitle.Content = "Enter your authorization code";
AuthCode.Focus();
}
}
async private void ReturnMFACodes(object sender, MouseButtonEventArgs e) {
DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"];
string code = AuthCode.Text;
@@ -214,17 +220,14 @@ namespace ZitiDesktopEdge {
}
}
async private void DoAuthenticate(object sender, MouseButtonEventArgs e) {
async private void DoAuthenticate() {
if (!this._executing) {
this._executing = true;
string code = AuthCode.Text;
if (AuthCode.MaxLength == 8) {
if (code.Length != 8) this.ShowError("You must enter a valid recovery code");
} else {
if (code.Length != 6) this.ShowError("You must enter a valid code");
}
DataClient serviceClient = (DataClient)Application.Current.Properties["ServiceClient"];
this.OnLoad?.Invoke(false, "Authentication", "One Moment Please...");
if (this.Type == 1) {
SvcResponse authResult = await serviceClient.AuthMFA(this._identity.Fingerprint, code);
if (authResult?.Code != 0) {
@@ -238,6 +241,7 @@ namespace ZitiDesktopEdge {
this.OnClose?.Invoke(true);
this._executing = false;
}
this.OnLoad?.Invoke(true, "", "");
} else if (this.Type == 2) {
MfaRecoveryCodesResponse codeResponse = await serviceClient.ReturnMFACodes(this._identity.Fingerprint, code);
if (codeResponse?.Code != 0) {
@@ -250,6 +254,7 @@ namespace ZitiDesktopEdge {
this.OnClose?.Invoke(true);
this._executing = false;
}
this.OnLoad?.Invoke(true, "", "");
} else if (this.Type == 3) {
SvcResponse authResult = await serviceClient.RemoveMFA(this._identity.Fingerprint, code);
if (authResult?.Code != 0) {
@@ -261,6 +266,7 @@ namespace ZitiDesktopEdge {
this.OnClose?.Invoke(true);
this._executing = false;
}
this.OnLoad?.Invoke(true, "", "");
} else if (this.Type == 4) {
MfaRecoveryCodesResponse codeResponse = await serviceClient.GenerateMFACodes(this._identity.Fingerprint, code);
if (codeResponse?.Code != 0) {
@@ -273,6 +279,7 @@ namespace ZitiDesktopEdge {
this.OnClose?.Invoke(true);
this._executing = false;
}
this.OnLoad?.Invoke(true, "", "");
}
}
}
@@ -301,7 +308,7 @@ namespace ZitiDesktopEdge {
private void AuthCode_KeyUp(object sender, KeyEventArgs e) {
if (e.Key == Key.Return) {
DoAuthenticate(sender, null);
DoAuthenticate();
}
}
}
+1 -1
View File
@@ -230,7 +230,7 @@ namespace ZitiDesktopEdge {
ConfigIp.Value = Application.Current.Properties["ip"]?.ToString();
ConfigSubnet.Value = Application.Current.Properties["subnet"]?.ToString();
ConfigMtu.Value = Application.Current.Properties["mtu"]?.ToString();
ConfigDns.Value = Application.Current.Properties["dns"]?.ToString();
ConfigDns.Value = (bool)Application.Current.Properties["dnsenabled"] ? Application.Current.Properties["dns"]?.ToString() : "disabled";
} else if (menuState == "Identities") {
MenuTitle.Content = "Identities";
IdListScrollView.Visibility = Visibility.Visible;
+1
View File
@@ -91,6 +91,7 @@
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.7.8\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="PresentationFramework.Aero2" />
<Reference Include="QRCoder, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\QRCoder.1.4.1\lib\net40\QRCoder.dll</HintPath>
</Reference>
+2 -2
View File
@@ -23,10 +23,10 @@
<ROW Property="CTRLS" Value="2"/>
<ROW Property="Manufacturer" Value="NetFoundry, Inc."/>
<ROW Property="MsiLogging" MultiBuildValue="AnyCPU_Release:vp"/>
<ROW Property="ProductCode" Value="1033:{147D2A26-A83E-4878-A57C-9646407357C3} " Type="16"/>
<ROW Property="ProductCode" Value="1033:{758AA10B-B1A9-45FB-97AB-20759FAB902D} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="Ziti Desktop Edge"/>
<ROW Property="ProductVersion" Value="1.9.2" Type="32"/>
<ROW Property="ProductVersion" Value="1.9.3" Type="32"/>
<ROW Property="REBOOT" MultiBuildValue="AnyCPU_Release:Suppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
<ROW Property="UpgradeCode" Value="{CB7E173A-663D-4A23-BB22-D1E8D885C2F3}"/>
@@ -264,7 +264,11 @@ namespace ZitiDesktopEdge.DataStructures {
public int Prefix { get; set; }
public override string ToString() {
return Hostname;
if (IsHost) {
return Hostname;
} else {
return IP + "/" + Prefix;
}
}
}
@@ -333,6 +337,7 @@ namespace ZitiDesktopEdge.DataStructures {
public string LogLevel { get; set; }
public ServiceVersion ServiceVersion { get; set; }
public bool AddDns { get; set; }
public void Dump(System.IO.TextWriter writer)
{
@@ -408,12 +413,17 @@ namespace ZitiDesktopEdge.DataStructures {
public List<Identity> Identities { get; set; }
}
public class ServiceEvent : ActionEvent
{
public class ServiceEvent : ActionEvent {
public string Fingerprint { get; set; }
public Service Service { get; set; }
}
public class BulkServiceEvent : ActionEvent {
public string Fingerprint { get; set; }
public List<Service> AddedServices { get; set; }
public List<Service> RemovedServices { get; set; }
}
public class IdentityEvent : ActionEvent
{
public Identity Id { get; set; }
@@ -35,6 +35,7 @@ namespace ZitiDesktopEdge.ServiceClient {
public event EventHandler<ServiceEvent> OnServiceEvent;
public event EventHandler<LogLevelEvent> OnLogLevelEvent;
public event EventHandler<MfaEvent> OnMfaEvent;
public event EventHandler<BulkServiceEvent> OnBulkServiceEvent;
protected override void ShutdownEvent(StatusEvent e) {
Logger.Debug("Clean shutdown detected from ziti");
@@ -57,12 +58,15 @@ namespace ZitiDesktopEdge.ServiceClient {
protected virtual void ServiceEvent(ServiceEvent e) {
OnServiceEvent?.Invoke(this, e);
}
protected virtual void BulkServiceEvent(BulkServiceEvent e) {
OnBulkServiceEvent?.Invoke(this, e);
}
protected virtual void LogLevelEvent(LogLevelEvent e) {
OnLogLevelEvent?.Invoke(this, e);
}
protected virtual void MfaEvent(MfaEvent e) {
OnMfaEvent?.Invoke(this, e);
}
@@ -199,21 +203,6 @@ namespace ZitiDesktopEdge.ServiceClient {
return;
}
async public Task SetLogLevelAsync(LogLevelEnum level) {
try {
await sendAsync(new SetLogLevelFunction(Enum.GetName(level.GetType(), level)));
SvcResponse resp = await readAsync<SvcResponse>(ipcReader);
return;
} catch (Exception ioe) {
//almost certainly a problem with the pipe - recreate the pipe...
//setupPipe();
//throw ioe;
Logger.Error(ioe, "Unexpected error");
CommunicationError(ioe);
}
return;
}
async public Task<Identity> IdentityOnOffAsync(string fingerprint, bool onOff) {
try {
await sendAsync(new IdentityToggleFunction(fingerprint, onOff));
@@ -351,6 +340,14 @@ namespace ZitiDesktopEdge.ServiceClient {
ServiceEvent(svc);
}
break;
case "bulkservice":
//dbg comment Logger.Warn("BULKSERVICE EVENT: \n" + respAsString);
BulkServiceEvent bsvc = serializer.Deserialize<BulkServiceEvent>(jsonReader);
if (bsvc != null) {
BulkServiceEvent(bsvc);
}
break;
case "logLevel":
LogLevelEvent ll = serializer.Deserialize<LogLevelEvent>(jsonReader);
+3 -3
View File
@@ -30,6 +30,6 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.9.2.0")]
[assembly: AssemblyVersion("1.9.2.0")]
[assembly: AssemblyFileVersion("1.9.2.0")]
// [assembly: AssemblyVersion("1.9.3.0")]
[assembly: AssemblyVersion("1.9.3.0")]
[assembly: AssemblyFileVersion("1.9.3.0")]
+33
View File
@@ -1,3 +1,36 @@
# Release 1.9.3
## What's New
* Page Service List
* Sort Service on Identity Details Page
* Consistent UX for MFA Screens
## Other changes:
* Service to UI interaction is now with one bulk update of services instead of one event per service update
* Added a configuration option to control if DNS is applied to the TUN. Some users are having issues with NRPT-only working. For now we'll add a boolean that allows the user to control if DNS should be added to the TUN
## Bugs fixed:
* none
## Dependency Updates
* none
# Release 1.9.3
## What's New
* Page Service List
* Sort Service on Identity Details Page
* Consistent UX for MFA Screens
## Other changes:
* Service to UI interaction is now with one bulk update of services instead of one event per service update
## Bugs fixed:
* none
## Dependency Updates
* none
# Release 1.9.2
## What's New
+7
View File
@@ -0,0 +1,7 @@
@echo off
powershell "Get-Process | where -Property Name -eq "ziti-tunnel" | select -ExpandProperty id" > zid
set /p zid= < zid
del zid
echo %zid%
"c:\Program Files\JetBrains\GoLand 2020.1.1\plugins\go\lib\dlv\windows\dlv.exe" attach %zid% --headless --log --only-same-user=false --api-version=2 --accept-multiclient --listen=localhost:50000
-29
View File
@@ -151,35 +151,6 @@ dns_manager* get_dns_mgr_from_c() {
return &dns_mgr_c;
}
/* functions allowing go to iterate tunneleed_service_t* */
protocol_t* stailq_first_protocol(tunneled_service_t* ts) {
if(ts == NULL || ts->intercept == NULL ) {
return NULL;
}
return STAILQ_FIRST(&ts->intercept->protocols);
}
address_t* stailq_first_address(tunneled_service_t* ts) {
if(ts == NULL || ts->intercept == NULL ) {
return NULL;
}
return STAILQ_FIRST(&ts->intercept->addresses);
}
port_range_t* stailq_first_port_range(tunneled_service_t* ts) {
if(ts == NULL || ts->intercept == NULL ) {
return NULL;
}
return STAILQ_FIRST(&ts->intercept->port_ranges);
}
protocol_t* stailq_next_protocol(protocol_t* cur) {
return STAILQ_NEXT(cur, entries);
}
address_t* stailq_next_address(address_t* cur) {
return STAILQ_NEXT(cur, entries);
}
port_range_t* stailq_next_port_range(port_range_t* cur) {
return STAILQ_NEXT(cur, entries);
}
char* ziti_char_array_get(char** arr, int idx) {
return arr ? arr[idx] : NULL;
}
+220 -192
View File
@@ -41,37 +41,28 @@ void ziti_dump_to_file(void *ctx, char* outputPath);
int ziti_dump_to_log(void *ctx, void* stringsBuilder);
void* stailq_first_forgo(void* entries);
protocol_t* stailq_first_protocol(tunneled_service_t* ts);
address_t* stailq_first_address(tunneled_service_t* ts);
port_range_t* stailq_first_port_range(tunneled_service_t* ts);
protocol_t* stailq_next_protocol(protocol_t* cur);
address_t* stailq_next_address(address_t* cur);
port_range_t* stailq_next_port_range(port_range_t* cur);
*/
import "C"
import (
"encoding/json"
"errors"
"github.com/openziti/desktop-edge-win/service/windns"
"github.com/openziti/desktop-edge-win/service/ziti-tunnel/api"
"github.com/openziti/desktop-edge-win/service/ziti-tunnel/dto"
"github.com/openziti/desktop-edge-win/service/ziti-tunnel/util/logging"
"net"
"os"
"strings"
"sync"
"unsafe"
)
const (
ADDED = "added"
REMOVED = "removed"
)
var log = logging.Logger()
var noFileLog = logging.NoFilenameLogger()
var Version dto.ServiceVersion
var ServiceChanges = make(chan dto.ServiceEvent, 256)
var BulkServiceChanges = make(chan BulkServiceChange, 32)
var cCfgZitiTunnelerClientV1 = C.CString("ziti-tunneler-client.v1")
var cCfgInterceptV1 = C.CString("intercept.v1")
type sdk struct {
libuvCtx *C.libuv_ctx
@@ -81,6 +72,13 @@ type ServiceChange struct {
Service *ZService
ZitiContext *ZIdentity
}
type BulkServiceChange struct {
Fingerprint string
HostnamesToAdd map[string]bool
HostnamesToRemove map[string]bool
ServicesToRemove []*dto.Service
ServicesToAdd []*dto.Service
}
var _impl sdk
@@ -150,54 +148,54 @@ func NewZid(statusChange func(int)) *ZIdentity {
return zid
}
func (c *ZIdentity) GetMetrics() (int64, int64, bool) {
if c == nil {
func (zid *ZIdentity) GetMetrics() (int64, int64, bool) {
if zid == nil {
return 0, 0, false
}
if unsafe.Pointer(c.czctx) == C.NULL {
log.Debugf("ziti context is still null. the identity is probably not initialized yet: %s", c.Fingerprint)
if unsafe.Pointer(zid.czctx) == C.NULL {
log.Debugf("ziti context is still null. the identity is probably not initialized yet: %s", zid.Fingerprint)
return 0, 0, false
}
var up, down C.double
C.ziti_get_transfer_rates(c.czctx, &up, &down)
C.ziti_get_transfer_rates(zid.czctx, &up, &down)
return int64(up), int64(down), true
}
func (c *ZIdentity) UnsafePointer() unsafe.Pointer {
return unsafe.Pointer(c.czctx)
func (zid *ZIdentity) UnsafePointer() unsafe.Pointer {
return unsafe.Pointer(zid.czctx)
}
func (c *ZIdentity) AsKey() string {
func (zid *ZIdentity) AsKey() string {
return "marker askey"
}
func (c *ZIdentity) Status() (int, error) {
return c.status, c.statusErr
func (zid *ZIdentity) Status() (int, error) {
return zid.status, zid.statusErr
}
func (c *ZIdentity) setVersionFromId() string {
if len(c.Version) > 0 {
return c.Version
func (zid *ZIdentity) setVersionFromId() string {
if len(zid.Version) > 0 {
return zid.Version
}
c.Version = "<unknown version>"
if c != nil {
if c.czctx != nil {
v1 := C.ziti_get_controller_version(c.czctx)
zid.Version = "<unknown version>"
if zid != nil {
if zid.czctx != nil {
v1 := C.ziti_get_controller_version(zid.czctx)
return C.GoString(v1.version)
}
}
return c.Version
return zid.Version
}
func (c *ZIdentity) setNameFromId() string {
if len(c.Name) > 0 {
return c.Name
func (zid *ZIdentity) setNameFromId() string {
if len(zid.Name) > 0 {
return zid.Name
}
c.Name = "<unknown>"
if c != nil {
if c.czid != nil {
if c.czid.name != nil {
c.Name = C.GoString(c.czid.name)
zid.Name = "<unknown>"
if zid != nil {
if zid.czid != nil {
if zid.czid.name != nil {
zid.Name = C.GoString(zid.czid.name)
} else {
log.Debug("in Name - c.zid.name was nil")
}
@@ -207,12 +205,12 @@ func (c *ZIdentity) setNameFromId() string {
} else {
log.Debug("in Name - c was nil")
}
return c.Name
return zid.Name
}
func (c *ZIdentity) Tags() []string {
if c.czctx != nil && c.czid != nil {
C.c_mapiter(&c.czid.tags)
func (zid *ZIdentity) Tags() []string {
if zid.czctx != nil && zid.czid != nil {
C.c_mapiter(&zid.czid.tags)
/*
it := C.model_map_iterator(&c.zid.tags)
for {
@@ -230,11 +228,11 @@ func (c *ZIdentity) Tags() []string {
return nil
}
func (c *ZIdentity) Controller() string {
if c.czctx != nil {
return C.GoString(C.ziti_get_controller(c.czctx))
func (zid *ZIdentity) Controller() string {
if zid.czctx != nil {
return C.GoString(C.ziti_get_controller(zid.czctx))
}
return C.GoString(c.Options.controller)
return C.GoString(zid.Options.controller)
}
func (zid *ZIdentity) Shutdown() {
@@ -253,24 +251,84 @@ func doZitiShutdown(async *C.uv_async_t) {
C.ziti_shutdown(ctx)
}
func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, zid *ZIdentity) []dto.Address {
type clientV1Cfg struct {
//{"addresses":["eth0.ziti.ranged","eth0.second","eth0.third"],"portRanges":[{"high":80,"low":80},{"high":443,"low":443}],"protocols":["tcp"]}
Addresses []string `json:"addresses"`
PortRanges []dto.PortRange `json:"portRanges"`
Protocols []string `json:"protocols"`
}
type v1ClientCfg struct {
//{"hostname":"192.168.15.15","port":80}
Hostname string `json:"hostname"`
Port int `json:"port"`
}
func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, zid *ZIdentity) *dto.Service {
if zid == nil {
log.Errorf("in serviceCB with nil zid??? ")
return make([]dto.Address, 0)
return nil
}
name := C.GoString(service.name)
svcId := C.GoString(service.id)
log.Debugf("============ INSIDE serviceCB - status: %s:%s - %v, %v ============", name, svcId, status, service.perm_flags)
ts := C.ziti_sdk_c_on_service(ziti_ctx, service, status, unsafe.Pointer(theTun.tunCtx))
C.ziti_sdk_c_on_service(ziti_ctx, service, status, unsafe.Pointer(theTun.tunCtx))
var protocols []string
var portRanges []dto.PortRange
var addresses []dto.Address
strClientV1cfg := C.GoString(C.ziti_service_get_raw_config(service, cCfgInterceptV1))
if strClientV1cfg != "" {
log.Tracef("intercept.v1: %s", strClientV1cfg)
var obj clientV1Cfg
uerr := json.Unmarshal([]byte(strClientV1cfg), &obj)
if uerr != nil {
log.Errorf("could not marshall json? %v", uerr)
}
protocols = obj.Protocols
portRanges = obj.PortRanges
a := make([]dto.Address, len(obj.Addresses))
for idx, add := range obj.Addresses {
a[idx] = toAddy(add)
}
addresses = append(a)
} else {
strZitiTunnelerClientV1 := C.GoString(C.ziti_service_get_raw_config(service, cCfgZitiTunnelerClientV1))
log.Tracef("ziti-tunneler-client.v1: %s", strZitiTunnelerClientV1)
var obj v1ClientCfg
uerr := json.Unmarshal([]byte(strZitiTunnelerClientV1), &obj)
if uerr != nil {
log.Errorf("could not marshall json? %v", uerr)
}
protocols = []string{"UDP", "TCP"}
portRanges = []dto.PortRange{{Low: obj.Port, High: obj.Port}}
addresses = []dto.Address{toAddy(obj.Hostname)}
}
protocols := getTunneledServiceProtocols(ts)
addresses := getTunneledServiceAddresses(ts)
portRanges := getTunneledServicePortRanges(ts)
log.Infof("service update: %s, id: %s, portocols:%s, addresses:%v, portRanges: %v", name, svcId, protocols, addresses, portRanges)
var svc *dto.Service
if status == C.ZITI_SERVICE_UNAVAILABLE {
serviceUnavailable(zid, svcId, name)
log.Debugf("serivce has become unavailable: %s [%s]", name, svcId)
f, ok := zid.Services.Load(svcId)
if ok {
svc = &dto.Service{
Name: name,
Id: svcId,
Addresses: addresses,
}
found := f.(*ZService)
if found != nil {
found.Service = nil
}
zid.Services.Delete(svcId)
return svc
} else {
log.Warnf("could not remove service? service not found with id: %s, name: %s in context %d", svcId, name, &zid)
}
} else if status == C.ZITI_OK {
pcIds := make(map[string]bool)
var postureChecks []dto.PostureCheck
@@ -315,7 +373,7 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z
}
}
svc := &dto.Service{
svc = &dto.Service{
Name: name,
Id: svcId,
Protocols: protocols,
@@ -325,46 +383,47 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z
PostureChecks: postureChecks,
IsAccessable: hasAccess,
}
added := ZService{
Name: name,
Id: svcId,
Service: svc,
Czctx: ziti_ctx,
}
se := dto.ServiceEvent{
ActionEvent: dto.SERVICE_ADDED,
Fingerprint: zid.Fingerprint,
Service: svc,
}
zid.Services.Store(svcId, &added)
go func() {ServiceChanges <- se}() //do this in a go routine just in case it gets blocked so the uv loop doesn't block
}
return addresses
return svc
}
func serviceUnavailable(ctx *ZIdentity, svcId string, name string) {
log.Debugf("serivce has become unavailalbe: %s [%s]", name, svcId)
f, ok := ctx.Services.Load(svcId)
if ok {
found := f.(*ZService)
found.Service = nil
ctx.Services.Delete(svcId)
se := dto.ServiceEvent{
ActionEvent: dto.SERVICE_REMOVED,
Fingerprint: ctx.Fingerprint,
Service: &dto.Service{
Name: name,
Id: svcId,
},
}
ServiceChanges <- se
log.Debugf("dispatched serivce has become unavailalbe to ui: %s [%s]", name, svcId)
} else {
log.Warnf("could not remove service? service not found with id: %s, name: %s in context %d", svcId, name, &ctx)
func toAddy(hostOrIpOrCidr string) dto.Address {
addy := dto.Address{
IsHost: false,
HostName: "",
IP: "",
Prefix: 0,
}
log.Debugf("parsing %s to address", hostOrIpOrCidr)
ip, ipnet, err := net.ParseCIDR(hostOrIpOrCidr)
if err != nil {
//must not be an CIDR... try IP parse
ip = net.ParseIP(hostOrIpOrCidr)
if ip == nil {
log.Debugf("%s does not appear to be an ip/cidr combination. considering it a hostname", hostOrIpOrCidr)
addy.HostName = hostOrIpOrCidr
addy.IsHost = true
} else {
log.Debugf("%s determined to be ip", hostOrIpOrCidr)
addy.IP = ip.String()
}
} else {
log.Debugf("%s appears to be a proper CIDR", hostOrIpOrCidr)
ones, _ := ipnet.Mask.Size()
addy.IP = ip.String()
addy.Prefix = ones
}
log.Tracef("parsed address: %v from %s", addy, hostOrIpOrCidr)
return addy
}
//export eventCB
@@ -402,6 +461,9 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
case C.ZitiServiceEvent:
hostnamesToAdd := make(map[string]bool)
hostnamesToRemove := make(map[string]bool)
servicesToRemove := make([]*dto.Service, 0)
servicesToAdd := make([]*dto.Service, 0)
srvEvent := C.ziti_event_service_event(event)
for i := 0; true; i++ {
@@ -409,11 +471,16 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
if unsafe.Pointer(removed) == C.NULL {
break
}
addys := serviceCB(ztx, removed, C.ZITI_SERVICE_UNAVAILABLE, zid)
for _, toRemove := range addys {
if toRemove.IsHost {
hostnamesToRemove[toRemove.HostName] = true
svcToRemove := serviceCB(ztx, removed, C.ZITI_SERVICE_UNAVAILABLE, zid)
if svcToRemove != nil {
remAddys := svcToRemove.Addresses
for _, toRemove := range remAddys {
if toRemove.IsHost && hostnameRemoved(toRemove.HostName) == 0 {
hostnamesToRemove[toRemove.HostName] = true
}
}
servicesToRemove = append(servicesToRemove, svcToRemove)
}
}
for i := 0; true; i++ {
@@ -421,19 +488,29 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
if unsafe.Pointer(changed) == C.NULL {
break
}
log.Info("service changed remove the service then add it back immediately", C.GoString(changed.name))
addys := serviceCB(ztx, changed, C.ZITI_SERVICE_UNAVAILABLE, zid)
for _, toRemove := range addys {
if toRemove.IsHost {
hostnamesToRemove[toRemove.HostName] = true
svcToRemove := serviceCB(ztx, changed, C.ZITI_SERVICE_UNAVAILABLE, zid)
if svcToRemove != nil {
remAddys := svcToRemove.Addresses
for _, toRemove := range remAddys {
if toRemove.IsHost && hostnameRemoved(toRemove.HostName) == 0 {
hostnamesToRemove[toRemove.HostName] = true
}
}
servicesToRemove = append(servicesToRemove, svcToRemove)
}
addys = serviceCB(ztx, changed, C.ZITI_OK, zid)
for _, toAdd := range addys {
if toAdd.IsHost {
hostnamesToAdd[toAdd.HostName] = true
svcToAdd := serviceCB(ztx, changed, C.ZITI_OK, zid)
if svcToAdd != nil {
addAddys := svcToAdd.Addresses
for _, toAdd := range addAddys {
if toAdd.IsHost && hostnameAdded(toAdd.HostName) == 1 {
hostnamesToAdd[toAdd.HostName] = true
}
}
servicesToAdd = append(servicesToAdd, svcToAdd)
}
}
for i := 0; true; i++ {
@@ -441,40 +518,37 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
if unsafe.Pointer(added) == C.NULL {
break
}
addys := serviceCB(ztx, added, C.ZITI_OK, zid)
for _, toAdd := range addys {
if toAdd.IsHost {
hostnamesToAdd[toAdd.HostName] = true
svcToAdd := serviceCB(ztx, added, C.ZITI_OK, zid)
if svcToAdd != nil {
addAddys := svcToAdd.Addresses
for _, toAdd := range addAddys {
if toAdd.IsHost && hostnameAdded(toAdd.HostName) == 1 {
hostnamesToAdd[toAdd.HostName] = true
}
}
servicesToAdd = append(servicesToAdd, svcToAdd)
}
}
if len(hostnamesToAdd) > 0 {
windns.AddNrptRules(hostnamesToAdd, dnsip.String())
log.Infof("mapped the following hostnames: %v", hostnamesToAdd)
svcChange := BulkServiceChange{
Fingerprint: zid.Fingerprint,
HostnamesToAdd: hostnamesToAdd,
HostnamesToRemove: hostnamesToRemove,
ServicesToAdd: servicesToAdd,
ServicesToRemove: servicesToRemove,
}
if len(hostnamesToRemove) > 0 {
windns.RemoveNrptRules(hostnamesToRemove)
log.Infof("unmapped the following hostnames: %v", hostnamesToRemove)
if len(BulkServiceChanges) == cap(BulkServiceChanges) {
log.Warn("Service changes are not being processed fast enough. This client is out of date from the controller! This is unexpected. If you see this warning please report")
} else {
BulkServiceChanges <- svcChange
}
var m = dto.IdentityEvent{
ActionEvent: dto.IdentityUpdateComplete,
Id: dto.Identity{
FingerPrint: zid.Fingerprint,
},
}
goapi.BroadcastEvent(m)
default:
log.Infof("event %d not handled", event._type)
}
}
func zitiContextEvent(ztx C.ziti_context, status C.int, zid *ZIdentity) {
zid.status = int(status)
zid.statusErr = zitiError(status)
zid.czctx = ztx
@@ -605,81 +679,35 @@ func ziti_dump_go_to_log_cb(stringsBuilder unsafe.Pointer, charData *C.char) {
sb := (*strings.Builder)(stringsBuilder)
sb.WriteString(C.GoString(charData))
}
func ZitiDumpOnShutdown(zid *ZIdentity, sb *strings.Builder) {
C.ziti_dump_to_log(unsafe.Pointer(zid.czctx), unsafe.Pointer(sb))
}
func getTunneledServiceProtocols(ts *C.tunneled_service_t) []string {
var protocols []string
next := C.stailq_first_protocol(ts)
if unsafe.Pointer(next) != C.NULL {
protocols = append(protocols, C.GoString(next.protocol))
for {
next = C.stailq_next_protocol(next)
if unsafe.Pointer(next) != C.NULL {
protocols = append(protocols, C.GoString(next.protocol))
} else {
break
}
}
}
return protocols
}
func getTunneledServicePortRanges(ts *C.tunneled_service_t) []dto.PortRange {
var values []dto.PortRange
next := C.stailq_first_port_range(ts)
if unsafe.Pointer(next) != C.NULL {
p := dto.PortRange{
High: int(next.high),
Low: int(next.low),
}
values = append(values, p)
for {
next = C.stailq_next_port_range(next)
if unsafe.Pointer(next) != C.NULL {
p := dto.PortRange{
High: int(next.high),
Low: int(next.low),
}
values = append(values, p)
} else {
break
}
}
}
return values
}
func getTunneledServiceAddresses(ts *C.tunneled_service_t) []dto.Address {
var values []dto.Address
next := C.stailq_first_address(ts)
if unsafe.Pointer(next) != C.NULL {
p := dto.Address{
IsHost: bool(next.is_hostname),
HostName: C.GoString(&next.str[0]),
IP: C.GoString(C.ipaddr_ntoa(&next.ip)),
Prefix: int(next.prefix_len),
}
values = append(values, p)
for {
next = C.stailq_next_address(next)
if unsafe.Pointer(next) != C.NULL {
p := dto.Address{
IsHost: bool(next.is_hostname),
HostName: C.GoString(&next.str[0]),
IP: C.GoString(C.ipaddr_ntoa(&next.ip)),
Prefix: int(next.prefix_len),
}
values = append(values, p)
} else {
break
}
}
}
return values
func ZitiDumpOnShutdown(zid *ZIdentity) {
sb := strings.Builder{}
C.ziti_dump_to_log(unsafe.Pointer(zid.czctx), unsafe.Pointer(&sb))
log.Infof("working around the c sdk's limitation of embedding newlines on calling ziti_shutdown\n %s", sb.String())
}
func InitTunnelerDns(ipBase uint32, mask int) {
C.ziti_tunneler_init_dns(C.uint32_t(ipBase), C.int(mask))
}
var addressCount = make(map[string]int)
func hostnameAdded(host string) int {
count := 1
if cur, ok := addressCount[host]; ok {
count = cur + 1
}
addressCount[host] = count
log.Debugf("hostname added: %s. count now: %d", host, count)
return count
}
func hostnameRemoved(host string) int {
count := 0
if cur, ok := addressCount[host]; ok {
if cur > 0 {
count = cur - 1
}
}
addressCount[host] = count
log.Debugf("hostname removed %s. count now: %d", host, count)
return count
}
+1 -1
View File
@@ -1 +1 @@
cls & "c:\Program Files\JetBrains\GoLand 2020.1.1\plugins\go\lib\dlv\windows\dlv.exe" --listen=localhost:50000 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec c:\git\github\openziti\desktop-edge-win\service\ziti-tunnel.exe debug
cls & "c:\Program Files\JetBrains\GoLand 2020.1.1\plugins\go\lib\dlv\windows\dlv.exe" --listen=localhost:50000 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec ziti-tunnel.exe debug
+60 -25
View File
@@ -27,7 +27,12 @@ import (
"strings"
)
const (
MAX_POWERSHELL_SCRIPT_LEN = 7500 //represents how long the powershell script can be. as of apr 2021 the limit was 8k (8192). leaves a little room for the rest of the script
)
var log = logging.Logger()
var exeName = "ziti-tunnel"
func GetConnectionSpecificDomains() []string {
script := `Get-DnsClient | Select-Object ConnectionSpecificSuffix -Unique | ForEach-Object { $_.ConnectionSpecificSuffix }; (Get-DnsClientGlobalSetting).SuffixSearchList`
@@ -103,8 +108,8 @@ func FlushDNS() {
}
func RemoveAllNrptRules() {
script := fmt.Sprintf(`Get-DnsClientNrptRule | Where { $_.Comment.StartsWith("Added by ziti-tunnel") } | Remove-DnsClientNrptRule -ErrorAction SilentlyContinue -Force`)
log.Debugf("removing all nrpt rules with: %s", script)
script := fmt.Sprintf(`Get-DnsClientNrptRule | Where { $_.Comment.StartsWith("Added by %s") } | Remove-DnsClientNrptRule -ErrorAction SilentlyContinue -Force`, exeName)
log.Tracef("removing all nrpt rules with: %s", script)
cmd := exec.Command("powershell", "-Command", script)
cmd.Stderr = os.Stdout
@@ -116,24 +121,27 @@ func RemoveAllNrptRules() {
}
}
var namespaceTemplate = `%s@{n="%s";}`
var namespaceTemplatePadding = len(namespaceTemplate)
func AddNrptRules(domainsToMap map[string]bool, dnsServer string) {
if len(domainsToMap) == 0 {
log.Debug("no domains to map specified to AddNrptRules. exiting early")
return
}
blockSize := 50
if len(domainsToMap) > blockSize {
log.Debugf("domainsToMap is too long [%d] and may fail on some systems. splitting the requested entries into blocks of %d", len(domainsToMap), blockSize)
}
maxBucketSize := 500
currentSize := 0
hostnames := make([]string, blockSize)
hostnames := make([]string, maxBucketSize)
ruleSize := 0
for hostname := range domainsToMap {
if currentSize >= blockSize {
ruleSize = ruleSize + len(hostname) + namespaceTemplatePadding
if ruleSize > MAX_POWERSHELL_SCRIPT_LEN || currentSize >= maxBucketSize {
log.Debugf("sending chunk of domains to be added to NRPT")
chunkedAddNrptRules(hostnames, dnsServer)
hostnames = make([]string, blockSize)
chunkedAddNrptRules(hostnames[:currentSize], dnsServer)
hostnames = make([]string, maxBucketSize)
currentSize = 0
ruleSize = len(hostname) + namespaceTemplatePadding
}
hostnames[currentSize] = hostname
currentSize++
@@ -146,21 +154,23 @@ func AddNrptRules(domainsToMap map[string]bool, dnsServer string) {
func chunkedAddNrptRules(domainsToAdd []string, dnsServer string) {
sb := strings.Builder{}
sb.WriteString(`$Rules = @(
sb.WriteString(`$Namespaces = @(
`)
for _, hostname := range domainsToAdd {
sb.WriteString(fmt.Sprintf(`@{ Namespace ="%s"; NameServers = @("%s"); Comment = "Added by ziti-tunnel"; DisplayName = "ziti-tunnel:%s"; }%s`, hostname, dnsServer, hostname, "\n"))
sb.WriteString(fmt.Sprintf(namespaceTemplate, "\n", hostname))
}
sb.WriteString(fmt.Sprintf(`)
ForEach ($Rule in $Rules) {
Add-DnsClientNrptRule @Rule
}`))
ForEach ($Namespace in $Namespaces) {
$ns=$Namespace["n"]
$Rule = @{Namespace="${ns}"; NameServers=@("%s"); Comment="Added by %s"; DisplayName="%s:${ns}"; }
Add-DnsClientNrptRule @Rule
}`, dnsServer, exeName, exeName))
script := sb.String()
log.Debugf("Executing NRPT script containing %d domains:\n%s", len(domainsToAdd), script)
log.Tracef("Executing ADD NRPT script containing %d domains. total script size: %d\n%s", len(domainsToAdd), len(script), script)
cmd := exec.Command("powershell", "-Command", script)
cmd.Stderr = os.Stdout
@@ -172,27 +182,52 @@ ForEach ($Rule in $Rules) {
}
}
func RemoveNrptRules(domainsToMap map[string]bool) {
if len(domainsToMap) == 0 {
func RemoveNrptRules(domainsToRemove map[string]bool) {
if len(domainsToRemove) == 0 {
log.Debug("no domains to map specified to RemoveNrptRules. exiting early")
return
}
sb := strings.Builder{}
sb.WriteString(`$toRemove = @(
`)
maxBucketSize := 500
currentSize := 0
hostnames := make([]string, maxBucketSize)
ruleSize := 0
for hostname := range domainsToRemove {
ruleSize = ruleSize + len(hostname) + namespaceTemplatePadding
if ruleSize > MAX_POWERSHELL_SCRIPT_LEN || currentSize >= maxBucketSize {
log.Debugf("sending chunk of domains to be added to NRPT")
chunkedRemoveNrptRules(hostnames[:currentSize])
hostnames = make([]string, maxBucketSize)
currentSize = 0
ruleSize = len(hostname) + namespaceTemplatePadding
}
hostnames[currentSize] = hostname
currentSize++
}
for hostname := range domainsToMap {
sb.WriteString(fmt.Sprintf(`"%s"%s`, hostname, "\n"))
if currentSize > 0 {
//means there's a chunk still to add....
chunkedRemoveNrptRules(hostnames[:currentSize])
}
}
func chunkedRemoveNrptRules(domainsToRemove []string) {
sb := strings.Builder{}
sb.WriteString(`$toRemove = @(`)
for _, hostname := range domainsToRemove {
sb.WriteString(fmt.Sprintf(namespaceTemplate, "\n", hostname))
}
sb.WriteString(fmt.Sprintf(`)
Get-DnsClientNrptRule | Where { $toRemove -contains $_.DisplayName } | Remove-DnsClientNrptRule -ErrorAction SilentlyContinue -Force
ForEach ($ns in $toRemove){
Get-DnsClientNrptRule | where Namespace -eq $ns["n"] | Remove-DnsClientNrptRule -Force -ErrorAction SilentlyContinue
}
`))
script := sb.String()
log.Debugf("Executing NRPT script:\n%s", script)
log.Tracef("Executing REMOVE NRPT script containing %d domains. total script size: %d\n%s", len(domainsToRemove), len(script), script)
cmd := exec.Command("powershell", "-Command", script)
cmd.Stderr = os.Stdout
+1 -1
View File
@@ -18,9 +18,9 @@ package cli
*/
import (
"fmt"
"bufio"
"encoding/json"
"fmt"
"io"
"net"
"strings"
+5 -3
View File
@@ -119,6 +119,7 @@ type TunnelStatus struct {
TunIpv4 string
TunIpv4Mask int
Status string
AddDns bool
}
type ServiceVersion struct {
@@ -152,10 +153,11 @@ type MetricsEvent struct {
Identities []*Identity
}
type ServiceEvent struct {
type BulkServiceEvent struct {
ActionEvent
Fingerprint string
Service *Service
Fingerprint string
AddedServices []*Service
RemovedServices []*Service
}
type IdentityEvent struct {
+11 -5
View File
@@ -18,17 +18,19 @@ package dto
const (
ADDED = "added"
BULK = "bulk"
REMOVED = "removed"
ERROR = "error"
UPDATED = "updated"
CHANGED = "changed"
NORMAL = "Normal"
SERVICE_OP = "service"
IDENTITY_OP = "identity"
LOGLEVEL_OP = "logLevel"
FEEDBACK_OP = "CaptureLogs"
MFA_OP = "mfa"
SERVICE_OP = "service"
BULK_SERVICE_OP = "bulkservice"
IDENTITY_OP = "identity"
LOGLEVEL_OP = "logLevel"
FEEDBACK_OP = "CaptureLogs"
MFA_OP = "mfa"
MFAEnrollmentChallengAtion = "enrollment_challenge"
MFAEnrollmentVerificationAction = "enrollment_verification"
@@ -45,6 +47,10 @@ var SERVICE_REMOVED = ActionEvent{
StatusEvent: StatusEvent{Op: SERVICE_OP},
Action: REMOVED,
}
var SERVICE_BULK = ActionEvent{
StatusEvent: StatusEvent{Op: BULK_SERVICE_OP},
Action: BULK,
}
var IDENTITY_ADDED = ActionEvent{
StatusEvent: StatusEvent{Op: IDENTITY_OP},
+6 -22
View File
@@ -25,11 +25,11 @@ import (
func dbg() {
r := rts.ToStatus(true)
events.broadcast <- dto.TunnelStatusEvent{
rts.BroadcastEvent(dto.TunnelStatusEvent{
StatusEvent: dto.StatusEvent{Op: "status"},
Status: r,
ApiVersion: API_VERSION,
}
})
svcs := make([]*dto.Service, 2)
svcs[0] = &dto.Service{
@@ -39,7 +39,7 @@ func dbg() {
Name: "Second Fake Service",
}
events.broadcast <- dto.IdentityEvent{
rts.BroadcastEvent(dto.IdentityEvent{
ActionEvent: dto.IDENTITY_ADDED,
Id: dto.Identity{
Name: "NewIdentity",
@@ -52,25 +52,9 @@ func dbg() {
Services: svcs,
Metrics: nil,
},
}
})
events.broadcast <- dto.ServiceEvent{
ActionEvent: dto.SERVICE_ADDED,
Service: &dto.Service{
Name: "New Service",
},
Fingerprint: "new_id_fingerprint",
}
events.broadcast <- dto.ServiceEvent{
ActionEvent: dto.SERVICE_REMOVED,
Service: &dto.Service{
Name: "New Service",
},
Fingerprint: "new_id_fingerprint",
}
events.broadcast <- dto.IdentityEvent{
rts.BroadcastEvent(dto.IdentityEvent{
ActionEvent: dto.IDENTITY_REMOVED,
Id: dto.Identity{
Name: "",
@@ -81,5 +65,5 @@ func dbg() {
Services: nil,
Metrics: nil,
},
}
})
}
+69 -25
View File
@@ -119,9 +119,7 @@ func SubMain(ops chan string, changes chan<- svc.Status) error {
log.Debug("shutting down. start a ZitiDump")
for _, id := range rts.ids {
if id.CId != nil {
sb := strings.Builder{}
cziti.ZitiDumpOnShutdown(id.CId, &sb)
log.Infof("working around the c sdk's limitation of embedding newlines on calling ziti_shutdown\n %s", sb.String())
cziti.ZitiDumpOnShutdown(id.CId)
}
}
log.Debug("shutting down. ZitiDump complete")
@@ -129,9 +127,9 @@ func SubMain(ops chan string, changes chan<- svc.Status) error {
requestShutdown("service shutdown")
// signal to any connected consumers that the service is shutting down normally
events.broadcast <- dto.StatusEvent{
rts.BroadcastEvent(dto.StatusEvent{
Op: "shutdown",
}
})
// wait 1 second for the shutdown to send to clients
shutdownDelay := make(chan bool)
@@ -282,7 +280,7 @@ func initialize(cLogLevel int) error {
dnsIpAsUint32 := binary.BigEndian.Uint32(ipnet.IP)
cziti.InitTunnelerDns(dnsIpAsUint32, len(ipnet.Mask))
assignedIp, t, err := rts.CreateTun(rts.state.TunIpv4, rts.state.TunIpv4Mask)
assignedIp, t, err := rts.CreateTun(rts.state.TunIpv4, rts.state.TunIpv4Mask, rts.state.AddDns)
if err != nil {
return err
}
@@ -681,8 +679,9 @@ func serveEvents(conn net.Conn) {
log.Debugf("accepting a new client for serveEvents. total connection count: %d", eventsConnections)
consumer := make(chan interface{}, 8)
events.register(randomInt, consumer)
defer events.unregister(randomInt)
id := fmt.Sprintf("serveEvents:%d", randomInt)
events.register(id, consumer)
defer events.unregister(id)
w := bufio.NewWriter(conn)
o := json.NewEncoder(w)
@@ -704,12 +703,18 @@ loop:
for {
select {
case msg := <-consumer:
err := o.Encode(msg)
if err != nil {
log.Debugf("exiting from serveEvents - %v", err)
log.Tracef("sending event to id: %s [%v]", id, msg)
eerr := o.Encode(msg)
if eerr != nil {
log.Warnf("exiting from serveEvents due to error: %v", eerr)
break loop
}
_ = w.Flush()
ferr := w.Flush()
if ferr != nil {
log.Warnf("flush error: %v", ferr)
return
}
log.Tracef("sent event to id: %s [%v]", id, msg)
case <-interrupt:
break loop
}
@@ -717,6 +722,10 @@ loop:
log.Info("a connected event client has disconnected")
}
func writerFlush(writer bufio.Writer) {
writer.Flush()
}
func reportStatus(out *json.Encoder) {
s := rts.ToStatus(true)
respond(out, dto.ZitiTunnelStatus{
@@ -901,10 +910,10 @@ func connectIdentity(id *Id) {
return true
})
events.broadcast <- dto.IdentityEvent{
rts.BroadcastEvent(dto.IdentityEvent{
ActionEvent: dto.IDENTITY_ADDED,
Id: id.Identity,
}
})
log.Infof("connecting identity completed: %s[%s] %t/%t", id.Name, id.FingerPrint, id.MfaEnabled, id.MfaNeeded)
}
}
@@ -1003,15 +1012,50 @@ func acceptServices() {
select {
case <-shutdown:
return
case serviceChange := <-cziti.ServiceChanges:
log.Debugf("processing service change event. id:%s name:%s", serviceChange.Service.Id, serviceChange.Service.Name)
change := dto.ServiceEvent(serviceChange)
events.broadcast <- change
log.Debugf("dispatched %s service change event", serviceChange.Op)
case bulkServiceChange := <-cziti.BulkServiceChanges:
log.Debugf("processing a bulk service change event. Hostnames to add/remove:[%d/%d] service notifications added/removed: [%d/%d]",
len(bulkServiceChange.HostnamesToAdd),
len(bulkServiceChange.HostnamesToRemove),
len(bulkServiceChange.ServicesToAdd),
len(bulkServiceChange.ServicesToRemove))
handleBulkServiceChange(bulkServiceChange)
}
}
}
func handleBulkServiceChange(sc cziti.BulkServiceChange) {
if len(sc.HostnamesToRemove) > 0 {
log.Debug("removing rules from NRPT")
windns.RemoveNrptRules(sc.HostnamesToRemove)
log.Info("removed NRPT rules for: %v", sc.HostnamesToRemove)
} else {
log.Debug("bulk service change had no hostnames to remove")
}
if len(sc.HostnamesToAdd) > 0 {
log.Debug("adding rules to NRPT")
windns.AddNrptRules(sc.HostnamesToAdd, rts.state.TunIpv4)
log.Infof("mapped the following hostnames: %v", sc.HostnamesToAdd)
}
be := dto.BulkServiceEvent{
ActionEvent: dto.SERVICE_BULK,
Fingerprint: sc.Fingerprint,
AddedServices: sc.ServicesToAdd,
RemovedServices: sc.ServicesToRemove,
}
rts.BroadcastEvent(be)
var m = dto.IdentityEvent{
ActionEvent: dto.IdentityUpdateComplete,
Id: dto.Identity{
FingerPrint: sc.Fingerprint,
},
}
rts.BroadcastEvent(m)
}
func handleEvents(isInitialized chan struct{}) {
events.run()
d := 5 * time.Second
@@ -1027,10 +1071,10 @@ func handleEvents(isInitialized chan struct{}) {
case <-every5s.C:
s := rts.ToMetrics()
events.broadcast <- dto.MetricsEvent{
rts.BroadcastEvent(dto.MetricsEvent{
StatusEvent: dto.StatusEvent{Op: "metrics"},
Identities: s.Identities,
}
})
}
}
}
@@ -1098,10 +1142,10 @@ func authMfa(out *json.Encoder, fingerprint string, code string) {
// when the log level is updated through command line, the message is broadcasted to UI and update service as well
func sendLogLevelAndNotify(enc *json.Encoder, loglevel string) {
events.broadcast <- dto.LogLevelEvent{
rts.BroadcastEvent(dto.LogLevelEvent{
ActionEvent: dto.LOGLEVEL_CHANGED,
LogLevel: loglevel,
}
})
message := fmt.Sprintf("Loglevel %s is sent to the events channel", loglevel)
log.Info(message)
resp := dto.Response{Message: "success", Code: SUCCESS, Error: "", Payload: message}
@@ -1112,10 +1156,10 @@ func sendLogLevelAndNotify(enc *json.Encoder, loglevel string) {
func sendIdentityAndNotifyUI(enc *json.Encoder, fingerprint string) {
for _, id := range rts.ids {
if id.FingerPrint == fingerprint {
events.broadcast <- dto.IdentityEvent{
rts.BroadcastEvent(dto.IdentityEvent{
ActionEvent: dto.IDENTITY_ADDED,
Id: id.Identity,
}
})
message := fmt.Sprintf("Identity %s - %s updated message is sent to the events channel", id.Identity.Name, id.Identity.FingerPrint)
log.Info(message)
resp := dto.Response{Message: "success", Code: SUCCESS, Error: "", Payload: message}
+13 -3
View File
@@ -21,6 +21,7 @@ import (
"bufio"
"encoding/json"
"fmt"
"golang.org/x/sys/windows"
"io"
"io/ioutil"
"net"
@@ -121,6 +122,7 @@ func (t *RuntimeState) ToStatus(onlyInitialized bool) dto.TunnelStatus {
ServiceVersion: Version,
TunIpv4: t.state.TunIpv4,
TunIpv4Mask: t.state.TunIpv4Mask,
AddDns: t.state.AddDns,
}
i := 0
@@ -162,7 +164,7 @@ func (t *RuntimeState) ToMetrics() dto.TunnelStatus {
return clean
}
func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device, error) {
func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int, applyDns bool) (net.IP, *tun.Device, error) {
log.Infof("creating TUN device: %s", TunName)
tunDevice, err := tun.CreateTUN(TunName, 64*1024-1)
if err == nil {
@@ -219,6 +221,11 @@ func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device
}
log.Info("routing applied")
if applyDns {
//for windows 10+, could 'domains' be able to replace NRPT? dunno - didn't test it
luid.SetDNS(windows.AF_INET, []net.IP{ip}, nil)
}
return ip, t.tun, nil
}
@@ -262,10 +269,10 @@ func (t *RuntimeState) LoadIdentity(id *Id, refreshInterval int) {
id.MfaEnabled = id.CId.MfaEnabled
id.MfaNeeded = id.CId.MfaNeeded
events.broadcast <- dto.IdentityEvent{
rts.BroadcastEvent(dto.IdentityEvent{
ActionEvent: dto.IDENTITY_ADDED,
Id: id.Identity,
}
})
log.Infof("connecting identity completed: %s[%s] %t/%t", id.Name, id.FingerPrint, id.MfaEnabled, id.MfaNeeded)
}
@@ -477,6 +484,9 @@ func (t *RuntimeState) ReleaseIP() {
}
func (t *RuntimeState) BroadcastEvent(event interface{}) {
if len(events.broadcast) == cap(events.broadcast) {
log.Warn("event channel is full and is about to block!")
}
events.broadcast <- event
}
+8 -5
View File
@@ -19,23 +19,23 @@ package service
type topic struct {
broadcast chan interface{}
channels map[int]chan interface{}
channels map[string]chan interface{}
done chan bool
}
func newTopic(cap int16) topic {
return topic{
broadcast: make(chan interface{}, cap),
channels: make(map[int]chan interface{}, cap),
channels: make(map[string]chan interface{}, cap),
done: make(chan bool, cap),
}
}
func (t *topic) register(id int, c chan interface{}) {
func (t *topic) register(id string, c chan interface{}) {
t.channels[id] = c
}
func (t *topic) unregister(id int) {
func (t *topic) unregister(id string) {
delete(t.channels, id)
}
@@ -48,7 +48,10 @@ func (t *topic) run() {
for {
select {
case msg := <-t.broadcast:
for _, c := range t.channels {
for id, c := range t.channels {
if len(c) == cap(c) {
log.Warnf("channel with id [%s] is about to block!", id)
}
c <- msg
}
break
+4 -4
View File
@@ -20,9 +20,9 @@
package main
const (
Version = "1.9.2"
Revision = "4e9875920983"
Branch = "main"
Version = "1.9.3"
Revision = "b6361cdb1b8d"
Branch = "release-next"
BuildUser = ""
BuildDate = "2021-04-08 11:05:00"
BuildDate = "2021-04-12 17:20:21"
)
+1 -1
View File
@@ -1 +1 @@
1.9.2
1.9.3