Windows Server Core (ja DNS-palvelin)

Päivänä muutamana olen joutunut luomaan erinäisiä peruspalveluita Windows Server Coren päälle (versio 2016). Yleinen säätötieto Coresta on hieman hajallaan pitkin internettiä - dokumentaatiota toki löytyy ja se on erinomaista, mutta harvalla sivulla käydään läpi semmoinen kunnollinen asennusskenario kaikkine vaiheineen.

Tällä sivulla pyrin noin muistilapun omaisesti kuvailemaan vaiheita jotka tulivat vastaan tehdessä Coresta DNS-palvelinta aidon ja ilkeän Internetin puolelle (ei siis mikään takki auki -säätö sisäverkossa) - piti olla säädön helppoutta alussa ja melko tiukkaa liikennerajoitusta sitten valmiissa deploymentissä (tyyliin core networking + DNS + NTP + ping saa mennä läpi, muu ei).

Säätö suoritettiin Hyper-V:n päällä, mikä teki elämästä melko paljon helpompaa. Alussa oli kaksi äkäisesti pudotettua Core Server -asennusta (nimiltään NS1 ja NS2), ja näiden kyljessä ihan desktopilla varustettu Windows 2016 Standard, johon sitten pudottelin etähallintatyökaluja avittelemaan asioita. Alussa nämä kolme konetta oli verkotettu täysin internetistä irrallaan olevaan verkkoon (joka tuo tietenkin hieman haasteita kun nettiin ei ole pääsyä), ja säätöjen jälkeen NS1 ja NS2 pudotettiin isoon pahaan internettiin vaihtamalla virtuaalikytkintä jossa em. koneiden virtuaaliverkkokortit olivat kiinni (suomeksi siis napsautettiin vain toiset verkot koneille Hyper-V:stä).

Koneen nimen vaihtaminen, domain suffix ja IP
Etäältä säätäminen (Server Manager, WinRM)
DNS Recursive Queries
Palomuurin kanssa pulaaminen
Windows Update

Asennus

Sen kun asennat vaan. Kuten normaalia on, Hyper-V:ssä ISO-image mapataan DVD-driveksi ja sitten lähinnä painetaan next next next. Lopputulemana on kone jolla on verkkokortti (DHCP-asiakkaana) ja arvottu UNC-nimi. Kovin ihmeellisesti ei vehkeeseen voi vaikuttaa asennustapahtuman aikana. Kun Core on asennettu ja bootannut, ruudulla on lähinnä komentorivi joka haluaa tietää administrator-tunnuksen salasanan.

Koneen nimen vaihtaminen, domain suffix ja IP

Nimen vaihtaminen on melko triviaali toimenpide (joskin vaatii bootin nimenvaihdon jälkeen)

netdom renamecomputer VANHANIMI /NewName:UUSINIMI

...ja sitten bootataan. Boottaaminen käy notta:

shutdown /r /f /t 0

...jossa /r on Restart, /f on Force (eipä jäädä venaileen kaikkea tyhmää) ja /t on Time (montako sekuntia odotetaan ennen boottia).

Primary domain suffix

Domain suffix (kun kone ei ole osa domainia - tässä tapauksessa) onkin sitten vähän kinkkisempi homma. Tämä kuulemma kävisi helpommin säätämällä suoraan rekisteriä, väittävät. Itselleni jäi hieman jälkioireita vanhasta koneen nimestä, sillä vaihdoin koneen nimen useampaan kertaan. Oli miten oli, koneen domain-suffiksin vaihtaminen käypi notta:

netdom computername KONEENNIMI /Add:koneennimi.domain.tld
netdom computername KONEENNIMI /MakePrimary:koneennimi.domain.tld

Ensin palvelimelle lisätään uusi aliasnimi, ja toisessa tehdään tästä nimestä sitten ensisijainen. Juu, vähän on hämärää. Helpommin kuulemma menee avaamalla Regedit (tämä löytyy Server Coresta) ja sorkkimalla rekisterin haaraa

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters] joka alla avaimet
    Domain
    Searchlist

Molempiin sama domain.tld niin pitäisi toimia a-ok. Ja kyllä, tässä välissä päästään jälleen boottaamaan ennen kuin asetukset ovat voimassa - joskin mahdollisesti haluat tehdä ensin muut verkkoasetukset, jotta lopputulemana päästään ipconfig-komennolla katsomaan että kaikki asiat (koneen nimi, primary domain suffix, osoitteet, gateway, dns ja niin edelleen) ovat oikein.

IP-osoite

Varoituksen sana: tässä sohitaan asioita netsh-komennon avulla. Se on kuulemma väistymässä koska powershell. Korvaan joku päivä nämä harvat osuudet powershell-vastineilla - mutta toistaiseksi elämä on hieman helpompaa käyttämällä netsh:ta.

Aluksi tarttis tietää kätevyyden nimissä koneessa olevien aktiivisten verkkokorttien indeksinumerot:

netsh interface ipv4 show interfaces 
Idx     Met         MTU          State                Name
---  ----------  ----------  ------------  ---------------------------
  1          75  4294967295  connected     Loopback Pseudo-Interface 1
 17          15        1500  connected     Ethernet 

Melkolailla helposti arvataan että kortti jota haluamme säätää, on tässä koneessa indeksillä 17. Annetaan kahdella komennolla kortille asetuksia - ensimmäisessä staattinen IP-osoite, aliverkon maski ja gateway, jälkimmäisessä jokin DNS-palvelin. Tässä omassa säädössä palvelimesta itsestään oli tulossa DNS-palvelin, joten saattanette harkita DNS-palvelimen osoitteen muuttamista lopullisessa konfiguraatiossa (niin että dns-resolveri osoittaa laitteen omaan ip-osoitteeseen).

netsh interface ipv4 set address name="17" source=static address=10.0.0.50 mask=255.255.255.0 gateway=10.0.0.1

netsh interface ipv4 add dnsserver name="17" address=8.8.8.8

Sivumennen, kun dns-palvelimen osoitetta myöhemmin halutaan muuttaa osoittamaan esim. tässä esimerkissä koneeseen itseensä, on vanha(t) dns-palvelimet poistettava ja vasta sitten lisätään uusi/uudet. Poistaminen ja uuden lisääminen käy (ylläolevaa esimerkkiä mukaillen) komennoilla

netsh interface ipv4 delete dnsserver name="17" address=8.8.8.8
netsh interface ipv4 add dnsserver name="17" address=10.0.0.50

Ja nonnyyn! Koneella on nimi, primary domain suffix, ip-osoite, dns-palvelin, aliverkon maski ja gateway. Kaikkien säätöjen pitäisi näkyä simppelisti ajamalla ipconfig /all -komento (jos muistit bootata palvelimen suffiksin vaihdon jälkeen).

Etäältä säätäminen (Server Manager, WinRM)

Koska olin tekemässä Coresta DNS-palvelinta, piti siihen luonnollisesti asentaa em. rooli. Tämä käy melko mukavasti powershellin avulla. Kannattaa pitää mielessä että Coren oletusshell on CMD, ei suinkaan powershell. Joten esin CMD:stä powershell käyntiin:

powershell

(melko ilmiselvää.) Kun PS on nyt käynnissä, lisätään DNS-palvelin koneeseen komennolla

Install-WindowsFeature DNS

...ja siinä se suunnilleen. DNS-palvelimen säätäminen tehdään (tässä esimerkissä) sitten aivan aidolta GUI:lla varustetulta palvelimelta joka löytyy samasta aliverkosta. Tähän tarvitaan sitten muutama sääto niin Core-koneessa kuin siinä GUI-koneessakin. Ensin avaamme pääsyn Core-koneelle server manageria varten (komento ajetaan siis Core-koneen powershellissä) :

Configure-SMRemoting.exe –Enable

WinRM (Windows Remote Management) on oletuksena päällä Core-koneessa, joten sitä emme sorki.

GUI-koneessa pitää tehdä parikin asiaa. Ensinnäkin pitää Server Manageria varten laittaa GUI-koneeseen tunnus ja salasana jota GUI-kone käyttelee sitten MMC-konsoleissa, ja WinRM:ää varten pitää GUI-koneessa lisätä säädettävä Core-kone luotettujen koneiden listalle (alla olevassa esimerkissä Core-koneen nimi on "NS1"):

cmdkey /add:NS1 /user:NS1\Administrator /pass:corekoneenadmininsalasana
winrm s winrm/config/client '@{TrustedHosts="NS1"}'

Sivumennen, TrustedHosts-lista voi sisältää useita koneita - parametriin laitetaan pilkulla erotettu lista ("NS1,NS2,NS3").

Kun tämä on tehty, voidaan GUI-koneesta avata Server Manager ja lisätä hallittava Core-kone hallittavien koneiden listalle (All Servers). Jos kaikki on kunnossa, koneen tiedot ilmestyvät Server Manageriin näkyviin. Nyt GUI-koneen Server Managerilla voidaan vaikkapa lisätä uusia rooleja ja featureita Core-koneeseen. Alla kuvassa koneita säädettiin kahta yhtä aikaa, ja GUI-kone on oletusniminen hässäkkä.

Server Manager

DNS:n hallintaa varten tarvitaan DNS-hallintatyökalu GUI-koneeseen. Tämä tapahtuu lisäämällä oikea RSAT-työkalu (ei siis suinkaan asentamalla GUI-koneeseen DNS-roolia tai etsiskelemällä turhaan em. roolin alta hallintatyökaluja valittavaksi):

RSAT

Kun asennus on valmis, voidaan DNS-työkalu avata ja kytkeytyä sen avulla NS1-koneeseen. Jotkin toiminnot (esim. hiiren oikean napin kliksuttelu DNS-työkalussa serveripuun juuressa) hämmentävästi voivat joskus kestää minuutin, tiedä mikä sitten on pielessä. Mutta kaikki toimii kyllä, ja esim. zonejen ja dns-tietueiden lisääminen käy kuin paikallisella koneella ikään.

DNS Recursive Queries

Ei saa laittaa internettiin DNS-palvelinta joka sallii rekursiiviset kyselyt kaikille dns-tietueita pyytäville laitteille. Tällä saa aikaan paljon pahaa verta ja sähköposteja Ficoralta. Homman nimi on "DNS Amplification Attack", ja Google kertonee teille aiheesta enemmän.

Julkisessa internetissä oleva DNS-palvelin saa (näin oman käsitykseni mukaan) hyväksyä rekursiivisen kyselyn siitä omasta kotiverkosta ja hostilta itseltään. Linux (ja Unix)-miesten BINDissä on esiprosessori ja kaikenlaisia optioita joilla em. rajoitus tehdään. Nykyään peräti Windowsinkin puolella moinen on mahdollista. Yhtä helposti kuin Linuxissa? No ei tietenkään.

BIND-kaverit laittavat sinne named.conf:iinsa että "options {allow-recursion {10.0.0.0/24; };}" ja happy days, rekursio sallitaan vain tästä verkosta. Windows-säätäjä saa tehdä tästä tietty politiikan, subnetin ja säännön, koska Windows.

Ensin ammutaan alas juuressa oleva sääntö joka rekursion sallii (käytössä jälleen powershell, ja komennot suoritetaan nimenomaisesti Core-koneelta käsin):

Set-DnsServerRecursionScope -Name . -EnableRecursion $false

Nuin ikkään. Nyt DNS ei salli rekursiivisia kyselyitä lainkaan. Tämä ei ole toivottavaa paikallisen aliverkkomme jäsenille (käytämme esimerkeissä jälleen 10.0.0.0/24-verkkoa), joten ensin määritellään subnet johon lisätään em. verkko sekä localhost -osoitteet:

Add-DnsServerClientSubnet -Name "OmaSubnet" -IPv4Subnet 10.0.0.0/24,127.0.0.0/24

Tämän jälkeen luomme Recursion Scopen jossa rekursio sallitaan:

Add-DNSServerRecursionScope -Name "OmaScope" -EnableRecursion $True

...ja lopuksi teemme näistä palikoista säännön (tahikka elikkä politiikan):

Add-DnsServerQueryResolutionPolicy -Name "OmaPolicy" -Action ALLOW -ApplyOnRecursion -RecursionScope "OmaScope" -ClientSubnet "EQ,OmaSubnet"

Phew. Huomatkaa lopussa vielä EQ (as in "Equals"). Onhan se kiva kun on säätövaraa mutta joskus asiat näyttävät hippasen verran tarpeettoman monimutkaisilta. Mutta tämän säädön jälkeen vain paikalliselta koneelta ja 10-aliverkosta tulevat rekursiiviset kyselyt sallitaan. Voitto!

Scopen muuttaminen

Lisätietona - jos sallittuja aliverkkoja pitää muuttaa, se käy kommennolla

Set-DnsServerClientSubnet -Action REPLACE -Name "OmaSubnet" -IPv4Subnet 192.168.1.0/24,10.0.0.0/24,127.0.0.0/24

Tässä lisäämme OmaSubnet -aliverkkoon yhden 192.168.-privaattiverkon edellisen esimerkin aliverkkojen rinnalle. Jos yksi aliverkoista pitäisi vaikkapa poistaa, se puolestaan käy komennolla

Set-DnsServerClientSubnet -Action REMOVE -Name "OmaSubnet" -IPv4Subnet 192.168.1.0/24

Sikäli kun aliverkkojen asetukset haluaa tarkistaa, komento on

Get-DnsServerClientSubnet

Palomuurin kanssa pulaaminen

Jos modernin Windowsin palomuuri ei ole tuttu asia, tässä kohden voi tulla tiettyjä vaikeuksia. Yritän lieventää niitä maalaamalla pikaisen kuvan siitä miten Advanced Firewall noin logiikkansa puolesta toimii:

Public-profiili on kaikkein rajoitetuin (sekä lähtevän että saapuvan datan suhteen), Private:ssa toimivat jo esim. levypalvelut ja sen sellainen ja Domain-profiilissa kaikenlainen toimialueen sälä ja säätö. Tämä siis oletuksena. Tietenkin voit säätää palomuurin sääntöjä niin että profiili voi olla miten hyvänsä avoin tai rajoitettu eikä em. lähtötilanne enää pidä paikkaansa.

Mennään vielä toisin päin: Kun teet palomuuriin säännön, sääntö on joko sallittu- tai kielletty-sääntö (Allow/Deny), sen koskeeko sääntö saapuvia vai lähteviä paketteja (Inbound/Outbound), lähdeosoitteen, kohdeosoitteen (tai aliverkkoja), porttinumeron tai porttinumeroalueen sekä mahdollisesti ohjelman jota sääntö koskee. Sääntö on siis olemassa, mutta ei välttämättä vaikuta mihinkään jos se a) ei ole päällä (Enabled/Disabled) ja b) jos sääntö ei ole asetettu profiiliin jossa yksikään koneen verkkokortti on (Profile: Public/Private/Domain).

Sekavaa? Juu.

Coressa sääntöjä voi tarkastella komennolla (jälleen powershell, ja tämä ei palauta säännöistä kuin kursoriset tiedot)

Get-netfirewallrule | format-table name, displaygroup, action, direction, enabled, profile -autosize

Suosittelen pienentämään komentorivi-ikkunan fonttia ja leventämään ikkunaa ennen kuin komentoa ajetaan.

Oletus-Coressa säätämisen jälkeen ammuin alas seuraavat palomuurisäännöt (säännöt kuuluvat ryhmiin, ja alla poistetaan kokonaisia sääntöryhmiä käytöstä):

Disable-NetFirewallRule -DisplayGroup "Alljoyn Router"
Disable-NetFirewallRule -DisplayGroup "Windows Remote Management"
Disable-NetFirewallRule -DisplayGroup "mDNS"
Disable-NetFirewallRule -DisplayGroup "Windows Management Instrumentation (WMI)"
Disable-NetFirewallRule -DisplayGroup "DiagTrack"

Disable-NetFirewallRule -Name "DNSSrv-RPCEPMAP-TCP-IN"

Viimeinen komento poistaa DNS:n RPC-säännön (ts. disabloi yhden yksittäisen säännön, ei kokonaista ryhmää). Tämän jälkeen sitten se etäältä säätäminen GUI-Windowsilla ei enää toimi, WinRM ja WMI on suljettu.

Koska halusin että kone saa aikansa internetistä NTP:n läpi, tein erikseen säännöt NTP:lle:

New-NetFirewallRule -Name "NTP (in)" -Description "Network Time" -DisplayName "NTP (in)" -Enabled:True -Profile Public -Direction Inbound -Action Allow -Protocol UDP -LocalPort 123
New-NetFirewallRule -Name "NTP (out)" -Description "Network Time" -DisplayName "NTP (out)" -Enabled:True -Profile Public -Direction Outbound -Action Allow -Protocol UDP -RemotePort 123

Jos koneen aika-asetuksia haluaa säätää (mukaanlukien se mistä NTP-klientti hakee koneelle ajan ja aikavyöhykkeen valinta), se käy avaamalla Control Panel -appletti komennolla

control timedate.cpl

w32tm -komento toimii myös, mutta siinä on paljon enemmän tekelehtimistä kuin appletin kautta pikaisesti säätämällä. Suosittelen NTP-palvelimeksi muuten pool.ntp.org -osoitetta, sieltä pullahtaa helposti aika.

Jos tykätään että koneen pitäisi vastata ICMP Ping:iin (ja että koneesta voi pingata muita koneita), tarvitsee avata sitä koskeva valmis sääntö:

Set-NetFirewallRule -Name FPS-ICMP4-ERQ-In -Enabled True
Set-NetFirewallRule -Name FPS-ICMP4-ERQ-out -Enabled True

Verkkokortin profiilin näyttäminen ja asettaminen

Edellä on jo saattanut paljonkin kiinnostaa millaiseen profiiliin verkkokortti koneessa on asetettu. Tämä selviää powershell-komennolla

Get-NetConnectionProfile
		  
Name : Ethernet
InterfaceAlias : Internet
InterfaceIndex : 13
NetworkCategory : Public
IPv4Connectivity : LocalNetwork
IPv6Connectivity : LocalNetwork

Edellä näkyy myös se kuuluisa InterfaceIndex, jota aiemmin netsh-komennon kanssa pulatessa tarvittiin. Jos kortin haluaa pakottaa johonkin muuhun profiiliin (edellä näkyvässä esimerkkitulosteessa InterfaceIndex on 13), se puolestaan käy komennolla

Set-NetConnectionProfile -InterfaceIndex 13 -NetworkCategory Private

Windows Update

Ilmeisesti olis mukavakin että Server Coreen saisi päivityksiäkin. 2016:ssa on oletuksena päällä melko automaattinen Windows Update -asetus, mutta operoivalle operaattorille on joskus mukavampi että saa pakotettua päivitykset koneeseen per heti ja niin että jotain näkyvääkin tapahtuu.

Helppoa tapaa tähän ei luonnollisesti ole. Käytännössä pulaamme koneeseen skriptin joka tekee päivitysten tarkistus- ja asennustemput. Jos RDP:tä ei ole avattu, hyper-v:n konsolilta sitten notepad auki Server Coressa ja hyper-v:n puolella "type clipboard text". Ja pala kerrallaan, kun se puskuri ei ole määrättömän kokoinen. Kun kaikki alta on saatu siirrettyä Server Coren notepadiin, tallennus vaikkapa hakemistoon "C:\Update\" ja nimelle WUA_SearchDownloadInstall.vbs - nimi jota MS:n oma dokumentaatio käyttää

Set updateSession = CreateObject("Microsoft.Update.Session")
updateSession.ClientApplicationID = "MSDN Sample Script"

Set updateSearcher = updateSession.CreateUpdateSearcher()

WScript.Echo "Searching for updates..." & vbCRLF

Set searchResult = _
updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")

WScript.Echo "List of applicable items on the machine:"

For I = 0 To searchResult.Updates.Count-1
    Set update = searchResult.Updates.Item(I)
    WScript.Echo I + 1 & "> " & update.Title
Next

If searchResult.Updates.Count = 0 Then
    WScript.Echo "There are no applicable updates."
    WScript.Quit
End If

WScript.Echo vbCRLF & "Creating collection of updates to download:"

Set updatesToDownload = CreateObject("Microsoft.Update.UpdateColl")

For I = 0 to searchResult.Updates.Count-1
    Set update = searchResult.Updates.Item(I)
    addThisUpdate = false
    If update.InstallationBehavior.CanRequestUserInput = true Then
        WScript.Echo I + 1 & "> skipping: " & update.Title & _
        " because it requires user input"
    Else
        If update.EulaAccepted = false Then
            WScript.Echo I + 1 & "> note: " & update.Title & _
            " has a license agreement that must be accepted:"
            WScript.Echo update.EulaText
            WScript.Echo "Do you accept this license agreement? (Y/N)"
            strInput = WScript.StdIn.Readline
            WScript.Echo 
            If (strInput = "Y" or strInput = "y") Then
                update.AcceptEula()
                addThisUpdate = true
            Else
                WScript.Echo I + 1 & "> skipping: " & update.Title & _
                " because the license agreement was declined"
            End If
        Else
            addThisUpdate = true
        End If
    End If
    If addThisUpdate = true Then
        WScript.Echo I + 1 & "> adding: " & update.Title 
        updatesToDownload.Add(update)
    End If
Next

If updatesToDownload.Count = 0 Then
    WScript.Echo "All applicable updates were skipped."
    WScript.Quit
End If
    
WScript.Echo vbCRLF & "Downloading updates..."

Set downloader = updateSession.CreateUpdateDownloader() 
downloader.Updates = updatesToDownload
downloader.Download()

Set updatesToInstall = CreateObject("Microsoft.Update.UpdateColl")

rebootMayBeRequired = false

WScript.Echo vbCRLF & "Successfully downloaded updates:"

For I = 0 To searchResult.Updates.Count-1
    set update = searchResult.Updates.Item(I)
    If update.IsDownloaded = true Then
        WScript.Echo I + 1 & "> " & update.Title 
        updatesToInstall.Add(update) 
        If update.InstallationBehavior.RebootBehavior > 0 Then
            rebootMayBeRequired = true
        End If
    End If
Next

If updatesToInstall.Count = 0 Then
    WScript.Echo "No updates were successfully downloaded."
    WScript.Quit
End If

If rebootMayBeRequired = true Then
    WScript.Echo vbCRLF & "These updates may require a reboot."
End If

WScript.Echo  vbCRLF & "Would you like to install updates now? (Y/N)"
strInput = WScript.StdIn.Readline
WScript.Echo 

If (strInput = "Y" or strInput = "y") Then
    WScript.Echo "Installing updates..."
    Set installer = updateSession.CreateUpdateInstaller()
    installer.Updates = updatesToInstall
    Set installationResult = installer.Install()
 
    'Output results of install
    WScript.Echo "Installation Result: " & _
    installationResult.ResultCode 
    WScript.Echo "Reboot Required: " & _ 
    installationResult.RebootRequired & vbCRLF 
    WScript.Echo "Listing of updates installed " & _
    "and individual installation results:" 
 
    For I = 0 to updatesToInstall.Count - 1
        WScript.Echo I + 1 & "> " & _
        updatesToInstall.Item(i).Title & _
        ": " & installationResult.GetUpdateResult(i).ResultCode   
    Next
End If

Kun skripti on tavalla tai toisella saatu paikalliselle Server Corelle, ajetaan sitä komennolla

cscript WUA_SearchDownloadInstall.vbs

Skripti kyselee asioita ja kertoo asennusten onnistumisesta, ynnä mahdollisesta uudelleenkäynnistyksen tarpeesta.

wupdate

Kuten kuvasta voidaan päätellä, kolmisen updatea oli ajamatta. Palvelin täytyy myös uudelleenkäynnistää, ei tapahdu itsestään.