Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7722ef3da |
@@ -87,6 +87,9 @@ docker run -d \
|
||||
webhooker
|
||||
```
|
||||
|
||||
The first-boot password goes to the container's log. Read it with `docker logs`
|
||||
and the container's name.
|
||||
|
||||
In production, put a reverse proxy in front of webhooker: a server that accepts
|
||||
the public connections and passes them on. Read
|
||||
[Behind a reverse proxy](#behind-a-reverse-proxy) first.
|
||||
@@ -182,7 +185,8 @@ creates every SQLite file with mode `0600`.
|
||||
from this repository's `Dockerfile` and runs it. Add no port mapping, because
|
||||
upaas publishes every mapped port on all interfaces of the host. Put the app on
|
||||
your reverse proxy's Docker network instead, and mount one host directory at
|
||||
`/var/lib/webhooker`.
|
||||
`/var/lib/webhooker`. The first-boot password goes to the container's log. Read
|
||||
it with `docker logs` and the name upaas gave the container.
|
||||
|
||||
### Behind a reverse proxy
|
||||
|
||||
@@ -190,7 +194,7 @@ webhooker speaks plain HTTP. It does not handle TLS, the encryption behind
|
||||
HTTPS: it holds no certificate and accepts no HTTPS connections. The reverse
|
||||
proxy holds the certificate and reaches webhooker over a private address. On a
|
||||
bare host that is loopback, the address only this host can reach. For the image
|
||||
it is the Docker bridge.
|
||||
it is the Docker bridge, the private network Docker puts containers on.
|
||||
|
||||
Four things must be right.
|
||||
|
||||
@@ -202,10 +206,10 @@ Four things must be right.
|
||||
from any connecting machine, so the proxy must overwrite whatever the client
|
||||
sent.
|
||||
3. **Cover the proxy in `TRUSTED_PROXIES`.** Otherwise every client shares one
|
||||
rate-limit bucket, the count a limit keeps per client. The default list
|
||||
covers the Docker bridge address, such as `172.17.0.1`, that the image sees a
|
||||
proxy at. It does not cover loopback, so on a bare host set
|
||||
`TRUSTED_PROXIES=127.0.0.1`.
|
||||
rate-limit bucket, the count a limit keeps per client. A proxy on the host
|
||||
reaches the container from the Docker bridge address, such as `172.17.0.1`.
|
||||
The default list covers it. The list does not cover loopback, so on a bare
|
||||
host set `TRUSTED_PROXIES=127.0.0.1`.
|
||||
4. **Pass on the client's `Host` header with its port.** webhooker checks a
|
||||
form's `Origin` against the `Host` header it receives. A `Host` without the
|
||||
port makes every form submission fail with `403` on any port other than 443.
|
||||
@@ -233,7 +237,9 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
The block matches webhooker's 1 MB body cap and its 60 second request timeout.
|
||||
The block matches webhooker's 1 MB body cap. Its `proxy_read_timeout` is longer
|
||||
than webhooker's 60 second request timeout, so webhooker's own `504` reaches the
|
||||
client first.
|
||||
|
||||
### HSTS
|
||||
|
||||
@@ -375,12 +381,13 @@ SSRF guard refuses these ranges:
|
||||
The SSRF guard runs when a target is saved and again when each delivery
|
||||
connects, so a hostname that changes its address later is still checked.
|
||||
Redirects are followed up to ten hops, and each hop is checked. A hop to another
|
||||
host or port, or from `https` to `http`, carries none of the configured or
|
||||
forwarded headers, so a redirect cannot pass a credential to another host.
|
||||
host or port, or from `https` to `http`, drops every configured or forwarded
|
||||
header except `Content-Type`. So a redirect cannot pass a credential to another
|
||||
host.
|
||||
|
||||
Egress means the connections webhooker opens outward to deliver.
|
||||
[`ALLOWED_EGRESS_CIDRS`](#allowing-egress-to-your-own-network) opens named
|
||||
networks to it.
|
||||
[`ALLOWED_EGRESS_CIDRS`](#allowing-egress-to-your-own-network) lets deliveries
|
||||
reach networks you name.
|
||||
|
||||
### What the logs contain
|
||||
|
||||
@@ -394,8 +401,8 @@ The `admin` password is printed in the clear on the first boot and by
|
||||
query string, headers and body.
|
||||
|
||||
When `SENTRY_DSN` is set, error reports go to Sentry, an error reporting
|
||||
service. A report carries no query string, body or cookies, and the route
|
||||
pattern (the route as registered, such as `/h/{uuid}`) in place of the path.
|
||||
service. A report carries no query string, body or cookies. In place of the path
|
||||
it carries the route pattern, the route as registered, such as `/h/{uuid}`.
|
||||
|
||||
## Design
|
||||
|
||||
@@ -410,8 +417,9 @@ delivery.
|
||||
|
||||
The main database holds configuration. Each webhook has its own event database,
|
||||
so writes for one webhook never wait on another webhook's database. The program
|
||||
starts in `cmd/webhooker/main.go`, and its packages are under `internal/`. The
|
||||
pages' HTML, CSS and JavaScript are embedded in the binary.
|
||||
starts in `cmd/webhooker/main.go`, and its logic is under `internal/`. The
|
||||
`static` and `templates` packages embed the pages' HTML, CSS and JavaScript in
|
||||
the binary.
|
||||
|
||||
## Configuration reference
|
||||
|
||||
@@ -432,7 +440,10 @@ is allowed. In `prod`, no CORS headers are sent.
|
||||
|
||||
### `PORT`
|
||||
|
||||
The TCP port the listener binds. Default `8080`.
|
||||
The TCP port the listener binds. Default `8080`. Docker marks the image's
|
||||
container healthy by a check every 30 seconds. The check requests port `8080`
|
||||
whatever `PORT` says, so a container started with another `PORT` reports
|
||||
unhealthy.
|
||||
|
||||
### `BIND_ADDRESS`
|
||||
|
||||
@@ -522,8 +533,10 @@ you list, and read the responses in the event log. List the smallest blocks that
|
||||
cover your destinations, and never `0.0.0.0/0` or `::/0`.
|
||||
|
||||
These addresses stay blocked whatever you list. They are link-local space, the
|
||||
unspecified addresses, and cloud metadata endpoints, the addresses a cloud gives
|
||||
each virtual machine for its own credentials:
|
||||
unspecified addresses and cloud metadata endpoints. The unspecified addresses
|
||||
are `0.0.0.0` and `::`, which name no host. A cloud metadata endpoint is the
|
||||
address a cloud gives each virtual machine for its own credentials. The blocked
|
||||
addresses are:
|
||||
|
||||
- `169.254.0.0/16` and `fe80::/10`;
|
||||
- `fd00:ec2::254/128` and `fd00:ec2::23/128`;
|
||||
@@ -545,11 +558,11 @@ default but is reopened by a list that covers it.
|
||||
An `http` target posts the event body to its URL with the event's `Content-Type`
|
||||
and headers, then the target's configured **Headers**, which replace event
|
||||
headers of the same name. Every `http` and `slack` request carries
|
||||
`User-Agent: webhooker/{version}`, replacing the sender's `User-Agent` and any
|
||||
configured one. A `2xx` response is a success. The first 4 KiB of the response
|
||||
body is stored with the attempt. **Timeout** is at most 300 seconds, and blank
|
||||
means the 30 second default. **Pass the query string on to this target** appends
|
||||
the event's query string to the URL.
|
||||
`User-Agent: webhooker/{version}`. The **Headers** field refuses `User-Agent`. A
|
||||
`2xx` response is a success. The first 4 KiB of the response body is stored with
|
||||
the attempt. **Timeout** is at most 300 seconds, and blank means the 30 second
|
||||
default. **Pass the query string on to this target** appends the event's query
|
||||
string to the URL.
|
||||
|
||||
A `slack` target posts a message that describes the event and shows its body in
|
||||
a code block, cut at 3500 bytes. An archive target, whose stored type is
|
||||
|
||||
Reference in New Issue
Block a user