From b973178a95fb13e7ce323cf38516464520c20bd5 Mon Sep 17 00:00:00 2001 From: Musiker15 Date: Fri, 30 Oct 2026 18:08:20 +0100 Subject: [PATCH 1/2] Document the spam protection and API keys of MSK Paste and MSK Shortener Creating through the API now needs a key, the web form runs Cloudflare Turnstile and further spam checks, and both tools turn down certain content (paste: encoded files, link spam, repeats; shortener: own links, other shorteners, IP hosts, phishing hostnames). - API pages: authentication with Bearer keys, the new error codes and the content or target checks, every example sends the key. - FAQ: where the key comes from, the daily limits, refusals with 409/422, a form that creates nothing without Turnstile keys. - Usage: the spam protection and the new limits. - Installation: Node.js 24, the new environment variables, managing keys with scripts/api-key.sh, a note for the update. - Privacy: Cloudflare Turnstile as the only third-party script, how the client address is taken, the language cookie is NEXT_LOCALE. --- ecosystem/msk-paste/api.md | 67 ++++++++++--- ecosystem/msk-paste/faq.md | 19 +++- ecosystem/msk-paste/installation.md | 54 ++++++++++- ecosystem/msk-paste/privacy.md | 36 +++++-- ecosystem/msk-paste/usage.md | 18 +++- ecosystem/msk-shortener/api.md | 76 ++++++++++++--- ecosystem/msk-shortener/faq.md | 22 +++-- ecosystem/msk-shortener/installation.md | 50 +++++++++- ecosystem/msk-shortener/privacy.md | 38 ++++++-- ecosystem/msk-shortener/usage.md | 19 +++- .../current/msk-paste/api.md | 84 ++++++++++++++--- .../current/msk-paste/faq.md | 40 ++++++-- .../current/msk-paste/installation.md | 71 +++++++++++++- .../current/msk-paste/privacy.md | 51 +++++++--- .../current/msk-paste/usage.md | 20 +++- .../current/msk-shortener/api.md | 94 +++++++++++++++---- .../current/msk-shortener/faq.md | 42 +++++++-- .../current/msk-shortener/installation.md | 67 ++++++++++++- .../current/msk-shortener/privacy.md | 53 ++++++++--- .../current/msk-shortener/usage.md | 21 ++++- 20 files changed, 804 insertions(+), 138 deletions(-) diff --git a/ecosystem/msk-paste/api.md b/ecosystem/msk-paste/api.md index c19365a..e97cfff 100644 --- a/ecosystem/msk-paste/api.md +++ b/ecosystem/msk-paste/api.md @@ -13,7 +13,23 @@ MSK Paste exposes a full JSON REST API. All features available in the web UI are ## Authentication -There is **no authentication**. All endpoints are public. Write operations (create / delete) are protected by rate limiting and, in the case of deletion, a one-time random delete token. +Creating a paste through the API needs an API key. Send it as a Bearer header: + +```http +Authorization: Bearer mskp_... +``` + +Keys for MSK Paste start with `mskp_`. The operator issues them on the server, there is no sign-up and no self-service. The hosted instance at paste.msk-scripts.de does not issue keys to others; create pastes there through the website. On your own instance you create keys with the key script, see [Installation](installation.md#api-keys). + +Without a key, `POST /api/pastes` only accepts requests from the site's own form and answers `401`: + +```json +{ "error": "Creating pastes through the API requires an API key", "code": "api-key-required" } +``` + +A wrong or revoked key also gets `401`, with `"code": "api-key-invalid"`. + +Everything else works without a key, as before: fetching a paste, unlocking it with `POST /api/pastes/:id/verify`, deleting it with its delete token, and `GET /api/stats`. --- @@ -24,23 +40,25 @@ All errors share the same shape: ```json { "error": "Human-readable message", + "code": "api-key-required", "details": { "fieldName": ["validation error 1", "validation error 2"] } } ``` -`details` is only present on `400 Bad Request` (validation failures). +`code` names the reason in a fixed, machine-readable form. It comes with the refusals of `POST /api/pastes` for the API key, the limits and the content checks, so scripts should check `code` rather than the message. `details` is only present on `400 Bad Request` (validation failures). | Status | Meaning | |---|---| | `400` | Validation failed, see `details` | -| `401` | Wrong password (on `/verify`) | +| `401` | Missing or invalid API key (on create), wrong password (on `/verify`) | | `403` | Password required (on `/api/pastes/:id`) | | `404` | Paste does not exist, has expired, or was burned | -| `409` | Custom ID is already in use | +| `409` | Custom ID is already in use, or the same content was pasted within the last 24 hours (`code: "duplicate"`) | | `413` | Content exceeds 1 MB | -| `429` | Rate limit exceeded, see `Retry-After` header | +| `422` | Content refused by the [content checks](#content-checks) | +| `429` | Rate limit exceeded, see [Rate limiting](#rate-limiting) | --- @@ -50,6 +68,7 @@ All errors share the same shape: ```http POST /api/pastes HTTP/1.1 +Authorization: Bearer mskp_... Content-Type: application/json { @@ -97,6 +116,7 @@ The `deleteToken` is the **only** way to delete the paste later. Save it. It is ```bash curl -X POST https://paste.msk-scripts.de/api/pastes \ + -H "Authorization: Bearer $MSK_PASTE_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "print(\"hello\")", @@ -107,6 +127,16 @@ curl -X POST https://paste.msk-scripts.de/api/pastes \ --- +## Content checks + +Every new paste goes through the same checks, whether it comes from the web form or from an API key. A paste is refused when it + +- contains an encoded file: an unbroken run of 16 KB of base64 or hex characters, or a paste of 64 KB or more made almost only of wrapped base64 (`422`, `code: "blob"`) +- is mostly links: 3 or more links that make up at least half of the text, or 3 or more BBCode `[url=` or HTML `"`, see [Installation](installation.md#api-keys). Fetching, unlocking and deleting with the delete token still work without a key. ### Why am I getting `429 Too Many Requests`? -You've hit the rate limit on `POST /api/pastes`. Check the `Retry-After` header for how many seconds to wait. If you're self-hosting and need a higher limit, change `RATE_LIMIT_CREATE_PER_HOUR` in `.env`. +You've hit a limit on `POST /api/pastes`. The `code` field in the response says which one, and the `Retry-After` header, where present, says how many seconds to wait: + +- **With an API key:** 120 pastes per hour per key (`key-limit`) +- **From the web form:** 10 pastes per hour per IP hash (`rate-limit`), and 20 pastes and 5 MB in total per IP address and 24 hours (`daily-limit`) + +If you're self-hosting and need higher limits, change `API_KEY_CREATE_PER_HOUR`, `RATE_LIMIT_CREATE_PER_HOUR`, `DAILY_PASTES_PER_IP` or `DAILY_BYTES_PER_IP` in `.env`. + +### Why is my paste refused with `422` or `409`? + +It failed a content check. Encoded files (long runs of base64 or hex), pastes that are mostly links, and the same content again within 24 hours are refused, from the form and from API keys alike. The `code` field says which check it was: `blob`, `links` or `duplicate`. Details on the [REST API page](api.md#content-checks). ### How do I delete a paste via the API? @@ -118,6 +127,10 @@ openssl rand -hex 32 Paste it into `.env`, then restart the service. +### The form refuses to create pastes + +In production the form needs Cloudflare Turnstile. Without `CAPTCHA_SITE_KEY` and `CAPTCHA_SECRET_KEY` in `.env` it creates nothing and answers `503` with `code: "captcha-unavailable"`. Create a Turnstile widget for your instance's hostname in the Cloudflare dashboard, put both keys into `.env` and restart the service. Cloudflare's test keys only work outside production. See [Installation](installation.md#environment-variables). + ### Let's Encrypt rate limit hit If you've been experimenting with certbot and hit a rate limit, use the **staging** environment for testing: diff --git a/ecosystem/msk-paste/installation.md b/ecosystem/msk-paste/installation.md index 8a0e2c7..2031005 100644 --- a/ecosystem/msk-paste/installation.md +++ b/ecosystem/msk-paste/installation.md @@ -14,7 +14,7 @@ This guide walks you through installing MSK Paste on your own Debian or Ubuntu s | Component | Minimum Version | |---|---| | OS | Debian 11+ / Ubuntu 22.04+ | -| Node.js | 20.x or 22.x LTS | +| Node.js | 24 LTS or newer | | MariaDB | 10.6+ | | Apache | 2.4+ (with `mod_proxy`, `mod_proxy_http`, `mod_headers`, `mod_ssl`) | | Domain | A subdomain pointing to your server (e.g. `paste.example.com`) | @@ -54,10 +54,10 @@ When it finishes, the service is live at your domain over HTTPS. If you prefer to do it step by step: -### 1. Install Node.js 22 +### 1. Install Node.js 24 ```bash -curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - sudo apt install -y nodejs ``` @@ -147,9 +147,20 @@ DB_NAME=msk_paste # Generate with: openssl rand -hex 32 IP_HASH_SECRET=change_me_with_openssl_rand_hex_32 +# ─── Spam protection ───────────────────────────────────────────────── +# Cloudflare Turnstile for the create form, required in production +CAPTCHA_SITE_KEY= +CAPTCHA_SECRET_KEY= +# Pastes and bytes one IP address may create through the form per 24 hours +DAILY_PASTES_PER_IP=20 +DAILY_BYTES_PER_IP=5242880 +# Pastes one API key may create per hour +API_KEY_CREATE_PER_HOUR=120 + # ─── Limits ────────────────────────────────────────────────────────── RATE_LIMIT_CREATE_PER_HOUR=10 -MAX_PASTE_SIZE_BYTES=1048576 # 1 MB +# 1 MB +MAX_PASTE_SIZE_BYTES=1048576 PASTE_ID_LENGTH=8 PASTE_ID_MIN_CUSTOM=4 PASTE_ID_MAX_CUSTOM=32 @@ -159,6 +170,10 @@ PASTE_ID_MAX_CUSTOM=32 The `IP_HASH_SECRET` must be at least 32 hex characters. Generate it with `openssl rand -hex 32`. **Never commit it to git.** ::: +:::info +The create form needs a Cloudflare Turnstile widget. Create one in the Cloudflare dashboard under **Turnstile** for the hostname of your instance (e.g. `paste.example.com`) and put its site key and secret key into `CAPTCHA_SITE_KEY` and `CAPTCHA_SECRET_KEY`. Both are required in production: without them the form refuses to create pastes. Cloudflare's test keys (site key `1x00000000000000000000AA`, secret `1x0000000000000000000000000000000AA`) always pass, but only outside production. +::: + --- ## Scheduled tasks @@ -179,6 +194,33 @@ sudo crontab -e --- +## API keys + +Creating pastes through the [REST API](api.md#authentication) needs an API key. There is no self-service, you issue keys on the server with the key script. It needs read access to `/opt/msk-paste/.env`, so run it as root or as the service user: + +```bash +sudo bash /opt/msk-paste/scripts/api-key.sh help +``` + +The commands: + +- `list`: all keys with id, prefix, label, creation time, last use and state +- `create [""`, see [Installation](installation.md#api-keys). Looking up links, verifying passwords and deleting with the delete token still work without a key. ### Why am I getting `429 Too Many Requests`? -You've hit a rate limit. Check the `Retry-After` header. Two limits exist: +You've hit a limit. The `code` field in the response says which one, and the `Retry-After` header, where present, says how many seconds to wait. These limits exist: -- **Create:** 20 per hour per IP hash +- **Create with an API key:** 120 per hour per key (`key-limit`) +- **Create from the web form:** 20 per hour per IP hash (`rate-limit`) and 20 per IP address and 24 hours (`daily-limit`) - **Verify (password):** 10 per 5 minutes per IP hash (brute-force protection) -If you're self-hosting and need a higher create limit, change `RATE_LIMIT_CREATE_PER_HOUR` in `.env`. +If you're self-hosting and need higher create limits, change `API_KEY_CREATE_PER_HOUR`, `RATE_LIMIT_CREATE_PER_HOUR` or `DAILY_LINKS_PER_IP` in `.env`. ### How do I delete a link via the API? @@ -65,6 +66,10 @@ curl -X DELETE https://s.msk-scripts.de/api/links/msk \ You tried to shorten a URL pointing at a private IP range or localhost. This is **SSRF protection**, see [Privacy & Security](privacy.md#ssrf-protection). The shortener is only meant for public destinations. +### Why does the API refuse my link with `422`? + +The target failed a target check. Links to the shortener itself, to other URL shorteners, to bare IP addresses and to hostnames built like phishing pages are refused, from the form and from API keys alike. The `code` field says which check it was: `own-link`, `shortener`, `ip-host` or `phishing`. Details in [Usage](usage.md#what-gets-blocked). + ### Can I get a list of all links I've created? No. There is no per-user history, the database doesn't know which links belong to "you". Save the delete tokens client-side if you want a personal history. @@ -132,6 +137,10 @@ Paste it into `.env`, then restart the service. If you change `IP_HASH_SECRET` after the fact, all existing rate-limit hashes and click hashes become "different visitors", but you don't lose any data, just continuity in the rate-limit window. ::: +### The form refuses to shorten links + +In production the form needs Cloudflare Turnstile. Without `CAPTCHA_SITE_KEY` and `CAPTCHA_SECRET_KEY` in `.env` it shortens nothing and answers `503` with `code: "captcha-unavailable"`. Create a Turnstile widget for your instance's hostname in the Cloudflare dashboard, put both keys into `.env` and restart the service. Cloudflare's test keys only work outside production. See [Installation](installation.md#environment-variables). + ### Let's Encrypt rate limit hit If you've been experimenting with certbot and hit a rate limit, use the **staging** environment for testing: @@ -178,12 +187,13 @@ Not in the current design. The shortener assumes a single base URL per instance ### Can I bulk-import or bulk-export links? -Not via the UI, but the REST API can be scripted for both directions: +Not via the UI, but the REST API can be scripted for both directions. Bulk-creating needs an API key, and one key shortens up to 120 links per hour: ```bash # Bulk-create while read url; do curl -sS -X POST https://s.msk-scripts.de/api/links \ + -H "Authorization: Bearer $MSK_SHORTENER_KEY" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg u "$url" '{url: $u}')" \ | jq -r '"\(.shortUrl) \(.deleteToken)"' diff --git a/ecosystem/msk-shortener/installation.md b/ecosystem/msk-shortener/installation.md index dd031c3..9282e14 100644 --- a/ecosystem/msk-shortener/installation.md +++ b/ecosystem/msk-shortener/installation.md @@ -14,7 +14,7 @@ This guide walks you through installing MSK Shortener on your own Debian or Ubun | Component | Minimum Version | |---|---| | OS | Debian 11+ / Ubuntu 22.04+ | -| Node.js | 20.x or 22.x LTS | +| Node.js | 24 LTS or newer | | MariaDB | 10.6+ | | Apache | 2.4+ (with `mod_proxy`, `mod_proxy_http`, `mod_headers`, `mod_ssl`) | | Domain | A short subdomain pointing to your server (e.g. `s.example.com`) | @@ -51,10 +51,10 @@ When it finishes, the service is live at your domain over HTTPS. If you prefer to do it step by step: -### 1. Install Node.js 22 +### 1. Install Node.js 24 ```bash -curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - sudo apt install -y nodejs ``` @@ -147,6 +147,15 @@ IP_HASH_SECRET=change_me_with_openssl_rand_hex_32 # ─── Rate limit ────────────────────────────────────────────────────── RATE_LIMIT_CREATE_PER_HOUR=20 +# ─── Spam protection ───────────────────────────────────────────────── +# Cloudflare Turnstile for the create form, required in production +CAPTCHA_SITE_KEY= +CAPTCHA_SECRET_KEY= +# Links one IP address may shorten through the form per 24 hours +DAILY_LINKS_PER_IP=20 +# Links one API key may shorten per hour +API_KEY_CREATE_PER_HOUR=120 + # ─── Short-code settings ───────────────────────────────────────────── SHORTCODE_LENGTH=7 SHORTCODE_MIN_CUSTOM=3 @@ -157,6 +166,10 @@ SHORTCODE_MAX_CUSTOM=20 The `IP_HASH_SECRET` must be at least 32 hex characters. Generate it with `openssl rand -hex 32`. **Never commit it to git**, without it, your click statistics lose their anonymization property. ::: +:::info +The create form needs a Cloudflare Turnstile widget. Create one in the Cloudflare dashboard under **Turnstile** for the hostname of your instance (e.g. `s.example.com`) and put its site key and secret key into `CAPTCHA_SITE_KEY` and `CAPTCHA_SECRET_KEY`. Both are required in production: without them the form refuses to shorten links. Cloudflare's test keys (site key `1x00000000000000000000AA`, secret `1x0000000000000000000000000000000AA`) always pass, but only outside production. +::: + --- ## Scheduled tasks @@ -177,6 +190,33 @@ sudo crontab -e --- +## API keys + +Creating links through the [REST API](api.md#authentication) needs an API key. There is no self-service, you issue keys on the server with the key script. It needs read access to `/opt/msk-shortener/.env`, so run it as root or as the service user: + +```bash +sudo bash /opt/msk-shortener/scripts/api-key.sh help +``` + +The commands: + +- `list`: all keys with id, prefix, label, creation time, last use and state +- `create [""` an, siehe +[Installation](installation.md#api-keys). Abrufen, Freischalten und Löschen mit dem +Lösch-Token gehen weiterhin ohne Key. ### Warum bekomme ich `429 Too Many Requests`? -Du hast die Ratenbegrenzung von `POST /api/pastes` erreicht. Im Header `Retry-After` steht, -wie viele Sekunden du warten musst. Betreibst du eine eigene Instanz und brauchst mehr, -ändere `RATE_LIMIT_CREATE_PER_HOUR` in der `.env`. +Du hast eine Grenze von `POST /api/pastes` erreicht. Welche, steht im Feld `code` der +Antwort, und wie viele Sekunden du warten musst, im Header `Retry-After`, sofern er +mitkommt: + +- **Mit API-Key:** 120 Pastes pro Stunde je Key (`key-limit`) +- **Aus dem Formular:** 10 Pastes pro Stunde je IP-Hash (`rate-limit`) sowie 20 Pastes und zusammen 5 MB je IP-Adresse und 24 Stunden (`daily-limit`) + +Betreibst du eine eigene Instanz und brauchst mehr, ändere `API_KEY_CREATE_PER_HOUR`, +`RATE_LIMIT_CREATE_PER_HOUR`, `DAILY_PASTES_PER_IP` oder `DAILY_BYTES_PER_IP` in der `.env`. + +### Warum wird mein Paste mit `422` oder `409` abgelehnt? + +Er ist an der Inhaltsprüfung hängen geblieben. Kodierte Dateien (lange Blöcke aus Base64 +oder Hex), Pastes, die vor allem aus Links bestehen, und derselbe Inhalt noch einmal binnen +24 Stunden werden abgelehnt, im Formular genauso wie mit API-Key. Welche Prüfung es war, +steht im Feld `code`: `blob`, `links` oder `duplicate`. Einzelheiten auf der +[Seite zur REST-API](api.md#inhaltsprüfung). ### Wie lösche ich einen Paste über die API? @@ -136,6 +155,15 @@ openssl rand -hex 32 Trag es in die `.env` ein und starte den Dienst neu. +### Das Formular legt keine Pastes an + +In Produktion braucht das Formular Cloudflare Turnstile. Fehlen `CAPTCHA_SITE_KEY` und +`CAPTCHA_SECRET_KEY` in der `.env`, legt es nichts an und antwortet mit `503` und +`code: "captcha-unavailable"`. Lege im Cloudflare-Dashboard ein Turnstile-Widget für den +Hostnamen deiner Instanz an, trag beide Schlüssel in die `.env` ein und starte den Dienst +neu. Die Testschlüssel von Cloudflare funktionieren nur außerhalb der Produktion. Siehe +[Installation](installation.md#umgebungsvariablen). + ### Ratenbegrenzung von Let's Encrypt erreicht Hast du beim Ausprobieren mit certbot die Grenze gerissen, nimm zum Testen die diff --git a/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-paste/installation.md b/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-paste/installation.md index a74f3b5..4f426fd 100644 --- a/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-paste/installation.md +++ b/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-paste/installation.md @@ -16,7 +16,7 @@ oder Ubuntu-Server. Willst du einfach die gehostete Instanz nutzen, geh direkt z | Komponente | Mindestversion | |---|---| | Betriebssystem | Debian 11+ / Ubuntu 22.04+ | -| Node.js | 20.x oder 22.x LTS | +| Node.js | 24 LTS oder neuer | | MariaDB | 10.6+ | | Apache | 2.4+ (mit `mod_proxy`, `mod_proxy_http`, `mod_headers`, `mod_ssl`) | | Domain | Eine Subdomain, die auf deinen Server zeigt, etwa `paste.example.com` | @@ -59,10 +59,10 @@ Ist es durch, läuft der Dienst unter deiner Domain über HTTPS. Wenn du lieber Schritt für Schritt vorgehst: -### 1. Node.js 22 installieren +### 1. Node.js 24 installieren ```bash -curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - sudo apt install -y nodejs ``` @@ -152,9 +152,20 @@ DB_NAME=msk_paste # Erzeugen mit: openssl rand -hex 32 IP_HASH_SECRET=change_me_with_openssl_rand_hex_32 +# ─── Spamschutz ────────────────────────────────────────────────────── +# Cloudflare Turnstile für das Formular, in Produktion Pflicht +CAPTCHA_SITE_KEY= +CAPTCHA_SECRET_KEY= +# Pastes und Bytes, die eine IP-Adresse über das Formular in 24 Stunden anlegen darf +DAILY_PASTES_PER_IP=20 +DAILY_BYTES_PER_IP=5242880 +# Pastes, die ein API-Key pro Stunde anlegen darf +API_KEY_CREATE_PER_HOUR=120 + # ─── Grenzen ───────────────────────────────────────────────────────── RATE_LIMIT_CREATE_PER_HOUR=10 -MAX_PASTE_SIZE_BYTES=1048576 # 1 MB +# 1 MB +MAX_PASTE_SIZE_BYTES=1048576 PASTE_ID_LENGTH=8 PASTE_ID_MIN_CUSTOM=4 PASTE_ID_MAX_CUSTOM=32 @@ -165,6 +176,16 @@ Das `IP_HASH_SECRET` muss mindestens 32 Hex-Zeichen lang sein. Erzeuge es mit `openssl rand -hex 32` und **committe es niemals nach Git.** ::: +:::info +Das Formular zum Anlegen braucht ein Turnstile-Widget von Cloudflare. Lege es im +Cloudflare-Dashboard unter **Turnstile** für den Hostnamen deiner Instanz an, etwa +`paste.example.com`, und trag Site-Key und Secret-Key in `CAPTCHA_SITE_KEY` und +`CAPTCHA_SECRET_KEY` ein. In Produktion sind beide Pflicht, ohne sie legt das Formular +keine Pastes an. Die Testschlüssel von Cloudflare (Site-Key `1x00000000000000000000AA`, +Secret `1x0000000000000000000000000000000AA`) bestehen immer, aber nur außerhalb der +Produktion. +::: + --- ## Geplante Aufgaben @@ -185,6 +206,40 @@ sudo crontab -e --- +## API-Keys + +Wer über die [REST-API](api.md#authentifizierung) Pastes anlegen will, braucht einen +API-Key. Selbstbedienung gibt es nicht, du stellst Keys auf dem Server mit dem Key-Skript +aus. Es muss `/opt/msk-paste/.env` lesen können, führe es also als root oder als +Dienstnutzer aus: + +```bash +sudo bash /opt/msk-paste/scripts/api-key.sh help +``` + +Die Befehle: + +- `list`: alle Keys mit ID, Präfix, Bezeichnung, Anlagezeitpunkt, letzter Nutzung und Zustand +- `create [""` +an, siehe [Installation](installation.md#api-keys). Links nachschlagen, Passwörter prüfen +und mit dem Lösch-Token löschen geht weiterhin ohne Key. ### Warum bekomme ich `429 Too Many Requests`? -Du hast eine Ratenbegrenzung erreicht. Schau in den Header `Retry-After`. Es gibt zwei +Du hast eine Grenze erreicht. Welche, steht im Feld `code` der Antwort, und wie viele +Sekunden du warten musst, im Header `Retry-After`, sofern er mitkommt. Es gibt diese Grenzen: -- **Anlegen:** 20 pro Stunde je IP-Hash +- **Anlegen mit API-Key:** 120 pro Stunde je Key (`key-limit`) +- **Anlegen aus dem Formular:** 20 pro Stunde je IP-Hash (`rate-limit`) und 20 je IP-Adresse und 24 Stunden (`daily-limit`) - **Prüfen (Passwort):** 10 je 5 Minuten und IP-Hash, als Schutz gegen Durchprobieren -Betreibst du eine eigene Instanz und brauchst mehr, ändere `RATE_LIMIT_CREATE_PER_HOUR` in -der `.env`. +Betreibst du eine eigene Instanz und brauchst mehr, ändere `API_KEY_CREATE_PER_HOUR`, +`RATE_LIMIT_CREATE_PER_HOUR` oder `DAILY_LINKS_PER_IP` in der `.env`. ### Wie lösche ich einen Link über die API? @@ -82,6 +89,14 @@ Du wolltest eine URL kürzen, die auf einen privaten IP-Bereich oder auf localho ist der **SSRF-Schutz**, siehe [Datenschutz und Sicherheit](privacy.md#ssrf-schutz). Der Kürzer ist nur für öffentliche Ziele gedacht. +### Warum lehnt die API meinen Link mit `422` ab? + +Das Ziel ist an der Zielprüfung hängen geblieben. Links auf den Kürzer selbst, auf andere +URL-Kürzer, auf nackte IP-Adressen und auf Hostnamen, die wie Phishing-Seiten gebaut sind, +werden abgelehnt, im Formular genauso wie mit API-Key. Welche Prüfung es war, steht im Feld +`code`: `own-link`, `shortener`, `ip-host` oder `phishing`. Einzelheiten unter +[Bedienung](usage.md#was-blockiert-wird). + ### Bekomme ich eine Liste aller Links, die ich angelegt habe? Nein. Es gibt keinen Verlauf je Nutzer, die Datenbank weiß nicht, welche Links „dir" @@ -155,6 +170,15 @@ Ratenbegrenzung und der Klicks als „andere Besucher". Daten verlierst du dabei die Kontinuität im Fenster der Ratenbegrenzung. ::: +### Das Formular kürzt keine Links + +In Produktion braucht das Formular Cloudflare Turnstile. Fehlen `CAPTCHA_SITE_KEY` und +`CAPTCHA_SECRET_KEY` in der `.env`, kürzt es nichts und antwortet mit `503` und +`code: "captcha-unavailable"`. Lege im Cloudflare-Dashboard ein Turnstile-Widget für den +Hostnamen deiner Instanz an, trag beide Schlüssel in die `.env` ein und starte den Dienst +neu. Die Testschlüssel von Cloudflare funktionieren nur außerhalb der Produktion. Siehe +[Installation](installation.md#umgebungsvariablen). + ### Ratenbegrenzung von Let's Encrypt erreicht Hast du beim Ausprobieren mit certbot die Grenze gerissen, nimm zum Testen die @@ -215,12 +239,14 @@ betreibe eine zweite Instanz. ### Kann ich Links stapelweise importieren oder exportieren? -Über die Oberfläche nicht, aber die REST-API lässt sich für beide Richtungen skripten: +Über die Oberfläche nicht, aber die REST-API lässt sich für beide Richtungen skripten. Zum +stapelweisen Anlegen brauchst du einen API-Key, ein Key kürzt bis zu 120 Links pro Stunde: ```bash # Stapelweise anlegen while read url; do curl -sS -X POST https://s.msk-scripts.de/api/links \ + -H "Authorization: Bearer $MSK_SHORTENER_KEY" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg u "$url" '{url: $u}')" \ | jq -r '"\(.shortUrl) \(.deleteToken)"' diff --git a/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-shortener/installation.md b/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-shortener/installation.md index 0aa62f3..c52377b 100644 --- a/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-shortener/installation.md +++ b/i18n/de/docusaurus-plugin-content-docs-ecosystem/current/msk-shortener/installation.md @@ -16,7 +16,7 @@ Debian- oder Ubuntu-Server. Willst du einfach nur einen Kurzlink, geh direkt zu | Komponente | Mindestversion | |---|---| | Betriebssystem | Debian 11+ / Ubuntu 22.04+ | -| Node.js | 20.x oder 22.x LTS | +| Node.js | 24 LTS oder neuer | | MariaDB | 10.6+ | | Apache | 2.4+ (mit `mod_proxy`, `mod_proxy_http`, `mod_headers`, `mod_ssl`) | | Domain | Eine kurze Subdomain, die auf deinen Server zeigt, etwa `s.example.com` | @@ -56,10 +56,10 @@ Ist es durch, läuft der Dienst unter deiner Domain über HTTPS. Wenn du lieber Schritt für Schritt vorgehst: -### 1. Node.js 22 installieren +### 1. Node.js 24 installieren ```bash -curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - sudo apt install -y nodejs ``` @@ -152,6 +152,15 @@ IP_HASH_SECRET=change_me_with_openssl_rand_hex_32 # ─── Ratenbegrenzung ───────────────────────────────────────────────── RATE_LIMIT_CREATE_PER_HOUR=20 +# ─── Spamschutz ────────────────────────────────────────────────────── +# Cloudflare Turnstile für das Formular, in Produktion Pflicht +CAPTCHA_SITE_KEY= +CAPTCHA_SECRET_KEY= +# Links, die eine IP-Adresse über das Formular in 24 Stunden kürzen darf +DAILY_LINKS_PER_IP=20 +# Links, die ein API-Key pro Stunde kürzen darf +API_KEY_CREATE_PER_HOUR=120 + # ─── Einstellungen der Kurzcodes ───────────────────────────────────── SHORTCODE_LENGTH=7 SHORTCODE_MIN_CUSTOM=3 @@ -164,6 +173,16 @@ Das `IP_HASH_SECRET` muss mindestens 32 Hex-Zeichen lang sein. Erzeuge es mit deine Klickstatistik ihre Anonymität. ::: +:::info +Das Formular zum Kürzen braucht ein Turnstile-Widget von Cloudflare. Lege es im +Cloudflare-Dashboard unter **Turnstile** für den Hostnamen deiner Instanz an, etwa +`s.example.com`, und trag Site-Key und Secret-Key in `CAPTCHA_SITE_KEY` und +`CAPTCHA_SECRET_KEY` ein. In Produktion sind beide Pflicht, ohne sie kürzt das Formular +keine Links. Die Testschlüssel von Cloudflare (Site-Key `1x00000000000000000000AA`, +Secret `1x0000000000000000000000000000000AA`) bestehen immer, aber nur außerhalb der +Produktion. +::: + --- ## Geplante Aufgaben @@ -184,6 +203,40 @@ sudo crontab -e --- +## API-Keys + +Wer über die [REST-API](api.md#authentifizierung) Links anlegen will, braucht einen +API-Key. Selbstbedienung gibt es nicht, du stellst Keys auf dem Server mit dem Key-Skript +aus. Es muss `/opt/msk-shortener/.env` lesen können, führe es also als root oder als +Dienstnutzer aus: + +```bash +sudo bash /opt/msk-shortener/scripts/api-key.sh help +``` + +Die Befehle: + +- `list`: alle Keys mit ID, Präfix, Bezeichnung, Anlagezeitpunkt, letzter Nutzung und Zustand +- `create ["