Skip to content

Commit b6845df

Browse files
Jakub Konkayuroitaki
andauthored
feat(notary): add JWT-based authorization mode (#817)
* feat(server): add JWT-based authorization mode This mode is an alternative to whitelist authorization mode. It extracts the JWT from the authorization header (bearer token), validates token's signature, claimed expiry times and additional (user-configurable) claims. * Fix formatting and lints * Address review comments * feat(server): remove JwtClaimType config property * Fix missing README comments * Address review comments * Address review comments --------- Co-authored-by: yuroitaki <[email protected]>
1 parent 31def9e commit b6845df

File tree

18 files changed

+804
-240
lines changed

18 files changed

+804
-240
lines changed

Cargo.lock

Lines changed: 66 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/notary/client/src/client.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use http_body_util::{BodyExt as _, Either, Empty, Full};
77
use hyper::{
88
body::{Bytes, Incoming},
99
client::conn::http1::Parts,
10+
header::AUTHORIZATION,
1011
Request, Response, StatusCode,
1112
};
1213
use hyper_util::rt::TokioIo;
@@ -137,6 +138,10 @@ pub struct NotaryClient {
137138
/// in notary server.
138139
#[builder(setter(into, strip_option), default)]
139140
api_key: Option<String>,
141+
/// JWT token used to call notary server endpoints if JWT authorization is
142+
/// enabled in notary server.
143+
#[builder(setter(into, strip_option), default)]
144+
jwt: Option<String>,
140145
/// The duration of notarization request timeout in seconds.
141146
#[builder(default = "60")]
142147
request_timeout: usize,
@@ -291,6 +296,11 @@ impl NotaryClient {
291296
configuration_request_builder.header(X_API_KEY_HEADER, api_key);
292297
}
293298

299+
if let Some(jwt) = &self.jwt {
300+
configuration_request_builder =
301+
configuration_request_builder.header(AUTHORIZATION, format!("Bearer {jwt}"));
302+
}
303+
294304
let configuration_request = configuration_request_builder
295305
.body(Either::Left(Full::new(Bytes::from(
296306
configuration_request_payload,

crates/notary/server/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ http = { workspace = true }
3232
http-body-util = { workspace = true }
3333
hyper = { workspace = true, features = ["client", "http1", "server"] }
3434
hyper-util = { workspace = true, features = ["full"] }
35+
jsonwebtoken = { version = "9.3.1", features = ["use_pem"] }
3536
k256 = { workspace = true }
3637
notify = { version = "6.1.1", default-features = false, features = [
3738
"macos_kqueue",
@@ -43,9 +44,11 @@ rand06-compat = { workspace = true }
4344
rustls = { workspace = true }
4445
rustls-pemfile = { workspace = true }
4546
serde = { workspace = true, features = ["derive"] }
47+
serde_json = { workspace = true }
4648
serde_yaml = { version = "0.9" }
4749
sha1 = { version = "0.10" }
4850
structopt = { version = "0.3" }
51+
strum = { version = "0.27", features = ["derive"] }
4952
thiserror = { workspace = true }
5053
tokio = { workspace = true, features = ["full"] }
5154
tokio-rustls = { workspace = true }

crates/notary/server/README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ log:
9090

9191
auth:
9292
enabled: false
93-
whitelist_path: null
93+
whitelist: null
9494
```
9595
⚠️ By default, `notarization.private_key_path` is `null`, which means a **random, ephemeral** signing key will be generated at runtime (see [Signing](#signing) for more details).
9696

@@ -168,10 +168,35 @@ TLS needs to be turned on between the prover and the notary for security purpose
168168
The toggle to turn on TLS, as well as paths to the TLS private key and certificate can be defined in the config (`tls` field).
169169

170170
### Authorization
171-
An optional authorization module is available to only allow requests with a valid API key attached in the custom HTTP header `X-API-Key`. The API key whitelist path, as well as the flag to enable/disable this module, can be changed in the config (`authorization` field).
171+
An optional authorization module is available to only allow requests with a valid credential attached. Currently, two modes are supported: whitelist and JWT.
172+
173+
Please note that only *one* mode can be active at any one time.
174+
175+
#### Whitelist mode
176+
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, as well as the flag to enable/disable this module, can be changed in the config (`auth` field).
172177

173178
Hot reloading of the whitelist is supported, i.e. changes to the whitelist file are automatically applied without needing to restart the server.
174179

180+
#### JWT mode
181+
In JWT mode, JSON Web Token is attached in the standard `Authorization` HTTP header as a bearer token. The algorithm, the path to verifying key, as well as custom user claims, can be changed in the config (`auth` field).
182+
183+
Care should be taken when defining custom user claims as the middleware will:
184+
- accept any claim if no custom claim is defined,
185+
- as long as user defined claims are found, other unknown claims will be ignored.
186+
187+
An example JWT config may look something like this:
188+
189+
```yaml
190+
auth:
191+
enabled: true
192+
jwt:
193+
algorithm: "RS256"
194+
public_key_path: "./fixture/auth/jwt.key.pub"
195+
claims:
196+
- name: sub
197+
values: ["tlsnotary"]
198+
```
199+
175200
### Logging
176201
The default logging strategy of this server is set to `DEBUG` verbosity level for the crates that are useful for most debugging scenarios, i.e. using the following filtering logic.
177202

crates/notary/server/openapi.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ paths:
1515
security:
1616
- {} # make security optional
1717
- ApiKeyAuth: []
18+
- BearerAuth: []
1819
responses:
1920
'200':
2021
description: Ok response from server
@@ -38,6 +39,7 @@ paths:
3839
security:
3940
- {} # make security optional
4041
- ApiKeyAuth: []
42+
- BearerAuth: []
4143
responses:
4244
'200':
4345
description: Info response from server
@@ -60,6 +62,7 @@ paths:
6062
security:
6163
- {} # make security optional
6264
- ApiKeyAuth: []
65+
- BearerAuth: []
6366
parameters:
6467
- in: header
6568
name: Content-Type
@@ -212,4 +215,9 @@ components:
212215
type: apiKey
213216
in: header
214217
name: X-API-Key
215-
description: Whitelisted API key if auth module is turned on
218+
description: Whitelisted API key if auth module is turned on and in whitelist mode
219+
BearerAuth:
220+
type: http
221+
scheme: bearer
222+
bearerFormat: JWT
223+
description: JSON Web Token if auth module is turned on and in JWT mode

0 commit comments

Comments
 (0)