14 KiB
MijnBesluit uitrollen op een Windows-server met IIS
Uitgangspunt: mijnbesluit.nl wijst al naar de server en daar draait IIS.
Dit is geen webservice zoals in WinDev. IIS doet hier alleen de voordeur: hij neemt het verzoek aan en geeft het door aan het proces van de applicatie. Er zit geen SOAP, geen extra serialisatie en geen tweede laag tussen. De website praat vanuit dat proces rechtstreeks met MariaDB.
1. Wat er op de server moet staan
MariaDB 10.6 of nieuwer. Haal de MSI van mariadb.org, installeer met een sterk
root-wachtwoord en laat "Enable networking" uit staan als de website op dezelfde server draait —
dan is de database alleen via localhost te bereiken en kan niemand er van buitenaf bij. Staat de
database op een andere machine, zet bind-address dan op het interne adres en open poort 3306
alleen voor de webserver.
Het .NET 8 Hosting Bundle — dus niet de SDK. Zoek op "ASP.NET Core Hosting Bundle" en neem de 8.0-versie. Die installeert de runtime én de IIS-module die het verzoek doorgeeft. Daarna:
net stop was /y
net start w3svc
WebSocket Protocol in IIS. Dit wordt vaak vergeten en dan werkt de site half: pagina's laden wel, maar knoppen doen niets. Blazor Server houdt namelijk een websocket open. Aanzetten via Serverbeheer → Rollen en onderdelen toevoegen → Webserver (IIS) → Webserver → Toepassingsontwikkeling → WebSocket Protocol.
2. Mappen op de server
Zet de gegevens buiten de map waar je publiceert. Alles in de publicatiemap wordt bij een volgende publicatie overschreven, en juist deze drie dingen mag je nooit kwijtraken:
D:\MijnBesluitData\
keys\masterkey.json de sleutel van alle kluizen
dataprotection\ cookiesleutels van ASP.NET
kluisbestanden\ versleutelde bijlagen
appsettings.secrets.json connectiereeks en e-mailinstellingen
D:\Sites\mijnbesluit\ hier komt de gepubliceerde applicatie
3. Database inrichten
3a. MariaDB installeren (als hij er nog niet staat)
Haal de Windows-MSI van https://mariadb.org/download/ — neem de "Stable"-versie, 64 bits. Tijdens het installeren zijn er drie keuzes die er echt toe doen:
Root-wachtwoord. Bedenk een lang, willekeurig wachtwoord en zet het in je wachtwoordbeheer. Je hebt het hierna twee keer nodig en daarna bijna nooit meer.
"Enable networking". Draait de website op dezelfde server, haal dit vinkje dan weg, of
zet daaronder het adres op 127.0.0.1. De database is dan alleen vanaf de server zelf te
bereiken en er is van buitenaf simpelweg geen deur om op te kloppen. Dat scheelt je de hele
categorie problemen van een database die per ongeluk aan het internet hangt.
"Use UTF8 as default server's character set". Aanvinken. Het schema zet utf8mb4 overal zelf al, maar dit voorkomt verrassingen bij alles wat je er later handmatig bij maakt.
De installatie zet een Windows-service neer die MariaDB heet en automatisch meestart. Controleer
dat even in services.msc.
Installeer op de server meteen ook HeidiSQL (zit bij de MariaDB-installatie, of los te downloaden). Dat werkt prettiger dan de opdrachtregel, en omdat je toch op de server bent ingelogd hoef je de database niet open te zetten.
3b. Database en gebruiker aanmaken
Kopieer de map db uit het project naar de server, bijvoorbeeld naar D:\db.
Pas eerst in 02_applicatiegebruiker.sql de regel met VERVANG_DIT_WACHTWOORD aan. Kies een
ander wachtwoord dan op je ontwikkelmachine: raakt die ene uit, dan staat de andere er niet mee
open.
Met HeidiSQL op de server: verbinden als root op 127.0.0.1, dan Bestand →
SQL-bestand uitvoeren (Ctrl+Shift+O), eerst 01_schema.sql, daarna 02_applicatiegebruiker.sql.
Of vanaf de opdrachtregel, in een cmd-venster (niet PowerShell, die kent < niet):
"C:\Program Files\MariaDB 11.4\bin\mysql.exe" -u root -p < D:\db\01_schema.sql
"C:\Program Files\MariaDB 11.4\bin\mysql.exe" -u root -p < D:\db\02_applicatiegebruiker.sql
Het versienummer in dat pad verschilt per installatie; kijk even in C:\Program Files.
Beide scripts mogen meerdere keren gedraaid worden; bestaande tabellen en rechten blijven staan.
3c. Controleren
SHOW DATABASES;
USE mijnbesluit;
SHOW TABLES; -- hoort 14 tabellen te geven
SHOW GRANTS FOR 'mijnbesluit_app'@'localhost';
Bij de rechten hoort audit_log alleen SELECT en INSERT te hebben, de andere tabellen ook UPDATE
en DELETE, en nergens CREATE, ALTER of DROP. Zo kan de applicatie het logboek wel vullen maar
niet opschonen, en kan een fout in de code nooit een tabel weggooien.
Test daarna of het applicatieaccount echt werkt, want daar loopt het straks op vast:
"C:\Program Files\MariaDB 11.4\bin\mysql.exe" -u mijnbesluit_app -p -D mijnbesluit -e "SELECT COUNT(*) FROM users;"
Komt daar 0 uit, dan klopt zowel het wachtwoord als de rechten. Dat wachtwoord zet je straks in
appsettings.secrets.json (stap 6).
3d. Vanaf je eigen pc meekijken
Zet MariaDB daarvoor niet open naar buiten. Twee betere manieren: werk in HeidiSQL op de server zelf via de externe-bureaubladverbinding, of maak in HeidiSQL op je eigen pc een verbinding van het type SSH-tunnel als er een SSH-server op de machine draait. Een database die rechtstreeks aan het internet hangt wordt binnen een dag gevonden en geprobeerd.
4. Publiceren — in het vervolg met twee opdrachten
Liever met vinkjes en een knop dan met twee PowerShell-vensters? Zie Uitrollen met een venster. Die twee programma's roepen dezelfde scripts aan als hieronder staan; de scripts blijven dus de waarheid.
Net als de "publish site" uit WinDev, maar dan in twee stappen omdat de server niet bij jouw schijven kan.
Op je eigen pc:
cd D:\MijnBesluit
.\tools\Publiceren.ps1
Dat bouwt de Release-versie, gooit appsettings.Development.json eruit (die hoort niet op een
server) en maakt er D:\publish\mijnbesluit.zip van.
Op de server, in een PowerShell-venster als administrator:
.\Uitrollen.ps1 -Zip \\tsclient\D\publish\mijnbesluit.zip
Dat script:
- haalt het pad en de toepassingsgroep uit IIS, dus je hoeft niets in te typen;
- waarschuwt als de toepassingsgroep niet op "Geen beheerde code" staat;
- pakt eerst helemaal uit en zet de site pas daarna een paar seconden stil;
- bewaart de vorige versie in
_vorige-versiesnaast de sitemap; - zet bij een fout de vorige versie automatisch terug;
- controleert daarna of
https://mijnbesluit.nl/inloggenantwoordt — het leidt dat adres af uit de bindingen van de site; - ruimt alles op behalve de laatste vijf versies.
Kopieer Uitrollen.ps1 één keer naar de server, bijvoorbeeld naar C:\Beheer. Werkt de
stationsdoorverwijzing van je externe-bureaubladverbinding niet, kopieer dan eerst het
zip-bestand naar de server en geef dat pad op:
.\Uitrollen.ps1 -Zip D:\mijnbesluit.zip
Terugvallen op de vorige versie kan altijd; het script laat de precieze opdrachten zien als de controle mislukt.
Handmatig publiceren
Liever zelf, of eenmalig:
4b. Handmatig publiceren
Op je ontwikkelmachine:
dotnet publish src\MijnBesluit.Web -c Release -o D:\publish\mijnbesluit
Kopieer de inhoud van D:\publish\mijnbesluit naar D:\Sites\mijnbesluit op de server. In
Visual Studio kan het ook met Publiceren → Map, of met Web Deploy als dat op de server aanstaat.
dotnet publish maakt zelf een web.config aan met de juiste verwijzing naar de IIS-module;
daar hoef je niets aan te doen.
5. De site in IIS
Toepassingsgroep aanmaken, naam MijnBesluit:
- .NET CLR-versie: Geen beheerde code. Dat lijkt vreemd, maar klopt: de applicatie draait in een eigen proces en IIS hoeft er geen .NET voor te laden.
- Identiteit:
ApplicationPoolIdentity(heet danIIS AppPool\MijnBesluit). - Bij Geavanceerde instellingen: Leegloop-time-out op 0 en Vaste interval voor recyclen op 0. Anders wordt de applicatie na twintig minuten zonder bezoek afgesloten en duurt het eerste bezoek daarna onnodig lang.
Website aanmaken:
- Fysiek pad:
D:\Sites\mijnbesluit - Toepassingsgroep:
MijnBesluit - Binding: https op poort 443, hostnaam
mijnbesluit.nl, met je certificaat. Doe hetzelfde voorwww.mijnbesluit.nlals je die gebruikt. - Een binding op poort 80 mag, maar laat die alleen doorsturen naar https — de applicatie doet dat zelf ook.
Rechten. Geef IIS AppPool\MijnBesluit:
- Lezen en uitvoeren op
D:\Sites\mijnbesluit - Wijzigen op
D:\MijnBesluitData(hij moet indataprotectionenkluisbestandenkunnen schrijven, enkeys\masterkey.jsonkunnen lezen)
6. Instellingen op de server
In appsettings.Production.json (die is meegepubliceerd) staan alleen paden en de site-URL.
De geheimen komen uit het bestand waar ExtraConfigFile naar wijst. Maak
D:\MijnBesluitData\appsettings.secrets.json:
{
"ConnectionStrings": {
"MijnBesluit": "Server=localhost;Port=3306;Database=mijnbesluit;User Id=mijnbesluit_app;Password=HET-WACHTWOORD;"
},
"Smtp": {
"Host": "smtp.solcon.nl",
"Port": 587,
"UseStartTls": true,
"Username": "flietstra03",
"Password": "HET-MAILWACHTWOORD",
"FromAddress": "jonathan@flietstra.nl",
"FromName": "MijnBesluit",
"WriteToDiskInsteadOfSending": false
}
}
Let op het afzenderadres. Een mailserver van een provider accepteert vaak alleen een afzender
die bij het account hoort: Solcon weigert noreply@mijnbesluit.nl met "Verification failed".
Gebruik daarom voorlopig je eigen adres. Zodra mijnbesluit.nl een eigen mailbox of verzenddienst
heeft, zet je Host, Username, Password en FromAddress daarop om — dat is ook nodig voor
SPF en DKIM, anders komen uitnodigingen bij Gmail en Outlook in de spam.
Zo test je een afzenderadres los van de applicatie, op de server zelf:
$cred = Get-Credential
Send-MailMessage -SmtpServer smtp.solcon.nl -Port 587 -UseSsl -Credential $cred `
-From "jonathan@flietstra.nl" -To "jonathan@flietstra.nl" -Subject "test" -Body "test"
Let op: geen RedirectAllTo hier. Die hoort alleen op je testmachine; op de server moet post
gewoon naar de echte ontvanger.
Geef dit bestand alleen leesrecht aan IIS AppPool\MijnBesluit en aan beheerders.
7. Het sleutelbestand aanmaken
Eenmalig, op de server, in een opdrachtvenster:
cd D:\Sites\mijnbesluit
MijnBesluit.Web.exe --maak-sleutel D:\MijnBesluitData\keys\masterkey.json
Hij weigert een bestaand bestand te overschrijven — met opzet, want dat zou elke bestaande kluis in één klap onleesbaar maken.
Maak hier meteen een back-up van, op een andere plek dan je database-back-up. Zonder dit bestand is niets meer te openen; mét dit bestand én een databasekopie is alles leesbaar. Die twee horen dus niet bij elkaar te liggen.
8. Controleren
Ga naar https://mijnbesluit.nl/inloggen. Werkt dat, controleer dan ook:
- Vraag een code aan. Komt de e-mail aan, dan klopt de SMTP-instelling.
- Log in en klik door naar Kluis. Werken de knoppen, dan doet de websocket het.
- Herstart de toepassingsgroep in IIS en ververs de pagina. Blijf je ingelogd, dan staat de
sleutelring van ASP.NET goed. Word je uitgelogd, kijk dan naar
Security:DataProtectionPathen de schrijfrechten op die map.
9. Back-up
Drie dingen, en het liefst niet op dezelfde plek:
- De database. Bijvoorbeeld dagelijks met een taak:
mysqldump -u root -p --single-transaction mijnbesluit > D:\Backup\mijnbesluit-%DATE%.sql D:\MijnBesluitData\kluisbestanden— de versleutelde bijlagen.D:\MijnBesluitData\keys\masterkey.json— apart, en niet in dezelfde back-upset als 1 en 2.
Beproef ook eens een keer of terugzetten werkt. Een back-up die je nooit hebt teruggezet is een aanname, geen back-up.
10. Als er iets misgaat
Kijk eerst in de log. De applicatie schrijft zelf naar C:\MijnBesluitData\logs, één
bestand per dag. Daar staat de échte fout in; de bezoeker krijgt alleen een nette melding.
Get-ChildItem C:\MijnBesluitData\logs | Sort-Object LastWriteTime -Descending |
Select-Object -First 1 | Get-Content -Tail 60
"We konden de e-mail met je code niet versturen" — de SMTP-instellingen. Ontbreekt het
blok Smtp in appsettings.secrets.json, dan probeert de applicatie localhost:587 en is er
niets om verbinding mee te maken. Staat het blok er wel, dan zegt de log wat de mailserver
antwoordde: een time-out betekent dat de poort dichtstaat op de server, en een 5xx-antwoord dat
de mailserver de server niet als afzender accepteert.
HTTP 500.19 — IIS kan web.config niet lezen. Meestal is het Hosting Bundle niet
geïnstalleerd, of is het na IIS geïnstalleerd. Installeer het (opnieuw) en herstart IIS.
HTTP 500.30 — de applicatie start niet. Zet in web.config tijdelijk
stdoutLogEnabled="true" en kijk in de map logs. Negen van de tien keer is het de
connectiereeks of een ontbrekend sleutelbestand.
HTTP 502.5 — verkeerde runtime-versie, of het Hosting Bundle ontbreekt.
De pagina laadt maar knoppen doen niets — WebSocket Protocol staat niet aan in IIS. Zie stap 1.
HTTP 400 bij het insturen van een formulier — de sleutelring van ASP.NET is weg. Zie stap 8.
"Access denied for user" — het wachtwoord in appsettings.secrets.json komt niet overeen met
dat in de database. Test het eerst met de MariaDB-client op de server zelf.
11. En de SocketServer?
Die heb je pas nodig zodra de Android-app er is; de website werkt er los van. Wil je hem alvast
draaien: publiceer MijnBesluit.SocketServer, zet hem op dezelfde server neer, gebruik hetzelfde
D:\MijnBesluitData en installeer hem als service met het beheerschermpje. Open dan poort 5200
in de firewall. Zie docs/SocketServer-installeren.md.