
Ein Reverse-Proxy wie nginx, basierend auf Pingora, einfach und effizient.
# Pingap
Ein Reverse-Proxy und Application Gateway, aufgebaut auf [Cloudflare Pingora](https://github.com/cloudflare/pingora). Er wird über TOML-Dateien oder eine Web-Administration konfiguriert, die Teil der Binärdatei ist, übernimmt Änderungen ohne Neustart und bringt mehr als dreißig Plugins mit.
[中文说明](https://github.com/vicanso/pingap/blob/main/README_zh.md) | [Dokumentation](https://pingap.io/) · [中文文档](https://pingap.io/zh/) | [Beispiele](https://github.com/vicanso/pingap/blob/main/examples/README.md) | [Plugins](https://github.com/vicanso/pingap/blob/main/pingap-plugin/README.md) | [Crates](https://github.com/vicanso/pingap/blob/main/docs/README.md) | [Upgrading](https://pingap.io/guide/upgrading)

## Was ihn auszeichnet
- **Eine Web-Administration in der Binärdatei.** Jede Einstellung hat ein Formular. Eine Änderung wird so geprüft, wie `pingap -t` sie prüft, bevor sie gespeichert wird, frühere Versionen können aufbewahrt und wiederhergestellt werden, und es gibt schreibgeschützte Konten und API-Tokens. Die Startseite stellt den Datenverkehr der letzten fünfzehn Minuten grafisch dar – Rate, Status, Latenz, jeder Upstream – ohne Prometheus.
- **Änderungen ohne Ausfallzeit.** Upstreams, Locations, Plugins und Zertifikate werden im laufenden Prozess ersetzt (`--autoreload`). Was neue Listener erfordert, wird durch einen Neustart angewendet, der die Sockets übergibt (`--autorestart`, für einen Daemon unter Linux), und der laufende Prozess bedient weiter, falls sein Nachfolger nicht hochkommt.
- **Über dreißig Plugins**, pro Location und in der Reihenfolge, in der du sie auflistest: Authentifizierung (JWT, OIDC, Key, Basic, HMAC, Forward Auth), Zugriffsregeln (IP, Referer, User Agent, blockierte Pfade), Datenverkehr (Raten- und Bandbreitenbegrenzungen, Cache, Mirroring, Traffic-Splitting), Inhalte (Komprimierung, Body-Ersetzung, Weiterleitungen, statische Dateien, Bildoptimierung) und Betrieb (Wartungsmodus, Health-Endpunkt, Fehlerseiten). [Alle Plugins](https://github.com/vicanso/pingap/blob/main/pingap-plugin/README.md)
- **Zertifikate ohne Cron-Job.** Let's Encrypt oder jede andere ACME-CA, per HTTP-01 oder DNS-01 (Alibaba Cloud, Cloudflare, Tencent Cloud, Huawei Cloud), RSA und ECDSA nebeneinander, OCSP-Stapling sowie gegenseitiges TLS gegenüber Clients und gegenüber Upstreams.
- **Konfiguration, die vor der Ausführung geprüft wird.** `-t` baut alles auf, was ein Start aufbauen würde, `--strict` lehnt Schlüssel ab, die es nicht kennt, `--diff` zeigt, was eine Änderung bewirken würde. TOML, HCL oder KDL, in Dateien oder in etcd, mit Secrets aus der Umgebung oder aus Dateien.
- **Zum Beobachten gemacht.** Prometheus-Metriken (Pull und Push), OpenTelemetry-Traces, Zugriffslogs als Text oder JSON mit mehr als dreißig Feldern und JA4-TLS-Fingerabdrücke der Clients.
Der Rest dessen, was ein Reverse-Proxy tut, ist ebenfalls vorhanden: HTTP/1.1, HTTP/2, WebSocket und gRPC-web; Routing nach Host, Pfad und regulärem Ausdruck; Round Robin, Least Connections, Consistent Hashing und Sticky Sessions, mit Health Checks, Retries und einem Circuit Breaker; Backends aus DNS (A/AAAA, SRV) oder Docker-Labels; das PROXY-Protokoll auf Listenern und gegenüber Upstreams; OpenSSL oder rustls.
Metriken, Tracing und Bildoptimierung sind in den `-full`-Builds enthalten.
## 🚀 Erste Schritte
### Docker Compose
```yaml
# docker-compose.yml
services:
pingap:
image: vicanso/pingap:latest # for production, pin a release: vicanso/pingap:<version>-full
container_name: pingap-instance
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# everything Pingap keeps: configuration, certificates, cache
- ./pingap_data:/opt/pingap
environment:
- PINGAP_CONF=/opt/pingap/conf
- PINGAP_ADMIN_ADDR=0.0.0.0:80/pingap
- PINGAP_ADMIN_USER=pingap
- PINGAP_ADMIN_PASSWORD=<YourSecurePassword> # change this
command:
- pingap
- --autoreload
```
```bash
mkdir pingap_data
docker compose up -d
```
Der Admin ist unter http://localhost/pingap erreichbar. Images sind mit `latest`, `full` und `rustls-full` sowie `<version>`, `<version>-full`, `<version>-rustls-full` für ein Release getaggt. Jedes hat außerdem einen `-distroless`-Zwilling ohne Shell, bei dem `command` wie oben mit `pingap` beginnen muss.
### Binary
```bash
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
```
installiert die neueste Version nach `/usr/local/bin/pingap` unter Linux und macOS (x86_64 und arm64). Variablen vor `sh` legen fest, was installiert wird:
| Variable | Wirkung |
| --- | --- |
| `PINGAP_FULL=1` | Der `-full`-Build mit allen optionalen Funktionen |
| `PINGAP_TLS=rustls` | Linux: der `-rustls-full`-Build ohne OpenSSL (siehe [TLS-Backend](#tls-backend)) |
| `PINGAP_LIBC=gnu` | Linux: der glibc-Build anstelle des statischen musl-Builds |
| `PINGAP_SERVICE=1` | Linux mit systemd: zusätzlich der `pingap`-Dienst und eine Startkonfiguration in `/etc/pingap/conf`; er wird nicht aktiviert oder gestartet |
| `PINGAP_VERSION=0.15.0` | Diese Version anstelle der neuesten |
Alle Assets befinden sich auf der [Releases-Seite](https://github.com/vicanso/pingap/releases).
### Ein Befehl, keine Konfigurationsdatei
```bash
# the certificate comes from Let's Encrypt
pingap --domain=pingap.io --upstream=192.168.1.1:3000
# or bring your own
pingap --domain=pingap.io --upstream=192.168.1.1:3000 --cert=/etc/ssl/pingap.io
```
- Ohne `--cert` wird das Zertifikat per HTTP-01 bestellt: Die Domain muss auf diesen Host auflösen, wobei Port 80 erreichbar sein muss. Es wird in `~/.pingap/acme/<domains>.toml` gespeichert und beim nächsten Start erneut verwendet. Löschen Sie es nicht, die Ausstellung ist rate-limitiert.
- `--cert` ist das Zertifikat oder das Verzeichnis, in dem es liegt (`fullchain.pem` / `privkey.pem`, `cert.pem` / `key.pem` und `tls.crt` / `tls.key` werden gefunden; `--key` benennt alles andere). `--upstream` und `--domain` nehmen durch Kommas getrennte Listen entgegen, `--addr` die Adresse, auf der gelauscht werden soll.
- Die Konfiguration wird bei jedem Start neu erstellt, sodass der Administrator sie nicht bearbeiten kann. Für mehr als einen Server verwenden Sie `--conf`, womit diese Flags nicht kombiniert werden können.
## 📝 Konfiguration
```toml
[upstreams.api]
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
[plugins.staticServe]
category = "directory"
path = "~/Downloads"
[locations.github-api]
upstream = "api"
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
[locations.static]
plugins = ["staticServe"]
[servers.test]
addr = "127.0.0.1:6118"
locations = ["github-api", "static"]
```
Dasselbe kann in HCL oder KDL geschrieben werden. Jeder Schlüssel ist in der [Konfigurationsreferenz](https://pingap.io/crates/config) beschrieben.
| | |
| --- | --- |
| `--autoreload` | Eine Änderung wird ohne Neustart angewendet: innerhalb von zehn Sekunden aus Dateien, sofort aus etcd. Was einen Neustart erfordert, wird gemeldet und wartet auf einen. Der Modus für Container |
| `-a`, `--autorestart` | Dasselbe, und ein Neustart, der keine Anfrage verwirft für das, was ein Reload nicht ändern kann, eine Listen-Adresse für eine. Für einen Daemon (`-d`) unter Linux; überall sonst wird neu geladen und gemeldet, was wartet |
| `-t` | Baut jeden Upstream, jede Location, jedes Plugin, jedes Zertifikat und jeden Server wie bei einem Start und bindet nichts |
| `--strict` | Ein Schlüssel, den Pingap nicht kennt, oder einer, für den dieser Build keine Funktion hat, ist ein Fehler, wo er ansonsten eine Warnung ist. Für `-t` in CI |
| `--diff <other>` | Gibt aus, was sich ändern würde, wenn `<other>` an die Stelle der Konfiguration träte, Anmeldedaten als Prüfsummen angezeigt |
| `$ENV:NAME`, `$FILE:/path` | Als ganzer Wert: ersetzt durch die Variable oder die Datei, wenn die Konfiguration geladen wird |
## 🔄 Wie eine Anfrage behandelt wird

1. Der **Server** nimmt die Verbindung an und wählt die **Location**, deren Host und Pfad am besten passen. Eine Anfrage, die auf keine passt, wird mit `404` beantwortet.
2. Die **Plugins** der Location laufen in der Reihenfolge, in der sie aufgeführt sind, jedes in seinem eigenen Schritt: auf dem Hinweg oder auf dem Rückweg. Ein Plugin kann die Anfrage selbst beantworten – ein Cache-Treffer, eine fehlgeschlagene Authentifizierung, eine Weiterleitung, eine statische Datei – und der Upstream wird dann nie gefragt.
3. Andernfalls wählt der **Upstream** der Location ein gesundes Backend und die Anfrage wird weitergeleitet. Mit `max_retries` wird eine Verbindung, die nicht hergestellt werden kann, an einem anderen Backend versucht; mit `fallback_upstream` geht eine Anfrage, die der Upstream nicht annehmen kann, an einen zweiten Upstream.
4. Die Antwort geht durch die Response-Plugins zurück zum Client, und die Anfrage wird **protokolliert**: Access-Log, Metriken und Traces.
## 📊 Leistung
Etwa 147.000 Anfragen pro Sekunde für `/ping` auf einem Thread eines M4 Pro, mit einem 99. Perzentil von 116µs (`wrk`, 10 Verbindungen, kein Access-Log).
<details>
<summary>Der Lauf</summary>
```
wrk 'http://127.0.0.1:6118/ping' --latency
Running 10s test @ http://127.0.0.1:6118/ping
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 66.41us 23.67us 1.11ms 76.54%
Req/Sec 73.99k 2.88k 79.77k 68.81%
Latency Distribution
50% 67.00us
75% 80.00us
90% 91.00us
99% 116.00us
1487330 requests in 10.10s, 194.32MB read
Requests/sec: 147260.15
Transfer/sec: 19.24MB
```
</details>
## 🔧 Entwicklung
```bash
make dev # needs bacon: cargo install bacon
make build-web # after a change under web/, needs Node.js
make fmt && make lint && make test
```
`make dev` baut mit den `full`-Features, führt Pingap mit der Konfiguration in `~/tmp/pingap` mit `--autoreload` aus, stellt das Admin-Interface unter `127.0.0.1:3018` bereit (`pingap` / `123123`) und baut bei Änderungen an den Quellen neu. Das Admin-Interface wird aus `dist/` in die Binärdatei eingebettet, das von `make build-web` befüllt wird. Die minimal unterstützte Rust-Version ist 1.96.
### TLS-Backend
Der Standard-Build verwendet OpenSSL, aus dem Quellcode kompiliert. Für rustls:
```bash
cargo build --release --no-default-features --features tls-rustls,full
```
Ein rustls-Build bietet immer TLS 1.2 und 1.3 mit den eigenen Cipher Suites von rustls an, daher werden die pro Server geltenden Einstellungen `tls_min_version`, `tls_max_version`, `tls_cipher_list` und `tls_ciphersuites` bei der Konfigurationsprüfung abgelehnt. Alles andere verhält sich gleich; die Unterschiede sind beim [certificate crate](https://pingap.io/crates/certificate) aufgeführt. `pingap --version` gibt an, über welches Backend eine Binärdatei verfügt.
## 🤝 Mitwirken
Pull Requests sind willkommen.
- Für ein neues Feature eröffne bitte zuerst ein Issue, damit es besprochen werden kann, bevor Code geschrieben wird.
- Bitte eröffne keinen Pull Request nur, um einen Tippfehler oder die Formatierung eines Kommentars zu korrigieren; diese werden in Stapeln behoben.
- Führe vor dem Pushen `make fmt`, `make lint` und `make test` aus. Die CI führt bei jedem Pull Request dieselben Prüfungen aus.
- Ein Beitrag erfolgt unter dem [Contributor License Agreement](https://github.com/vicanso/pingap/blob/main/CLA.md), das durch Ankreuzen des entsprechenden Kästchens in der Pull-Request-Vorlage akzeptiert wird: Die Arbeit bleibt deine und wird dem Projekt unter seiner Lizenz zur Verfügung gestellt.
## 📄 Lizenz
[Apache License, Version 2.0](https://github.com/vicanso/pingap/blob/main/LICENSE).