Letzten Freitag hat sich ein IT-Sicherheitsunternehmen (Crowdstrike Holdings, Inc. ) nicht
gerade mit Ruhm bekleckert. Das Ergebnis: zahlreiche Systemadministratoren mussten viele
Überstunden leisten und das Wochenende war für viele Menschen außerhalb der IT-Branche
ruiniert.
Dank der Disziplin und Expertise weltweit beschäftigter Systemadministratoren konnten die
meisten Systeme schlussendlich wieder in Betrieb genommen werden. Auch wenn mein
Arbeitgeber nicht direkt betroffen war und mein Team sich folglich stressfrei ins Wochenende
verabschiedete, möchte ich pünktlich zum System Administrator Appreciation Day
allen interessierten Informatikern mit diesem Beitrag meine Wertschätzung ausdrücken und
einige praktische Lösungsschablonen aus meiner beruflichen Praxis vorstellen.
Lösungsschablonen:
↓ Entscheidungsfindung für Dateidownload über ein Datumsvergleich
↓ Microsoft Exchange 2016: Berichtserstellung über Verteilergruppenbesitzer
↓ PHP: LDAP-Authentifizierung
Entscheidungsfindung für Dateidownload über ein Datumsvergleich
In der beruflichen Praxis eines Systemadministrators kommt es häufig vor, dass bestimmte
Dateien nur dann heruntergeladen oder aktualisiert werden müssen, wenn sie neuer sind als
die bereits vorhandenen. Eine einfache, aber effektive Methode zur Entscheidungsfindung für Dateidownloads basiert auf einem Datumsvergleich.
Hinzu kommt, dass der vom Betriebssystem aufgedrückte Zeitstempel nach dem Download mit
dem Zeitstempel der Downloadquelle synchronisiert werden muss, um einen effizienten
Datumsvergleich (ab dem zweiten Durchlauf) durchzuführen.
Ausschlaggebend für den Download ist der Zeitstempel - beispielsweise: 2024-07-26 13:45.
Hier ist ein praktisches Beispiel, wie dies mit einem PowerShell-Skript umgesetzt werden kann:
<#
RSA-Update-Downloader Ver. 0.2
(C) Alexander H. / STEALTHCHIP.DE
#>
Start-Transcript -Path "C:\Skripte\RSAUPDATE\RSAKEYDATE-Transcript.log"
$url = "https://trustcenter-data.itsg.de/dale/gesamt-rsa4096.key"
$verzeichnis = "C:\Skripte\RSAUPDATE\RSAFILE"
$logdateipfad = "C:\Skripte\RSAUPDATE\RSAKEYDATE.log"
$dateipfad = Join-Path $verzeichnis (Split-Path $url -Leaf)
if (Test-Path $dateipfad) {
$lastModifiedRemote = (Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing).Headers["Last-Modified"]
$lastModifiedRemoteDateTime = [DateTime]::ParseExact($lastModifiedRemote, "ddd, dd MMM yyyy HH:mm:ss 'GMT'", [System.Globalization.CultureInfo]::InvariantCulture)
$creationTimeLocal = (Get-Item $dateipfad).CreationTime
if ($creationTimeLocal -lt $lastModifiedRemoteDateTime) {
Invoke-WebRequest -Uri $url -OutFile $dateipfad
(Get-Item $dateipfad).CreationTime = $lastModifiedRemoteDateTime
$logText = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Datei heruntergeladen und Erstelldatum aktualisiert."
Add-Content -Path $logdateipfad -Value $logText
} else {
$logText = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Die Datei ist bereits vorhanden und aktuell."
Add-Content -Path $logdateipfad -Value $logText
}
} else {
Invoke-WebRequest -Uri $url -OutFile $dateipfad
$lastModified = (Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing).Headers["Last-Modified"]
$lastModifiedDateTime = [DateTime]::ParseExact($lastModified, "ddd, dd MMM yyyy HH:mm:ss 'GMT'", [System.Globalization.CultureInfo]::InvariantCulture)
(Get-Item $dateipfad).CreationTime = $lastModifiedDateTime
$logText = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Datei heruntergeladen und Erstelldatum aktualisiert."
Add-Content -Path $logdateipfad -Value $logText
}
Stop-Transcript
Der unter $verzeichnis angegebene Ordner muss vor Skriptausführung erstellt werden.
Ein häufiger Fehler beim Kontrollieren ist; visuell abweichende, aber korrekte Zeitstempel aufgrund von
unterschiedlichen Zeitzonen falsch zu interpretieren (zum Beispiel 2024-07-26 13:45 > 2024-07-26 11:45).
Visuell abweichender aber korrekter Zeitstempel (2024-07-26 13:45 > 2024-07-26 11:45) - Zeitzone...
Update 2024-09-10:
Microsoft Exchange 2016: Berichtserstellung über Verteilergruppenbesitzer
Benutzer, Gruppen, Postfächer und andere Exchange-bezogene Einstellungen werden über das
EAC (Exchange Admin Center) verwaltet. Die webbasierte Verwaltungsoberfläche für Exchange
Server bietet jedoch nicht immer die gewünschten Lösungen - insbesondere wenn es um die
Exportmöglichkeit aller Verteilergruppen mit ihren Besitzern geht. Hier bietet die Exchange
Management Shell eine leistungsstarke Alternative.
Im integrierten Reportdialog stehen die Verteilerbesitzer nicht zur Auswahl.
Mit einem PowerShell-Skript lassen sich detaillierte Berichte über die Besitzer von
Verteilergruppen erstellen. Im Folgenden wird gezeigt, wie dies umgesetzt werden kann:
<#
Exchange-Verwaltungsgruppen-Reportgenerator Ver. 0.1
(C) Alexander H. / STEALTHCHIP.DE
#>
# Ermitteln des Skriptverzeichnisses
$scriptPath = $PSScriptRoot
$exportPath = Join-Path -Path $scriptPath -ChildPath "DistributionGroups.csv"
$logPath = Join-Path -Path $scriptPath -ChildPath "DistributionGroups.log"
# Logdatei erstellen oder überschreiben
Out-File -FilePath $logPath -Encoding UTF8
# Alle Verteilergruppen und dynamischen Verteilergruppen abrufen
$distributionGroups = Get-DistributionGroup -ResultSize Unlimited
$dynamicDistributionGroups = Get-DynamicDistributionGroup -ResultSize Unlimited
# Beide Gruppenarten in einer Sammlung zusammenführen
$allGroups = $distributionGroups + $dynamicDistributionGroups
# Ein Array initialisieren, um die Gruppeninformationen zu speichern
$result = @()
# Über jede Gruppe iterieren, um die erforderlichen Informationen zu sammeln
foreach ($group in $allGroups) {
$owners = $group.ManagedBy
$ownerEmails = @()
foreach ($owner in $owners) {
# Versuchen, die E-Mail-Adresse des Besitzers abzurufen
$ownerEmail = (Get-Recipient $owner -ErrorAction SilentlyContinue).PrimarySmtpAddress
if ($ownerEmail) {
$ownerEmails += $ownerEmail
} else {
# Protokollieren des Fehlers in die Logdatei
$logMessage = "Der Besitzer $owner konnte nicht abgerufen werden. Zugewiesene Gruppe: $($group.DisplayName)"
$logMessage | Out-File -FilePath $logPath -Append -Encoding UTF8
}
}
$groupType = if ($group.RecipientTypeDetails -eq "DynamicDistributionGroup") { "Dynamische Verteilergruppe" } else { "Verteilergruppe" }
$result += [PSCustomObject]@{
DisplayName = $group.DisplayName
EmailAddress = $group.PrimarySmtpAddress
GroupType = $groupType
Owners = $ownerEmails -join "; "
}
}
# Die Ergebnisse in eine CSV-Datei exportieren
$result | Export-Csv -Path $exportPath -NoTypeInformation -Encoding UTF8
Write-Host "Export abgeschlossen. Die CSV-Datei befindet sich unter $exportPath"
Write-Host "Fehlerhafte Besitzer wurden in der Logdatei protokolliert: $logPath"
Update 2024-09-13:
PHP: LDAP-Authentifizierung
In vielen Unternehmensnetzwerken ist LDAP (Lightweight Directory Access Protocol) ein
essenzieller Bestandteil zur Verwaltung und Authentifizierung von Benutzern.
Eine LDAP-Authentifizierung kann auch in einer PHP-Anwendung implementiert werden:
<!DOCTYPE html>
<html>
<head>
<title>LDAP-Anmeldungstemplate Ver. 0.1</title>
<!--(C) Alexander H. / STEALTHCHIP.DE-->
<meta charset="UTF-8">
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.login-form {
display: flex;
flex-direction: column;
width: 300px;
}
.login-form label {
text-align: left;
margin-bottom: 5px;
}
.login-form input[type="text"], .login-form input[type="password"], .login-form input[type="submit"] {
width: 100%;
margin-bottom: 10px;
padding: 8px;
box-sizing: border-box;
}
</style>
<script>
function logToConsole(message) {
console.log(message);
}
</script>
</head>
<body>
<div class="login-container">
<form class="login-form" method="POST" action="">
<label for="username">Benutzername:</label>
<input type="text" id="username" name="username">
<label for="password">Passwort:</label>
<input type="password" id="password" name="password">
<input type="submit" value="Anmelden">
</form>
</div>
<?php
function log_message($message) {
echo "<script>logToConsole('$message');</script>";
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = isset($_POST['username']) ? $_POST['username'] : '';
$password = isset($_POST['password']) ? $_POST['password'] : '';
if (empty($username) || empty($password)) {
log_message('Benutzername und Passwort dürfen nicht leer sein.');
} else {
$server = '';
$domain = '@';
$port = 389;
$ldap_connection = ldap_connect($server, $port);
if (!$ldap_connection) {
log_message('Verbindung zum LDAP-Server fehlgeschlagen.');
exit;
}
ldap_set_option($ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_connection, LDAP_OPT_REFERRALS, 0);
$ldap_bind = @ldap_bind($ldap_connection, $username . $domain, $password);
if (!$ldap_bind) {
log_message('Anmeldung am LDAP-Server fehlgeschlagen.');
} else {
log_message('Anmeldung erfolgreich!');
}
ldap_close($ldap_connection);
}
}
?>
</body>
</html>
Diese LDAP-Authentifizierungslogik kann als grundlegende Vorlage für eigene Webdienste
verwendet werden. Für eine erfolgreiche Authentifizierung sind jedoch noch der Server
und die Domäne in den dafür vorgesehenen Variablen anzugeben.
Fehler- und Erfolgsmeldungen werden in der Debugkonsole des Browsers ausgegeben.
|