Een server afschermen en een WAF voor uw app plaatsen

Vanaf de eerste uitrol laat erpc worker SSH alleen toe vanaf uw adres en verwerpt het elke andere inkomende verbinding. Een tabel [waf] zet een filterende proxy voor uw API.
Deze pagina beschrijft ERPC CLI v0.1.0-rc.4, een pre-release.

De firewall

Een uitrol stelt de firewall van de server in voordat de app voor het eerst start:
  • SSH alleen vanaf firewall.ssh_allow en het adres waarvandaan de uitrol komt;
  • elke andere inkomende verbinding verworpen, behalve run.port vanaf firewall.app_allow; voor een app achter een WAF in plaats daarvan de poort van de WAF, en alleen waar de WAF op de server al de regels draait die [waf] nu voorschrijft (hieronder).
Elke wijziging loopt onder een rollbacktimer van 120 seconden en wordt pas behouden zodra een nieuwe verbinding binnenkomt, zodat een foute wijziging u niet kan buitensluiten.
De allowlist overleeft wel het adres waarvandaan hij is gemaakt: als uw thuisadres verandert, is de weg terug naar binnen ERPC-support voor een ERPC-server, of de console van de provider voor een eigen server. Zet een tweede adres waarvandaan u de server kunt bereiken (een kantoor, een VPN-uitgang) in firewall.ssh_allow voordat u op een thuisadres vertrouwt.
Als uw adres verandert, voeg dan elk vast adres toe dat uw beheerders gebruiken:
bash
erpc worker firewall allow --to ssh 203.0.113.10 198.51.100.7
CommandoWat het doet
erpc worker firewall listToont wie de server mag bereiken: erpc.toml naast de tabel van de server
erpc worker firewall allow --to ssh|app <addresses>Laat adressen binnen en voert dat door. SSH wordt toegestaan vanaf afzonderlijke adressen; een netwerk wordt alleen geaccepteerd met --to app
erpc worker firewall deny --to ssh|app <addresses>Laat ze niet meer toe
erpc worker firewall syncMaakt de firewall van de server weer gelijk aan wat erpc.toml zegt

Een WAF voor uw app plaatsen

Voeg één tabel toe aan erpc.toml:
toml
[waf]
port = 8080
erpc worker deploy draait daarna een HTTP-reverse proxy op de server, in een eigen systemd-unit (erpc-waf-<app>-<env>): een dynamische gebruiker zonder privileges, zonder sleutel, zonder secret en zonder iets van de worker, beperkt tot 1 GiB geheugen. Hij luistert op waf.port, leest elk verzoek voordat de app dat doet, weigert wat zijn regels noemen en stuurt de rest door naar de app op 127.0.0.1:<run.port>.
  • De app blijft op loopback. Zijn eigen poort wordt nooit van buitenaf geopend. erpc worker api en de health check bereiken de app via loopback, buiten de WAF om.
  • Wie verbinding mag maken, wordt nog steeds bepaald door de lijst van de firewall, en de poort van de WAF is de enige plek waar die lijst iedereen mag noemen:
    sh
    erpc worker firewall allow --to app 0.0.0.0/0     # and ::/0
    Staat er niemand op de lijst, dan luistert de WAF en wordt niemand toegelaten, en de uitrol meldt dat.
  • De poort wordt geopend zodra de WAF erop luistert, nooit eerder. Een uitrol waarvan de WAF niet opkomt, mislukt en zegt waarom, terwijl de app blijft draaien.
  • Als u [waf] wijzigt, wordt de poort voor niemand nieuws geopend totdat een uitrol de nieuwe regels op hun plaats heeft gezet.
  • [waf] weghalen uit erpc.toml en uitrollen verwijdert de WAF van de server.

WAF-instellingen

Alleen port is verplicht. Elke sleutel, met zijn standaardwaarde:
toml
[waf]
port = 8080          # the public port, 1024 or above; the app keeps [run] port
methods = ["GET", "POST", "PUT", "DELETE"]
allow_paths = []     # every path
deny_paths = []
max_body_bytes = 1048576
max_header_bytes = 16384
rate = { per_second = 50, burst = 100 }
max_connections_per_address = 32
ban = { after = 100, window_secs = 60, for_secs = 600 }
block_probes = true
header_timeout_secs = 10
body_timeout_secs = 30
upstream_timeout_secs = 30
new_connections = { per_second = 50, burst = 100 }
SleutelWat het doetWat geweigerd wordt, krijgt
methodsDe methoden die worden doorgestuurd, in hoofdletters (hoogstens 16; nooit CONNECT)405, met Allow
allow_pathsDe paden die worden doorgestuurd; staat er geen op de lijst, dan is dat elk pad. Een patroon is één pad, of alles onder een prefix als het eindigt op *: /api/* is /api en alles daaronder. Vergeleken met het pad na eenmalige percent-decodering, hoofdlettergevoelig404
deny_pathsGeweigerde paden, op dezelfde manier geschreven en als eerste gecontroleerd403
max_body_bytesDe grootste body van een verzoek, tot 64 MiB413
max_header_bytesDe grootste verzoekregel en headers samen, 1024 tot 65536431
rateVerzoeken per seconde vanaf één adres, en hoeveel tegelijk voordat de snelheidslimiet geldt429, met Retry-After
max_connections_per_addressVerbindingen die één adres tegelijk openhoudtDe verbinding gesloten, onbeantwoord
banNa after geblokkeerde verzoeken binnen window_secs wordt het adres for_secs lang geweigerd (tot 30 dagen)Zijn verbindingen gesloten, onbeantwoord
block_probesWeigert wat scanners opvragen (/.env, /.git, /wp-login.php en dergelijke)403
header_timeout_secsHoeveel tijd een client heeft om de kop van een verzoek te versturen, 1 tot 120. Ook hoelang er wordt gewacht op een client die midden in een body is gestopt, of die is gestopt met het afnemen van zijn antwoord408 voor de kop; 400 voor een body die stopte; de verbinding gesloten voor een antwoord dat niet wordt afgenomen
body_timeout_secsHoelang de body van een verzoek in totaal mag duren, 1 tot 600, zonder de tijd mee te tellen die de WAF op de app wacht408
upstream_timeout_secsHoeveel tijd de app heeft om de verbinding aan te nemen en te beginnen met antwoorden, 1 tot 600504; 502 als de app niet luistert
new_connectionsDe eigen limiet van de firewall op nieuwe TCP-verbindingen per seconde vanaf één adres naar de poort van de WAF, door nftables verworpen voordat de proxy ze ziet. Zolang de adresset van nftables vol is, wordt een adres dat er niet in staat niet geteld, zodat deze limiet er niet voor geldt totdat de set weer ruimte heeft; de eigen limieten van de proxy (rate, max_connections_per_address, ban) gelden nog steedsHet pakket verworpen
rate, max_connections_per_address, ban en new_connections worden uitgeschakeld met false (ban = false), en een weggelaten sleutel heeft zijn standaardwaarde. erpc worker check zegt vóór elke uitrol wat de tabel [waf] zal doen en wat er mis mee is.

Wat de WAF doet zonder dat er iets ingesteld hoeft te worden

  • Een verzoek dat zich niet laat lezen als één ondubbelzinnig HTTP/1-verzoek krijgt 400, en een pad dat niet in normaalvorm staat ook.
  • Een verzoek om van protocol te wisselen (Upgrade) krijgt 501.
  • Elke weigering telt mee voor de ban, en elke weigering beëindigt haar verbinding.
  • Een adres is een IPv4-adres, of een IPv6-/64.
  • De WAF stuurt de headers van de verbinding tussen de client en de proxy niet door (Connection en de headers die deze noemt, Keep-Alive, Proxy-Authenticate, Proxy-Authorization, Proxy-Connection, TE, Trailer), evenmin Expect, headers met een underscore in hun naam, of de eigen Forwarded, X-Real-IP en X-Forwarded-Proto van de client. Hij zet X-Real-IP op het adres waarvandaan de verbinding kwam en voegt dat adres toe aan het einde van X-Forwarded-For; alleen voor de laatste vermelding van X-Forwarded-For staat de proxy in.

Zien wat de WAF doet

CommandoToont
erpc worker statusDe poort van de WAF, waarheen hij doorstuurt, sinds wanneer, en de tellingen sindsdien
erpc worker firewall listWie de poort van de WAF mag bereiken, en wat er is geblokkeerd, per regel
erpc worker logs --wafElk geblokkeerd verzoek: de regel, de status, het adres van de client, de methode en de eerste 200 bytes van het doel
Een doorgestuurd verzoek laat geen logregel achter, en er wordt nooit een header of body gelogd; de query van een geblokkeerd verzoek wel, voor zover de 200 bytes reiken. Wat een client stuurt, kan niets op uw terminal schrijven: de WAF schrijft zijn journal als afdrukbare ASCII.

Wat de WAF niet doet

  • Geen TLS. Hij spreekt onversleuteld HTTP/1.1 op zijn poort.
  • Geen HTTP/2: daarop antwoordt hij met 400.
  • Geen WebSocket, en ook geen andere Upgrade: 501.
  • Geen blik in bodies. Een body wordt gemeten, nooit gelezen. De probelijst is kort en een ruisfilter, geen garantie: de app blijft zijn invoer controleren.
  • Geen authenticatie. Wie de app mag aanroepen, is aan de app om te bepalen, en aan de lijst van de firewall.
  • Eén adres is één client. Clients achter één NAT, proxy of CDN delen een snelheidslimiet en een ban; een client met veel adressen heeft evenveel snelheidslimieten.
  • Bans en tellingen worden vergeten wanneer de WAF herstart.
  • Hij onthoudt hoogstens 8192 adressen en houdt hoogstens 1024 verbindingen tegelijk open.
  • Hij luistert alleen op IPv4, dus een IPv6-vermelding in app_allow bereikt nog niets.
  • Een pad wordt vergeleken als de bytes waarnaar het decodeert. Een app die paden losser leest (hoofdletters negeren, parameters met ;, dubbel decoderen) kan worden bereikt onder een naam die deny_paths niet dekt. allow_paths heeft zo'n gat niet: wat niet op de lijst staat, wordt niet doorgestuurd.
  • Een trage lezer wordt tot het einde bediend. Een client die een antwoord blijft afnemen, hoe langzaam ook, wordt tot het einde bediend.
  • Een flood kan een grote upload afbreken. De WAF draait op één thread, dus een flood kan een grote upload afbreken met 408. Een app die grote bodies aanneemt, verhoogt body_timeout_secs samen met max_body_bytes.
De worker-handleiding bevat elke regel, ook wanneer een wijziging aan app_allow tot de volgende uitrol wordt aangehouden.