Jak szybko wygenerować certyfikaty dla Always On
Zadanie niby trywialne – skonfigurować TLS na SQL Server z grupami Always On. Chwila szukania i wiem co musi mieć template certyfikatu. https://technet.microsoft.com//en-us/library/ms189067(v=sql.105).aspx#Anchor_2 mówi, że jest potrzebne m.in.
The certificate must be meant for server authentication. This requires the Enhanced Key Usage property of the certificate to specify Server Authentication (1.3.6.1.5.5.7.3.1).
Pech chciał, że akurat miałem pasujący template, który poza tym pozwalał na client authentication i postanowiłem go użyć. Niestety takie podejście okazało się skuchą bo certyfikat musi mieć wyłącznie OID 1.3.6.1.5.5.7.3.1 . Jeśli pojawi się cokolwiek innego to koniec, kaplica, bez szans. SQL go nie uzna i nie pozwoli na użycie do szyfrowania połączeń. Z punktu widzenie bezpieczeństwa ma to sens ale mogło by się gdzieś w dokumentacji pojawić, że leniuchy będę musiały stracić czas na zastanawianie się czemu nie działa. Koniec końców, całkiem nieźle sprawdziłPKI się standardowy template Web Server :-)
Drugim problemem dla leniwego admina pojawia się wygenerowanie requestów z wszystkim nazwami listenerów (purystów przepraszam za moją hiperniepoprawną polszczyznę) na wszystkich serwerach. https://msdn.microsoft.com/en-us/library/hh213417.aspx#SSLcertificates bardzo ładnie podaje, że temat certyfikatu to hostname bądź fqdn serwera a SAN musi zawierać wszystkie nazwy DNSowe listenerów (zarówno skrócone jak i fqdn). I tu pojawiają się dwa problemy (korporacyjnie: wyzwania) bo po pierwsze serwerów jest dużo a po drugie każdy z nich ma po kilkanaście listenerów. Jako, że nie chce mi się tyle klikać w GUI a tym bardziej nie chcę zrobić nigdzie literówki, piszę coś takiego i paroma linijkami kodu ratuję sobie resztę dnia ;)
#import SQL PS module
ipmo sqlps
#get all availability groups
#
# Replace HOSTNAME and INSTANCE with correct names
#
$allag = Get-ChildItem SQLSERVER:\SQL\HOSTNAME\INSTANCE\AvailabilityGroups
#Get list of all Subject Alternative Names
# (FQDNs and short names of all listeners)
[String[]] $listeners = @()
$allag | Select-Object Name | %{$listeners += ($_.Name + "." + $env:userdnsdomain); $listeners += $_.Name }
$fqdn = ([System.Net.Dns]::GetHostByName(($env:computerName)).HostName)
#Add server's hostname and FQDN to SANs
$listeners += $env:computerName
$listeners += $fqdn.ToString()
#Set subject as server's FQDN
[String]$subject = ("CN=" + $fqdn)
#Request certificate
Get-Certificate -Template WebServer -SubjectName $subject -DnsName $listeners -CertStoreLocation cert:\LocalMachine\My








mój zewnętrzny IP
nie jest to skomplikowane, wystarczy użyć invoke-webRequest, który jest świetną przeglądarką textową. do testów wybrałem stronę 'http://whatismyipaddress.com’ ale można wybrać dowolny inny. po wejściu od razu widzę, że jestem przekierowany na 'http://whatismyipaddress.com/pl/moj-ip’. żeby oszczędzić sobie trochę grzebaniny robię inspekcję elementu, gdzie jest adres i widzę. że jest to DIV o ID=’section_left’. można to olać, ale przyda się żeby trochę zawęzić wyszukiwanie.
teraz z linii poleceń.
na początek trzeba się zaznajomić z obiektem więc warto sprawdzić:
C:\...ive\_ScriptZ :))o- $page StatusCode : 200 StatusDescription : OK Content : <!doctype html> <html lang="pl"> <head> <meta charset="utf-8"> <meta name="robots" content="noarchive"> <title>What Is My IP Address? NarzÄ?dzia do adresA3w IP i nie tylko</title> <meta name="desc... <CUT> C:\...ive\_ScriptZ :))o- $page|gm TypeName: Microsoft.PowerShell.Commands.HtmlWebResponseObject Name MemberType Definition ---- ---------- ---------- Dispose Method void Dispose(), void IDisposable.Dispose() Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() AllElements Property Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;} BaseResponse Property System.Net.WebResponse BaseResponse {get;set;} Content Property string Content {get;} Forms Property Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;} Headers Property System.Collections.Generic.Dictionary[string,string] Headers {get;} Images Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;} InputFields Property Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;} Links Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;} ParsedHtml Property mshtml.IHTMLDocument2 ParsedHtml {get;} RawContent Property string RawContent {get;set;} RawContentLength Property long RawContentLength {get;} RawContentStream Property System.IO.MemoryStream RawContentStream {get;} Scripts Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;} StatusCode Property int StatusCode {get;} StatusDescription Property string StatusDescription {get;}polecam sprawdzić jeszcze $page.links oraz $page.images – może się kiedyś przydać. po wykonaniu
będzie już widać kawałek interesującego kodu. teraz trzeba wyłowić IP. do tego celu warto zrobić wyrażenie regularne, bo do tego są niezastąpione. np. takie super-proste, które po prostu szuka czterech liczb oddzielonych kropką, gdzie każda liczba ma od 1-3 cyfr:
[regex]$rxIP="\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}"do celów skryptu można by się poważyć o napisanie porządnej walidacji, czyli dodatkowo, żeby te liczby miały wartości od 1-255. ale to nie ten wpis. po przepuszczeniu wyniku będzie wyświetlone IP:
C:\...ive\_ScriptZ :))o- $page.AllElements|? id -eq 'section_left'|%{ write-host -ForegroundColor Yellow $rxIP.Match($_.outerText)}można teraz się dalej bawić, potestować na innych stronach, analizując elementy zwracanej strony. nie takie trudne (:
eN.