# gnix a simple stupid reverse proxy ## Features - HTTP/1.1 and HTTP/2 - Simple to configure (see below) - Somewhat fast (about 150k req/s on a rpi5 for simple routes) - Composable modules - Handles connection upgrades correctly by default (websocket, etc.) - TLS support - Configuration hot-reloading - Client authentification (http basic auth, cookie) - _TODO: h3; connection pooling_ ## Links - [AUR Package](https://aur.archlinux.org/packages/gnix) - [Matrix Room](https://matrix.to/#/#gnix:metamuffin.org) for questions, announcements and general discussion around the software. ## Quick Start Run the binary with the a path to the configuration as the first argument. The configuration file is written in YAML and could look like this: ```toml # Both the 'http' and 'https' sections are optional http: # the value for 'bind' can either be a string or a list of strings bind: "[::1]:8080" https: bind: "[::1]:8443" cert_path: "/etc/letsencrypt/live" # Automatically scans and selects certificates # tls_cert: "certs/fullchain.pem" # tls_key: "certs/privkey.pem" # !hosts multiplexes requests for different hostnames. handler: !hosts # requests for `example.org` are forwarded to 127.0.0.1:8000. "example.org": !proxy { backend: "127.0.0.1:8000" } # requests for `mydomain.com` will access files from /srv/http. "mydomain.com": !files root: "/srv/http" index: true # requests for `panel.mydomain.com` will be logged, authentificated and passed on to `otherserver`. "panel.mydomain.com": !access_log file: "/var/log/gnix/panel" next: !cookie_auth users: "/etc/panel_creds" expire: 86400 fail: !file { path: "/usr/share/gnix/login.html" } next: !proxy { backend: "otherserver:80" } ``` ## Reference The configuration uses YAML formatting. When the configuration file is changed, it will automatically be loaded and applied if valid. - **section `http`** - Optional section. Omit to disable unencrypted http. - `bind`: Addresses to accept http requests on (string or list of strings). - **section `https`** - Optional section. Omit to disable https. - `bind`: Addresses to accept https requests on (string or list of strings). - `cert_path`: Path to a directory structure that certificates are loaded from. The hierachy should contain directories containing corresponding `fullchain.pem` and `privkey.pem` files. The correct certificate is selected automatically by subject (`CN`). Pointing this directly at `/etc/letsencrypt/live` is possible. (string or list of strings) - `cert_fallback`: Path to a single directory containing a key-cert pair that is used when no other certificate seems appropriate. This is useful for testing locally with a self-signed subjectless certificate. (optional string) - **section `limits`** - Note: Make sure you do not exceed the maximum file descriptor limit on your platform. - `max_incoming_connections` number of maximum incoming (downstream) connections. excess connections are rejected. Default: 512 - `max_outgoing_connections` number of maximum outgoing (upstream) connections. excess connections are rejected. Default: 256 - **section `handler`** - A module to handle all requests. Usually an instance of `hosts`. - `watch_config`: boolean if to watch the configuration file for changes and apply them accordingly. Default: true (Note: This will watch the entire parent directory of the config since most editors first move the file. Currently any change will trigger a reload. TODO) ### Modules Modules handle requests. Some of them have arguments which are modules themselves; in that case the request is passed on. - **module `hosts`** - Hands over the requests to different modules depending on the `host` header. - Takes a map from hostname (string) to handler (module) - **module `paths`** - Routes requests by matching the path against regexes. - Takes a map from a global regex that is applied to the path (string) to handler (module) - **module `proxy`** - Forwards the request as-is to some other server. the `x-real-ip` header is injected into the request. Connection upgrades are handled by direct forwarding of network traffic. - `backend`: socket address (string) to the backend server - **module `files`** - Provides a simple built-in fileserver. The server handles `accept-ranges`. The `content-type` header is inferred from the file extension and falls back to `application/octet-stream`. If a directory is requested `index.html` will be served or else when indexing is enabled, `index.banner.html` will be prepended to the response. - `root`: root directory to be served (string) - `index`: enables directory indexing (boolean) - `localize`: sends a localized version if possible. for localized content the ISO 639-1 language code is injected in front of the first file name extension (boolean) **semi-experimental** - **module `file`** - Replies with static content. - `path`: file path to the content. _This will be loaded into memory once!_ (string) - `content`: inline declaration of the content. (string) - `type`: type of content reflected in `content-type` header. (string) - **module `access_log`** - Logs requests to a file. - `file`: file path to log (string) - `reject_on_fail`: rejects requests if log could not be written (boolean) - `flush`: flushes log on every request (boolean) - `next`: module for further handling of the request (module) - **module `error`** - Rejects every request with a custom error message. - Takes an error message (string) - **module `http_basic_auth`** - Filters requests via HTTP Basic Authentification. Unauthorized clients will be challenged. - `realm`: describes what the user is logging into (most modern browsers dont show this anymore -_-) - `users`: list of valid logins (credentials) - `next`: a module to handle this request on successfully authentificated. (module) - **module `cookie_auth`** - Authentificates a client based on cookies. The cookies are set on login by a POST request to `/_gnix_login` with form fields `password` and `username` (optional, default: "user") in `x-www-form-urlencoded` format. In any case the users submitting this request will be directed back to the page they come from. For successful logins two cookies are set: `gnix_username` containing the username and `gnix_auth` containing an opaque authentification token. The `gnix_username` cookie is authentificated by gnix and can therefore be used by applications. - `users`: list of valid logins (credentials) - `expire`: seconds before logins expire; not setting this option keeps the login valid forever on the server but cleared after the session on the client (optional number) - `secure`: makes the cookies accessable from secure contexts exclusively i.e. HTTPS (boolean) - `next`: a module to handle this request on successfully authentificated. (module) - `fail`: a module to handle the request when a user is not authorized. This could show an HTML form prompting the user to log in. An implementation of such a form is provided with the distribution of this software, usually in `/usr/share/gnix/login.html` (module) - **module `switch`** - Decides between two possible routes based on a condition. - `condition`: - `!is_websocket_upgrade`: Checks if a websocket was requested. - `!is_get`: Checks if this is a GET request - `!is_post`: Checks if this is a POST request - `!path_starts_with `: Checks if the URI path starts with some prefix - `!path_is `: Checks if the URI path is exactly what you specified - `!has_header `: Checks if the request includes a certain header. - `!any [conditions]`: Checks if any of a set of conditions are satisfied. - `!all [conditions]`: Checks if all conditions are satisfied. - `case_true` Handler with matched requests (module) - `case_false` Handler for all other requests (module) - **module `headers`** - Appends multiple headers to the response. - `headers`: A map of all header name-value pairs. (object string:string) - `inner`: The handler to add the headers to. (module) - **module `redirect`** - Responds with a permanent redirect. - Takes the location to redirect to. (string) - **module `cgi`** - Runs a CGI script on the request. **This is experimental and probably vulnerable! Don't use this.** - `bin`: Path to the CGI binary - `user`: User that the script is executed as. Requires to run gnix as root. (optional string) - **module `debug`** - Replies with information about the request to debug. Includes source address, HTTP version, URI and headers. #### Credentials config format Login credentials for `cookie_auth` and `http_basic_auth` are supplied as either an object mapping usernames to PHC strings or a file path pointing to a file that contains that map in YALM format. Currently only `argon2id` is supported. ### Additional Notes Internally gnix processes requests as they would be sent in HTTP/1.1. HTTP/2 is translated on arrival. Paths matching `/_gnix*` might be used internally in gnix for purposes like OpenID callback or login action endpoints. I hope your application doesn't rely on using them for itself. ## License AGPL-3.0-only; see [COPYING](./COPYING)