feature: the router has a two-level CA with an optional offline root, issuing CAs for P2S, site-to-site and general use, templates, published certificates and CRLs and an ACME server, site-to-site connections authenticate with certificates, and setup installs Caddy from Ubuntu or Caddy's repository.
Release / release (push) Successful in 13s
Release / release (push) Successful in 13s
This commit is contained in:
1 parent
fe45272f7c
commit
8d3469d10a
37 files changed
+2499
-921
No files matched your search
@@ -2,8 +2,11 @@
|
||||
|
||||
A Debian package that configures a Linux host or virtual machine as a VPN router:
|
||||
|
||||
- site-to-site IKEv2 IPSec with pre-shared key authentication (strongSwan swanctl)
|
||||
- road-warrior (P2S) access using IKEv2 EAP-TLS, with a bundled certificate authority
|
||||
- site-to-site IKEv2 IPSec with pre-shared key or certificate authentication (strongSwan swanctl)
|
||||
- road-warrior (P2S) access using IKEv2 EAP-TLS
|
||||
- a two-level certificate authority: a root, which may be kept offline, and issuing CAs for the VPN
|
||||
clients, for site-to-site and for general use, with certificate templates, published certificates
|
||||
and CRLs, and an optional ACME server
|
||||
- OpenVPN (P2S) access with a certificate and a password per user, the users kept in a local
|
||||
LDAP directory and managed with `vpn-router-user` or the web service
|
||||
- optional WireGuard endpoint
|
||||
@@ -110,7 +113,6 @@ must have the type shown below; an unknown section or setting is an error.
|
||||
| `site_to_site` | | object | | The site-to-site connections, each a name and an object of the settings below |
|
||||
| `p2s` | `enabled` | boolean | `false` | Road-warrior access |
|
||||
| `p2s` | `address_pool` | string | | CIDR assigned to road-warrior clients |
|
||||
| `p2s` | `ca_name` | string | `VPN Router CA` | Common name for a locally created CA |
|
||||
| `wireguard` | `enabled` | boolean | `false` | WireGuard endpoint |
|
||||
| `wireguard` | `address` | string | | Address and prefix for `wg0`, for example `192.168.200.1/24` |
|
||||
| `wireguard` | `listen_port` | integer | `51820` | UDP port |
|
||||
@@ -130,6 +132,18 @@ must have the type shown below; an unknown section or setting is an error.
|
||||
| `https` | `email` | string | | The e-mail address of the Let's Encrypt account |
|
||||
| `https` | `web_port` | integer | `7444` | HTTPS port of the status web service |
|
||||
| `https` | `manager_port` | integer | `7443` | HTTPS port of the Router Manager |
|
||||
| `https` | `caddy_source` | string | `ubuntu` | Where Caddy is installed from: `ubuntu`, the Ubuntu archive, or `upstream`, Caddy's own repository (see HTTPS) |
|
||||
| `pki` | `root_name` | string | `VPN Router Root CA` | Common name of the root CA, when the router creates it |
|
||||
| `pki` | `root_days` | integer | `7300` | Days the root CA's certificate is valid |
|
||||
| `pki` | `ca_days` | integer | `3650` | Days an issuing CA's certificate is valid |
|
||||
| `pki` | `key` | string | `rsa-4096` | Key of the root and the issuing CAs: `rsa-2048`, `rsa-3072`, `rsa-4096`, `ec-p256` or `ec-p384` |
|
||||
| `pki` | `crl_days` | integer | `7` | Days the CRLs of the issuing CAs are valid |
|
||||
| `pki` | `root_crl_days` | integer | `180` | Days the root CRL is valid, when the router signs it |
|
||||
| `pki` | `publish` | list | `http` | Where the CA certificates and CRLs are published: `http`, `http-80`, `https`, `https-default` (see Certificates) |
|
||||
| `pki` | `p2s_name` | string | `VPN Router P2S CA` | Common name of the P2S issuing CA |
|
||||
| `pki` | `s2s_name` | string | `VPN Router Site-to-Site CA` | Common name of the site-to-site issuing CA |
|
||||
| `pki` | `issuing_cas` | object | | The general-purpose issuing CAs, each a name and `{name, templates, acme}` (see Certificates) |
|
||||
| `pki` | `templates` | object | | Certificate templates, each a name and the options it changes or defines (see Certificates) |
|
||||
| `metrics` | `enabled` | boolean | `true` | The metrics service: collection, history and `/metrics` |
|
||||
| `metrics` | `listen` | string | `internal` | The interfaces `/metrics` is served on: `internal`, `external` or `both` |
|
||||
| `metrics` | `port` | integer | `9464` | TCP port of `/metrics` |
|
||||
@@ -176,13 +190,22 @@ of the status web service, which the Router Manager uses.
|
||||
| `cidrs` | list | | Remote subnets reachable through the tunnel |
|
||||
| `local_cidrs` | list | | Local subnets advertised into the tunnel |
|
||||
| `local_id_mode` | string | `fqdn` | The router's IKE identity: `fqdn` is `wan.local_fqdn`, `public_ip` the address it resolves to, `internal_ip` the internal address |
|
||||
| `psk` | string | | Pre-shared key |
|
||||
| `auth` | string | `psk` | How both sides authenticate: `psk`, a pre-shared key, or `certificate` |
|
||||
| `psk` | string | | Pre-shared key, with `auth` `psk` |
|
||||
| `psk_file` | string | | Path to a file holding the key; wins over `psk` |
|
||||
| `remote_ca` | string | | With `auth` `certificate`, the PEM certificate of the CA that issued the peer's certificate; without it the router's site-to-site CA |
|
||||
|
||||
A connection is set up once all of `addrs`, `id`, `cidrs`, `local_cidrs`, the key and its
|
||||
local identity are known; all such connections are written to
|
||||
`/etc/swanctl/conf.d/site-to-site.conf`. Traffic from a connection's `local_cidrs` to its
|
||||
`cidrs` is not masqueraded.
|
||||
A connection is set up once all of `addrs`, `id`, `cidrs`, `local_cidrs` and its local
|
||||
identity are known, with `psk` the key, and with `certificate` the router's site-to-site
|
||||
certificate; all such connections are written to `/etc/swanctl/conf.d/site-to-site.conf`.
|
||||
Traffic from a connection's `local_cidrs` to its `cidrs` is not masqueraded.
|
||||
|
||||
With `auth` `certificate`, the router authenticates with its site-to-site certificate, which the
|
||||
site-to-site issuing CA signs for `wan.local_fqdn`, the public address and the internal
|
||||
address, so it fits every `local_id_mode`. The peer's certificate must name `id` and be issued
|
||||
by the router's site-to-site CA, or by the CA of `remote_ca`. A peer with no PKI of its own gets
|
||||
its certificate from the router (template `site-to-site`, see Certificates), with the root and
|
||||
the site-to-site CA certificates as its trust anchors.
|
||||
|
||||
A minimal site-to-site configuration in `auto` mode:
|
||||
|
||||
@@ -341,57 +364,215 @@ anything:
|
||||
```
|
||||
|
||||
This does still write to the system: it renders into `/etc/swanctl/`,
|
||||
`/etc/systemd/resolved.conf.d/` and `/etc/wireguard/`, and if road-warrior access is
|
||||
enabled with an empty PKI directory it creates the certificate authority and adds it
|
||||
to the host trust store. What it leaves alone is routes, firewall rules and services -
|
||||
that is what `systemctl restart vpn-router-setup` adds.
|
||||
`/etc/systemd/resolved.conf.d/` and `/etc/wireguard/`, creates what the PKI is missing and
|
||||
adds the root CA to the host trust store. What it leaves alone is routes, firewall rules and
|
||||
services - that is what `systemctl restart vpn-router-setup` adds.
|
||||
|
||||
## Certificates
|
||||
|
||||
`/etc/vpn-router/pki` is the PKI directory, laid out the way `simple-ca` expects:
|
||||
The router has a two-level certificate authority in `/etc/vpn-router/pki` (mode 0700), kept by
|
||||
`vpn-router-setup` and managed with `vpn-router-ca`, `vpn-router-user` and `/ca` of the status
|
||||
web service, which the Router Manager uses:
|
||||
|
||||
```
|
||||
ca_cert.pem ca_key.pem ca_bundle.pem <label>_cert.pem <label>_key.pem crl.pem
|
||||
root/cert.pem root/key.pem root/crl.pem the root CA; key.pem is absent while the root is offline
|
||||
ca/<name>/cert.pem key.pem crl.pem csr.pem an issuing CA; csr.pem while it waits for the offline root
|
||||
router/vpn-server_cert.pem _key.pem the router's VPN server certificate
|
||||
router/site-to-site_cert.pem _key.pem the router's site-to-site certificate
|
||||
users/<user>_cert.pem _key.pem the users' certificates
|
||||
retired/<name>-<time>/ the issuing CAs taken out of use
|
||||
state.json the certificates issued, revocations and CRL numbers
|
||||
```
|
||||
|
||||
`<label>` is the first component of the router FQDN, so `router.example.com` gives
|
||||
`router_cert.pem`.
|
||||
### The CAs
|
||||
|
||||
Once a platform is chosen and while this directory is empty, a certificate authority and a CRL
|
||||
valid for ten years are created there automatically, whether or not road-warrior or OpenVPN
|
||||
access is enabled. A CA of the router's own (with `ca_key.pem`) without a CRL gets one, and
|
||||
without a server certificate gets one once `wan.local_fqdn` is set. User certificates are issued
|
||||
by `vpn-router-user` into `users/`. To use your own CA instead, place the files in that layout
|
||||
before choosing the platform; nothing present is overwritten.
|
||||
The root signs the issuing CAs only (path length 1), and the issuing CAs sign the certificates
|
||||
(path length 0):
|
||||
|
||||
Either way, the package copies what each consumer needs into place: the CA into
|
||||
`/etc/swanctl/x509ca/`, the server certificate and key into `/etc/swanctl/x509/` and
|
||||
`/etc/swanctl/private/`, a CRL into `/etc/swanctl/x509crl/`, and the CA into the system
|
||||
- `p2s`, named `pki.p2s_name`, signs the router's VPN server certificate, which IKEv2 and OpenVPN
|
||||
present, and the users' certificates. Road-warrior and OpenVPN accept client certificates of
|
||||
this CA only.
|
||||
- `s2s`, named `pki.s2s_name`, signs the router's site-to-site certificate and the certificates
|
||||
of its peers.
|
||||
- The general-purpose CAs of `pki.issuing_cas`, each a name and `{"name": common name,
|
||||
"templates": [...], "acme": false}`. The name starts with a lowercase letter, has at most 31
|
||||
lowercase letters, digits and hyphens, and is not `root`, `p2s` or `s2s`; `templates` are the
|
||||
templates the CA issues.
|
||||
|
||||
Once a platform is chosen, `vpn-router-setup` creates what is missing: the root when it has
|
||||
neither certificate nor key, each issuing CA, the router's certificates once `wan.local_fqdn` is
|
||||
set, and the CRLs. The names, lifetimes and key type of `pki` apply when something is created. An
|
||||
issuing CA removed from `pki.issuing_cas` is retired: its directory moves to `retired/` and its
|
||||
certificate is revoked on the root CRL. The router renews its own certificates when less than a
|
||||
third of their lifetime is left, when the names they need change, or when their CA changes; a
|
||||
user's certificate is renewed by removing and adding the user.
|
||||
|
||||
The package copies what each consumer needs: the root, `p2s` and `s2s` into
|
||||
`/etc/swanctl/x509ca/` as `root.pem`, `p2s.pem` and `s2s.pem` (strongSwan reads one certificate
|
||||
per file), the remote CA of each site-to-site connection that has one as `s2s-<name>.pem`, the
|
||||
router's certificates into `/etc/swanctl/x509/` and their keys into `/etc/swanctl/private/`, the
|
||||
CRLs into `/etc/swanctl/x509crl/`, the root and `p2s` with their CRLs into
|
||||
`/etc/openvpn/server/vpn-router-ca.pem` and `vpn-router-crl.pem`, and the root into the system
|
||||
trust store.
|
||||
|
||||
Issue a client certificate with the bundled tool:
|
||||
`vpn-router-ca status` shows the root, the issuing CAs and what needs attention:
|
||||
|
||||
```sh
|
||||
/usr/lib/vpn-router/simple-ca make-cert --ca-dir /etc/vpn-router/pki alice
|
||||
/usr/lib/vpn-router/simple-ca make-pfx --ca-dir /etc/vpn-router/pki \
|
||||
--password s3cr3t /etc/vpn-router/pki/alice_cert.pem
|
||||
vpn-router-ca status
|
||||
vpn-router-ca list --ca p2s
|
||||
```
|
||||
|
||||
`simple-ca` comes from a separate project, <https://gitea.koszewscy.waw.pl/slawek/simple-ca>,
|
||||
and also supports `make-crl` and `revoke-cert`.
|
||||
### Templates
|
||||
|
||||
A template says what a certificate holds:
|
||||
|
||||
| Option | Type | Default | Meaning |
|
||||
|---|---|---|---|
|
||||
| `key` | string | `rsa-3072` | `rsa-2048`, `rsa-3072`, `rsa-4096`, `ec-p256` or `ec-p384`, for a key the router makes |
|
||||
| `days` | integer | `365` | Days the certificate is valid, at most until its CA's certificate expires |
|
||||
| `key_usage` | list | `digital_signature` | Of `digital_signature`, `content_commitment`, `key_encipherment`, `data_encipherment`, `key_agreement` |
|
||||
| `extended_key_usage` | list | | Of `server_auth`, `client_auth`, `code_signing`, `email_protection`, `time_stamping`, `ocsp_signing`, `ipsec_ike`, `ike_intermediate` |
|
||||
| `subject` | string | `name` | The common name: `dns`, a DNS name, which is also a subject alternative name; `name`, any name; `email`, an e-mail address |
|
||||
| `sans` | list | | The subject alternative names allowed: of `dns`, `ip`, `email` |
|
||||
|
||||
The built-in templates:
|
||||
|
||||
| Template | Key usage | Extended key usage | Subject | Names | Issued by |
|
||||
|---|---|---|---|---|---|
|
||||
| `vpn-server` | digital signature, key encipherment | server auth, IKE intermediate | `dns` | `dns`, `ip` | `p2s`, for the router only |
|
||||
| `vpn-user` | digital signature | client auth | `name` | `dns`, `email` | `p2s`, for the users only |
|
||||
| `site-to-site` | digital signature, key encipherment | server auth, client auth | `dns` | `dns`, `ip` | `s2s` |
|
||||
| `server` | digital signature, key encipherment | server auth | `dns` | `dns`, `ip` | general-purpose CAs |
|
||||
| `client` | digital signature | client auth | `name` | `dns`, `email` | general-purpose CAs |
|
||||
|
||||
`pki.templates` changes options of the built-in templates, which cannot be removed, and adds
|
||||
templates of its own, named like the issuing CAs:
|
||||
|
||||
```json
|
||||
"pki": {
|
||||
"issuing_cas": { "servers": { "name": "Example Server CA", "templates": ["server", "web"] } },
|
||||
"templates": {
|
||||
"server": { "days": 397 },
|
||||
"web": { "key": "ec-p256", "days": 90, "key_usage": ["digital_signature"],
|
||||
"extended_key_usage": ["server_auth"], "subject": "dns", "sans": ["dns"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Issuing and revoking
|
||||
|
||||
```sh
|
||||
vpn-router-ca issue --ca servers --template server --csr web.csr > web.pem
|
||||
vpn-router-ca issue --ca servers --template server --subject web.example.com --san 10.0.0.5 --pfx web.pfx
|
||||
vpn-router-ca revoke 5F0A12C3D4 --reason key_compromise
|
||||
```
|
||||
|
||||
With a CSR, the certificate is for the CSR's key, common name and subject alternative names,
|
||||
checked against the template, and the certificate and the chain (issuing CA, root) are printed.
|
||||
Without one, the router makes a key as the template says and writes a PKCS#12 file with the key,
|
||||
the certificate and the chain, protected by the password read from the first line of standard
|
||||
input; the router keeps the certificate and not the key. `vpn-server` and `vpn-user` are issued by
|
||||
the router only.
|
||||
|
||||
`revoke` takes the serial number `list` shows and a reason: `unspecified`, `key_compromise`,
|
||||
`affiliation_changed`, `superseded`, `cessation_of_operation` or `privilege_withdrawn`. A user's
|
||||
certificate is revoked by removing the user, and an issuing CA by removing it from
|
||||
`pki.issuing_cas`; a revoked router certificate is issued again by `vpn-router-setup`.
|
||||
|
||||
### Published certificates and CRLs
|
||||
|
||||
Every certificate names its issuer's certificate (AIA) and CRL (CRL distribution point) at each
|
||||
endpoint of `pki.publish`, in that order. They are served, without authentication, as DER at
|
||||
`/pki/<name>.crt` and `/pki/<name>.crl`, the root as `root`, from copies in
|
||||
`/var/lib/vpn-router/pki-publish`:
|
||||
|
||||
| Endpoint | URL | Served by |
|
||||
|---|---|---|
|
||||
| `http` | `http://<wan.local_fqdn>:<web.port>/pki/` | the status web service; needs `web.enabled` |
|
||||
| `http-80` | `http://<wan.local_fqdn>/pki/` | Caddy, on port 80, open on both interfaces; needs `https.enabled` |
|
||||
| `https` | `https://<wan.local_fqdn>:<https.web_port>/pki/` | Caddy; needs `https.enabled` |
|
||||
| `https-default` | `https://<wan.local_fqdn>/pki/` | Caddy, on port 443, open on both interfaces; needs `https.enabled` |
|
||||
|
||||
The URLs are written into a certificate when it is signed, so a change of `pki.publish` or of the
|
||||
names applies to the certificates signed afterwards. An empty `pki.publish` leaves the extensions
|
||||
out.
|
||||
|
||||
The CRLs of the issuing CAs are valid for `pki.crl_days`, and the root CRL, when the router signs
|
||||
it, for `pki.root_crl_days`. A CRL is signed again at every revocation and, once half of its
|
||||
validity has passed, by `vpn-router-pki.timer`, which runs `vpn-router-ca maintain` daily; it also
|
||||
has `vpn-router-setup` renew the router's certificates when they are due. OpenVPN checks the CRL
|
||||
of every CA of a client's chain, the root's included, and refuses every client while one has
|
||||
expired.
|
||||
|
||||
### Offline root
|
||||
|
||||
Keeping the root key off the router is recommended, not required. Export it, encrypted with a
|
||||
passphrase read from the first line of standard input, keep the file in a safe place, and remove
|
||||
the key from the router:
|
||||
|
||||
```sh
|
||||
vpn-router-ca root export-key --out /root/vpn-router-root-key.pem
|
||||
vpn-router-ca root remove-key
|
||||
```
|
||||
|
||||
While the root key is not on the router, a new issuing CA gets its key and a CSR and waits;
|
||||
`vpn-router-ca status` says so. Print the CSR and the extensions to sign it with, sign it with the
|
||||
root on the offline machine, and import the certificate:
|
||||
|
||||
```sh
|
||||
vpn-router-ca ca csr servers # the CSR, then the [ issuing_ca ] extensions
|
||||
# on the offline machine, with the CSR in servers.csr and the extensions in issuing_ca.cnf:
|
||||
openssl x509 -req -in servers.csr -CA root-cert.pem -CAkey vpn-router-root-key.pem -CAcreateserial \
|
||||
-days 3650 -sha256 -extfile issuing_ca.cnf -extensions issuing_ca -out servers.pem
|
||||
vpn-router-ca ca import servers servers.pem
|
||||
```
|
||||
|
||||
The router cannot sign the root CRL without the key. Sign it where the key is, or put the key back
|
||||
for a moment, and take the CRL before the current one expires:
|
||||
|
||||
```sh
|
||||
vpn-router-ca root import-key /root/vpn-router-root-key.pem # the passphrase on standard input
|
||||
vpn-router-ca maintain
|
||||
vpn-router-ca root remove-key
|
||||
# or, with a CRL signed elsewhere:
|
||||
vpn-router-ca root import-crl root.crl
|
||||
```
|
||||
|
||||
An existing root CA of your own is used by placing its certificate, and its key if the router may
|
||||
use it, before choosing the platform: `vpn-router-ca root import-cert root-cert.pem`, or the files
|
||||
`root/cert.pem` and `root/key.pem`. Without the key, the issuing CAs wait for their certificates
|
||||
as above.
|
||||
|
||||
### ACME server
|
||||
|
||||
A general-purpose CA with `"acme": true` backs an ACME server of Caddy, while `https.enabled`, at
|
||||
`https://<wan.local_fqdn>:<https.web_port>/acme/<name>/directory`, so servers get and renew
|
||||
certificates with any ACME client:
|
||||
|
||||
```sh
|
||||
certbot certonly --standalone -d web.example.com \
|
||||
--server https://router.example.com:7444/acme/servers/directory
|
||||
```
|
||||
|
||||
Caddy proves the client's control of the name as Let's Encrypt does, so it must reach the client.
|
||||
The certificates are valid for 12 hours, carry Caddy's extensions, are not listed by
|
||||
`vpn-router-ca list`, cannot be revoked, and no template applies to them. Caddy reads the CA's
|
||||
certificate and key from `/etc/caddy/vpn-router-pki/<name>/`, readable by its group.
|
||||
|
||||
## OpenVPN and users
|
||||
|
||||
With `openvpn.enabled`, an `address_pool` and `wan.local_fqdn` set, `vpn-router-setup` runs
|
||||
`openvpn-server@vpn-router` with `/etc/openvpn/server/vpn-router.conf`. The server uses the
|
||||
router's CA, certificate and CRL (the copies under `/etc/swanctl`), pushes routes to
|
||||
`local.cidrs` and the router as DNS server, and opens `openvpn.port` on the external interface.
|
||||
router's VPN server certificate, the root and the P2S CA and their CRLs (see Certificates),
|
||||
pushes routes to `local.cidrs` and the router as DNS server, and opens `openvpn.port` on the
|
||||
external interface.
|
||||
|
||||
A client needs both a certificate of the router CA and the password of the user the
|
||||
A client needs both a certificate of the P2S CA and the password of the user the
|
||||
certificate names:
|
||||
|
||||
- the certificate is checked against the CA and the CRL (`crl-verify`, read at every
|
||||
connection);
|
||||
- the certificate is checked against the root and the P2S CA and their CRLs (`crl-verify`, read
|
||||
again when the file changes), and `/usr/lib/vpn-router/openvpn-issuer` (`tls-verify`) refuses
|
||||
a certificate another issuing CA signed;
|
||||
- the password is checked by `openvpn-auth-ldap`, which binds to the user directory as the
|
||||
user; a user enrolled for TOTP types the password followed by the current 6-digit code;
|
||||
- `/usr/lib/vpn-router/openvpn-same-user` refuses a login whose user name is not the common
|
||||
@@ -723,6 +904,7 @@ curl -H "Authorization: Bearer $TOKEN" http://10.0.0.4:7081/config
|
||||
| `wireguard` | The public key and port of `wg0`, and each peer's public key, endpoint, allowed IPs, handshake age and traffic; no private or preshared key |
|
||||
| `docker` | `docker.exposed_ports` and the rules of the `VPN-ROUTER-DOCKER` chain |
|
||||
| `postgresql` | `clusters`: each PostgreSQL cluster of `pg_lsclusters` with its version, name, port, status and data directory, and for one that is online its `settings`: `server_version`, `listen_addresses`, `port`, `max_connections`, `shared_buffers`, `effective_cache_size`, `work_mem`, `password_encryption`, `ssl` and `timezone`; `null` when `pg_lsclusters` fails |
|
||||
| `caddy` | The installed Caddy: `version`, and `source`, `upstream` once Caddy's repository is configured and `ubuntu` otherwise; `null` when Caddy is not installed |
|
||||
|
||||
### The /postgresql page
|
||||
|
||||
@@ -737,7 +919,7 @@ router:
|
||||
|
||||
`/users` manages users with the same token as `/config`, with the module `vpn-router-user` uses
|
||||
too, `/usr/lib/vpn-router/vpnrouter_users.py`, so both apply the same rules. Request
|
||||
and answer bodies are JSON; a request body is at most 4096 bytes.
|
||||
and answer bodies are JSON; a request body is at most 65536 bytes.
|
||||
|
||||
| Request | Body | Answer |
|
||||
| --- | --- | --- |
|
||||
@@ -830,8 +1012,8 @@ group.
|
||||
|
||||
| Request | Body | Answer |
|
||||
| --- | --- | --- |
|
||||
| `GET /site-to-site` | | `{"connections": [{"name", "addrs", "id", "cidrs", "local_cidrs", "local_id_mode", "psk_file", "psk_set", "established"}, ...]}` |
|
||||
| `POST /site-to-site` | `{"name", "addrs", "id", "cidrs", "local_cidrs", "local_id_mode", "psk"}`; `psk` is required | 201 and the connection |
|
||||
| `GET /site-to-site` | | `{"connections": [{"name", "addrs", "id", "cidrs", "local_cidrs", "local_id_mode", "auth", "psk_file", "remote_ca", "psk_set", "established"}, ...]}` |
|
||||
| `POST /site-to-site` | `{"name", "addrs", "id", "cidrs", "local_cidrs", "local_id_mode", "auth", "psk", "remote_ca"}`; `psk` is required with `auth` `psk`, the default | 201 and the connection |
|
||||
| `PUT /site-to-site/<name>` | any of the settings of `POST` but `name`; an empty or absent `psk` keeps the key, a `psk` given replaces `psk_file` | the connection |
|
||||
| `DELETE /site-to-site/<name>` | | `{"name", "removed": true}` |
|
||||
|
||||
@@ -865,6 +1047,36 @@ The answer of `GET /backup` holds:
|
||||
|
||||
A change is made, checked and applied as for `/site-to-site`.
|
||||
|
||||
### The /ca API
|
||||
|
||||
`/ca` manages the PKI (see Certificates) with the same token as `/users`, with the module
|
||||
`vpn-router-ca` uses too, `/usr/lib/vpn-router/vpnrouter_pki.py`.
|
||||
|
||||
| Request | Body | Answer |
|
||||
| --- | --- | --- |
|
||||
| `GET /ca` | | `{"settings", "templates", "status", "certificates"}` |
|
||||
| `PUT /ca` | any of the settings of the `pki` section | `{"settings", "templates"}` |
|
||||
| `POST /ca/certificates` | `{"ca", "template", "csr"}` | 201 and `{"serial", "certificate", "chain"}`, PEM |
|
||||
| `POST /ca/certificates` | `{"ca", "template", "subject", "sans", "password"}` | 201 and `{"serial", "pfx"}`, a PKCS#12 file in base64 |
|
||||
| `POST /ca/certificates/<serial>/revoke` | `{"reason"}`, optional | the certificate, with `reissue` for a router certificate |
|
||||
| `GET /ca/issuing/<name>/csr` | | `{"name", "csr", "extensions"}` of an issuing CA waiting for the offline root |
|
||||
| `PUT /ca/issuing/<name>/certificate` | `{"certificate"}`, PEM, signed by the root | `{"name", "subject", "expires"}` |
|
||||
| `PUT /ca/root/crl` | `{"crl"}`, PEM, signed by the root | `{"next_update"}` |
|
||||
|
||||
The answer of `GET /ca` holds:
|
||||
|
||||
- **`settings`:** the `pki` section.
|
||||
- **`templates`:** every template, the built-in ones included, with all its options.
|
||||
- **`status`:** `root` (`subject`, `expires`, `key`, whether the root key is on the router, `crl_next_update` and `crl_source`, `signed` or `uploaded`), `authorities`, one per issuing CA (`name`, `common_name`, `state`: `active`, `pending` or `missing`, `builtin`, `templates`, `acme`, `subject`, `expires`, `key`, `crl_next_update`, `acme_url`), `retired` and `warnings`.
|
||||
- **`certificates`:** the certificates issued, newest first, each `{"serial", "ca", "kind", "template", "subject", "sans", "created", "expires", "revoked", "reason", "state"}`; `kind` is `ca`, `router`, `user` or `external`, and `state` `valid`, `expired` or `revoked`.
|
||||
|
||||
A change of the settings is made, checked and applied as for `/site-to-site`; an imported issuing
|
||||
CA certificate and a revoked router certificate restart `vpn-router-setup` too. A failure is
|
||||
`{"error": ...}` with 400 for an invalid request, 404 for something that does not exist, 409 when
|
||||
the state does not allow it, as for a revoked certificate or an issuing CA that has its
|
||||
certificate, and 500 otherwise. The root key never crosses the API; `vpn-router-ca` exports,
|
||||
removes and imports it.
|
||||
|
||||
The service starts on its own, without waiting for `vpn-router-setup`, and keeps running. Every
|
||||
30 seconds it reads `web.listen`, `web.port` and `web.enabled` from `vpn-router.json` and works
|
||||
out the addresses of the interfaces `web.listen` names as `general.mode` directs; it listens on
|
||||
@@ -877,7 +1089,7 @@ and the facts for `/config` from `/run/vpn-router/web.json` (readable by root on
|
||||
|
||||
## HTTPS
|
||||
|
||||
Caddy, from Ubuntu's `caddy` package, serves the status web service on `https.web_port` (7444 by
|
||||
Caddy serves the status web service on `https.web_port` (7444 by
|
||||
default), while the Router Manager is installed the manager on `https.manager_port` (7443), and
|
||||
while metrics are enabled the metrics service on `metrics.https_port` (9465), all for `wan.local_fqdn`, while `https.enabled` is `true` and `wan.local_fqdn` is set. It obtains
|
||||
and renews the certificate from Let's Encrypt with the HTTP challenge, so `wan.local_fqdn` must
|
||||
@@ -891,7 +1103,8 @@ package's drop-in `/usr/lib/systemd/system/caddy.service.d/vpn-router.conf` make
|
||||
read; `caddy.service` does not start before the file exists, and `/etc/caddy/Caddyfile` is not
|
||||
used. Setup starts Caddy, reloads it when the file changes, and stops it and removes the file when
|
||||
HTTPS is off. In the firewall it opens port 80 on the external interface and the HTTPS ports on the
|
||||
internal and the external interface. Caddy calls both services on `127.0.0.1`, where they listen
|
||||
internal and the external interface, and ports 80 and 443 on both when `pki.publish` has `http-80`
|
||||
and `https-default`, on which Caddy serves only the CA files (see Certificates). Caddy calls both services on `127.0.0.1`, where they listen
|
||||
whatever `web.listen` and the manager's `listen` say, and passes the client's address in
|
||||
`X-Forwarded-For`, which the manager trusts from `127.0.0.1` and the web service by default, as
|
||||
the address the call came in on; a `web.trusted_proxies` that is set must list `127.0.0.1`. The plain HTTP ports stay as
|
||||
@@ -902,6 +1115,34 @@ the HTTPS ports while `https.enabled` is `true`.
|
||||
curl https://router.example.com:7444/status
|
||||
```
|
||||
|
||||
### Installing Caddy
|
||||
|
||||
The package does not depend on Caddy; `vpn-router-setup` has it installed, while HTTPS is on,
|
||||
from the source `https.caddy_source` names:
|
||||
|
||||
- `ubuntu`, the default: the `caddy` package of the Ubuntu archive.
|
||||
- `upstream`: Caddy's own repository, `https://dl.cloudsmith.io/public/caddy/stable/deb/debian`,
|
||||
whose source the package writes to `/etc/apt/sources.list.d/caddy-stable.sources` with its key,
|
||||
downloaded over HTTPS, in `/etc/apt/keyrings/caddy-stable.asc`.
|
||||
|
||||
apt runs in `vpn-router-caddy.service`, which waits for dpkg's lock, so setup run by a package
|
||||
installation does not wait for itself; the unit runs `vpn-router-caddy apply` and then setup
|
||||
again, which serves with the new Caddy. `vpn-router-caddy status` shows the configured source, the
|
||||
source in effect and the versions.
|
||||
|
||||
A router installed with `ubuntu` is switched to upstream's Caddy with:
|
||||
|
||||
```sh
|
||||
vpn-router-caddy use-upstream
|
||||
```
|
||||
|
||||
It sets `https.caddy_source` to `upstream`, configures the repository and replaces Caddy, printing
|
||||
each step. Upstream versions sort above Ubuntu's, so once the repository is configured apt keeps
|
||||
upstream's Caddy, and `ubuntu` has no effect; setup says so. Going back is a downgrade nothing
|
||||
does on its own: remove the source and the key, run `apt-get update`, and install Ubuntu's version
|
||||
with `apt-get install --allow-downgrades caddy=<version>`, the version `apt-cache policy caddy`
|
||||
lists for the archive.
|
||||
|
||||
## Metrics
|
||||
|
||||
While `metrics.enabled` is `true`, `vpn-router-setup` runs `vpn-router-metrics`, which every minute
|
||||
@@ -1023,7 +1264,7 @@ A backup holds the state that `vpn-router-setup` cannot make again:
|
||||
|
||||
| Item | Content |
|
||||
|---|---|
|
||||
| `/etc/vpn-router` | `vpn-router.json`, the secrets, `rclone.conf`, the PKI and the certificates of the users |
|
||||
| `/etc/vpn-router` | `vpn-router.json`, the secrets, `rclone.conf`, and the PKI: the CAs, the root key while it is on the router, and the certificates of the router and the users |
|
||||
| `/etc/wireguard` | The WireGuard keys and the peers |
|
||||
| `/etc/ufw`, `/etc/default/ufw` | The firewall rules |
|
||||
| `/etc/vpn-router-manager` | The Router Manager's settings, when it is installed |
|
||||
@@ -1107,7 +1348,7 @@ What it replaces is kept in `/var/lib/vpn-router/backup/before-restore/<UTC time
|
||||
|
||||
The `vpn-router-manager` package, built in the repository `linux-cloud-router-manager`, is a web
|
||||
application that shows the state of the router and manages its users, site-to-site
|
||||
connections and backups through the status web service. It can be installed on the router or on another Linux system. While it is installed on
|
||||
connections, backups and certificates through the status web service. It can be installed on the router or on another Linux system. While it is installed on
|
||||
the router, `vpn-router-setup` opens its port (`port` in `/etc/vpn-router-manager/manager.json`,
|
||||
7080 by default, HTTP; HTTPS is Caddy's, on `https.manager_port`) on the internal and the external
|
||||
interface; restart `vpn-router-setup` after changing it. The manager's `listen` chooses the
|
||||
@@ -1116,8 +1357,8 @@ interfaces it listens on.
|
||||
## Removal
|
||||
|
||||
`apt-get remove` stops `vpn-router-docker`, which removes the Docker rules. It terminates the
|
||||
tunnels and takes `wg-quick@wg0`, `openvpn-server@vpn-router`, `caddy`, `vpn-router-metrics` and
|
||||
`vpn-router-backup.timer` down, but leaves the
|
||||
tunnels and takes `wg-quick@wg0`, `openvpn-server@vpn-router`, `caddy`, `vpn-router-metrics`,
|
||||
`vpn-router-backup.timer` and `vpn-router-pki.timer` down, but leaves the
|
||||
configuration in place. `strongswan` is left enabled, since it is a shared service that
|
||||
may be in use by something else.
|
||||
|
||||
@@ -1127,8 +1368,10 @@ before deleting it so a later reinstall starts clean. It also removes the web se
|
||||
signing key, the password file of the metrics service account, and the database of the refresh
|
||||
tokens and the metrics, its role and their password
|
||||
file when the package created them, and Caddy's configuration `/etc/caddy/vpn-router.Caddyfile`; the certificate in
|
||||
`/var/lib/caddy` belongs to the `caddy` package. It removes the backup timer's schedule and the
|
||||
record of the last backup. Key material, users and backups are never deleted: `/etc/vpn-router/pki`,
|
||||
`/var/lib/caddy` belongs to the `caddy` package. It removes the backup timer's schedule, the
|
||||
record of the last backup, the published CA files, the copies of the ACME CAs in
|
||||
`/etc/caddy/vpn-router-pki` and OpenVPN's CA and CRL files. Caddy stays installed, and with it the
|
||||
source of Caddy's repository. Key material, users and backups are never deleted: `/etc/vpn-router/pki`,
|
||||
`/etc/vpn-router/ldap`, `/etc/vpn-router/rclone.conf`, the user directory in slapd, the WireGuard
|
||||
key pair, everything under `/etc/swanctl`, the archives in `backup.path` and
|
||||
`/var/lib/vpn-router/backup/before-restore` are left alone.
|
||||
|
||||
Reference in new issue
Block a user