Netzwerke debuggen mit PowerShell
Oft kommt es vor das ich Netzwerkkonfigurationen debuggen muss, und dies meist von einem Client aus. Der folgende Artikel geht auf die verschiedenen Stolpersteine ein und zeigt, wie man Probleme sichtbar machen kann.
Hinweis: Dieser Artikel dient nur dazu Probleme transparent zu machen, deswegen werden mögliche Lösungen nur sehr grob angeschnitten.
Kurze Einführung
Um besser die Probleme zu beschreiben, will ich hier kurz die Grundarchitektur des Netzwerkes skizzieren. Dabei handelt es sich mehr oder weniger um ein vereinfachtes Bild des Netzwerkes eines Unternehmen.
Auf dem Bild sieht man drei Netze. Das wohl bekannteste Netz ist die Rote Bubble rechts. Diese stellt sehr vereinfacht das Internet dar. Die Company hat hier zwei primäre Abhängigkeiten zu diesem "Netzwerk". Einmal der public Internet Auftritt der Company und zum anderen einen Öffentlichen DNS Service, wie zum Beispiel 8.8.8.8 oder 1.1.1.1. Am Ende spielt der Anbieter hier aber erstmal keine Rolle.
Das zweite Netz ist das "private Network". Dieses Netzwerk ist das primäre Company Netzwerk. Hier finden sich z.B. alle Client PCs wieder. Dieses Netzwerk hat auch nochmal eine interne Namensauflösung über einen DNS. Dieser dient z.B. für Firmen interne Tools bzw. Webservices. Der interne DNS leitet Anfragen zu unbekannten Hostnamen an den Public DNS weiter. Somit kann der Client im privaten Netz über den Internal DNS z.B. auch bing.com auflösen. Eine weitere Besonderheit in diesem Netzwerk ist, dass kein Client direkt mit dem Internet kommunizieren kann. Dies verbietet die Firewall, welche zwischen dem Private Network und dem Internet steht. An dieser Firewall kommt ausschließlich der Proxy vorbei. Somit muss jeder Client im Netzwerk die Proxy Adresse kennen und kann auch nur via HTTP oder HTTPS mit dem Internet kommunizieren. Dies wird heutzutage primäre wegen der Sicherheit getan, die so eine Architektur mit sich bringt. Bevor es HTTPS gab wurden Proxy auch häufig zum Cachen eingesetzt. Dies ist heute durch HTTPS kaum noch möglich.
Das dritte und letzte Netz ist einfach ein DMZ (Demilitarisierte Zone) innerhalb der Company, welche ein Netzwerk darstellt in dem sensible Services liegen, welche vom restlichen Firmennetz nur über die Festgelegten Ports / Services erreichbar sein sollen. Diese ist nur mit einer Firewall vor dem Clientnetzwerk "geschützt".
Ziel dieses Beitrags soll sein, dass der Client sowohl Internetseiten benutzen kann als auch die internen Seiten aus der DMZ.
DNS Auflösung
Oftmals kommt es vor, dass DNS-Einträge falsch gesetzt sind, oder aber noch nicht auf dem Client angekommen sind. Um eine solche Vermutung zu validieren gibt es das CMDLET Resolve-DnsName
. Dieses gibt zum DNS-Name alle möglichen IP-Adressen zurück.
Resolve-DnsName codez.one
Name Type TTL Section IPAddress
---- ---- --- ------- ---------
codez.one AAAA 85106 Answer 2a01:4f8:13b:2047::2
codez.one A 85106 Answer 94.130.203.248
Sollte der Client die IP auflösen können, allerdings trotzdem einen Fehler bekommen, kann es z.B. an einer Fehlkonfiguration am Proxy liegen. Dies kann z.B. passieren wenn der Proxy nur an dem Internet DNS angebunden ist. Dann wird jede Anfrage zu internen Hostnamen auf einen Fehler laufen.
Solche Fehler sind besonders schwer zu analysieren, da fast jedes Tool einem eine richtige Konfiguration ausgibt. Eine solche Fehlkonfiguration am Proxy zeichnet sich meist dadurch aus, dass man nur eine erfolgreiche Auflösung der IP am DNS hat. Allerdings weder ein TCP-Connect auf dem Entsprechenden Port (üblicherweise 80 / 443) bekommt, noch die Webseite erfolgreich über den Proxy auflösen kann.
Route Konfiguration
Mit der DNS-Auflösung sieht man erstmal nur, ob die IP-Adressen richtig aufgelöst wurden. Die Aufgelösten Adressen sollten alle über dieselbe Route erreichbar sein. Dafür kann man das CMDLET Find-NetRoute
verwenden. Dies findet zu einer Remote Addresse die Routen, welche benutzt werden mit dem Ziel zu kommunizieren.
Find-NetRoute -RemoteIPAddress 94.130.203.248
IPAddress : 192.168.2.122
InterfaceIndex : 21
InterfaceAlias : Ethernet 2
AddressFamily : IPv4
Type : Unicast
PrefixLength : 24
PrefixOrigin : Dhcp
SuffixOrigin : Dhcp
AddressState : Preferred
ValidLifetime : 20.13:56:39
PreferredLifetime : 20.13:56:39
SkipAsSource : False
PolicyStore : ActiveStore
Caption :
Description :
ElementName :
InstanceID : :8:8:8:9:55<;55;C<8;@B8<8;55;
AdminDistance :
DestinationAddress :
IsStatic :
RouteMetric : 0
TypeOfRoute : 3
AddressFamily : IPv4
CompartmentId : 1
DestinationPrefix : 0.0.0.0/0
InterfaceAlias : Ethernet 2
InterfaceIndex : 21
InterfaceMetric : 25
NextHop : 192.168.2.1
PreferredLifetime : 21.00:00:00
Protocol : NetMgmt
Publish : No
State : Alive
Store : ActiveStore
ValidLifetime : 21.00:00:00
PSComputerName :
ifIndex : 21
Dieser Command gibt zwei Blöcke zurück. Einmal die Informationen zu der NetIpAddress
welche verwendet wird auf dem PC, um die Verbindung aufzubauen. (outgoing NIC) Und das zweite Objekt, welches zurückgegeben wird, welches die NetRoute
enthält welche für diese IP-Adresse verwendet wird.
Da wir zu unserem Hostname codez.one
zwei IPs gefunden haben, kann die Ausgabe von Find-NetRoute
schnell unübersichtlich werden. Deswegen ist der folgende Code etwas komplexer, zeichnet aber eine Übersichtliche Tabelle über alle wichtigen Informationen. Besonders wichtig ist dabei die Spalte Problems
. Ist diese bei allen abgefragten IP-Adressen gefüllt, so fehlt eine Route in das Netz des Hosts von codez.one
. Eine weitere wichtig Spalte ist die Spalte Router
. Diese gibt das Ziel der Route an, dies ist in der Regel nicht wirklich das Ziel (codez.one
) sondern ein Router.
Resolve-DnsName "codez.one" | %{
$ipAddr = $_;
try{
$routes = Find-NetRoute -RemoteIPAddress $ipAddr.IPAddress -ErrorAction Stop;
$result = [PSCustomObject]@{Interface = $routes[0]; Route = $routes[1]; Target=$ipAddr; Errors = $null;};
return $result
}catch [System.Management.Automation.RuntimeException]{
return [PSCustomObject]@{Interface =$null; Route = $null; Target = $ipAddr; Errors = $_;};
}
} | Format-Table @{Label="Target IP"; Expression={$_.Target.IPAddress}},
@{Label="Interface Index"; Expression={$_.Interface.InterfaceIndex}},
@{Label="IpAddress"; Expression={$_.Interface.IPAddress}},
@{Label="ipv4/ipv6"; Expression={$_.Interface.AddressFamily}},
@{Label="Route"; Expression={$_.Route.DestinationPrefix}} ,
@{Label="Router"; Expression={$_.Route.NextHop}},
@{Label="Problems"; Expression={$_.Errors}};
Target IP Interface Index IpAddress ipv4/ipv6 Route Rout
er
--------- --------------- --------- --------- ----- ----
2a01:4f8:13b:3008::2 20 2003:c4:9f14:d4ac:35ee:42a0:bc0c:55bc IPv6 ::/0 fe8…
94.130.140.55 20 192.168.2.122 IPv4 0.0.0.0/0 192…
Wichtig ist hier, dass der Client nicht unbedingt von einer Route zum Ziel wissen muss. Diese muss streng genommen nur der Proxy wissen.
Dies ist z.b. eine valide Konfiguration in einem Proxy Netzwerk. Hier kennt der Client z.B. den Weg zu den IP-Addressen von codez.one
nicht.
Target IP Interface Index IpAddress ipv4/ipv6 Route Router Problems
--------- --------------- --------- --------- ----- ------ --------
2a01:4f8:13b:3008::2 The network location cannot be reached. For information about network troubleshooting, see Windows Help...
94.130.140.55 The network location cannot be reached. For information about network troubleshooting, see Windows Help...
Bekommt aber trotzdem über den Proxy die richtige Antwort.
Testen ob der Port im Ziel geöffnet ist
Nun sind wir an einem Punkt wo wir sicher sind das unser Client sowohl die IP-Adresse unseres Zieles rausfinden kann als auch die Netzwerk-Topologie der Routen sichergestellt ist. Der Nächste Punkt ist nun zu Prüfen ob die Firewall (bzw. alle Firewalls auf dem Weg zum Host) den Traffic auch durchlassen. Dazu gibt es das CMDLET Test-NetConnection
. Mit diesem kann man einen Verbindungstest auf einem bestimmten Port ausführen.
Test-NetConnection "codez.one" -Port 443
ComputerName : codez.one
RemoteAddress : 2a01:4f8:13b:2047::2
RemotePort : 443
InterfaceAlias : Ethernet 2
SourceAddress : 2003:c4:9f14:d4ac:35ee:42a0:bc0c:55bc
TcpTestSucceeded : True
Dies kann man auch wieder für alle IPAddress des Hostnames machen.
Resolve-DnsName "codez.one" | %{
Test-NetConnection $_.IPAddress -Port 443
} | Format-Table @{Label="Target IP"; Expression={$_.ComputerName}},
@{Label="Target Port"; Expression={$_.RemotePort}},
@{Label="TCP Test"; Expression={$_.TcpTestSucceeded}}
Target IP Target Port TCP Test
--------- ----------- --------
2a01:4f8:13b:2047::2 443 True
94.130.203.248 443 True
Dieser Test wird aber immer fehlschlagen, wenn man z.B. in einem Unternehmensnetzwerk ist, welches einen Proxy benutzt. In der Regel verbieten diese Netzwerke eine direkte Verbindung in verschiedene Netze. (z.B. Internet) Dies wird getan, um den Trafik besser verwalten zu können, da der gesamte Trafik über den Proxy geht und dieser nur bestimmte Protokolle unterstützt. Außerdem kann dieser mit einer Blacklist auch eine Schutzfunktion erfüllen, um schädliche Website für die Nutzer direkt zu blockieren.
Zeigt ein solcher Test an, dass man direkten Zugriff auf das Ziel hat und ein direktes Aufrufen der Webseite im Browser führt dennoch zu einem Fehler, ist dies ein Indiz dafür, das dem Client hier eine Ausnahme fehlt.
Dies sieht man an dem Schaubild sehr deutlich. Hier versucht der Proxy auf die interne DMZ zuzugreifen, Aufgrund der Anfrage des Clients. Dies wird aber aktiv von der DMZ Firewall blockiert. Nach dem Eintragen der Proxy Ausnahme würde der Client direkt über die Firewall der DMZ auf den internen Service zugreifen.
Testen der Verbindung in einem Proxy Netzwerk
Theoretisch zeigen die oben aufgeführten Diagnosen alle Probleme die man in einem Netz haben kann. Allerdings sind Proxy Netzwerke hier etwas anders. Wie bereits erwähnt wird der Test auf offenen Port fehlschlagen, bei Adressen die jenseits des Proxys leben. Trotzdem kann man allerdings Webseiten besuchen, welche hinter dem Proxy leben. Dies hat den Hintergrund das der Client immer nur einen Auftrag an den Proxy weiterreicht und der Proxy dann die eigentliche Request ausführt. Dies ist nix was der Client beeinfluss kann. Nun ist es aber so, dass ein Proxy nur das kennen kann was für ihn bekannt ist. In dies kann vom Client stark abweichen. Deswegen gibt es Proxy-Ausnahmen. Diese können auf dem Client konfiguriert werden, und versuchen über die "normalen" Netzwerkwege ihr Ziel zureichen.
Um zu sehen ob ein Problem vorliegt kann man versuchen mit Invoke-WebRequest
den Fehler zu analysieren.
Dazu müssen wir uns die Exception genauer anschauen. Genauer den ServicePoint
und die Proxy
Eigenschaft. Sind diese identisch wissen wir das die Request definitiv über den Proxy gelaufen ist. Dies führt zu Fehlern bei Anfragen die z.B. nur im localen DNS eingetragen sind. (z.B. Entwicklungsserver) Selbes gilt natürlich auch andersherum, sollte man eine Ausnahme zu viel eingerichtet haben geht diese nicht über den Proxy und wird von der Firewall blockiert.
Um das zielgerichtet Debugging leichter zu machen habe ich ein kleines Module angefangen zu schreiben, welches ihr einfach Installieren könnt:
Install-Module -Name CZ.PowerShell.NetworkTools -Scope CurrentUser -AllowPrerelease
In diesem ist das CMDLET Test-CzProxyConfiguration
. Diesem kann man eine URL übergeben.
Test-CzProxyConfiguration "https://www.codez.one/"
Damit bekommt man dann z.b. folgende Ausgabe:
TestedHostname DirectAccessPossible Message
-------------- -------------------- -------
www.codez.one True [OK] Everything is configure right for 'www.codez.one'.
Diese Ausgabe zeigt, das ein direkter Zurgriff für meinen PC möglich ist und das ich für diesen Hostname auch keinen Proxy verwende.
Sollte ein Proxy verwendet werden, wird eine Warnung ausgegeben:
TestedHostname DirectAccessPossible Message
-------------- -------------------- -------
www.codez.one True [WARN] You can access the site. The configuration isn't optimal, because you have direct access but uses the proxy. Add 'www.codez.one' to the ByPass list.
Sollte der Proxy z.b. keinen Zugriff auf die Seite Codez.one
kann eine Ausgabe z.B. so aussehen:
TestedHostname DirectAccessPossible Message
-------------- -------------------- -------
www.codez.one True [ERROR] You have direct access but uses the proxy. Add 'www.codez.one' to the ByPass list.
Hoffe dieses Module hilft einigen. Ich würde mich auch über Issues und PullRequests freuen.
Für mehr Details kann man die Command-Beschreibung lesen.