Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 68 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/notary/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use http_body_util::{BodyExt as _, Either, Empty, Full};
use hyper::{
body::{Bytes, Incoming},
client::conn::http1::Parts,
header::AUTHORIZATION,
Request, Response, StatusCode,
};
use hyper_util::rt::TokioIo;
Expand Down Expand Up @@ -137,6 +138,10 @@ pub struct NotaryClient {
/// in notary server.
#[builder(setter(into, strip_option), default)]
api_key: Option<String>,
/// JWT token used to call notary server endpoints if JWT authorization is
/// enabled in notary server.
#[builder(setter(into, strip_option), default)]
jwt: Option<String>,
/// The duration of notarization request timeout in seconds.
#[builder(default = "60")]
request_timeout: usize,
Expand Down Expand Up @@ -291,6 +296,11 @@ impl NotaryClient {
configuration_request_builder.header(X_API_KEY_HEADER, api_key);
}

if let Some(jwt) = &self.jwt {
configuration_request_builder =
configuration_request_builder.header(AUTHORIZATION, format!("Bearer {jwt}"));
}

let configuration_request = configuration_request_builder
.body(Either::Left(Full::new(Bytes::from(
configuration_request_payload,
Expand Down
3 changes: 3 additions & 0 deletions crates/notary/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ http = { workspace = true }
http-body-util = { workspace = true }
hyper = { workspace = true, features = ["client", "http1", "server"] }
hyper-util = { workspace = true, features = ["full"] }
jsonwebtoken = { version = "9.3.1", features = ["use_pem"] }
k256 = { workspace = true }
notify = { version = "6.1.1", default-features = false, features = [
"macos_kqueue",
Expand All @@ -43,9 +44,11 @@ rand06-compat = { workspace = true }
rustls = { workspace = true }
rustls-pemfile = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_yaml = { version = "0.9" }
sha1 = { version = "0.10" }
structopt = { version = "0.3" }
strum = { version = "0.27", features = ["derive"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tokio-rustls = { workspace = true }
Expand Down
54 changes: 54 additions & 0 deletions crates/notary/server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,60 @@ The main objective of a notary server is to perform notarizations together with
1. TCP client — which has access and control over the transport layer, i.e. TCP.
2. WebSocket client — which has no access over TCP and instead uses WebSocket for notarizations.

### Features
#### Notarization Configuration
To perform a notarization, some parameters need to be configured by the prover and the notary server (more details in the [OpenAPI specification](./openapi.yaml)), i.e.
- maximum data that can be sent and received
- unique session id

To streamline this process, a single HTTP endpoint (`/session`) is used by both TCP and WebSocket clients.

#### Notarization
After calling the configuration endpoint above, the prover can proceed to start the notarization. For a TCP client, that means calling the `/notarize` endpoint using HTTP (`https`), while a WebSocket client should call the same endpoint but using WebSocket (`wss`). Example implementations of these clients can be found in the [integration test](../tests-integration/tests/notary.rs).

#### Signatures
Currently, both the private key (and cert) used to establish a TLS connection with the prover, and the private key used by the notary server to sign the notarized transcript, are hardcoded PEM keys stored in this repository. Though the paths of these keys can be changed in the config (`notary-key` field) to use different keys instead.

#### Authorization
An optional authorization module is available to only allow requests with a valid credential attached. Currently, two modes are supported: whitelist and JWT.

Please note that only *one* mode can be active at any one time.

##### Whitelist mode
In whitelist mode, a valid API key needs to be attached in the custom HTTP header `X-API-Key`. The path of the API key whitelist, path (as well as the flag to enable/disable this module), can be changed in the config (`authorization` field).

Hot reloading of the whitelist is supported, i.e. modification of the whitelist file will be automatically applied without needing to restart the server. Please take note of the following
- Avoid using auto save mode when editing the whitelist to prevent spamming hot reloads
- Once the edit is saved, ensure that it has been reloaded successfully by checking the server log

##### JWT mode
In JWT mode, JSON Web Token is attached in the standard `Authorization` HTTP header as a bearer token. The path to decoding key as well as custom user claims can be changed in the
config (`authorization` field).

Care should be taken when defining custom user claims as the middleware will:
- accept any claim if no custom claim is defined,
- as long as user defined claims are found, other unknown claims will be ignored.

An example JWT config may look something like this:

```yaml
authorization:
enabled: true
jwt:
algorithm: "RS256"
public_key_path: "./fixture/auth/jwt.key.pub"
claims:
- name: sub
values: ["tlsnotary"]
```

#### Optional TLS
TLS between the prover and the notary is currently manually handled in this server, though it can be turned off if any of the following is true
- This server is run locally
- TLS is to be handled by an external environment, e.g. reverse proxy, cloud setup

The toggle to turn on/off TLS is in the config (`tls` field). Alternatively, use the CLI argument `--tls-enabled` (see [this](#configuration)).

### Design Choices
#### Web Framework
Axum is chosen as the framework to serve HTTP and WebSocket requests from the prover clients due to its rich and well supported features, e.g. native integration with Tokio/Hyper/Tower, customizable middleware, the ability to support lower level integrations of TLS ([example](https://github.com/tokio-rs/axum/blob/main/examples/low-level-rustls/src/main.rs)). To simplify the notary server setup, a single Axum router is used to support both HTTP and WebSocket connections, i.e. all requests can be made to the same port of the notary server.
Expand Down
10 changes: 9 additions & 1 deletion crates/notary/server/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ paths:
security:
- {} # make security optional
- ApiKeyAuth: []
- BearerAuth: []
responses:
'200':
description: Ok response from server
Expand All @@ -38,6 +39,7 @@ paths:
security:
- {} # make security optional
- ApiKeyAuth: []
- BearerAuth: []
responses:
'200':
description: Info response from server
Expand All @@ -60,6 +62,7 @@ paths:
security:
- {} # make security optional
- ApiKeyAuth: []
- BearerAuth: []
parameters:
- in: header
name: Content-Type
Expand Down Expand Up @@ -212,4 +215,9 @@ components:
type: apiKey
in: header
name: X-API-Key
description: Whitelisted API key if auth module is turned on
description: Whitelisted API key if auth module is turned on and in whitelist mode
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JSON Web Token if auth module is turned on and in JWT mode
Loading
Loading