wagon/README.md

127 lines
7.3 KiB
Markdown

# wagon
2022-2023 Keith Irwin ([ki9.gf4.pw](https://ki9.gf4.pw/))\
[![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
`wagon` is a an api, user dashboard, and admin ui for managing devices and services on a dns-enabled wireguard network. It was built with small web communities in mind.
## IP address allocation
`wagon` manages devices on a wireguard network, `10.X.Y.Z/16` where `X` is a number associated with the community, configured at the first setup. Servers are hosted on `10.X.0.Z/24`, where each `Z` is a different server. Finally, each user gets a number `Y` (between 1 and 254) and a `/24` subnet for each of their their devices, `Z`.
**For example:** Your home pc could be `10.11.1.1` and your phone `10.11.1.2`. After configuring wireguard, you can use either device to ping your friend, whose pc is at `10.11.2.1`.
IPv6 is also preconfigured for a `/96` subnet, with users getting their own `/112` network for each `/128` device.
## Nameserver
`wagon` manages a bind9 nameserver through the `nsupdate` command. Device domain names have a similar structure as IP addresses, `A.B.C`, where:
- `C` is the community's top-level-domain (TLD)
- `B` is the username
- `A` is hostname of the device
The TLD could be anything that isn't already a global TLD like `.com`. The recursive nameserver takes these private domains as questions and provides private wireguard IPs as answers. It only responds to `.mynet` queries from its own network on `10.X.0.0/16`. This nameserver is then preconfigured in clients' wireguard configs' `DNS = ` setting. Optionally the community's TLD can be set as a search domain for the wireguard interface.
**For example:** Your home pc could be `pc.myuser.mynet` and your phone `phone.myuser.mynet`. Either can ping up `phone.myfriend.mynet`. These would point to `10.11.1.1`, `10.11.1.2`, and `10.11.2.1` respectively
Servers (IPs with `Y=0`) have domains of the form `A.C`, as above. Wildcard subdomains are CNAMEd to their base address, that is, `*.phone.myuser.mynet` CNAMEs to `phone.myuser.mynet` and `*.myerver.mynet` CNAMEs to `myserver.mynet`. Of course, this means no username can match a server hostname.
The nameserver is also preconfigured for rDNS so you can perform lookups on IP addresses:
```sh
$ nslookup 10.11.1.1
1.1.11.10.in-addr.arpa name = pc.myuser.mynet.
```
Using this setup, the community's entire wireguard network and DNS can be accessed from any device by importing a single wireguard config. One purpose of `wagon` is to easily generate these device configs.
## Certificate authority
`wagon` also automatically signs SSL certs for its own invented TLD (`.mynet`) so network-hosted services can be encrypted. The certificates are signed for the IPs and wildcard domains listed above, so, with the right web proxy configuration, a user can use the *same* certificate to self-host a service on any of these domains:
- `https://10.11.1.2/`
- `https://[fd69:1337:0:420:f4:11:1:2]/`
- `https://mypc.myuser.mynet/`
- `https://myservice.mypc.myuser.mynet/`
- `https://myotherservice.mypc.myuser.mynet/`
- `https://anyotherservice.mypc.myuser.mynet/`
The `wagon` user dashboard provides a server certificate and key that the user can download. Any wireguard-connected device with the community's ca certificate imported in their browser or OS will be able to see a green lock on their browser when visiting these private sites.
These certs can be used for other internet protocols like irc or imap.
## Self-hosting and firewalls
Since firewalls and web proxies understand CIDR notation, controlling access to services is easy:
- Allow `10.11.1.1/32` to allow access to pc.myuser.mynet only
- Allow `10.11.1.0/24` to allow access to any of myuser's devices
- Allow `10.11.2.1/32` to allow access to a friend's pc
- Allow `10.11.2.0/24` to allow access to any of a friend's devices
- Allow `10.11.0.0/16` to allow access to anyone in the community
- Allow `10.0.0.0/8` to allow access to any other community (someday, maybe)
Allowing access to virtual webservers is just as simple. For example, I can let my friend access my development server with these allow/deny lines in an nginx vhost config (only showing IPv4, but works with IPv6 too).
```nginx
server {
server_name dev.mypc.myuser.mynet;
listen 10.11.1.1:443 ssl http2;
ssl_certificate /path/to/downloaded/mypc.myuser.mynet/cert.pem;
ssl_certificate_key /path/to/downloaded/mypc.myuser.mynet/key.pem;
ssl_stapling off;
allow 10.11.1.0/24; # My devices
allow 10.11.2.0/24; # My friend's devices
deny all; # Everyone else
# Proxy to local dev server
location / {
proxy_pass http://localhost:8080;
}
}
```
## Dashboard
Users can access a dashboard with a list of devices and links to download `cert.pem` and `key.pem`. Users can add and delete these devices, and admins can add/delete devices and users from a seperate admin interface. When adding a new user or device, the dashboard displays a wireguard configuration which must be copied or saved before the page is refreshed.
In this way, there is no central server storing all the private keys, like with most wireguard dashboards. In fact, `wagon` does not have a database and does not store any data at all; everything is stored in the server's nameserver and wireguard config.
This also means there is no login to the dashboard. Users simply connect to the dashboard over wireguard from any connected device, and `wagon` will recognize your IP and serve up a list of other devices on that same `/24` subnet.
## Structure
`wagon` is written in bash and run as a cgi script. It can be run in- or outside of docker. Why bash? Because it has great "SDKs" for wireguard (`wg`), nameserver updates (`nsupdate`), and SSL certs (`openssl`). The libraries used in the scripts are themselves scripts. Each script follows the unix philosophy of handling text through standard means (arguments and stdin to stdout), so individual libraries could be replaced with faster alternatives (c, rust, go) in the future.
## Project status
The wireguard dashboard and admin are in a working alpha state. I have it implemented on my own network, [gf4](https://www.gf4.pw/), and it seems to be working there. Work is still needed on all these unchecked boxes.
```
| Feature | Multiuser daemon | Dashboard CRUD | Documented | Tests |
+-----------+------------------+----------------+------------+-------+
| wireguard | [X] | [X] | [X] | [ ] |
| bind9 | [X] | [X] | [X] | [ ] |
| TLS certs | [X] | [X] | [X] | [ ] |
| nginx | [X] | [ ] | [ ] | [ ] |
| postfix | [X] | [ ] | [ ] | [ ] |
| asterisk | [ ] | [ ] | [ ] | [ ] |
[ ] Replace bash scripts with binaries*
[ ] API documentation
[ ] A good CLI/TUI
[ ] Hooks
```
*binaries compiled from languages like c/rust/go/haskell, as long as SDKs are available.
## Installation
You want to use this half-built product on your server at your own risk? Fine, go right ahead! Take a look at [INSTALL.md](INSTALL.md). If you have trouble, ask for help in the matrix room below.
## Contributing
If you think you can help with any of these checkboxes, come by [#wagon:gf4.pw](https://matrix.to/#/#wagon:gf4.pw) on matrix.