summaryrefslogtreecommitdiff
path: root/readme.md
blob: 428fabd89ec3691176e8b4db7a18c22ecf707675 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# gnix

a simple stupid reverse proxy

## Features

- HTTP/1.1
- 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: h2; h3; match on uris; connection pooling; custom connection timeouts_

## 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"
  tls_cert: "ssl/cert.pem"
  tls_key: "ssl/key.pem" # only accepts pkcs8

# !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`**
  - `bind`: string or list of strings with addresses to listen on.

- **section `https`**
  - `bind`: string or list of strings with addresses to listen on.
  - `tls_cert`: path to the SSL certificate. (Sometimes called `fullchain.pem`)
  - `tls_key`: path to the SSL key. (Often called `key.pem` or `privkey.pem`)

- **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 `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)

- **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 <prefix>`: Checks if the URI path starts with some
      prefix
    - `!path_is <path>`: Checks if the URI path is exactly what you specified
    - `!has_header <name>`: Checks if the request includes a certain header.
  - `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)

#### 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.

## License

AGPL-3.0-only; see [COPYING](./COPYING)