333 lines
14 KiB
Markdown
333 lines
14 KiB
Markdown
# 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
|
|
|
|
```sql
|
|
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](Uitrollen-met-het-venster.md). 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:**
|
|
|
|
```powershell
|
|
cd D:\MijnBesluit VS\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:
|
|
|
|
```powershell
|
|
.\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-versies` naast de sitemap;
|
|
* zet bij een fout de vorige versie automatisch terug;
|
|
* controleert daarna of `https://mijnbesluit.nl/inloggen` antwoordt — 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:
|
|
|
|
```powershell
|
|
.\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 dan `IIS 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 voor
|
|
`www.mijnbesluit.nl` als 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 in `dataprotection` en `kluisbestanden` kunnen
|
|
schrijven, en `keys\masterkey.json` kunnen 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`:
|
|
|
|
```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:
|
|
|
|
```powershell
|
|
$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:DataProtectionPath`
|
|
en de schrijfrechten op die map.
|
|
|
|
## 9. Back-up
|
|
|
|
Drie dingen, en het liefst niet op dezelfde plek:
|
|
|
|
1. De database. Bijvoorbeeld dagelijks met een taak:
|
|
`mysqldump -u root -p --single-transaction mijnbesluit > D:\Backup\mijnbesluit-%DATE%.sql`
|
|
2. `D:\MijnBesluitData\kluisbestanden` — de versleutelde bijlagen.
|
|
3. `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.
|
|
|
|
```powershell
|
|
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`.
|