IIS rapidFailProtection - Schutz für schnelle Fehler: IIS Administratoren sollten diese Option eventuell deaktivieren

27.11.2020 15:51:08, Jürgen Auer, keine Kommentare

Innerhalb des Internet Information Servers (IIS) gibt es seit langem die Option, ApplicationPools / Anwendungspools mit der Option rapidFailProtection = true zu konfigurieren.

Die Folge: Stürzt eine Gesamtanwendung / Website, die diesen ApplicationPool verwendet, bsp. 5 mal innerhalb von 5 Minuten ab, wird die Website sicherheitshalber angehalten. Üblicherweise wird dann ein 503-Status "Dienst nicht verfügbar" ausgegeben.

Die beiden Werte "5 Abstürze" innerhalb von 5 Minuten lassen sich über die beiden Parameter rapidFailProtectionMaxCrashes / rapidFailProtectionInterval individuell konfigurieren.

Üblich ist die Empfehlung, diese Standardeinstellungen zunächst so zu übernehmen. Soweit die Theorie.

Ergänzend: Der "Absturz" einer Gesamtanwendung ist strikt zu unterscheiden vom Absturz einer einzelnen Seitenausführung. Die Ausführung einer einzelnen Seite kann problemlos wiederholt abstürzen, ohne deshalb die Gesamtanwendung crashen zu lassen.

--

Konsequenz: Seit dem Start von Server-Daten 2006 wurde rapidFailProtection = "true" mit diesen beiden Standardwerten genutzt. In den letzten 14 Jahren kam es nie zu einem Anhalten der Website aufgrund von 5 Anwendungsabstürzen innerhalb von 5 Minuten.

Selbst bei neu eingespieltem und noch fehlerhaftem Code auf dem Testsystem stürzte nur die Seitenausführung ab. Lediglich in ganz wenigen Fällen, in welchen Initialisierungscode der Gesamtanwendung geändert und bsp. eine Kapselung mit Try/Catch vergessen wurde, kam es auf dem Testsystem zu Abstürzen des Gesamtsystems. Die nun aber natürlich nicht fünfmal hintereinander ausgeführt wurden, da externe Nutzer keinen Zugriff aufs Testsystem haben. Den Code gefixt, dann funktionierte das wieder.

--

Vorgestern, am Mittwochmorgen, gab es innerhalb von wenigen Minuten 5 Crashes der Gesamtanwendung. Die Folge: Aufgrund von rapidFailProtection = true wurde die Website angehalten. Das bemerkt, die Website wieder gestartet, ab dann lief wieder alles wie gewohnt. Das einzigste Problem. Zum Zeitpunkt des Crashs war ich offline. Als ich online war, war das eine Sache von Minuten.

--

Ein Nachforschen listete diverse "komische Aufrufe", etwa /.|/.|/.|/winnt.ini mit zusätzlichen Headern. Ferner im EventLog Aufzeichnungen der Crashs, die immer den folgenden StackTrace ausgaben:

--
StackTrace:
bei System.Web.CachedPathData.GetPhysicalPath(VirtualPath virtualPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.CachedPathData.GetConfigPathData(String configPath)
bei System.Web.HttpContext.GetFilePathData()
bei System.Web.Configuration.RuntimeConfig.GetConfig(HttpContext context)
bei System.Web.HttpCookie.SetDefaultsFromConfig()
bei System.Web.HttpRequest.CreateCookieFromString(String s, Boolean useConfiguredDefaults)
bei System.Web.HttpRequest.FillInCookiesCollection(HttpCookieCollection cookieCollection, Boolean includeResponse)
bei System.Web.TraceContext.EndRequest()
bei System.Web.Util.Profiler.EndRequest(HttpContext context)
bei System.Web.HttpApplication.ReleaseAppInstance()
bei System.Web.HttpRuntime.FinishPipelineRequest(HttpContext context)
bei System.Web.HttpRuntime.FinishRequestNotification(IIS7WorkerRequest wr, HttpContext context, RequestNotificationStatus& status)
bei System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)
bei System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
bei System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
--

Eine StackTrace-Ausgabe zeigt, welche Schachtelung von Funktionen beim Crash grade aufgerufen wurde. Den StackTrace muß man von unten nach oben lesen.

System.Web.HttpApplication.ReleaseAppInstance - Ende der Verarbeitung, gib die Instanz frei.
System.Web.Util.Profiler.EndRequest - Methode, die zum Schluß der Bearbeitung einer Seitenverarbeitung aufgerufen wird
System.Web.HttpRequest.FillInCookiesCollection - Sammeln der zu verschickenden Cookies
System.Web.HttpRequest.CreateCookieFromString - ein Cookie soll erzeugt werden
System.Web.HttpCookie.SetDefaultsFromConfig - dafür wird nach den Standardwerten in der Config-Datei gesucht
System.Web.Configuration.RuntimeConfig.GetConfig - dafür wird auf die aktuelle Konfiguration zugegriffen
System.Web.HttpContext.GetFilePathData - dafür muß der absolute Pfad der Konfigurationsdatei ermittelt werden
System.Web.CachedPathData.GetConfigPathData - dafür werden verschiedene Orte durchsucht
System.Web.CachedPathData.GetPhysicalPath - der Versuch, beim letzten Aufruf den Pfad zu ermitteln, führt zu einem Crash.

Dieser Crash wurde von den früheren Aufrufen nicht abgefangen. Also crashte die Gesamtanwendung. Das wiederholte sich fünf mal. Das stoppte die Gesamtanwendung.

--

Man sieht: Es gibt nirgendwo einen Aufruf einer Server-Daten-Methode dazwischen. Das sind nur Aufrufe innerhalb der .NET-Architektur.

Ferner handelt es sich um "Aufräumarbeiten" am Ende der Verarbeitung eines Aufrufs. Vor dem Senden der Daten müssen die Cookies gesendet werden. Dafür muß geprüft werden, ob zu einem Cookienamen Einträge in Konfigurationsdateien existieren. Dafür wird die hierarchische Liste von möglichen Konfigurationsdateien "von unten nach oben" durchlaufen.

Folgerung: Es handelt sich um einen Bug innerhalb der .NET-Architektur, bei dem eine Methode System.Web.CachedPathData.GetPhysicalPath fehlschlägt, das nicht abgefangen ist und deshalb die übergeordnete Anwendung crasht.

Es kann sein, daß der Bug uralt ist und seit Jahren in der .NET-Architektur schlummert. Oder daß der Bug bei einem der letzten Patches neu reingekommen ist. Ferner macht sich der Bug nur dann bemerkbar, wenn gewisse "komische Urls" mit weiteren Headern abgerufen werden. Aber die .NET-Architektur muß es aushalten, wenn irgendwelche "komischen Urls" mit zusätzlichen komischen Headern abgerufen werden, ohne deshalb zu crashen.

Konsequenz: Da nach dem Anhalten der Website ein manuelles Eingreifen für einen Neustart notwendig ist und dies nicht 24 Stunden möglich ist, wurde die Einstellung

rapidFailProtection = false

gesetzt.

Das eigentliche Problem des Crashens läßt sich damit also nicht lösen. Aber mehrere Crashs führen nicht mehr zu dem Seiteneffekt einer gestoppten Anwendung. Wobei es durchaus Fälle geben kann, in denen ein Stoppen der Anwendung und eine Überprüfung des Problems sinnvoller ist als ein simples Neustarten. Etwa dann, wenn eigener Code crasht.

Aber crasht lediglich .NET-Code und können solche Crashs "von außen" her zu jeder beliebigen Uhrzeit ausgelöst werden, dann ist das Risiko zu groß, daß zum Zeitpunkt eines solchen Anhaltens grade nicht eingegriffen werden kann. Deshalb - aufgrund dieser neuen Erfahrungen - nun die Änderung nach 14 Jahren auf rapidFailProtection = false.

--

Wer also ebenfalls einen IIS online betreibt und bei sich ab und zu "komische Hackversuche" sieht (die es seit Jahren gibt und die bis jetzt nie zu einem solchen Crash geführt hatten), der sollte prüfen, ob er nicht diese rapidFailProtection ebenfalls abschaltet. Oder die Werte bsp. auf 30 Crashs in einer Minute abändert, so daß zumindest nicht nach 5 Crashs innerhalb von 5 Minuten gestoppt wird.

Ferner läuft innerhalb von Server-Daten schon seit Jahren eine Logik, die bei jedem Neustart von Server-Daten eine Mail verschickt. Da die Anwendung regelmäßig um 03:00 neu gestartet wird, gibt es eine Mail täglich. Ferner gibt es Mails, falls neuer Code eingespielt wird oder die Server gepatcht und neu gestartet werden. Damit können "unerwartete Neustarts" einfach überwacht werden (die es bis jetzt praktisch nie gab).

Fazit: Die Option rapidFailProtection ist "gut gemeint", aber für ein System, bei dem es ständig Hackversuche gibt und bei dem solche versteckten .NET-Bugs getriggert werden können, im Zweifelsfall ungeeignet. Bislang war ich eher davon ausgegangen, daß es solche versteckten .NET-Bugs, welche die Gesamtanwendung abstürzen lassen, nicht gibt. Diese Einschätzung war zu optimistisch. Also wird die Option abgeschaltet.

|<<>>|1 / 11