feature: users have a first name, a last name and an e-mail address.
Release / release (push) Successful in 11s
Release / release (push) Successful in 11s
This commit is contained in:
1 parent
b9003bacb9
commit
08693f7e68
9 files changed
+872
-290
No files matched your search
@@ -322,8 +322,10 @@ is switched on:
|
||||
slapd package creates; root manages it through the local socket, with no administrator
|
||||
password;
|
||||
- `ou=users` holds one `inetOrgPerson` per user, named by `uid`;
|
||||
- `cn=user-manager,ou=services` is the account that changes users; it may write `ou=users` and
|
||||
set passwords, never read them, and its password is in
|
||||
- `ou=groups` holds one `groupOfNames` per group, named by `cn`, with its members' DNs in
|
||||
`member` and an optional `description`;
|
||||
- `cn=user-manager,ou=services` is the account that changes users and groups; it may write
|
||||
`ou=users` and `ou=groups` and set passwords, never read them, and its password is in
|
||||
`/etc/vpn-router/ldap/user-manager.secret` (root only);
|
||||
- passwords are stored as ARGON2 hashes, and a disabled user is locked with the password policy
|
||||
(`pwdAccountLockedTime`);
|
||||
@@ -333,14 +335,21 @@ The directory is kept on purge.
|
||||
|
||||
### Managing users
|
||||
|
||||
`vpn-router-user` manages the users. A user is the directory entry and a client certificate
|
||||
with the user name as its common name, in `/etc/vpn-router/pki/users`. Passwords are read from
|
||||
the first line of standard input. The answer is JSON.
|
||||
`vpn-router-user` manages the users. A user is the directory entry, with a first name, a last
|
||||
name and an e-mail address (`givenName`, `sn` and `mail`; `cn` is the first and the last name),
|
||||
and a client certificate with the user name as its common name, in `/etc/vpn-router/pki/users`.
|
||||
Passwords are read from the first line of standard input. The answer is text: a table of the
|
||||
users, the client profile or a short message, and a failure is a message on standard error.
|
||||
`--json`, before the command, gives the answer as JSON on standard output, and a failure as
|
||||
`{"error": ...}` there too; the web service uses it. `-h` or `--help` shows the commands, and
|
||||
after a command its options.
|
||||
|
||||
```sh
|
||||
echo 's3cr3t-pass' | vpn-router-user add alice
|
||||
echo 's3cr3t-pass' | vpn-router-user add alice --first-name Alice --last-name Smith --email alice@example.com
|
||||
vpn-router-user update alice --email a.smith@example.com
|
||||
vpn-router-user list
|
||||
vpn-router-user profile alice
|
||||
vpn-router-user profile alice > alice.ovpn
|
||||
vpn-router-user --json list
|
||||
echo 'n3w-pass' | vpn-router-user passwd alice
|
||||
vpn-router-user disable alice
|
||||
vpn-router-user enable alice
|
||||
@@ -349,20 +358,52 @@ vpn-router-user remove alice
|
||||
|
||||
| Command | Effect |
|
||||
| --- | --- |
|
||||
| `list` | The users, whether each is enabled and has a certificate |
|
||||
| `add <name>` | Adds the user and issues the certificate |
|
||||
| `list` | The users with their names and e-mail addresses, whether each is enabled and has a certificate |
|
||||
| `add <name> --first-name F --last-name L --email E` | Adds the user and issues the certificate; all three are required |
|
||||
| `update <name> [--first-name F] [--last-name L] [--email E]` | Changes the names or the e-mail address |
|
||||
| `passwd <name>` | Sets the password |
|
||||
| `disable <name>`, `enable <name>` | Locks the user out of OpenVPN, and lets the user in again |
|
||||
| `remove <name>` | Revokes the certificate, renews the CRL and removes the user |
|
||||
| `remove <name>` | Takes the user out of its groups, revokes the certificate, renews the CRL and removes the user; refused while the user is the last member of a group |
|
||||
| `profile <name>` | The OpenVPN client profile, with the CA, the certificate and the key inline |
|
||||
|
||||
A user name is 1 to 32 lowercase letters, digits and hyphens, starting with a letter; a
|
||||
password has 8 to 128 characters. The exit status is 0 on success, 2 for an invalid request,
|
||||
password has 8 to 128 characters; a first or last name has 1 to 64 printable characters; an
|
||||
e-mail address has one `@`, no spaces and a dot in its domain. In the answers a user added
|
||||
without the names or the e-mail address has them as `null`. The exit status is 0 on success, 2 for an invalid request,
|
||||
3 when the user does not exist, 4 when the user exists and 1 for any other failure.
|
||||
|
||||
The certificate is also accepted by road-warrior (IKEv2 EAP-TLS) access, which checks
|
||||
certificates only, so `disable` does not affect it; `remove` revokes the certificate for both.
|
||||
|
||||
### Managing groups
|
||||
|
||||
`vpn-router-group` manages the groups. A group has a name, with the rules of a user name, an
|
||||
optional description of at most 256 printable characters, and at least one member, each a user
|
||||
of the directory. Groups record membership only; nothing on the router reads them. The output,
|
||||
`--json`, `-h` and `--help` work as for `vpn-router-user`.
|
||||
|
||||
```sh
|
||||
vpn-router-group add ops --member alice --member bob --description 'Operations team'
|
||||
vpn-router-group list
|
||||
vpn-router-group update ops --name netops
|
||||
vpn-router-group update netops --description ''
|
||||
vpn-router-group member-add netops carol
|
||||
vpn-router-group member-remove netops bob
|
||||
vpn-router-group remove netops
|
||||
```
|
||||
|
||||
| Command | Effect |
|
||||
| --- | --- |
|
||||
| `list` | The groups with their members and descriptions |
|
||||
| `add <name> --member U [--member U ...] [--description D]` | Adds the group with its members |
|
||||
| `update <name> [--name N] [--description D]` | Renames the group or sets its description; `''` removes it |
|
||||
| `remove <name>` | Removes the group; its members are kept |
|
||||
| `member-add <name> <user>` | Adds the user to the group |
|
||||
| `member-remove <name> <user>` | Takes the user out of the group; refused for the last member |
|
||||
|
||||
The exit status is 0 on success, 2 for an invalid request, 3 when the group or the user does
|
||||
not exist, 4 when the group exists and 1 for any other failure.
|
||||
|
||||
## Platform modules
|
||||
|
||||
Platform-specific values do not belong in the core, so they are Python modules in
|
||||
@@ -407,8 +448,8 @@ modules shipped with the package; the configuration file accepts any module inst
|
||||
## Status web service
|
||||
|
||||
The `vpn-router-web` service shows the state of the router on its internal address and
|
||||
`web.port` (7081 by default). It uses only the Python standard library, serves HTTP without
|
||||
authentication, and shows no configuration: no key, identity, address or subnet.
|
||||
`web.port` (7081 by default). Its pages `/` and `/status` need no authentication and show no
|
||||
configuration: no key, identity, address or subnet.
|
||||
|
||||
| Page | Content |
|
||||
| --- | --- |
|
||||
@@ -416,6 +457,7 @@ authentication, and shows no configuration: no key, identity, address or subnet.
|
||||
| `/status` | The same status as JSON |
|
||||
| `/config` | The configuration without its secret options, and what the router resolved and runs, as JSON; needs a token |
|
||||
| `/users` | The users of OpenVPN and road-warrior access; needs a token |
|
||||
| `/groups` | The groups of users; needs a token |
|
||||
|
||||
The status holds the state of `vpn-router-setup` and `slapd`, of `strongswan` when site-to-site or
|
||||
road-warrior access is configured, of `wg-quick@wg0` when WireGuard is, and of
|
||||
@@ -471,13 +513,15 @@ curl -H "Authorization: Bearer $TOKEN" http://10.0.0.4:7081/config
|
||||
|
||||
### The /users API
|
||||
|
||||
`/users` manages users with the same token as `/config`, by running `vpn-router-user`. Request
|
||||
`/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.
|
||||
|
||||
| Request | Body | Answer |
|
||||
| --- | --- | --- |
|
||||
| `GET /users` | | `{"users": [{"name", "enabled", "certificate"}, ...]}` |
|
||||
| `POST /users` | `{"name": ..., "password": ...}` | 201 and the user |
|
||||
| `GET /users` | | `{"users": [{"name", "enabled", "certificate", "first_name", "last_name", "email"}, ...]}` |
|
||||
| `POST /users` | `{"name", "password", "first_name", "last_name", "email"}`, all required | 201 and the user |
|
||||
| `PUT /users/<name>` | any of `{"first_name", "last_name", "email"}` | the user |
|
||||
| `PUT /users/<name>/password` | `{"password": ...}` | the user |
|
||||
| `POST /users/<name>/disable` | | the user |
|
||||
| `POST /users/<name>/enable` | | the user |
|
||||
@@ -491,9 +535,28 @@ network in plain HTTP, like the token.
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
|
||||
-d '{"name": "alice", "password": "s3cr3t-pass"}' http://10.0.0.4:7081/users
|
||||
-d '{"name": "alice", "password": "s3cr3t-pass", "first_name": "Alice", "last_name": "Smith", "email": "alice@example.com"}' \
|
||||
http://10.0.0.4:7081/users
|
||||
```
|
||||
|
||||
### The /groups API
|
||||
|
||||
`/groups` manages groups with the same token and the same module as `/users`.
|
||||
|
||||
| Request | Body | Answer |
|
||||
| --- | --- | --- |
|
||||
| `GET /groups` | | `{"groups": [{"name", "description", "members": [...]}, ...]}` |
|
||||
| `POST /groups` | `{"name", "members": [...]}`, and `"description"` | 201 and the group |
|
||||
| `PUT /groups/<name>` | any of `{"name", "description"}`; `""` removes the description | the group |
|
||||
| `DELETE /groups/<name>` | | `{"name", "removed": true}` |
|
||||
| `POST /groups/<name>/members` | `{"user": ...}` | the group |
|
||||
| `DELETE /groups/<name>/members/<user>` | | the group |
|
||||
|
||||
A failure is `{"error": ...}` with 400 for an invalid request, among them taking out the last
|
||||
member, 404 for a group or user that does not exist, 409 for a group that exists and 500
|
||||
otherwise. `DELETE /users/<name>` is refused with 400 while the user is the last member of a
|
||||
group.
|
||||
|
||||
The service starts on its own, without waiting for `vpn-router-setup`, and keeps running. Every
|
||||
30 seconds it works out the internal address from `vpn-router.json` as `general.mode` directs,
|
||||
and reads `web.port` and `web.enabled`; it listens there, moves when the address or the port
|
||||
|
||||
Reference in new issue
Block a user