
Un proxy inverso come nginx, costruito su pingora, semplice ed efficiente.
# Pingap
Un reverse proxy e application gateway basato su [Cloudflare Pingora](https://github.com/cloudflare/pingora). Si configura tramite file TOML o un'interfaccia web di amministrazione integrata nel binario, applica le modifiche senza riavvio e viene fornito con più di trenta plugin.
[中文说明](https://github.com/vicanso/pingap/blob/main/README_zh.md) | [Documentazione](https://pingap.io/) · [Documentazione in cinese](https://pingap.io/zh/) | [Esempi](https://github.com/vicanso/pingap/blob/main/examples/README.md) | [Plugin](https://github.com/vicanso/pingap/blob/main/pingap-plugin/README.md) | [Crates](https://github.com/vicanso/pingap/blob/main/docs/README.md) | [Aggiornamento](https://pingap.io/guide/upgrading)

## Cosa lo distingue
- **Un'interfaccia web di amministrazione nel binario.** Ogni impostazione ha un modulo. Una modifica viene verificata come farebbe `pingap -t` prima di essere salvata, le versioni precedenti possono essere conservate e ripristinate, e sono disponibili account in sola lettura e token API. La sua pagina iniziale mostra grafici del traffico degli ultimi quindici minuti - frequenza, stato, latenza, ogni upstream - senza Prometheus.
- **Modifiche senza downtime.** Upstream, location, plugin e certificati vengono sostituiti nel processo in esecuzione (`--autoreload`). Ciò che richiede nuovi listener viene applicato tramite un riavvio che passa i socket al nuovo processo (`--autorestart`, per un demone su Linux), e il processo in esecuzione continua a servire se il suo sostituto non si avvia.
- **Oltre trenta plugin**, per location e nell'ordine in cui li elenchi: autenticazione (JWT, OIDC, key, basic, HMAC, forward auth), regole di accesso (IP, referer, user agent, percorsi bloccati), traffico (limiti di frequenza e larghezza di banda, cache, mirroring, suddivisione del traffico), contenuto (compressione, sostituzione del body, redirect, file statici, ottimizzazione delle immagini) e operazioni (modalità manutenzione, endpoint di health, pagine di errore). [Tutti i plugin](https://github.com/vicanso/pingap/blob/main/pingap-plugin/README.md)
- **Certificati senza cron job.** Let's Encrypt o qualsiasi altra CA ACME, tramite HTTP-01 o DNS-01 (Alibaba Cloud, Cloudflare, Tencent Cloud, Huawei Cloud), RSA ed ECDSA affiancati, OCSP stapling e mutual TLS verso i client e verso gli upstream.
- **Configurazione verificata prima dell'esecuzione.** `-t` costruisce tutto ciò che farebbe un avvio, `--strict` rifiuta le chiavi che non conosce, `--diff` mostra cosa farebbe una modifica. TOML, HCL o KDL, in file o in etcd, con segreti presi dall'ambiente o da file.
- **Progettato per essere monitorato.** Metriche Prometheus (pull e push), tracce OpenTelemetry, log di accesso in testo o JSON con più di trenta campi e impronte TLS JA4 dei client.
C'è anche tutto il resto di ciò che fa un reverse proxy: HTTP/1.1, HTTP/2, WebSocket e gRPC-web; routing per host, percorso ed espressione regolare; round robin, least connections, consistent hashing e sticky sessions, con health check, retry e circuit breaker; backend da DNS (A/AAAA, SRV) o etichette Docker; il protocollo PROXY sui listener e verso gli upstream; OpenSSL o rustls.
Metriche, tracing e ottimizzazione delle immagini sono nelle build `-full`.
## 🚀 Per iniziare
### 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
```
L'amministratore si trova su http://localhost/pingap. Le immagini sono taggate `latest`, `full` e `rustls-full`, e `<version>`, `<version>-full`, `<version>-rustls-full` per una release. Ognuna ha anche una gemella `-distroless` senza shell, dove `command` deve iniziare con `pingap` come sopra.
### Binario
```bash
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
```
installa l'ultima release in `/usr/local/bin/pingap` su Linux e macOS (x86_64 e arm64). Le variabili anteposte a `sh` scelgono cosa viene installato:
| Variabile | Effetto |
| --- | --- |
| `PINGAP_FULL=1` | La build `-full`, con tutte le funzionalità opzionali |
| `PINGAP_TLS=rustls` | Linux: la build `-rustls-full`, senza OpenSSL (vedi [TLS backend](#tls-backend)) |
| `PINGAP_LIBC=gnu` | Linux: la build glibc al posto di quella statica musl |
| `PINGAP_SERVICE=1` | Linux con systemd: anche il servizio `pingap` e una configurazione iniziale in `/etc/pingap/conf`; non viene abilitato né avviato |
| `PINGAP_VERSION=0.15.0` | Quella release al posto dell'ultima |
Tutti gli asset sono nella [pagina delle release](https://github.com/vicanso/pingap/releases).
### Un solo comando, nessun file di configurazione
```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
```
- Senza `--cert` il certificato viene richiesto tramite HTTP-01: il dominio deve risolvere verso questo host, con la porta 80 raggiungibile. Viene conservato in `~/.pingap/acme/<domains>.toml` e riutilizzato al successivo avvio. Non eliminarlo, l'emissione è soggetta a limiti di frequenza.
- `--cert` è il certificato o la directory in cui si trova (`fullchain.pem` / `privkey.pem`, `cert.pem` / `key.pem` e `tls.crt` / `tls.key` vengono trovati; `--key` indica qualsiasi altro nome). `--upstream` e `--domain` accettano elenchi separati da virgole, `--addr` l'indirizzo su cui ascoltare.
- La configurazione viene creata ex novo a ogni avvio, quindi l'amministratore non può modificarla. Per più di un server usare `--conf`, che non può essere combinato con questi flag.
## 📝 Configurazione
```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"]
```
Lo stesso può essere scritto in HCL o KDL. Ogni chiave è descritta nella [riferimento di configurazione](https://pingap.io/crates/config).
| | |
| --- | --- |
| `--autoreload` | Una modifica viene applicata senza riavvio: entro dieci secondi dai file, immediatamente da etcd. Ciò che richiede un riavvio viene segnalato e attende. La modalità per i container |
| `-a`, `--autorestart` | Lo stesso, e un riavvio che non perde alcuna richiesta per ciò che un reload non può modificare, un indirizzo di ascolto per uno. Per un demone (`-d`) su Linux; altrove esegue il reload e segnala ciò che attende |
| `-t` | Costruisce ogni upstream, location, plugin, certificato e server come farebbe un avvio, e non esegue alcun binding |
| `--strict` | Una chiave che Pingap non conosce, o una per cui questa build non ha funzionalità, è un errore, dove altrimenti è un avviso. Per `-t` in CI |
| `--diff <other>` | Stampa ciò che cambierebbe se `<other>` prendesse il posto della configurazione, con le credenziali mostrate come checksum |
| `$ENV:NAME`, `$FILE:/path` | Come valore intero: sostituito dalla variabile o dal file al momento del caricamento della configurazione |
## 🔄 Come viene gestita una richiesta

1. Il **server** accetta la connessione e sceglie la **location** il cui host e percorso corrispondono meglio. Una richiesta che non corrisponde a nessuna riceve una risposta `404`.
2. I **plugin** della location vengono eseguiti nell'ordine in cui sono elencati, ciascuno al proprio step: all'ingresso o al ritorno. Un plugin può rispondere direttamente alla richiesta - un cache hit, un'autenticazione fallita, un redirect, un file statico - e l'upstream non viene quindi mai interrogato.
3. Altrimenti l'**upstream** della location sceglie un backend sano e la richiesta viene inoltrata. Con `max_retries`, una connessione che non può essere stabilita viene tentata su un altro backend; con `fallback_upstream`, una richiesta che l'upstream non può accettare va a un secondo upstream.
4. La risposta ritorna attraverso i plugin di risposta al client, e la richiesta viene **registrata**: access log, metriche e trace.
## 📊 Prestazioni
Circa 147.000 richieste al secondo per `/ping` su un singolo thread di un M4 Pro, con un 99° percentile di 116µs (`wrk`, 10 connessioni, senza access log).
<details>
<summary>L'esecuzione</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>
## 🔧 Sviluppo
```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` esegue la build con le feature `full`, avvia Pingap sulla configurazione in `~/tmp/pingap` con `--autoreload`, serve l'admin su `127.0.0.1:3018` (`pingap` / `123123`) e ricompila quando i sorgenti cambiano. L'admin è incorporato nel binario da `dist/`, che `make build-web` popola. La versione minima supportata di Rust è 1.96.
### Backend TLS
La build predefinita utilizza OpenSSL, compilato dai sorgenti. Per rustls:
```bash
cargo build --release --no-default-features --features tls-rustls,full
```
Una build rustls offre sempre TLS 1.2 e 1.3 con le suite di cifratura proprie di rustls, quindi `tls_min_version`, `tls_max_version`, `tls_cipher_list` e `tls_ciphersuites` per singolo server vengono rifiutati durante la verifica della configurazione. Tutto il resto si comporta allo stesso modo; le differenze sono elencate con il [crate certificate](https://pingap.io/crates/certificate). `pingap --version` indica quale backend ha un binario.
## 🤝 Contribuire
Le pull request sono benvenute.
- Per una nuova funzionalità, apri prima una issue, così che possa essere discussa prima di scrivere qualsiasi codice.
- Non aprire una pull request solo per correggere un refuso o la formattazione di un commento; questi vengono corretti in batch.
- Esegui `make fmt`, `make lint` e `make test` prima di fare push. La CI esegue gli stessi controlli su ogni pull request.
- Un contributo viene fornito secondo il [Contributor License Agreement](https://github.com/vicanso/pingap/blob/main/CLA.md), accettato spuntando la relativa casella nel template della pull request: il lavoro resta tuo, ed è concesso in licenza al progetto secondo la sua licenza.
## 📄 Licenza
[Apache License, Version 2.0](https://github.com/vicanso/pingap/blob/main/LICENSE).