TOTP Gateway is a lightweight, high-performance reverse proxy authentication gateway built on Cloudflare's Pingora framework.
It adds a Two-Factor Authentication (TOTP) layer in front of your internal admin panels, private tools, or sensitive API endpoints, effectively blocking unauthorized access. You can enhance security with just a single binary and a configuration file, without setting up complex authentication infrastructure.
- π TOTP-based 2FA Enforcement: Compatible with standard apps like Google Authenticator and Authy.
- π High Performance: Built on Pingora's asynchronous architecture for speed and stability.
- π‘οΈ Robust Security Policies:
- IP Blacklisting & Automatic Blocking (Brute-force protection).
- Configurable login attempts and ban duration.
- Session expiration control.
- π Flexible Routing: Supports Glob pattern matching for Hostnames (Subdomains) and Paths, plus perβroute protection
toggle via
protect. - π SSL/HTTPS Support: Easily enable TLS via configuration.
- π Hot Reload: Apply configuration changes (
config.toml) instantly without downtime. - π¨ Custom Login Page: Fully customizable HTML login interface.
- Note: This version is not published to crates.io until Pingora PR #425 is merged.
- Fix: On Windows, the program graceful shutdown immediately after startup.
- Feature: Add
mimallocfor faster, stabile memory allocation.
- Fix: Change wrong response settings.
- Change: Make can cache non-protected routes by default.
- Feature: Perβroute
protectoption to choose whether a route is secured by the gateway or proxied directly. Defaults totruefor full backward compatibility. See the updated examples above andexample_config.toml.
- Fix: Make don't cache any pages by default. (Issued when use with Cloudflare Tunnel)
Important
Starting from v0.1.6, this crate will not be published to crates.io until the upstream Pingora PR #425 (which has been pending for over a year) is merged.
Reason: To fix the immediate shutdown issue on Windows, i am currently using a forked version of Pingora. Since crates.io does not support dependencies specified via Git URLs (only version numbers), we cannot publish this version. Please use the Build from Source option below.
cargo install totp-gateway- Rust Toolchain (1.85 or later recommended)
git clone https://github.com/your-username/totp-gateway.git
cd totp-gateway
cargo build --releaseThe compiled binary will be located at target/release/totp-gateway (or totp-gateway.exe on Windows).
-
Generate Config: Run the binary. If no config exists, it will automatically create
config.tomlfrom the default template../target/release/totp-gateway
-
Edit Config: Open
config.tomland configurebind_addrandtotp_secretto match your environment. -
Run:
./target/release/totp-gateway --config config.toml
The config.toml file consists of the following sections:
[server]
bind_addr = "0.0.0.0:25000" # Listening address and port
default_upstream = "127.0.0.1:8080" # Default upstream server if no route matches
# Trusted proxy configuration for real IP extraction
# Essential when running behind Cloudflare Tunnel, Nginx, etc.
trusted_proxies = [
["127.0.0.1/32", "CF-Connecting-IP"],
["10.0.0.0/8", "X-Forwarded-For"]
][auth]
# TOTP Secret (Base32 encoded). Register this key in your Authenticator app.
totp_secret = "JBSWY3DPEHPK3PXP"
# Load from file: totp_secret_file = "./secret.txt"
# Load from env: totp_secret_env = "MY_TOTP_SECRET"
# Path to custom login HTML file (optional)
login_page_file = "./login_page.html"
# Session duration in seconds. Default: 1800 (30 mins)
session_duration = 3600 Core settings to defend against Brute-force attacks.
[security]
enabled = true # Enable security features
max_retries = 5 # Max failed attempts before banning
ban_duration = 3600 # IP ban duration (seconds)
ip_limit_duration = 3600 # Time window for tracking failures (seconds)
blacklist_size = 1000 # Max IPs in blacklist
blacklist_strategy = "overwrite" # "overwrite" (remove oldest) or "block" (reject new)If this section is present, the server runs in HTTPS mode.
[tls]
cert_file = "./certs/fullchain.pem" # Path to certificate
key_file = "./certs/privkey.pem" # Path to private keyDefine multiple routes. Matched in order from top to bottom.
- Matching fields:
host: glob pattern for hostname (e.g.,*.example.com)path: glob pattern for path (e.g.,/admin/*)path_prefix: simple prefix match for path (checked before host/path globs)
- Upstream target:
upstream_addr: targethost:port
- Protection control:
protect(bool, defaulttrue): whenfalse, the route bypasses authentication, sessions, blacklist, etc.
# 1. Route specific path to a different port (protected by default)
[[routes]]
path_prefix = "/admin"
upstream_addr = "127.0.0.1:9090"
protect = true
# 2. Subdomain matching (unprotected example)
[[routes]]
host = "api.example.com"
upstream_addr = "127.0.0.1:3000"
protect = false # bypass login and security for this route
# 3. Wildcard support
[[routes]]
host = "*.internal.com"
path = "/legacy/*"
upstream_addr = "127.0.0.1:4000"
protect = trueTip:
- If no route matches, traffic goes to
server.default_upstreamand remains protected (equivalent toprotect = true). - If you need a public endpoint, define an explicit route with
protect = false.
- User requests a resource via TOTP Gateway.
- Gateway checks for a valid Session Cookie (
SID). - No Session:
- User is redirected to the Login Page.
- User enters the TOTP code.
- Success: A session cookie is issued, and the request is proxied to the upstream server.
- Failure: Failure count increments. If it exceeds the limit, the IP is Banned temporarily.
- Valid Session: Request is immediately proxied to the upstream server.
- Unprotected Route (
protect=false): The gateway skips authentication and blacklist checks and proxies the request directly.
- The fallback route (when no
[[routes]]matches) is always protected. To expose a public endpoint, create a specific route and setprotect = false. - Be careful not to create a
protect = falseroute that matches/authunintentionally; doing so would proxy the login endpoint to your upstream instead of handling authentication. - Blacklist and rate limiting do not apply to
protect = falseroutes.
- The
protectfield is optional and defaults totrue. Existing configs withoutprotectcontinue to work unchanged.
[server]
bind_addr = "0.0.0.0:25000"
default_upstream = "127.0.0.1:8080"
[auth]
totp_secret = "JBSWY3DPEHPK3PXP"
[[routes]]
# Public healthcheck endpoint
path = "/health"
upstream_addr = "127.0.0.1:8080"
protect = false
[[routes]]
# Protected admin area
path = "/admin/*"
upstream_addr = "127.0.0.1:9090"
protect = trueBuilt on Cloudflare's Pingora framework, TOTP Gateway delivers:
- Low Latency: Sub-millisecond proxy overhead
- High Throughput: Handles thousands of concurrent connections
- Memory Efficient: Minimal resource footprint
- Production Ready: Battle-tested async runtime
Run the full test suite:
# Run all tests
cargo test
# Run with verbose output
cargo test -- --nocapture
# Run specific test
cargo test test_basic_auth_and_replay_protectionThe test suite includes:
- Unit tests for core components
- Integration tests with mock upstream servers
- Security feature tests (rate limiting, IP banning)
- Session management tests
- Concurrent request handling tests
- Route protection tests (
protect = true/false) validating bypass and enforcement behavior
Bug reports, feature suggestions, and Pull Requests are welcome!
- Fork the repository.
- Create your Feature Branch (
git checkout -b feature/AmazingFeature). - Commit your changes (
git commit -m 'Add some AmazingFeature'). - Push to the Branch (
git push origin feature/AmazingFeature). - Open a Pull Request.
Distributed under the MIT License.
