Wenn auf einen RPD-Zugang, aus dem Internet ohne ihn durch eine VPN-Tunnel zu schleusen, nicht verzichtet werden kann/will gibt es kein Möglichkeit den Zugang zu Filtern. Der Router leitet alle RDP-Anfragen an den Server weiter wenn dies so eingerichtet ist.
Mit ein paar keine Skripten kann aber dennoch überprüft werden ob zumindest die IP von welcher die Anmeldung erfolgt erlaubt ist und / oder ob der PC-Name korrekt ist. Wenn keine feste IP des RDP-Users bekannt ist gibt es noch die Möglichkeit via Dyn(namisches) DNS die IP zu Verifizieren. Aber dabei muss auf Userseite der DynDNS-Eintrag vor dem Verbindung erfolgen z.B. via Router ;-)

Des weiteren muss in den Überwachungs-Richtlinien das Anmelden eingeschaltet werden. Das geht über die lokalen Gruppenrichtlinien (gpedit.msc).

Richtlinien für Lokaler Computer > Computerconfiguration > Windows-Einstellungen > Sicherheitseinstellungen > Lokale Richtlinien > Überwachungsrichtlinie => Anmeldeereignisse überwachen : Diese Versucht überwachen Erfolgreich aktivieren lokale gruppenreichtline anmeldeereiginsse ueberwachen 
Sicherheitseinstellungen > Erweiterte Überwachungsrichtlinien > Systemüberwachungsrichtlinien - Lokales Gruppenrichtlinienobjekt > Anmelden/Abmelden -> Anmelden überwachen => folgende Überwachungsereiginsse konfigurieren Erfolg aktivieren.  



Das Überprüfen setzt allerdings voraus dass der User Administrator-Rechte hat. Doch gerade bei Server-System ist zwingend davon abzuraten einem User Admin-Rechte zu geben.
Es bleibt dann nur die Möglichkeit über ein Hilfsprogramm zeitweise Admin-Rechte zu bekommen.

Es werden 2 Beispiele mit RunAsTool und RunAsRob aufgezeigt.
- RunAsRob funktioniert in einer produktiven Umgebung nur mit einer gekauften Version! Sonst verlangt auf RusAsRob jedes mal eine Bestätigung zur Ausführung
- RunAsTool funtioniert nur wenn die Benutzerkontensteuerung UserAccountControlSettings.exe auf "Nie benachrichtigen" eingestellt ist sonst muss immer der Bestätigung-Dialog beantwortet werden.
benutzerkontnsteuerung

Der Vorgang besteht aus mehreren Teilen:
A) Eine Aufgabenplanung die bei der Anmeldung getriggert wird. Login und Relogin löses ein Startskript aus das nur relativ kurz sichtbar ist (Geht leider nicht ohne sichtbares CMD bzw. Powershell Fenster)
B) Ein Startskript welches durch die Aufgabenplanung gestartet wird.
C) Ein Powershell-Skript welches das Ereignis-Log auswertet (Hier werden die Admin-Rechte benötigt). Realisiert wird das via RunAsTool bzw. RunAsRob.
D) Ein zweites Powershell-Script liest die Info's aus dem ersten Skript ein und wertet die Einträge für den angemeldeten User aus. Danach wird in eine zweite Logdatei die Auswertung geschrieben und je nach Einstellung ein Email versendet und der User abgemeldet.
E) Ein weiteres Powershell-Skript kann dann ggf. das Benutzerkonto sperren und ein relogin verhindern.

Hier nun die einzelnen Punkte erklärt

A) Aufgabenplanung erstellen
    - Aufgabenplanung starten (taskschd.msc) und im Menü > Aktion > Aufgabe erstellen aufrufen
      * Register Allgemein:
        - Als erstes in den Sicherheitsoptionen die Benutzer-Gruppe auswählen aus dem AD oder lokal deren Gruppe verwendet werden soll.
        - Nur ausführen wenn der Benutzer angemeldet ist
        - Ausgeblendet anklicken
      * Register Trigger:
         Die beiden Aktionen auswählen und beide male "Jeder Benutzer" auswählen.
         - "Bei Anmeldung"
         - "Bei Verbindung mit Benutzersitzung" (Relogin)
      * Register Aktionen:
        Hier wird das "Startskript" eingetragen welche unter A beschrieben ist und die weiteren Aktionen B und C verdeckt ausführt. 
        - Programm Script: cscript.exe
        - Parameter: /nologo c:\checkrdp\run_initila.vbs (absoluter Skriptpfad)
        bzw.
        - Programm Script: powershell.exe
        - Parameter: -nologo c:\checkrdp\run_initila.ps1 (absoluter Skriptpfad)
      * Register Bedingungen: keine Auswahl
      * Register Einstellungen:
        - "Ausführen der Aufgabe bei Bedarf zulassen" auswählen
        - "Aufgabe beenden, falls Ausführung länger als 1 Stunde" aktivieren

 => Aufgabe speichern
Bei der nächsten Anmeldung per RDP wird jetzt der Benutzer überprüft

  aufgabenplanung rdp check allgemein aufgabenplanung rdp check trigger
aufgabenplanung rdp check trigger 2 aufgabenplanung rdp check aktion
aufgabenplanung rdp check bedingungen
aufgabenplanung rdp check einstellungen


B)  Dieses Skript (run_initial.vbs bzw. (run_initial.ps1)  hat nur die Aufgabe das folgende Skript (run_hidden.ps1) versteckt zu starten und enthält nur einen Befehl. Somit ist die Ausführungszeit recht kurz und das Fenster ebenfalls nur kurz zu sehen.
Das 2. Script (run_hidden.ps1) ruft wiederum das Skript um das Ereignis Protokollauszulesen (loginevents.ps1) auf und wartet bis es abgeschlossen ist. Danach das Script (checkuser.ps1) um die Zugangsparameter zu überprüfen.

C) Das Login-Evens-Skript durchläuft eine vorgegebene und gefilterte Anzahl von Ereignissen (ID 4624,4778) und schreibt das Ergebnis in eine Text-Datei (loginevents.txt)
   - Username
   - PC-Name RDP-Client (Smartphone- und iPad-Namen werden nicht in das Event-log eingetragen!).
   - IP-Adresse
   - Ameldezeitpunkt
   - Art des Logins

 D) Das Überprüfe-Benutzer-Skript (checkuser.ps1) holt die Infos des eigenen Benutzers aus der (loginevents.txt) und vergleicht diese mit den Vorgaben.
   - IP-Adresse (einzelne oder P-Bereich
   - DynDNS-Eintrag
   - Clinet-Name

Nach der Überprüfung kann bei Unstimmigkeiten folgende Aktionen ausgeführt werden:
   - nicht machen
   - Info-Email versenden (1 oder mehrere Empfänger)
   - Benutzer abmelden
   - Benutzerkonto sperren (zusätzlich zum abmelden)

Zusaätzlich wird ein Ereignis-Log erstellt werden:
   - Alle Logins protokollieren
   - Nur Ereignisse Protokollieren welche Ungereimtheiten hatten

Zusatz-Funktion
   - Für alle die den Kriterien Entsprochen haben kann eine Pop-Up-Box angezeigt werden. Einfach eine Datei mit Inhalt erzeugen (clientinfo.txt)

E) Sollte die Überprüfung ergeben dass der Login von einen falschen PC oder IP-Adresse erfolgt ist kann das Konto deaktiviert werden (deactivateuser.ps1).


Hier die Scripte

run_initial.ps1

# Startscript (Kann nicht versteckt ausgeführt werden)
if ([string]::IsNullOrEmpty($PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition} # Powershell 2.0
$agl = $PSScriptRoot + '\run_hidden.ps1'
Start-Process -FilePath  'powershell.exe' -ArgumentList $agl -WindowStyle Hidden

run_initial.vbs

' CSCRIPT
'CreateObject("Shell.Application").ShellExecute "cscript.exe", " /NoLogo " & Replace(WScript.ScriptFullName, WScript.ScriptName, vbNullString) & "run_hidden.vbs", vbNullString, vbNullString, 0
' Powershell
CreateObject("Shell.Application").ShellExecute "powershell.exe", " -NoLogo " & Replace(WScript.ScriptFullName, WScript.ScriptName, vbNullString) & "run_hidden.ps1", vbNullString, vbNullString, 0


run_hidden.ps1

<#-----------------------------------------------------------------------------------------
 Dieses Script wird vom 1. Startscript run_initial.vbs (.ps1) aufgerufen. Ab jetzt wird 
 alles unsichtbar ausgeführt. 
 Es wird ein Hilfsprogramm benötigt um das Powershell-Script 'loginevents.ps1' mit 
 Administrator-Rechten zu starten
 Es sind 3 Tools als bsp. aufgeführt
 - RunAsRob  
   Funktioniert nur mit einer lizenzierten Version!
   Alle Parameter müssen in RunAsRob eingetragen und dort die .xus erstellt werden.
   Passwort wird nicht entschlüsselt 
   
 - RunAsTool 
   Einstellung zur Benuterkontensteuerung muss auf 'nie Benachrichtigen' gesetzt werden. Im Suchenfeld UAC eingeben
   Freeware &gt; https://www.sordum.org/8727/runastool
   Alle Parameter müssen im RunAsTool eingetragen und ein Link erzeugt werden.
   Der Programmpfad und Start-Parameter stehen in diesem Link   
   
 - PsExec   
   PsTools &gt; https://docs.microsoft.com/en-us/sysinternals/downloads/pstools
   Das Programm ist eigentlich dafür gedacht auf einem entfernten PC/Server als ein
   bestimmter Benutzer etwas auszuführen. Hier wird der eingene PC/Server verwendet
   Zu Beachten: Das Passwort wird während des Scripts dekodiert und kann ausgelesen werden!
   
   Version 1.8
-----------------------------------------------------------------------------------------#>

$agpS = 'powershell.exe'
if ([string]::IsNullOrEmpty($PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition} # Powershell 2.0

#-----------------------------------------------------------------------------------------------------------------------
# RunAsRob 
$appRunAsAdmin = $PSScriptRoot + '\RunasRob.exe' #Pfad zum Programm RunAsRob
$argRunAsAdmin = $PSScriptRoot + '\loginevents.xus'

#-----------------------------------------------------------------------------------------------------------------------
# RunAsTool
#$appRunAsAdmin = $PSScriptRoot + '\RunAsTool_x64.exe' #"C:\Program Files\RunAsTool\RunAsTool.exe"
#$argRunAsAdmin = 'ehq' # Steht in der Verknüpfung (durch RunAsTool erstellt)

#-----------------------------------------------------------------------------------------------------------------------
# PsExec
#$appRunAsAdmin = $PSScriptRoot + '\PsExec64.exe' #'c:\checkrdp\PsExec64.exe' #Pfad zum Programm PSExec
#$admin = 'administrator'                # Benutzer
#$pwd = $PSScriptRoot + '\admin.cred'    # Passwort oder Pfad zur Datei welche mit 'create_credentials.ps1' erzeugt wurde
#$pwd = "nicht so geheimes PW ;-)"

#if (Test-Path $pwd) { # In $pwd steht der Pfad mit den Passwort-Credentials
#  $argRunAsAdmin = '\\' + $Env:COMPUTERNAME, '-u ' + $admin, ('-p ' + [System.Runtime.InteropServices.Marshal]::PtrToStringUni([System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode((GET-Content $pwd | ConvertTo-SecureString)))), $agpS, ($PSScriptRoot + '\loginevents.ps1')
#} else {
#  $argRunAsAdmin = '\\' + $Env:COMPUTERNAME, '-u ' + $admin, "-p $pwd", $agpS, ($PSScriptRoot + '\loginevents.ps1')
#}
#-----------------------------------------------------------------------------------------------------------------------

# Auslesen des Event.logs mit Administrator-Rechten
Start-Process -FilePath $appRunAsAdmin -ArgumentList $argRunAsAdmin -Wait -WindowStyle Hidden

# Benutzer überprüfen
$arg = $PSScriptRoot + '\checkuser.ps1'
Start-Process -FilePath $agpS -ArgumentList $arg -WindowStyle Hidden


loginevents.ps1

<#-------------------------------------------------------------------------------------------------------------------------------------
  Das Script liest die Event Einträge des Sicherheits-Logs aus.
  Es muss mit Administrator-Rechten ausgeführt werden!
  Info: Ereignisanzeige starten -&gt; %windir%\system32\eventvwr.msc /s  (Filter auf die EventID's 4624,4778 setzen)
  Folgene Info's werden ausgelesen
  - Benutzer-Name
  - Client-Name (nicht immer vorhanden. z.B Login mit Android, MacOS)
  - IP-Adresse des Clients
  - Datum & Uhrzeit
  - Art des Logins
  - Logische Info-Eintag über den User bis dieser vollständig eingelesen wurde.
  Das Log wird chronologisch rückwärts durchlaufen. Der letzte Eintrag ist Ereignis 10 und der erste Ereignis 3 die ausgewertet werden.
  Es kommt vor dass ein Ereigis 3 nicht erzeugt wird
  Ein Relogin erzeugt nur ein Eintrag.

  Vier Typen können ausgelesen werden. Login, Relogin, fehlgeschlagene Logins und Logout.
  Im Standard-Betrieb werden aber nur die erfolgreichen Login und Relogins benötigt.

  Script-Ausführungszeit
  Je nach Anzahl der geichzeitigen Logins muss die $newest Variable in der Funktion GET-LoginInfoOfUsers angepasst werden.
  Je größer desto länger dauert das Auslesen der Ereignisse. 50-100 sollten ausreichend sein.
  Version 2.0
--------------------------------------------------------------------------------------------------------------------------------------#>

if ([string]::IsNullOrEmpty($PSCommandPath)) { # Powershell 2.0
  $PSCommandPath = $MyInvocation.MyCommand.Definition
  $PSScriptRoot = Split-Path -Parent -Path $PSCommandPath 
}

# Dateipfad der Auswertung (Namensgleich dem Script).
$loginevents = $PSCommandPath.Replace('.ps1', '') + '.txt'

$deactivateuserPath = (Split-Path -Parent -Path $PSScriptRoot) + '\deactivateuser.txt'

# Müssen identisch mit chechuser.ps1 sein!
$sep = '|'        # Feld-Trennzeichen "`t" (Tabulator)
$missing = '-?-'  # Eintrag wenn ein Wert nicht ermittelt werden kann


Function GET-LoginInfoOfUsers {
  [CmdletBinding()]
  Param (
    [Parameter(Mandatory=$false,Position=0)]
	[String]$hostname=$Env:COMPUTERNAME,
    [Parameter(Mandatory=$false,Position=1)]
    [Int32]$newest=20,
    [Parameter(Mandatory=$false,Position=2)]
    [Int64[]]$eventtyp=(4624,4778), #4625 #4634
	[Parameter(Mandatory=$false,Position=3)]
	[String]$username
  )

  #In dieses Array werden die Login-Infos eingetragen
  # 0 -&gt; Username
  # 1 -> PC-Name (Client)
  # 2 -> IP-Adresse
  # 3 -&gt; Ameldezeitpunkt
  # 4 -> Art des Logins
  # 5 -&gt; Hilfsfeld für die Loginerfassung ob die Informationen schon komplett sind
  $arrUser = @()

  $events = get-eventlog security -InstanceID $eventtyp -ComputerName $hostname -newest $newest
  
  # Sollte testweise ein bestimmter Zeitabschnitt geprüft werden. Benötigt deulich länger um ein Ergebnis zu liefern
  #$Begin = Get-Date '13/9/2019 16:15:00'
  #$End = Get-Date  '13/9/2019 16:16:00'
  #$events = get-eventlog security -InstanceID $eventtyp -ComputerName $hostname -After $Begin -Before $End
  
  # Die einizelnen Ereignisse durchlaufen
  foreach($event in $events) {
    # In Message steht alles
    $Message = $event.Message
    $arrMessage = ($Message -split "`n").Trim()
    $kontoname = ($arrMessage -match 'Kontoname:').replace('Kontoname:', '').Trim() # English 'Account Name:'  oder? 'TargetUserName:'
    #$s =($kontoname -join ' | ') + ' | ' + $event.InstanceId  + ' | ' +  (Get-Date -format 'hh.mm.ss')
    $locked = $false
    $ip = ''
    switch ($event.InstanceId) {
      4624 { # Bei einem Login werden mehrere Ereignisse protokolliert
        $user = $kontoname[1]
        $atyp = ($arrMessage -match 'Anmeldetyp:').replace('Anmeldetyp:', '').Trim() # English 'LogonType:'
        if ($atyp -eq 3) {$locked = $true}		
        $pc = ($arrMessage -match 'Arbeitsstationsname:').replace('Arbeitsstationsname:', '').Trim() # English 'WorkstationName:'
        $ip = ($arrMessage -match 'Quellnetzwerkadresse:').replace('Quellnetzwerkadresse:', '').Trim()  # English 'IpAddress:'		
        $login = 'login'
        break
      }
      4778 { # Bei einem Relogin gibt es nur einen Eintrag im Ereignisprotokoll. Wird nach dem Login-Ereignis erzeugt
        $atyp = 10 # Damit ein neuer Eintrag erzeugt wird
        $user = $kontoname
        $pc =  ($arrMessage -match 'Clientname:').replace('Clientname:', '').Trim()
        $ip =  ($arrMessage -match 'Clientadresse:').replace('Clientadresse:', '').Trim()
        $locked = $true
        $login = 'relogin'
        break
      }
      4634 {# Abmelden
        $user = $kontoname
        $atyp = ($arrMessage -match 'Anmeldetyp:').replace('Anmeldetyp:', '').Trim() # English 'LogonType:'
        $pc =  ($arrMessage -match 'Kontodomäne:').replace('Kontodomäne:', '').Trim()  # English 'Account Domain:'
        $login = 'logout'
        break
      }
      4625 {# Fehlgeschlagene Logins
        $user = $kontoname[1]
        $login = 'faillogin'
        $locked = $true
        break
      }
    }

    $j = -1 # Vorbelegen falls es noch kein Eintrag gibt
    $ac = $arrUser.count - 1
    for ($i=0; $i -le $ac; $i++) {
      if ($arrUser[$i][0] -eq $user) {
        $j = $i
        break
      }
    }	
    
    if ($pc -As [IPAddress] -AS [Bool]) { # Steht in $pc eine IP-Adresse, generiert durch Smartphones, diese ausstreichen
      $pc = $missing
    } elseif ($pc.ToLower() -eq  $Env:COMPUTERNAME.ToLower()) { # Steht im $pc der Server-Name diesen ebenfalls ausstreichen
      $pc = $missing
    }
   
    if($j -eq -1) {            
      if ($atyp -eq 10) {# Das Ereignis 10 kommt immer als letztes bei einer erfolgreichen Anmeldung. Deshalb den DS erzeugen	
        $time = $event.TimeGenerated.ToString("dd.MM.yyyy HH:mm:ss") #.TimeWritten
        $arrUser+=(,($user, $pc, $ip, $time, $login, $locked, ""))
      }
    } elseif ($atyp -eq 3 -and !($arrUser[$j][5])) {# Das Ereignis 3 ist die erste Loginereignis welches ausgewertet wird

      $arrUser[$j][1] = $pc # Hier steht meist der Remote-PC-Name drin. Nicht von Smartphones
      $arrUser[$j][5] = $true
    }
  }

  foreach($s in $arrUser) {
    $array_user += ($s -join $sep) + "`n"
  }
  $array_user
}

function write_content() {
  try {set-Content -Value $arr_user -Path $loginevents}
  catch {
    Start-Sleep -Milliseconds 250 # Eine 1/4 Sek. warten und nochmal versuchen den Inhalt zu speichern
    set-Content -Value $arr_user -Path $loginevents
  }
}

$arr_user = GET-LoginInfoOfUsers
write_content

#cls
#Write-Host $arr_user
#Write-Host 'Fertig'


checkuser.ps1

<# 
 Das Script liest die Informationen welches mit dem loginevnets.ps1 Script erstellt wurde ein und Vergleicht diese mit den Vorgaben.
 Es können folgende Informationen des angemeldeten Benuzters verglichen werden.
  - IP des Clients
    * Interne(r) IP-Bereich(e). Wenn z.B. via VPN auf den Server zugegriffen wird
    * Externe IP, Fest oder via Dyn-DNS 
  - Computer-Name    
 Sollte die Überprüfung nicht mit den Kriterien übereinstimmen kann ein Email an den User/ Admin und/oder andere Benuzter versendet werden
#>

if ([string]::IsNullOrEmpty($PSCommandPath)) { # Powershell 2.0
  $PSCommandPath = $MyInvocation.MyCommand.Definition
  $PSScriptRoot = Split-Path -Parent -Path $PSCommandPath 
}

# Hier wird die Benutzer-Überprüfung gespeichert
# Im Pfad des Scripts
$LogFile = $PSCommandPath.Replace('.ps1', '.txt') 
# Eigener Pfad
#$LogFile = 'c:\eigenerPfad' + $PSCommandPath.Replace($PSScriptRoot,'').Replace('.ps1', '.txt') 
# für jeden Benutzer im eingenen Homedrive
#$LogFile = $env:HOMEDRIVE + $env:HOMEPATH + $PSCommandPath.Replace($PSScriptRoot,'').Replace('.ps1', '.txt') 

# Info-Datei wenn der Benutzer deaktiviert werden soll im run_hidden-Script
$deactivateuserPath = (Split-Path -Parent -Path $LogFile) + '\deactivateuser.txt'

# Soll dem User nach erfoilgreicher Anmeldung ein PopUp angezeigt werden muss in dieser Datei der PopUp-Text stehen
$clientinfo = $PSScriptRoot + '\clientinfo.txt'

# Sollen alle Login-Ereignisse protokolliert werden
$All = $true
 
# Soll eine Email bei Ungereimtheiten versendet werden
$sendmail = $false

# Was soll getan werden wenn Unstimmigkeiten vorkonmmen
# Hier kann die Aktion vorbelegt und in der Funktion CheckUser als Default-Rückgabewert festgelegt werden
# -1 = OK (nicht empfohlen)
#  0 = Email (Info-Email versenden)
#  1 = Logout (Abmelden)
#  2 = Konto (RDP-Zugriff deaktivieren)
$logoff = 0

# IP-Bereiche (Klasse C)
# Nur die ersten 3 Blöcke eintragen
$IPsubStringBranch1 = '192.168.0'
# Wenn mehr als ein Standort verwendet wird
$IPsubStringBranch2 = '192.168.1'
$IPsubStringBranch3 = '192.168.2'

# IP-Bereiche (Klasse B)
# Nur die ersten 3 Blöcke eintragen
#$IPsubStringBranch1 = '172.16'
# Wenn mehr als ein Standort verwendet wird
#$IPsubStringBranch2 = '172.16'
#$IPsubStringBranch3 = '172.31'
  
#...
$IPsubStringBranches = $IPsubStringBranch1, $IPsubStringBranch2, $IPsubStringBranch3 #, ...

# Email Einstellungen
$To = Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.'; $Cc = ''; $Bcc = ''
$Subject = 'RDP Login-Ereignis'
$Body = "Hallo Admin,`nes gab ein erfolgreiches Login. Aber die IP-Adresse bzw. der Client-Name sind nicht hinterlegt.`nBitte umgehend prüfen!`n`nViele Grüße`nLogin-Script`n`n"
$BodyIsHTML = $false
# Datei-Anhang
$AttachmentPath = ''
# Mail Server settings
$From = $To # Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.'
$SMTPUser = $From      #'username'
#$SMTPPassword = 'eigenespwvergeben' # Hier das Passwort ODER den Datei-Pfad. Diese Datei wird mit 'createedentials.ps1' erzeugt
$SMTPPassword = $PSScriptRoot + '\email.cred'
$SMTPServer = 'sslout.df.eu'
$SMTPServerPort = 465  #587  25

# In diesem Log wurden die Events gespeichert aus dem loginevensts.ps1 Script (Dateiname).
$loginevents = $PSScriptRoot + '\loginevents.txt'
$sep = '|'        # Feld-Trennzeichen "`t" (Tabulator)
$missing = '-?-'  # Eintrag wenn ein Wert nicht ermittelt werden kann

  
# ---------------------------------------------------------------------    
# Funktionen    
# ---------------------------------------------------------------------    

function CheckUser {
  <#
  .SYNOPSIS
  Returns the user status.
   -1 = OK
    0 = Email
    1 = Logout
  .DESCRIPTION
  checks user if loginparameter matches guidelines
  .EXAMPLE   
  CheckUser -UserName $UserName -ClientPCName $ClientPCName -ClientIP $ClientIP -Return $logoff
  .OUTPUTS
   -UserName $UserName -ClientPCName $ClientPCName -ClientIP $ClientIP -Return $logoff
  #>
  [CmdletBinding()]
  Param (
    [Parameter(Mandatory=$false,Position=0)]
    [String]$ClientPCName=$Env:COMPUTERNAME,
    [Parameter(Mandatory=$false,Position=1)]
    [String]$UserName=$Env:USERNAME.ToLower(),
    [Parameter(Mandatory=$false,Position=2)]
    [String]$ClientIP,
    [Parameter(Mandatory=$false,Position=3)]
    [int]$Return=$logoff
  )  
    
  if ($ClientIP -eq $missing) {
    $IPSubStringClient = $ClientIP
  } else {
    $arr = $ClientIP.Split('.')
    $arr = $arr[0..($arr.Count-2)]
    $IPSubStringClient = $arr -join '.'
    #$IPSubStringClient = $ClientIP.Substring(0, $ClientIP.LastIndexOfAny('.'))
  }
  
  switch ($UserName) {
    
    # Administrator kann sich von alles IP-Bereichen einloggen
    'internal_admin' {
      if ($IPsubStringBranches -eq $IPSubStringClient) {
        $Return = -1
      } 
      break
    }
    
    # Benutzer kann sich extern von einem bestimmten PC mit Namen z.B. 'externerPCName' und seinem iPhone einwählen
    'external_user' {
      if ('externerPCName', 'users-iPhone-name' -eq $ClientPCName) {        
        $Return = -1
      }
      break
    }
   
    # Benutzer kann sich extern von einer festen IP oder DynDNS-Eintrag einloggen. 
    # DynDNS-Eintrag wird z.B von der Fritzbox des Benutzers aktualisiert.
    'external_user2' {      
      if ('93.125.45.12' -eq $ClientIP) {
        $Return = -1
      } elseif ($IP = IPFromDynDns -DynDNS 'mypersonal.dyndns.org') {
        $Return = -1
      }
      break
    }
    
    # Mehrere Benutzer der Filiale 2 die sich nur aus dem IP-Bereich der Filiale 2 einloggen dürfen
    # Es wird eine Email versendet wenn die Benutzer sich aus anderen Bereichen eingeloggen
    ('user1_Branch2', 'user2_Branch2', 'user3_Branch2' -eq $_) { # 
      if ($IPsubStringBranch2 -eq $IPSubStringClient) {
        $Return = -1
      } elseif ($IPsubStringBranch1, $IPsubStringBranch3 -eq $IPSubStringClient) {
        $Return = 0
      } 
      break
    }    
   
   # VPN zum Router Filiale 1
   'externalViaVPN' {  
      if ($IPsubStringBranch1 -eq $IPSubStringClient) {
        $Return = -1
      }
      break
    }    
    
    # Generell alle erlauben die im eingenen Netzwerk sind
    Default {
      if ($IPsubStringBranches -eq $IPSubStringClient) {
        $Return = -1
        #$Return = 0
      } 
      break
    }
  }
  $Return
}


Function GET-IPFromDynDns() {
  <#
  .SYNOPSIS
  Returns the IP-Adress of a given DynDNS entry.
  Löst die IP-Adresse aus einem DynDns-Namen auf
  .EXAMPLE
  GET-IPFromDynDns 'mypersonal.dyndns.org'
  .EXAMPLE
  GET-RDPSessionId -UserName johndoe
  .OUTPUTS
  System.String (IP-Adress)
  #>
  [CmdletBinding()]
  Param (
    [Parameter(Mandatory=$false,Position=0)]
    [string]$DynDNS 
  )
  $PingResult = Test-Connection -ComputerName $DynDNS -Count 1
  #$return = $PingResult.ProtocolAddress
  $return = $PingResult.IPV4Address.IPAddressToString
  $return
}

# für ein Logoff wird die RDP-SessionID benötigt
function GET-RDPSessionId {
  <#
  .SYNOPSIS
  Returns the RDS session ID of a given user.
  .DESCRIPTION
  Leverages query.exe session in order to get the given user's session ID.
  .EXAMPLE
  GET-RDPSessionId
  .EXAMPLE
  GET-RDPSessionId -UserName johndoe
  .OUTPUTS
  System.String
  #>
  [CmdletBinding()]
  Param (
    # Identifies a user name (default: current user)
    [Parameter(ValueFromPipeline = $true)]
    [System.String]$UserName = $env:USERNAME
  )
  $returnValue = $null
  try {
    $ErrorActionPreference = 'Stop'
    $output = query.exe session $UserName |
      ForEach-Object {$_.Trim() -replace '\s+', ','} |
        ConvertFrom-Csv
    $returnValue = $output.ID
  } catch  {
    $_.Exception | Write-Error
  }
  New-Object psobject $returnValue
}

# Versendet eine Info-Email.
function sendEmail { 
  <#
  .SYNOPSIS
  Returns the RDS session ID of a given user.
  .DESCRIPTION
  Sends an Email using global variables
  .EXAMPLE
  sendEmail
  .OUTPUTS
  System.String
  #>

  # Check Port 
  $implicitSSL = $true
  $enableSSL = $true
  switch ($SMTPServerPort) {
    25   {$enableSSL = $false;break}   # default Port (unsecure, almost depreached)
    465  {break}                       # connection buildup is secure
    8465 {break}
    587  {$implicitSSL = $false;break} # connection buildup is not secure
    2525 {$implicitSSL = $false;break}
    8025 {$implicitSSL = $false;break}
  }

  # Ckeck Attachment
  if ($AttachmentPath -ne '') {    
    if (Test-Path $AttachmentPath) {
    } else {      
      #Write-Host 'Anhang wurde nicht gefunden ' + $AttachmentPath
      $AttachmentPath = ''
    }
  } 
  
  If (Test-Path $SMTPPassword) {
    $SMTPPassword = GET-Content $SMTPPassword | ConvertTo-SecureString 
  } Else {
    $SMTPPassword = $SMTPPassword | ConvertTo-SecureString -asPlainText -Force
  }
  
  $credentials = New-Object System.Management.Automation.PSCredential($SMTPUser, $SMTPPassword)
  
  if ([string]::IsNullOrEmpty($SMTPPassword)) {
  #if ($SMTPPassword -eq '') {
    "Mail cound't be sent. Missing Password"
  } elseif ($implicitSSL) {
    sentViaWebMail
  } else {
    sentViaSmtpClient
  }
}

function sentViaSmtpClient {
  # Set up server connection
  $smtpClient = New-Object System.Net.Mail.SmtpClient $SMTPServer, $SMTPServerPort
  $smtpClient.EnableSsl = $enableSSL
  $smtpClient.Timeout = 5000       # timeout in milliseconds
  $smtpClient.UseDefaultCredentials = $false;    
  $smtpClient.Credentials = $credentials
  # Create mail message 
  $message = New-Object System.Net.Mail.MailMessage $From, $To, $subject, $Body  
  $message.IsBodyHTML = $BodyIsHTML
  if ($AttachmentPath -ne '') {
    $attachment = New-Object System.Net.Mail.Attachment $AttachmentPath
    $message.Attachments.Add($attachment)
  } 
  if ($Bcc -ne '') {$message.Bcc.Add($Bcc)}
  if ($Cc -ne '') {$message.Cc.Add($Cc)}
   $return = ''
  # Send the message
  try {
    $smtpClient.Send($message)
    # Write-Output 'Message sent.'
  }
  catch {
    #Write-Error $_
    #Write-Output 'Message send failed.'
    #add-Content $LogFile 'Message send failed.' 
    $return = 'Message send failed.' 
  }
} 

function sentViaWebMail {
  # Load System.Web assembly
  [System.Reflection.Assembly]::LoadWithPartialName("System.Web") &gt; $null
  $schema = 'http://schemas.microsoft.com/cdo/configuration/'
  $Ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($SMTPPassword)
  # Convert to plain text
  $SMTPPlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
  # Create a new mail with the appropriate server settigns
  $mail = New-Object System.Web.Mail.MailMessage
  $mail.Fields.Add($schema + 'smtpserver', $SMTPServer)
  if ($enableSSL) {
    $mail.Fields.Add($schema + 'smtpserverport', $SMTPServerPort)
    $mail.Fields.Add($schema + 'smtpusessl', $enableSSL)
  }
  $mail.Fields.Add($schema + 'sendusername', $credentials.UserName)
  $mail.Fields.Add($schema + 'sendpassword', $SMTPPlainPassword)
  $mail.Fields.Add($schema + 'smtpconnectiontimeout', 5) # timeout in seconds
  # Use network SMTP server...
  $mail.Fields.Add($schema + 'sendusing', 2)
  # ... and basic authentication
  $mail.Fields.Add($schema + 'smtpauthenticate', 1)
  
  # Importance of email
  #$mail.Fields.Add('urn:schemas:httpmail:importance', 1) #0 = Low importance  1 = Normal importance (default) 2 = High importance  
  
  # Set up the mail message fields
  $mail.From = $From
  $mail.To = $To
  if ($Cc -ne '') {$mail.Cc = $Cc}
  if ($Bcc -ne '') {$mail.Bcc = $Bcc}
  $mail.Subject = $subject
  if ($BodyIsHTML) {$mail.BodyFormat = 'Html'}

  $mail.Body = $Body

  if ($AttachmentPath -ne '') {
    # Convert to full path and attach file to message    
    $AttachmentPath = (get-item $AttachmentPath).FullName
    $attachment = New-Object System.Web.Mail.MailAttachment $AttachmentPath
    $mail.Attachments.Add($attachment) &gt; $null
  }

  $return = ''
  # Send the message  
  try {
    [System.Web.Mail.SmtpMail]::Send($mail)
    # Write-Output 'Message sent.'
  }
  catch {
    #Write-Error $_
    #Write-Output 'Message send failed.'
    $return = 'Message send failed.' 
  }       
  
} 

function Get-RDSClientName {
  <#
  .SYNOPSIS
  Returns the RDS client name
  .DESCRIPTION
  Returns the value of HKCU:\Volatile Environment\<SessionID&gt;\CLIENTNAME
  .EXAMPLE
  Get-RDSClientName -SessionId 4
  .EXAMPLE
  Get-RDSClientName -SessionId Get-RDSSessionId
  .OUTPUTS
  System.String
  #>
  [CmdletBinding()]
  Param (
  # Identifies a RDS session ID
  # [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [Parameter(Mandatory = $false)]
    [System.String]$SessionId
  )
  $returnValue = $null
  $regKey = 'HKCU:\Volatile Environment\{0}' -f $SessionId
  try {
    $ErrorActionPreference = 'Stop'
    $regKeyValues = Get-ItemProperty $regKey
    $sessionName = $regKeyValues | ForEach-Object {$_.SESSIONNAME}
    if ($sessionName -ne 'Console') {
      $returnValue = $regKeyValues | ForEach-Object {$_.CLIENTNAME}
    } else {
      # Write-Warning 'Console session'
      # $returnValue = $env:COMPUTERNAME
    }
  }
  catch {
    $_.Exception | Write-Error
  }
  New-Object psobject $returnValue
}

# Sie SET-Variable CLIENTNAME ist zum Startzeitpunkt des Scriptes
# durch die Aufgabenplanung noch nicht belegt
# Erst durch die Autostart-Routine wird der korrekte CLIENTNAME ausgelesen.
<#
function get_set_content {  
  $filename = $env:APPDATA + "\~$setclientname.txt"
  if (Test-Path $filename) {    
    $setcontent = GET-Content $filename 
    if ($setcontent -ne '') {$return = $setcontent.Substring(11)} #CLIENTNAME=        
    Remove-Item -Path $filename -Force
  }
  $return
}
#>
# in den Autostart-Ordner des Benutzers eine 'snc.bat' erstellen. 
# Diese wird nach ein paar Sekunden nach dem RDP-Login ausgeführt 
# und schriebt in eine Text-Datei die SET-Variable CLIENTNAME 
# Nachdem die Datei ersellt und ausgelesen wurde wird sie wieder gelöscht
<#
function set_bat_content {
  Param (
    [Parameter(Mandatory=$false,Position=0)]
    [switch]$start=$true
  )
  $batfile = $env:APPDATA +"\Microsoft\Windows\Start Menu\Programs\Startup\$setclientname.bat" # Autostart-Ordner des jeweiligen Benutzers  
  if (Test-Path $batfile) {
    if ($start -eq $false){
      Remove-Item  -Path $batfile -Force
    }
  } else {
    $valu = 'set CLIENTNAME &gt; %APPDATA%\~' + $setclientname + '.txt' 
    set-Content -Path $batfile -value $valu
  }
}
#>

function Get-ClientUser-Info {
  # Aus der Liste in loginevents.txt den eigenen Eintrag auslesen
  Write-Host $clientinfo
  if (Test-Path $clientinfo) { 
    $clientinfocontent = GET-Content $clientinfo
    $popupinfo = $clientinfocontent.trim()
  } else {
    $popupinfo = ''
  }
  if ($popupinfo -ne '') {
    $wshell = New-Object -ComObject Wscript.Shell -ErrorAction Stop
    $wshell.Popup($popupinfo, 15, "Wichtiger Hinweis", 48)    
  }    
}

function add_content {  
  if (!(Test-Path $LogFile)) {        
    $LogFileUser = 'Jeder' # $env:USERDOMAIN + '\' + 'TS_User' ' Damit kann jeder in diese Datei schreiben
    set-Content -Path $LogFile -value 'User|PCName|Clinet-IP|LogOnTime|Action'    
    $aclVZ = get-acl $LogFile
    $arVZ = new-object system.security.accesscontrol.filesystemaccessrule($LogFileUser,"FullControl","Allow")
    $aclVZ.SetAccessRule($arVZ)
    Set-Acl -path $LogFile  $aclVZ    
  }
  add-Content -Path $LogFile -value $info
}

function set_deactivate_user {  
  set-Content -Path $deactivateuserPath -value $UserName
}

# ---------------------------------------------------------------------    
# Programm Teil
# ---------------------------------------------------------------------    

$UserName = $Env:USERNAME.ToLower()
$ComputerRame = $Env:COMPUTERNAME
$SessionID = GET-RDPSessionId

# Aus der Liste in loginevents.txt den eigenen Eintrag auslesen
if (Test-Path $loginevents) { 
  $logineventscontent = GET-Content $loginevents
  foreach ($row in $logineventscontent) {
    $column = $row.Split($sep)  
    if ($UserName -eq $column[0].ToLower()) {
      $ClientPCName = $column[1].ToLower()
      $ClientIP = $column[2]
      $LogOnTime =  $column[3]
      break
    }
  }
} else {
  $ClientPCName = $missing
}
 
if ([string]::IsNullOrEmpty($ClientIP)) {$ClientIP = $missing}  # Keine IP-Info vorhanden.   

# Wurde kein gültiger Client-Name in die Loginevents eingetragen. (iPhone, Smartphone)
# Muss dieser Nachtäglich durch ein externe .bat im Autostart ausgelesen werden
if ($ClientPCName -eq $missing) { 
  # Watezeit um den Client-PC-Name auszulesen (Dauert immer etwas. Wird nur benötigt wenn z.B. Smartphones im Event-Log keinen Namen hinterlassen)
  $waitforclinetname = 10
  $i = 0  
  #$setclientname = 'scn'
  #set_bat_content
  # Nach einer bestimmten Zeit steht er Name in der Set Variablen
#  set-Content -Path c:\checkrdp\sekunden.txt -value ('SessionID ' + $SessionID)
  do {
    Start-Sleep -Seconds 1
    #$ClientPCName = get_set_content
    $ClientPCName = Get-RDSClientName -SessionId $SessionID
    $i++    
#    if([string]::IsNullOrEmpty($ClientPCName)) {add-Content -Path c:\checkrdp\sekunden.txt -value 'IsNullOrEmpty' }
#    if(!([string]::IsNullOrEmpty($ClientPCName))) {add-Content -Path c:\checkrdp\sekunden.txt -value 'NOT IsNullOrEmpty' }    
#    add-Content -Path c:\checkrdp\sekunden.txt -value $ClientPCName
#    add-Content -Path c:\checkrdp\sekunden.txt -value $i
    
  } while ([string]::IsNullOrEmpty($ClientPCName) -and $i -le $waitforclinetname)
  #set_bat_content $false
}

$logoff = CheckUser -UserName $UserName -ClientPCName $ClientPCName -ClientIP $ClientIP -Return $logoff

if ([string]::IsNullOrEmpty($LogOnTime)) {
  $LogOnTime = GET-Date
  $LogOnTime = $LogOnTime.ToString("(dd.MM.yyyy HH:mm:ss)")
} 

$info = (&{If($logoff -eq -1) {'OK'} Elseif ($logoff -eq 0) {'Email'} Elseif ($logoff -eq 1) {'LogOff'} else {'Deaktivate'}}) 
$Body += 'Benutzer: ' + $UserName + "`nUserdomain: " + $ClientPCName +  "`nIP: " + $ClientIP + "`nLoginzeit: " + $LogOnTime + "`nAktion: " + $info
$info = $UserName + $sep + $ClientPCName + $sep + $ClientIP + $sep + $LogOnTime + $sep + $info

if ($logoff -ge 1) {     
  if ($sendmail) {sendEMail} 
  add_content
  logoff $sessionId /server:$COMPUTERNAME
  if ($logoff -gt 1) {set_deactivate_user}  
} elseif ($logoff -eq 0) {
  if ($sendmail) {sendEMail}
  add_content
} elseif ($All) {
    add_content
    Get-ClientUser-Info

} else {
  #'OK'
    Get-ClientUser-Info
}
#if (Test-Path $loginevents) {Remove-Item -Path $loginevents}

 
deactivateuser.ps1

#--------------------------------------------------------------------------
# Script zum Deaktivieren von Benutzer(n) welche im Script 'checkuser.ps1' als 
# zu deakivieren eingestuft wurden. Es wurde versucht sich von einer nicht zulässigen
# Arbeitsstation (PC, iPad, Smartphone), IP-Adresse eizuloggen.
# Version 1.1
# Dieser Script muss mit Administrator-Rechten durchgeführt werden!

# Im Pfad des Scripts
if ([string]::IsNullOrEmpty($PSCommandPath)) { # Powershell 2.0
  $PSCommandPath = $MyInvocation.MyCommand.Definition
  $PSScriptRoot = Split-Path -Parent -Path $PSCommandPath
}
# Datei mit den zu deaktivierenden Usern hat den selben Nanen wie diese Script Datei
$DeactivateUserFile = $PSCommandPath.Replace('.ps1', '.txt') 
# Eigener Pfad
#$DeactivateUserFile = 'c:\eigenerPfad' + $PSCommandPath.Replace($PSScriptRoot,'').Replace('.ps1', '.txt') 
# für jeden Benutzer im eingenen Homedrive
#$DeactivateUserFile = $env:HOMEDRIVE + $env:HOMEPATH + $PSCommandPath.Replace($PSScriptRoot,'').Replace('.ps1', '.txt') 

if (Test-Path $DeactivateUserFile) {
  $DeactivateUserFileContent = GET-Content $DeactivateUserFile
  foreach ($row in $DeactivateUserFileContent) {
    $user = $row.trim()  	      
	
	  # User in einer Domäne deaktivieren
    $agl =  '/c', 'net user', $user, '/domain', '/active:no'
    Start-Process -FilePath 'cmd' -ArgumentList $agl -WindowStyle Hidden  

    # User ohne Domäne deaktivieren
	  $agl =  '/c', 'net user', $user, '/active:no'	
    Start-Process -FilePath 'cmd' -ArgumentList $agl -WindowStyle Hidden  
  }
  Remove-Item -Path $DeactivateUserFile -Force
}

 
Zusatz-Tool um verschlüsselte Passwort-Datei zu erzeugen (Email)
create_cedentials.ps1

# In diese Datei wird das Passwort für den Emailversand verschlüsselt gespeichert
if ([string]::IsNullOrEmpty($PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent -Path  $MyInvocation.MyCommand.Definition} # Powershell 2.0 
$EMailCredentialFile = $PSScriptRoot + "\email.cred"
(GET-Credential -Message  "Email-Adresse & Passwort für Info-Email-Versand" -UserName "empfaenger@maildomain").password | ConvertFrom-SecureString  &gt; $EMailCredentialFile

#PsEcec
# In diese Datei wird das Passwort für den Administrator verschlüsselt gespeichert
#$AdministratorCredentialFile = $PSScriptRoot + "\admin.cred"
#(GET-Credential -Message "Passwort für Administrator: " -UserName ($Env:COMPUTERNAME + "\Administrator")).password | ConvertFrom-SecureString &gt; $AdministratorCredentialFile

 

In Windows 2012 Server ist die Farbe und Schriftgröße erst einmal nicht veränderbar. Wenn die Farbwahl nicht gefällt gibt es 2 einfache Möglichkeiten dies zu ändern.

window 2012 standard farbe


1.) Via CMD-Shell. Dort einfach folgenden Befehl mit Parametern ausführen: dism /online /enable-feature /all /featurename:DesktopExperience
 
Das dauert etwas und nach einen Neustart kann die Färbung angepasst werden.

2.) Via Server-Manager (ServerManager.exe starten). Dort Rollen und Features hinzufügen > Features > Benutzeroberflächen und Infrastruktur anklicken und installieren.

 

 Mit dem kostenfreien IPBan vom Jeff Johnson werden die Anmeldungen an einem Server ausgewertet und nachdem eine bestimme Anzahl vom Fehlanmeldungen überschritten wurde sperrt das Programm die IP-Adresse den "Angreifers" für einen vorgegebenen Zeitraum.


Installation

1.) Die Zip herunterladen und in einen Ordner kopieren.
z.B. in den Programme-Ordner da dort nicht jeder User Schreibrechte hat.
%ProgramFiles%\IPBan
ODER
%HOMEDRIVE%\IPBan

2) Prüfen ob .NET Framework 4.5 oder höher installiert ist. Am einfachsten eine CMD-Shell mit Administrator starten und folgenden Befehl ausführen (dauert etwas ;-)
Leider liefert dieser Befehl nicht immer das korrekte Ergebnis. Mit dem kostenlosen Tool .NET Version Detector geht das recht einfach.
Diese CMD-Shell für später geöffnet lassen!

wmic /namespace:\\root\cimv2 path win32_product where "name like '%.NET%'" get version

Sonst das aktuelle .Net Framework herunterladen und installieren.

3) Lokale Sicherheitsrichtlinie muss angepasst werden.
Gruppenrichtlinien für lokalen Computer starten (gpedit.msc) > Computerkonfiguration > Windows-Einstellungen > Sicherheitseinstellungen >Lokale Richtlinien > Überwachungsrichtlinie => "Anmeldeereignisse überwachen" Erfolgreich und Fehler anklicken.
In der CMD-Shell folgendes ausführen. Geht deutlich schneller ;-)

Deutsches System

auditpol /set /category:"An-/Abmeldung" /success:enable /failure:enable

Englisches System

auditpol /set /category:"Logon/Logoff" /success:enable /failure:enable

Konfiguration

Das Programm bietet einige Voreinstellungen. Diese werden in der IPBan.exe.config festgelegt. Sobald IPBan gestartet ist kann die Datei nur mit Administarator-Rechten bearbeitet werden.

Der Abschnitt "<appSettings>"

Anzahl fehlerhafter Logins bis die IP gebannt wird. Der vorgegebene Wert ist 5. Nach 5 Versuchen wird dann die IP gesperrt.
<!-- Number of failed audits in the event viewer before banning the ip address -->
<add key="FailedLoginAttemptsBeforeBan" value="5"/>

Zeitspanne bis die IP wieder frei gegeben wird um die Anmeldung wieder freizuschalten (Von 1 Sekunde bis nie mehr wieder).
Dies Sperre kann umgangen werden wenn der Router des Clients per Reconnet eine neue IP bekommt.
Das Format lautet hier (DD:HH:MM:SS). Voreingestellt ist 1 Tag. Wenn 00:00:00:00 eingetragen wurde ist die IP für immer gesperrt.
<!-- The duration of time to ban an ip address (DD:HH:MM:SS) - 00:00:00:00 for forever -->
<add key="BanTime" value="01:00:00:00"/>

Zeitspanne um den Zähler der Fehlversuche der Anmeldung zurücksetzt. Der vorgegebene Wert ist 30 Minuten. Wenn also z.B. nach 4 Versuchen eine Pause bis zum nächsten Ameldeversuch vom mehr als 30 Minuten gemacht wird kann wieder 4 mal versucht werden sich anzumelden.
<!-- The duration after the last failed login attempt that the ip is forgotten (count reset back to 0). Set to 00:00:00:00 to never forget an ip. (DD:HH:MM:SS) -->
<add key="ExpireTime" value="00:00:30:00"/>

Zeitraum bis die geänderten Einstellungen aktiv sind. Nach 15 Sekunden aktualisiert IPBan die Konfiguration.
<!-- The time between cycles to do house-keeping such as un-banning ip addresses, reloading config, etc. (DD:HH:MM:SS) -->
<add key="CycleTime" value="00:00:00:15"/>

Whitelist
IP-Adressen oder DNS-Namen die nie gesperrt werden sollen. Dies empfiehlt sich für die internen IP-Adress-Bereiche innerhalb des eigenen Netzwerkes. Die Liste ist Komma getrennt. Hier in diesem Bsp. localhost, Standardroute, loopback-Adresse, - (alle Adressen)
Falls eine IP gesperrt wurde und diese wieder frei gegeben werden soll muss diese hier eingetragen werden.
<!-- Comma separated list of ip addresses OR DNS names that are never banned -->
<add key="Whitelist" value="127.0.0.1,::1,0.0.0.0,-"/>

Regex Whitelist
Hier muss eine regulärer Ausdrück für eine IP-Bereich eingetragen werden die nie gesperrt werden soll.
Es empfiehlt sich hier den Adressbereich des/der eigene/n IP-Netze einzutragen.
In diesem Bsp. sind es 2 Bereiche 192.168.170.0 bis 192.168.171.255 ergibt ^192\.168\.17(0|1)\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
<!-- Regular expression for more advanced whitelisting. Shortcut: use * to allow any piece of an ip (i.e. 128.128.128.*).
Sample regex that whitelists a few ips: ^(128\.128\.128\.*)|(99\.99\.99\.[0-9])$
More info about regex: http://www.regular-expressions.info/ -->
<add key="WhitelistRegex" value="^192\.168\.17.*)$"/> (nicht ganz korrekt aber alle IP's ab 192.168.170.0. Es kommen ja nur IP's in Frage)
ODER
<add key="WhitelistRegex" value="^(192\.168\.170.*|192\.168\.171.*)$"/>
ODER
<add key="WhitelistRegex" value="^192\.168\.17(0|1)\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"/>

Blacklist
IP-Adressen oder DNS-Namen die IMMER gesperrt werden sollen. Dies empfiehlt sich für Angreifer IP-Adressen außerhalb des eigenen Netzwerkes. Die Liste ist Komma getrennt.
In diesem Bsp. ist noch keine IP gesperrt.
<!-- Comma separated list of ip addresses OR DNS names OR user names to always ban and NEVER unban -->
<add key="Blacklist" value=""/>

Regex Blacklist
Hier muss eine regulärer Austdrück für eine IP-Bereich eingetragen werden.
In diesem Bsp. noch keine IP gesperrt. Kann aber gleich dem Muster im WhitelistRegex gebildet werden. Mehrere Regex-Ausdrücke müssen per Pipe getrennt werden. (regex_1|regex_2|regex_3|regex_4)
Das Problem ist dass es massig angreifende IP-Bereiche gibt.
In diesem Bsp. ist noch keine IP-Bereich per RegEx gesperrt.
<!-- Regular expression for more advanced blacklisting. Shortcut: use * to allow any piece of an ip, dns name or user name (i.e. 128.128.128.*).
Sample regex that blacklists a few ips: ^(128\.128\.128\.*)|(99\.99\.99\.[0-9])$
More info about regex:http://www.regular-expressions.info/ -->
<add key="BlacklistRegex" value=""/>

Externe Anwendung starten wenn eine Sperrung aufgetreten ist. Das Programm und die Parameter müssen durch eine Pipe gestrennt eingetragen werden
Hier könnte z.B. ein Script ausgeführt werden das den Admin darüber informiert dass ein Benutzer gesperrt wurde.
<!--- Run an external process when a ban occurs. Separate the process and any arguments with a pipe (|). ###IPADDRESS### will be replaced with the actual IP which was banned. The pipe is required.
Example: c:\system files\on_ip_banned.exe|###IPADDRESS### -q -->
<add key="ProcessToRunOnBan" value=""/>

Logdatei für gesperrte IP-Adressen
In dieser Datei werden die gesperrten IP-Adressen geschrieben. Sie liegt per Default im gleichen Ordner wie IPBan.
<!-- The file to store banned ip addresses in -->
<add key="BanFile" value="banlog.txt"/>

Gesperrte IP-Liste leeren wenn IPBan neu gestartet wird
Wenn das Programm neu gestartet wird werden die gebannten IP-Einträge gelöscht und somit wieder für die Anmeldung freigegeben. In der Regel läuft IPBan als Dienst.
<!-- True to clear and unban all the ip addresses in the ban file when the service restarts, false otherwise -->
<add key="BanFileClearOnRestart" value="true"/>

Die URL liefert die IP-Adresse zurück, falls ein DNS-Name angegeben wurde
<!-- Url to query to get the external ip address, the url should return a string which is the external ip address.-->
<add key="ExternalIPAddressUrl" value="http://icanhazip.com"/>


Funktion noch nicht getestet bzw. verstanden was sie macht:-(
?
<!-- A url to get when the service updates, empty for none. ###IPADDRESS### will be replaced with the local ip. ###MACHINENAME### will be replaced with the fully qualified domain name of the machine.
Example:http://192.168.1.2/ipban/update?ip=###IPADDRESS###&name=###MACHINENAME### -->
<add key="GetUrlUpdate" value=""/>

?
<!-- A url to get when the service starts, empty for none. ###IPADDRESS### will be replaced with the local ip. ###MACHINENAME### will be replaced with the fully qualified domain name of the machine.
Example: http://192.168.1.2/ipban/start?ip=###IPADDRESS###&name=###MACHINENAME### -->
<add key="GetUrlStart" value=""/>

?
<!-- A url to get when the service stops, empty for none. ###IPADDRESS### will be replaced with the local ip. ###MACHINENAME### will be replaced with the fully qualified domain name of the machine.
Example: http://192.168.1.2/ipban/stop?ip=###IPADDRESS###&name=###MACHINENAME### -->
<add key="GetUrlStop" value=""/>


Ab hier wäre ich vorsichtig!

Benutzer-Name Liste. Wenn hier etwas eingetragen wir können sich nur diese Benutzer überhaupt anmelden. Die Liste ist Komma getrennt.
Diese Funktion bleibt außer Kraft wenn kein Name eingetragen ist. (noch nicht getestet)
<!--
Comma separated list of allowed user names. If any user login fails and the user name is not in this list, that user is banned. Keep in mind that if a valid user mis-types their user name, then this will lock them out UNLESS you have whitelisted their ip address. Leave empty to turn off this functionality.
***!!!*** Be very careful not to leave garbage characters or punctuation in this field because you may get locked out of your server if you fail to login with a valid account. ***!!!***
-->
<add key="AllowedUserNames" value=""/>

Prefix der Firewall-Regel(n). Dies sollte NIE geändert werden. Wenn also ein Sperr-Ereignis auftritt schreibt IPBan eine Firewall-Regel
(noch nicht überprüft)
<!--Rule prefix name for Windows Firewall rules, must contain only A-Z, 0-9 and It is recommended to never change this -->
<add key="RuleName" value="IPBan_BlockIPAddresses_"/>

 

Abbschnitt "<nlog>"

Logdateien
In der logfile.txt wird alles protokolliert. Es gibt eine Rotation der Daten von 28 Tagen. Es wird jeweils eine Archiv-Datei pro Tag (Day) erstellt.
<target name="logfile" xsi:type="File" fileName="${basedir}\logfile.txt" archiveNumbering="Sequence" archiveEvery="Day" maxArchiveFiles="28" encoding="UTF-8"/>

 

Abbschnitt "<ExpressionsToBlock>"

Microsoft SQL-Server
Wenn kein MS SQL-Server im Einsatz ist, können beide Gruppen (System u. Application) auskommentieren werden (<!--... -->)
 <!--
  <Group>
...
  </Group>
  <Group>
...
  </Group>

-->
Wenn nicht die Standard-Instanz (default) des MSSQL-Servers sondern eine benannte Instanz (named instance) verwendet wird muss der Eintrag MSSQLSERVER nach MSSQL$<Instanzname> umbenannt werden (z.B.. MSSQL$SQLEXPRESS).

 <!-- This group will block audit failures from failed login attempts to Microsoft SQL Server -->
...
<XPath>//Provider[@Name='MSSQL$SQLEXPRESS']</XPath>
...
<XPath>//Provider[@Name='MSSQL$SQLEXPRESS']</XPath>

MySQL Server
Wenn kein MYSQL-Server im Einsatz ist, kann die Gruppe (Application) auskommentieren werden. Siehe MS SQL-Server(<!-- ... -->)
<!-- This group will block audit failures from failed login attempts to MySQL Server -->

phpMyAdmin
Wenn kein MySQL-Server im Einsatz ist, kann die Gruppe (Application) auskommentieren werden. Siehe MS SQL-Server(<!-- ... -->)
<!-- This group will block audit failures from failed login attempts to phpMyAdmin Web Interface -->

Microsoft Exchange Server
Wenn kein Microsoft Exchange Server im Einsatz ist, kann die Gruppe (System) auskommentieren werden. Siehe MS SQL-Server(<!-- ... -->)
<!-- This group will block audit failures from failed login attempts to phpMyAdmin Web Interface -->

 


Test und Betrieb als Dienst

Um das Programm zu testen in der CMD-Shell das Pogramm IPBan.exe starten. Ein Output erfolgt sofort. Es werden auch gleich Daten in die banlog.txt geschrieben.

Wenn das Programm als Dienst laugen soll muss dieser Dienst auch in der CMD-Shell erstellt werden.

sc create IPBAN type= own start= auto binPath= %ProgramFiles%\IPBan\IPBan.exe DisplayName= IPBAN"

Das Ganze wieder rückgängig machen

Alle roten Zeilen sind in der CMD-Shell als Administrator auszuführen.

1) Dienst löschen
net stop IPBAN

sc delete IPBAN

2) Gruppenrichtlinie Überwachung der An- u. Abmeldungen wieder ausschalten

auditpol /set /category:"An-/Abmeldung" /success:disable /failure:disable

3) ggf. Ordner von IPBan löschen
rd /Q /S %ProgramFiles%\IPBan

 

 

Wenn auf ein Windows-Server per RDP zugegriffen werden soll gibt es ein paar Möglichkeiten dies abzusichern. Die erste Hürde ist bei einem Domänen-Server-System der Domänenname. Die meisten scheitern schon daran da dieser i.d.R. nicht bekannt ist und nur mit einem Benutzernamen ein Login versucht wird.

1. Einen VPN-Tunnel zum Router aufbauen und darin die RDP-Sitzung ausführen. Sehr einfacht geht das zwischen 2 Fritzboxen aber auch Lancom-Routern. Sehr sicher!

2. Den Standard-Port 3389  der RTP Verbindung am Router anpassen. Somit muss an die Zieladresse der Port angehängt werden. z.B. via DynDNS-Anbieter geheimeip.dyndns.org:3388. Nicht sehr sicher.

3. Anzahl Zugriffe beschränken indem die IP gesperrt wird nach einer bestimmten Anzahl vom Fehleingaben.
Hierzu gibt es ein einfach zu konfigurierendes Programm IPBan. Im Artikel IPBan konfigurieren wird die Funktionsweise erklärt. Sicher, je nach Konfiguration.

4.Anmeldeversuche beschränken und für eine bestimmten Zeitraum sperren.
Lokale Gruppenrichtline des Servers anpassen (gpedit.msc) > Computerkonfiguration > Windows-Einstellungen > Sicherheitseinstellungen >Kontorichtlinien > Kontosperrungsrichtlinien
=> "Kontensperrungsschwelle" anpassen und die Zahl auf z.B. 5 stellen. Danach wird die "Kontosperrdauer" und "Zurücksetzungsdauer des Kontosperrungszählers" auf 30 Min. gesetzt. Dies kann natürlich angepasst werden. Recht Sicher, je nach Konfiguration.

5. Anmeldescript am Server das nachdem die Anmeldung erfolgreich war den Client-Namen (PC) ausliest und mit einer erlaubten Namensliste vergleicht. Hier kann noch ein 3. Vergleich durchgeführt werden. Im Artikel "Benutzer prüfen nach RDP-Login" wird die Funktionsweise erklärt Relativ sicher, je nach Konfiguration.


Eine Kombination aus 3 und 5 scheint mir auch recht sicher zu sein wenn es nicht per VPN geht.

Wenn bei einen Windows Server keine Druckaufträge mehr ausgeführt werden können liegt es meist an der Druckerwarteschlange (Spooler).

Druck Druckspooler Dienst neu startem.
In der CMD-Shell als Administrator folgendes starten "net start spooler"
oder
die Dienste öffnen (services.msc)  und nach Druckerwarteschlange suchen und den Dienst (neu) starten.

Wenn zusätzlich noch die Druckaufträge gelöscht werden soll dieses Script speichern und danach ausführen.

@echo off
echo.
echo Druckerwarteschlange stoppen...
net stop Spooler
rem ping als pause
ping localhost -n 4 > nul
echo Druckauftraege loeschen...
del /q %SystemRoot%\system32\spool\printers\*.*
net start Spooler
rem ping als pause
ping localhost -n 4 > nul
echo erledigt!

 

Seite 1 von 2