BLEIBEN SIE INFORMIERT

Melden Sie sich für unsere Newsletter an und erhalten Sie exklusive Updates zu IT-Trends und Neuigkeiten der ORIDX AG.

BLEIBEN SIE INFORMIERT

Melden Sie sich für unsere Newsletter an und erhalten Sie exklusive Updates zu IT-Trends und Neuigkeiten der ORIDX AG.

8 Minuten Lesezeit (1509 Worte)

Und plötzlich startet der SQL Server nicht mehr – Was beim Einsatz von Zertifikaten zu beachten ist

Ein Neustart des SQL Servers – und plötzlich startet der Dienst nicht mehr. Der Grund ist oft unscheinbar, aber folgenschwer: Das konfigurierte Zertifikat wurde entfernt.

Gerade in Umgebungen, in denen Zertifikate zentral durch das Infrastruktur‑Team erneuert werden, kann der SQL Server sein Zertifikat „verlieren“ – und fällt dann beim nächsten Neustart aus.

In diesem Beitrag zeige ich, warum das passiert, wie der SQL Server Zertifikate beim Start prüft und wie ihr mit PowerShell sicherstellt, dass eure Instanzen immer korrekt konfiguriert bleiben.

Die Grundlagen: Transportverschlüsselung

Die Transportverschlüsselung schützt alle zwischen SQL Server und Client übertragenen Daten vor Mitlesen und Manipulation. Der Microsoft SQL Server nutzt dafür TLS (Transport Layer Security).

TLS erfordert ein digitales Zertifikat auf dem SQL Server. Dieses dient sowohl zur Verschlüsselung als auch zur Serverauthentifizierung gegenüber dem Client. Damit der SQL Server direkt nach der Installation betriebsbereit ist, erzeugt er beim Start automatisch ein selbstsigniertes Zertifikat.

Nachteil dieses Zertifikats: Die Authentizität des Servers kann nicht überprüft werden, Man-in-the-Middle-Angriffe sind damit möglich. Um deutlicher vor dieser Situation zu warnen, hat Microsoft vor einiger Zeit den Standard in seinen Client-Bibliotheken verändert. Seitdem muss nun bei der Verbindung zu einem solchen Server immer die Option „TrustServerCertificate“ gesetzt werden, im SQL Server Management Studio das Häkchen bei „Serverzertifikat vertrauen“. 

Best Practice: Einrichtung eines offiziellen Zertifikates

Für produktive Umgebungen sollten CA‑signierte Zertifikate verwendet werden, die von einer Unternehmens-PKI (Public Key Infrastructure) oder einer öffentlichen CA (Certificate Authority) ausgestellt wurden.

Wie genau diese Zertifikate beantragt und eingerichtet werden, darauf gehe ich in einem zukünftigen Beitrag ein. Hier will ich nur die Situation beim Kunden beschreiben, die bei euch vielleicht ganz ähnlich ist. Es gibt ein Infrastruktur-Team, das sich um die Windows Server und auch die Zertifikate kümmert. Dieses aktualisiert die Zertifikate regelmäßig auf allen Servern.

Und genau hier lag beim Kunden das (Kommunikations-)Problem: Dem Infrastruktur-Team war nicht bewusst, dass das Zertifikat auch vom SQL Server verwendet wird. Denn das Zertifikat liegt immer im Zertifikatsspeicher des Computers und wird in der Konfiguration des SQL Server nur über den Fingerabdruck (englisch „thumbprint“) referenziert. Dies ist ein kryptografischer Hash, der über den gesamten Inhalt des Zertifikats berechnet wird. Er wird meist als 40 Zeichen lange Zeichenkette dargestellt. 

Auf der Suche nach dem Zertifikat

Beim Start schaut der SQL Server in die Eigenschaft „Certificate“ des folgenden Registrierungsschlüssels:

HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\<Instanz-ID, beispielsweise „MSSQL17.MSSQLSERVER“>\MSSQLServer\SuperSocketNetLib

Ist die Eigenschaft leer, so erstellt er ein selbstsigniertes Zertifikat. Ist die Eigenschaft gefüllt, so interpretiert er die Zeichenkette als Fingerabdruck und sucht das entsprechende Zertifikat.

Wird das Zertifikat nicht gefunden, ist es abgelaufen oder kann er nicht auf den privaten Schlüssel zugreifen, so wird der Start abgebrochen und die folgende Fehlermeldung in die Datei ERRORLOG geschrieben: 

Error: 33555, Severity: 16, State: 1.
Unable to find the user-specified certificate [Cert Hash(sha1) "5BEE839A431C00DC46C6CEDB05CCECEFDDD85D55"] in the certificate store of the local computer. Please verify that certificate exists.
Error: 26014, Severity: 16, State: 1.
Unable to load user-specified certificate [Cert Hash(sha1) "5BEE839A431C00DC46C6CEDB05CCECEFDDD85D55"]. The server will not accept a connection. You should verify that the certificate is correctly installed. See "Configuring Certificate for Use by SSL" in Books Online.
Error: 17182, Severity: 16, State: 1.
TDSSNIClient initialization failed with error 0x80092004, status code 0x80. Reason: Unable to initialize SSL support. Cannot find object or property. . For more information, see https://go.microsoft.com/fwlink/?linkid=2322222.
Error: 17182, Severity: 16, State: 1.
TDSSNIClient initialization failed with error 0x80092004, status code 0x1. Reason: Initialization failed with an infrastructure error. Check for previous errors. Cannot find object or property. . For more information, see https://go.microsoft.com/fwlink/?linkid=2322222.
Error: 17826, Severity: 18, State: 3.
Could not start the network library because of an internal error in the network library. To determine the cause, review the errors immediately preceding this one in the error log. For more information, see https://go.microsoft.com/fwlink/?linkid=2322072.
Error: 17120, Severity: 16, State: 1.
SQL Server could not spawn FRunCommunicationsManager thread. Check the SQL Server error log and the operating system error log for information about possible related problems. For more information, see https://go.microsoft.com/fwlink/?linkid=2322142. 

Diese Prüfungen werden nur beim Start vorgenommen, daher läuft der SQL Server auch nach Ablauf oder der Entfernung des Zertifikates weiter. Die Falle schnappt also erst beim Neustart zu. 

Regelmäßige Kontrolle: Ist alles korrekt konfiguriert?

Um sicherzugehen, dass alle SQL Server korrekt konfiguriert sind, haben wir beim Kunden ein PowerShell-Skript implementiert, das regelmäßig ausgeführt wird und den aktuellen Status zentral ablegt.

Für diesen Beitrag habe ich den Code auf das Notwendigste gekürzt:

$currentThumbprint = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL??.MSSQLSERVER\MSSQLServer\SuperSocketNetLib" -Name Certificate).Certificate
$currentCertificate = if ($currentThumbprint) { Get-ChildItem -Path "Cert:\LocalMachine\My\$currentThumbprint" -ErrorAction SilentlyContinue }
$currentServiceAccess = if ($currentCertificate.PrivateKey) { (Get-Acl -Path "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$($currentCertificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)").Access | Where-Object { $_.IdentityReference -eq 'NT SERVICE\MSSQLSERVER' } }

[PSCustomObject]@{
    IsCertificateConfigured = if ($currentThumbprint) { $true } else { $false }
    IsCertificatePresent    = if ($currentCertificate) { $true } else { $false }
    IsCertificateValid      = if ($currentCertificate.NotAfter -gt (Get-Date)) { $true } else { $false }
    IsCertificateAccessable = if ($currentServiceAccess) { $true } else { $false }
    CertificateValidDays    = if ($currentCertificate.NotAfter -gt (Get-Date)) { ($currentCertificate.NotAfter - (Get-Date)).days } else { $null }
    Thumbprint              = $currentThumbprint
    Certificate             = $currentCertificate
} 

Ihr könnt den Code gerne bei euch verwenden, vor einem regelmäßigen produktiven Einsatz solltet ihr ihn aber an eure Umgebung anpassen bzw. erweitern. 

Regelmäßige Aktualisierung: Einrichtung eines neuen Zertifikates

Hat das Infrastruktur-Team ein neues Zertifikat eingerichtet, so sind folgende Schritte auszuführen:

  • Ermittlung des neuen Zertifikates
  • Evtl.: Prüfung, ob das Zertifikat geeignet ist
  • Einrichtung von Leserechten auf den privaten Schlüssel
  • Einrichtung des Zertifikates in der Registrierung des SQL Servers

Auf die Details des zweiten Schrittes werde ich in einem zukünftigen Beitrag eingehen. Wie beim Kunden gehen wir hier davon aus, dass das Infrastruktur-Team korrekt arbeitet und immer genau ein geeignetes Zertifikat einrichtet.

Die benötigten Leserechte können sowohl an das zum Start des Dienstes verwendete Konto als auch an das virtuelle Konto des Dienstes selbst vergeben werden. Wie auch beim Thema „Instant File Initialization“ (siehe hier) empfehlen wir, immer das virtuelle Konto des Dienstes selbst zu berechtigen. Damit bleiben die Rechte auch dann erhalten, wenn der SQL Server später beispielsweise mit einem gruppenverwalteten Dienstkonto (siehe hier) genutzt wird.

Auch hier habe ich für diesen Beitrag den Code auf das Notwendigste gekürzt: 

$currentThumbprint = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL??.MSSQLSERVER\MSSQLServer\SuperSocketNetLib" -Name Certificate).Certificate
$currentCertificate = if ($currentThumbprint) { Get-ChildItem -Path "Cert:\LocalMachine\My\$currentThumbprint" -ErrorAction SilentlyContinue }
$currentServiceAccess = if ($currentCertificate.PrivateKey) { (Get-Acl -Path "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$($currentCertificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)").Access | Where-Object { $_.IdentityReference -eq 'NT SERVICE\MSSQLSERVER' } }
if ($currentServiceAccess) {
    [PSCustomObject]@{
        IsCertificateConfigured = if ($currentThumbprint) { $true } else { $false }
        IsCertificatePresent    = if ($currentCertificate) { $true } else { $false }
        IsCertificateValid      = if ($currentCertificate.NotAfter -gt (Get-Date)) { $true } else { $false }
        IsCertificateAccessable = if ($currentServiceAccess) { $true } else { $false }
        CertificateValidDays    = if ($currentCertificate.NotAfter -gt (Get-Date)) { ($currentCertificate.NotAfter - (Get-Date)).Days } else { $null }
        Thumbprint              = $currentThumbprint
        Certificate             = $currentCertificate
        Changed                 = $false
        Failed                  = $false
        Message                 = $null
    }
} else {
    $message = $null
    $newCertificate = Get-ChildItem -Path Cert:\LocalMachine\My\ | Where-Object { $_.Subject -like "CN=$(hostname).*" -and $_.NotAfter -gt (Get-Date).AddDays(30) }
    if (-not $newCertificate) {
        $message = "No suitable certificate found"
    } elseif ($newCertificate.Count -gt 1) { 
        $message = "More than one suitable certificate found"
    } elseif (-not $newCertificate.PrivateKey) {
        $message = "Certificate has no private key"
    } else {
        try {
            $newCertificateAclPath = "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$($newCertificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)"
            $newCertificateAcl = Get-Acl -Path $newCertificateAclPath
            $currentServiceAccess = $newCertificateAcl.Access | Where-Object { $_.IdentityReference -eq 'NT SERVICE\MSSQLSERVER' }
            if (-not $currentServiceAccess) { 
                $accessRule = [System.Security.AccessControl.FileSystemAccessRule]::new('NT SERVICE\MSSQLSERVER', 'Read', 'Allow')
                $newCertificateAcl.AddAccessRule($accessRule)
                Set-Acl -Path $newCertificateAclPath -AclObject $newCertificateAcl
            }
            $newThumbprint = $newCertificate.Thumbprint.ToLowerInvariant()
            Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL??.MSSQLSERVER\MSSQLServer\SuperSocketNetLib" -Name Certificate -Value $newThumbprint
        } catch {
            $message = "Failure: $_"
        }
    }
    if ($message) {
        [PSCustomObject]@{
            IsCertificateConfigured = if ($currentThumbprint) { $true } else { $false }
            IsCertificatePresent    = if ($currentCertificate) { $true } else { $false }
            IsCertificateValid      = if ($currentCertificate.NotAfter -gt (Get-Date)) { $true } else { $false }
            IsCertificateAccessable = if ($currentServiceAccess) { $true } else { $false }
            CertificateValidDays    = if ($currentCertificate.NotAfter -gt (Get-Date)) { ($currentCertificate.NotAfter - (Get-Date)).Days } else { $null }
            Thumbprint              = $currentThumbprint
            Certificate             = $currentCertificate
            Changed                 = $false
            Failed                  = $true
            Message                 = $message
        }
    } else {
        [PSCustomObject]@{
            IsCertificateConfigured = $true
            IsCertificatePresent    = $true
            IsCertificateValid      = $true
            IsCertificateAccessable = $true
            CertificateValidDays    = ($newCertificate.NotAfter - (Get-Date)).Days
            Thumbprint              = $newThumbprint
            Certificate             = $newCertificate
            Changed                 = $true
            Failed                  = $false
            Message                 = $null
        }
    }
}
 

Beachtet beim Einsatz dieses Codes in eurer Umgebung bitte, dass hier nicht alle Fälle abgedeckt sind. So sind beispielsweise benannte Instanzen und Failover-Cluster-Instanzen nicht berücksichtigt. Auch empfehle ich für den produktiven Einsatz die Erweiterung um Protokollierung und Fehlerbehandlung. An „Changed“ und „Failed“ könnt ihr aber bereits erkennen, dass der Code mit ein paar Anpassungen auch für die Ausführung mit Ansible geeignet wäre.

Fazit: Behaltet eure Zertifikate im Blick – mit PowerShell

Mit nur wenigen Zeilen PowerShell könnt ihr euch einen Überblick über die Konfiguration der von den SQL Servern genutzten Zertifikate verschaffen.

Und mit ein paar weiteren Zeilen PowerShell könnt ihr die korrekte Konfiguration automatisiert sicherstellen.

Wenn ihr Unterstützung bei der Automatisierung oder Zertifikatsverwaltung eurer SQL Server benötigt, stehen wir euch gerne zur Verfügung. 

Seminarempfehlung

Principal Consultant bei ORDIX

Welche SearchEngine passt zu dir? OpenSearch und S...

Ähnliche Beiträge

 

Kommentare

Derzeit gibt es keine Kommentare. Schreibe den ersten Kommentar!
Dienstag, 03. Februar 2026

Sicherheitscode (Captcha)