-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* backend: add connection TTL and traffic limits * backend: return HTTP errors when peers are not available * common: add request / response copy functions * common: add Display implementation for CablePath * common: make CablePath struct more consistent * frontend: check routing_id * frontend: simplify plumbing * log things better * remove many unwraps * add more logging to library * cable-tunnel-server: increase TTL and add sample exchange * backend: implement flags * Migrated backend to dev version of hyper, and implement TLS. Works with Chrome and Safari. * Move some TLS bits out, add debugging * Migrate frontend to new version of hyper, and implement some basic routing. Move some boilerplate into common. Implement HTTPS frontend. * frontend: break loops, and remove excess state * shuffle more state * add docs * wip: self_tx mode, probably will remove * refactor out a bunch of futures stuff to propagate error states better * document tunnel server better, improve logging and error handling * Make http dependency optional * Define all cable-tunnel-server dependencies in the workspace, and share those with other components. * fix building cable docs * a bunch of wordsmithing, using tracing spans * document a bunch of things, instrument the frontend, make debug handler disableable * add some router tests * clippy * fmt
- Loading branch information
Showing
13 changed files
with
1,895 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# webauthn-rs caBLE tunnel server | ||
|
||
**Important:** it is only necessary for an *authenticator vendor* to run a caBLE | ||
tunnel service for their devices. Initiators (such as browsers and client | ||
applications) connect to a tunnel service of the *authenticator's* choosing. | ||
|
||
**Warning:** this is still a work in progress, and not yet fully implemented. | ||
|
||
However, you can run a single-task tunnel service with the `backend` alone: | ||
[see `./backend/README.md` for instructions][0]. | ||
|
||
[0]: ./backend/README.md | ||
|
||
## Background | ||
|
||
To facilitate two-way communication between an initiator (browser) and | ||
authenticator (mobile phone), caBLE uses a WebSocket tunnel server. There are | ||
tunnel servers run by Apple (`cable.auth.com`) and Google (`cable.ua5v.com`), | ||
and a facility to procedurally generate new tunnel server domain names | ||
([run `webauthn-authenticator-rs`' `cable_domain` example][1]). | ||
|
||
[1]: ../webauthn-authenticator-rs/examples/cable_tunnel.rs | ||
|
||
As far as the tunnel server is concerned, what happens is: | ||
|
||
1. The authenticator and initator choose a 16 byte tunnel ID. | ||
|
||
2. The authenticator connects to a tunnel server of its choosing, using HTTPS. | ||
|
||
3. The authenticator makes a WebSocket request to `/cable/new/${TUNNEL_ID}`[^new]. | ||
|
||
4. The tunnel server responds with a WebSocket handshake, and includes a 3 byte | ||
routing ID in the HTTP response headers to indicate which task is serving | ||
the request. | ||
|
||
5. The authenticator transmits the tunnel server ID and routing ID to the | ||
initiator using an encrypted Bluetooth Low Energy advertisement. | ||
|
||
6. The initiator decrypts the advertisement, and connects to the tunnel server | ||
using HTTPS. | ||
|
||
7. The initiator makes a WebSocket request to | ||
`/cable/connect/${ROUTING_ID}/${TUNNEL_ID}`. | ||
|
||
8. The tunnel server responds with a WebSocket handshake. | ||
|
||
9. The tunnel server relays binary WebSocket messages between the authenticator | ||
and initiator. | ||
|
||
The initiator starts a Noise channel with the authenticator for further | ||
communication such that the tunnel server cannot read their communications, and | ||
then does registration or authentication using the FIDO 2 protocol. | ||
|
||
Aside from implementing some basic request filtering, message limits and session | ||
limits, the tunnel server implementations are very simple. The tunnel server | ||
itself does not need to concern itself with the minutae of the Noise protocol - | ||
it only needs to pass binary messages across the tunnel verbatim. | ||
|
||
[^new]: | ||
This [follows Google's caBLE URL convention][2]. The URL used to establish a | ||
new channel [is not part of the FIDO 2.2 specification][3]. | ||
|
||
[2]: https://source.chromium.org/chromium/chromium/src/+/main:device/fido/cable/v2_handshake.cc?q=symbol%3A%5Cbdevice%3A%3Acablev2%3A%3Atunnelserver%3A%3AGetNewTunnelURL%5Cb%20case%3Ayes | ||
[3]: https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#ref-for-client-platform①⓪ | ||
|
||
## Design | ||
|
||
`webauthn-rs`' caBLE tunnel server consists of three parts: | ||
|
||
* [backend][]: serving binary which passes messages between the authenticator | ||
and initiator on a known tunnel ID. | ||
|
||
* [frontend][]: serving binary which routes requests to a `backend` task based | ||
on the routing ID (for `connect` / initiator requests), or some other load | ||
balancing algorithm (for `new` / authenticator requests). | ||
|
||
* [common][]: contains all the shared web server, TLS and caBLE components for | ||
the `backend` and `frontend` binaries. | ||
|
||
[backend]: ./backend/ | ||
[frontend]: ./frontend/ | ||
[common]: ./common/ | ||
|
||
### Backend | ||
|
||
**Source:** [`./backend/`][backend] | ||
|
||
It should be possible to run the `backend` without a `frontend` – in this case | ||
the routing ID will be ignored, and all tunnels exist inside of a single serving | ||
task. | ||
|
||
### Frontend | ||
|
||
**Warning:** The `frontend` is not yet fully implemented, and does not yet do | ||
everything described here. This would be necessary to operate a | ||
high-availability caBLE tunnel service. | ||
|
||
**Source:** [`./frontend/`][frontend] | ||
|
||
The `frontend` needs to do some basic request processing (for routing) before | ||
handing off the connection to a `backend`: | ||
|
||
* For connecting to existing tunnels, the `frontend` needs to connect to | ||
arbitrary `backend` tasks *in any location*. | ||
|
||
* For establishing new tunnels, the `frontend` should prefer to route to "local" | ||
`backend` tasks, taking into account backend availability and load balancing. | ||
|
||
This will probably need some distributed lock service to allocate the routing | ||
IDs. | ||
|
||
While it would be possible to route based on the tunnel ID *alone*, this would | ||
make tunnel create / fetch operations (in the `backend`) global. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[package] | ||
name = "cable-tunnel-server-backend" | ||
version = "0.1.0" | ||
authors = ["Michael Farrell <[email protected]>"] | ||
categories = ["authentication"] | ||
description = "webauthn-rs caBLE tunnel server backend" | ||
edition = "2021" | ||
keywords = ["cable", "hybrid", "fido", "webauthn"] | ||
license = "MPL-2.0" | ||
readme = "README.md" | ||
repository = "https://github.com/kanidm/webauthn-rs/" | ||
rust-version = "1.66.0" | ||
|
||
[dependencies] | ||
cable-tunnel-server-common.workspace = true | ||
|
||
clap.workspace = true | ||
futures.workspace = true | ||
hex.workspace = true | ||
http-body.workspace = true | ||
http-body-util.workspace = true | ||
hyper = { workspace = true, features = ["server"] } | ||
thiserror.workspace = true | ||
tokio.workspace = true | ||
tokio-native-tls.workspace = true | ||
tokio-tungstenite.workspace = true | ||
tracing.workspace = true | ||
tracing-subscriber.workspace = true | ||
tungstenite.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# webauthn-rs cable-tunnel-server-backend | ||
|
||
This binary provides a caBLE tunnel server, which is intended for | ||
*non-production use only*. | ||
|
||
The `backend` can run in two configurations: | ||
|
||
* a single-task configuration, directly serving requests with no frontend. | ||
|
||
In this configuration, caBLE [Routing IDs][background] are ignored, and it is | ||
presumed all incoming requests can be served out of a single running task. | ||
|
||
* a multi-task configuration, with many frontend tasks. | ||
|
||
In this configuration, the backend presumes it has frontend tasks in front of | ||
it to [handle caBLE Routing IDs][background]. However, the frontend is not yet | ||
fully implemented. | ||
|
||
The `backend` is stateless, and is not capable of communicating with other | ||
tasks on its own. Each tunnel exists within one (*and only one*) `backend` task, | ||
and `backend` tasks never process caBLE [Routing IDs][background]. | ||
|
||
[background]: ../README.md#background | ||
|
||
## Building | ||
|
||
You can build the `backend` using Cargo: | ||
|
||
```sh | ||
cargo build | ||
``` | ||
|
||
This will output a binary to `./target/debug/cable-tunnel-server-backend`. | ||
|
||
You can also run the server via Cargo: | ||
|
||
```sh | ||
cargo run -- --help | ||
``` | ||
|
||
## Configuring the server | ||
|
||
The server is configured with command-line flags, which can be seen by running | ||
the server with `--help`. | ||
|
||
To run the server at http://127.0.0.1:8080 (for testing with | ||
`webauthn-authenticator-rs` built with the `cable-override-tunnel` feature): | ||
|
||
```sh | ||
./cable-tunnel-server-backend \ | ||
--bind-address 127.0.0.1:8080 \ | ||
--insecure-http-server | ||
``` | ||
|
||
To run the server with HTTPS and strict `Origin` header checks: | ||
|
||
```sh | ||
./cable-tunnel-server-backend \ | ||
--bind-address 192.0.2.1:443 \ | ||
--tls-public-key /etc/ssl/certs/cable.example.com.pem \ | ||
--tls-private-key /etc/ssl/certs/cable.example.com.key \ | ||
--origin cable.example.com | ||
``` | ||
|
||
> **Important:** caBLE has an algorithm to deriving tunnel server domain names – | ||
> you cannot host the service on an arbitrary domain name of your choosing. | ||
> | ||
> Run [`webauthn-authenticator-rs`' `cable_domain` example][cable_domain] to | ||
> derive hostnames at the command line. | ||
[cable_domain]: ../../webauthn-authenticator-rs/examples/cable_domain.rs | ||
|
||
## Logging | ||
|
||
By default, the server runs at log level `info`. This can be changed with the | ||
`RUST_LOG` environment variable, using the | ||
[log levels available in the `tracing` crate][log-levels]. | ||
|
||
The server logs the following at each level, plus all the messages in the levels | ||
above it: | ||
|
||
* `error`: TLS handshake errors, TCP connection errors, incorrect or unknown | ||
HTTP requests | ||
|
||
* `warn`: warnings about using unencrypted HTTP | ||
|
||
* `info`: (default) start-up messages, HTTP connection lifetime, HTTP request | ||
logs, WebSocket tunnel lifetime | ||
|
||
* `debug`: n/a | ||
|
||
* `trace`: adds complete incoming HTTP requests, WebSocket tunnel messages | ||
|
||
[log-levels]: https://docs.rs/tracing/*/tracing/struct.Level.html | ||
|
||
## Monitoring | ||
|
||
The server exports some basic metrics at `/debug`: | ||
|
||
* `server_state.strong_count`: the number of strong references to | ||
`Arc<ServerState>` | ||
|
||
* `peer_map`: a `HashMap` of all pending tunnels - those where the authenticator | ||
has connected but the initiator has not yet connected. | ||
|
||
* `peer_map.capacity`: the capacity of the pending tunnels `HashMap` | ||
|
||
* `peer_map.len`: the number of pending tunnels |
Oops, something went wrong.