Our gateway software is called SkyEye. It is a reverse proxy, a web application firewall and a load balancer combined in a single binary written in Go. It sits in front of your application servers, terminates HTTPS, decodes and inspects every request, and forwards only what is clean to your backends.
This page explains what SkyEye is, how it processes traffic, what it defends against and how it balances load. It is written for anyone who wants to understand how the software works.
SkyEye replaces the usual stack of a reverse proxy, a separate firewall product and a load balancer with one process and one small configuration file. There is no database, no plugin system and no external service. All state lives in memory with hard capacity caps.
It provides a full web application firewall with detection for SQL, NoSQL, XSS, command, template, LDAP and XPath injection, plus XXE, SSRF and prototype pollution, using two independent engines per attack family. It routes by URL prefix like nginx location blocks, balances with round robin or IP hash, fails over automatically and supports WebSockets. HTTPS is automatic. Certificates come from Let's Encrypt, issued and renewed on their own, and every plain HTTP request is redirected safely to HTTPS.
It is also designed to be private. Abuse counters store hashes, never your visitors' URLs, and inspection runs in linear time so a hostile payload cannot slow the gateway down.
Attackers hide payloads behind layers of encoding. SkyEye unwraps up to eight layers of percent encoding, folds HTML entities and JSON escapes, strips SQL comment tricks and collapses spacing before it looks for attacks. A payload like UNION/**/SeLeCt is seen for exactly what it is.
Every signature match respects word boundaries, so ordinary words that merely contain a pattern never trigger. On top of the signature lists, structural engines parse the actual shape of SQL expressions, shell commands, HTML markup, template expressions and LDAP filters.
The attack families it detects:
SQL injection queries smuggled into inputs to read or alter your databaseNoSQL injection operator payloads that twist MongoDB-style queriesXSS scripts planted in requests to run in your visitors' browsersCommand injection shell commands hidden inside parametersTemplate injection expressions that make a template engine execute codePath traversal dot-dot sequences reaching for files outside the web rootXXE XML entities that pull in local files or internal URLsSSRF requests that trick your server into calling internal servicesLDAP injection filter metacharacters that rewrite directory queriesXPath injection query fragments that bypass XML-based loginsPrototype pollution __proto__ keys that poison JavaScript objectsCRLF splitting newline tricks that forge headers and split responsesRequest smuggling ambiguous framing that desyncs servers in a chainLog4Shell lookups JNDI strings aimed at vulnerable Java loggersSkyEye inspects JSON, XML, form and multipart bodies up to a 4 MiB window. Every JSON key and string is scanned, duplicates included, so a payload cannot hide behind a backend that picks first or last. Depth bombs past 128 levels are rejected before any parser sees them.
XML is analyzed lexically. External entities, DTDs, XInclude and stylesheets are detected and banned but never resolved, so the inspector itself can never be the victim. Multipart uploads are capped at 256 parts with every field scanned and file bytes forwarded untouched.
Capped uploads are spooled to an unlinked temp file, so a crash never leaves request bodies on disk. Streaming routes are inspected on a bounded prefix and then streamed live.
A single 404 means nothing. Thirty distinct probes for dotfiles and scanner-only paths inside a minute means someone is mapping your server. SkyEye watches behavior across a short burst window and a one hour slow-scan window and bans path scanners on both.
Abuse counters store SHA-256 hashes of paths, never the URLs themselves. Your traffic cannot be mined as a log.
A confirmed attack bans the address for 7 days. A second offense within 90 days raises it to 14 days and a third to 30. IPv6 attackers are banned by their whole /64 block, so rotating addresses inside one allocation changes nothing.
Only unambiguous attacks ban. Weaker signals get a refusal page that explicitly does not claim a ban. And traffic volume alone never bans anyone, a busy office IP is not an attacker, it just gets a polite 429 with Retry-After.
The block page tells the truth. It states the reason, that the event is logged, and that continued activity extends the restriction.
Bans live in memory by design. A restart is a built-in escape hatch for a bad ban, and the allowlist in skyeye.yaml survives forever.
Before routing, before inspection, every request must survive strict HTTP validation. Ambiguous framing that enables request smuggling is rejected outright, chunked encoding next to a Content-Length, doubled singleton headers, underscore aliases of security headers, unknown Connection options and method-override headers all die here.
Your backend's Server header is masked. Every response leaves stamped as SkyEye.
Give a route several backends and SkyEye spreads traffic across them, round robin by default or IP hash when a visitor should stick to one server. Routes work like nginx location blocks, longest prefix wins and a prefix can be stripped or remapped onto a backend base path.
When a backend refuses connections it is benched for 10 seconds and traffic flows to the healthy ones. An application error never benches a server, only a real connect failure does, so one bad response cannot take a backend out of rotation.
WebSockets pass through with a strictly validated handshake, streaming and server-sent events are flushed immediately with zero buffering, and HTTP/2 is used to backends when they support it.
Every request passes through the same pipeline, in the same order, before it can reach a backend.
SkyEye owns ports 80 and 443. Certificates come from Let's Encrypt automatically and plain HTTP is redirected to HTTPS with a 308 that preserves method and body.
Framing, headers, host, method, query and cookies are checked against strict HTTP rules. Smuggling attempts and malformed requests stop here with an honest status code.
The real client IP is resolved safely, even behind Cloudflare or nginx, and checked against the allowlist and the active ban table.
Path, query, cookies, user agent and body are decoded, normalized and run through the signature and structural engines of every attack family.
A sliding window rate limiter with soft and hard thresholds throttles floods with a 429. Static assets are exempt from the soft limit so real pages stay fast.
The matched route picks a healthy backend, headers are cleaned and stamped, and the response streams back with SkyEye's signature on it.
SkyEye is administered over a private unix socket that never touches the network. The same binary is the server and the client.
skyeye status live counters and capacity of every subsystemskyeye stats totals, requests per second, top requesters and offendersskyeye bans active bans with ID, time remaining and reasonskyeye ban 1.2.3.4 7d reason manual ban, up to 90 daysskyeye unban <id|ip> forgive an address and clear its historyskyeye check 1.2.3.4 full report on any single IPskyeye reload apply skyeye.yaml without restartingskyeye stop graceful shutdown that drains connectionsOn first run SkyEye writes a commented starter config next to the binary. You set your domains, your routes and your rate limits. The security is not a menu of toggles, every defense is always on with durations fixed in software, so a config mistake can never switch your protection off.
Edit the file, run skyeye reload, and the new config is published atomically while requests in flight keep a consistent snapshot.
tls: true
domains: [api.example.com]
trust_proxy_headers: false
rate:
window_seconds: 60
limit_max: 120
hard_max: 400
allowlist: [203.0.113.10]
routes:
- prefix: /
target: http://127.0.0.1:8080
- prefix: /media/
targets:
- http://127.0.0.1:8081
- http://127.0.0.1:8082
lb: ip_hash