-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide the ability to authenticate client requests through the use of a JSON Web Token (JWT). This requires a JSON Web Key (JWK) to be available for use by the REST service so that it can validate the JWT. The REST service can be started with the '--no-auth' argument to disable authentication. This is useful for test cases where authentication is unnecessary. The REST service can be started with the '--jwk' argument to enable authentication. The path to the relevant JWK file must also be provided. Example authentication files are provided in ../Mayastor/control-plane/rest/authentication for test purposes and should not be used in production.
- Loading branch information
Paul Yoong
committed
Mar 4, 2021
1 parent
2644d1a
commit 30c792d
Showing
20 changed files
with
449 additions
and
36 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,23 @@ | ||
**WARNING**: These are dummy example RSA keys and should not be used in production. | ||
|
||
There are various websites (such as https://russelldavies.github.io/jwk-creator/) which provide the capability of generating the JSON Web Key from the public RSA key. | ||
For convenience the 'jwk' file has already been generated from the provided public key. | ||
|
||
# Usage | ||
To try out the dummy JSON Web Key (JWK), execute the following steps from within the nix-shell: | ||
1. Run the deployer without launching the rest service | ||
```bash | ||
./target/debug/deployer start -a "Node, Pool, Volume" --no-rest | ||
``` | ||
2. Start the REST service within the nix-shell | ||
```bash | ||
./target/debug/rest --dummy-certificates --jwk "../Mayastor/control-plane/rest/authentication/jwk" | ||
``` | ||
2. Set the token value (located in ../Mayastor/control-plane/rest/authentication/token) | ||
```bash | ||
export TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJyYW5kb20gc3ViamVjdCIsImNvbXBhbnkiOiJteSBjb21wYW55IiwiZXhwIjoxMDAwMDAwMDAwMH0.GkcWHAJ4-qXihaR2j8ZvJgFB1OPpo9P5PkauTmb4PHvlDTYpDQy_nfTHmZCKHS1WEBtsH-HOXApKf32oJEU0K_2SAO76PVZrqvfMewccny-aB9gyu6WMlgSWK8wvGq4h_t_Ma4KIBlPv5PCQO1fyv9bWM3Y3Lu2rPxvNg0O_V_mfnq_Ynwcy4qhnZmse8pZ9zJJaM5OPv2ucWRPKWNzSX8OOz11MGBcdV5QBM-eBpjeSvejEwQ1xOxfiwZwZosFKjPnwMWn8dirMhMNqyRwWgjmOFU2hpc13Ik2VcSWEKTF4ndoUmMLXmCmQ2pSrn9MihEfkpO_VHx_sRVtmYVe2R4iy7ocul3eG7ZAvRq-_GIqBpwbcdUPANIyEFWUWgiPB5_kFvf4-iIBip7NhZ0_4DVoqukYBM2XodejXY863p2frglljt23EimNoKlrtqyxw1wXcbsYtiqCsd3cFTMUkrVesu9xNQPfpM8so37SmTsrC1nOssGEiADAGowqu5SsS | ||
``` | ||
3. Use curl to make a REST request using the above token | ||
```bash | ||
curl -X GET "https://localhost:8080/v0/nodes" -H "accept: application/json" -H "Authorization: Bearer ${TOKEN}" -k | ||
``` |
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,39 @@ | ||
-----BEGIN RSA PRIVATE KEY----- | ||
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 | ||
-----END RSA PRIVATE KEY----- |
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,11 @@ | ||
-----BEGIN PUBLIC KEY----- | ||
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAtTtUE2YgN2te7Hd29BZx | ||
eGjmagg0Ch9zvDIlHRjl7Y6Y9Gankign24dOXFC0t/3XzylySG0w56YkAgZPbu+7 | ||
NRUbjE8ev5gFEBVfHgXmPvFKwPSkCtZG94Kx+lK/BZ4oOieLSoqSSsCdm6Mr5q57 | ||
odkWghnXXohmRgKVgrg2OS1fUcw5l2AYljierf2vsFDGU6DU1PqeKiDrflsu8CFx | ||
DBAkVdUJCZH5BJcUMhjK41FCyYImtEb13eXRIr46rwxOGjwj6Szthd+sZIDDP/VV | ||
BJ3bGNk80buaWYQnojtllseNBg9pGCTBtYHB+kd+NNm2rwPWQLjmcY1ym9LtJmrQ | ||
CXvA4EUgsG7qBNj1dl2NHcG03eEoJBejQ5xwTNgQZ6311lXuKByP5gkiLctCtwn1 | ||
wGTJpjbLKo8xReNdKgFqrIOT1mC76oZpT3AsWlVH60H4aVTthuYEBCJgBQh5Bh6y | ||
44ANGcybj+q7sOOtuWi96sXNOCLczEbqKYpeuckYp1LPAgMBAAE= | ||
-----END PUBLIC KEY----- |
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,7 @@ | ||
{ | ||
"kty": "RSA", | ||
"n": "tTtUE2YgN2te7Hd29BZxeGjmagg0Ch9zvDIlHRjl7Y6Y9Gankign24dOXFC0t_3XzylySG0w56YkAgZPbu-7NRUbjE8ev5gFEBVfHgXmPvFKwPSkCtZG94Kx-lK_BZ4oOieLSoqSSsCdm6Mr5q57odkWghnXXohmRgKVgrg2OS1fUcw5l2AYljierf2vsFDGU6DU1PqeKiDrflsu8CFxDBAkVdUJCZH5BJcUMhjK41FCyYImtEb13eXRIr46rwxOGjwj6Szthd-sZIDDP_VVBJ3bGNk80buaWYQnojtllseNBg9pGCTBtYHB-kd-NNm2rwPWQLjmcY1ym9LtJmrQCXvA4EUgsG7qBNj1dl2NHcG03eEoJBejQ5xwTNgQZ6311lXuKByP5gkiLctCtwn1wGTJpjbLKo8xReNdKgFqrIOT1mC76oZpT3AsWlVH60H4aVTthuYEBCJgBQh5Bh6y44ANGcybj-q7sOOtuWi96sXNOCLczEbqKYpeuckYp1LP", | ||
"e": "AQAB", | ||
"alg": "RS256", | ||
"use": "sig" | ||
} |
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 @@ | ||
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJyYW5kb20gc3ViamVjdCIsImNvbXBhbnkiOiJteSBjb21wYW55IiwiZXhwIjoxMDAwMDAwMDAwMH0.GkcWHAJ4-qXihaR2j8ZvJgFB1OPpo9P5PkauTmb4PHvlDTYpDQy_nfTHmZCKHS1WEBtsH-HOXApKf32oJEU0K_2SAO76PVZrqvfMewccny-aB9gyu6WMlgSWK8wvGq4h_t_Ma4KIBlPv5PCQO1fyv9bWM3Y3Lu2rPxvNg0O_V_mfnq_Ynwcy4qhnZmse8pZ9zJJaM5OPv2ucWRPKWNzSX8OOz11MGBcdV5QBM-eBpjeSvejEwQ1xOxfiwZwZosFKjPnwMWn8dirMhMNqyRwWgjmOFU2hpc13Ik2VcSWEKTF4ndoUmMLXmCmQ2pSrn9MihEfkpO_VHx_sRVtmYVe2R4iy7ocul3eG7ZAvRq-_GIqBpwbcdUPANIyEFWUWgiPB5_kFvf4-iIBip7NhZ0_4DVoqukYBM2XodejXY863p2frglljt23EimNoKlrtqyxw1wXcbsYtiqCsd3cFTMUkrVesu9xNQPfpM8so37SmTsrC1nOssGEiADAGowqu5SsS |
Large diffs are not rendered by default.
Oops, something went wrong.
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,145 @@ | ||
use actix_web::{Error, HttpRequest}; | ||
use jsonwebtoken::{crypto, Algorithm, DecodingKey}; | ||
use std::str::FromStr; | ||
|
||
use http::HeaderValue; | ||
use std::fs::File; | ||
|
||
/// Initialise JWK with the contents of the file at 'jwk_path'. | ||
/// If jwk_path is 'None', authentication is disabled. | ||
pub fn init(jwk_path: Option<String>) -> JsonWebKey { | ||
match jwk_path { | ||
Some(path) => { | ||
let jwk_file = File::open(path).expect("Failed to open JWK file"); | ||
let jwk = serde_json::from_reader(jwk_file) | ||
.expect("Failed to deserialise JWK"); | ||
JsonWebKey { | ||
jwk, | ||
} | ||
} | ||
None => JsonWebKey { | ||
..Default::default() | ||
}, | ||
} | ||
} | ||
|
||
#[derive(Default, Debug)] | ||
pub struct JsonWebKey { | ||
jwk: serde_json::Value, | ||
} | ||
|
||
impl JsonWebKey { | ||
// Returns true if REST calls should be authenticated. | ||
fn auth_enabled(&self) -> bool { | ||
!self.jwk.is_null() | ||
} | ||
|
||
// Return the algorithm. | ||
fn algorithm(&self) -> Algorithm { | ||
Algorithm::from_str(self.jwk["alg"].as_str().unwrap()).unwrap() | ||
} | ||
|
||
// Return the modulus. | ||
fn modulus(&self) -> &str { | ||
self.jwk["n"].as_str().unwrap() | ||
} | ||
|
||
// Return the exponent. | ||
fn exponent(&self) -> &str { | ||
self.jwk["e"].as_str().unwrap() | ||
} | ||
|
||
// Return the decoding key | ||
fn decoding_key(&self) -> DecodingKey { | ||
DecodingKey::from_rsa_components(self.modulus(), self.exponent()) | ||
} | ||
} | ||
|
||
/// Authenticate the HTTP request by checking the authorisation token to ensure | ||
/// the sender is who they claim to be. | ||
pub fn authenticate(req: &HttpRequest) -> Result<(), Error> { | ||
let jwk: &JsonWebKey = req.app_data().unwrap(); | ||
|
||
// If authentication is disabled there is nothing to do. | ||
if !jwk.auth_enabled() { | ||
return Ok(()); | ||
} | ||
|
||
match req.headers().get(http::header::AUTHORIZATION) { | ||
Some(token) => validate(&format_token(token), jwk), | ||
None => { | ||
tracing::error!("Missing bearer token in HTTP request."); | ||
Err(Error::from(actix_web::HttpResponse::Unauthorized())) | ||
} | ||
} | ||
} | ||
|
||
// Ensure the token is formatted correctly by removing the "Bearer" prefix if | ||
// present. | ||
fn format_token(token: &HeaderValue) -> String { | ||
let token = token | ||
.to_str() | ||
.expect("Failed to convert token to string") | ||
.replace("Bearer", ""); | ||
token.trim().into() | ||
} | ||
|
||
/// Validate a bearer token. | ||
pub fn validate(token: &str, jwk: &JsonWebKey) -> Result<(), Error> { | ||
let (message, signature) = split_token(&token); | ||
return match crypto::verify( | ||
&signature, | ||
&message, | ||
&jwk.decoding_key(), | ||
jwk.algorithm(), | ||
) { | ||
Ok(true) => Ok(()), | ||
Ok(false) => { | ||
tracing::error!("Signature verification failed."); | ||
Err(Error::from(actix_web::HttpResponse::Unauthorized())) | ||
} | ||
Err(e) => { | ||
tracing::error!( | ||
"Failed to complete signature verification with error {}", | ||
e | ||
); | ||
Err(Error::from(actix_web::HttpResponse::Unauthorized())) | ||
} | ||
}; | ||
} | ||
|
||
// Split the JSON Web Token (JWT) into 2 parts, message and signature. | ||
// The message comprises the header and payload. | ||
// | ||
// JWT format: | ||
// <header>.<payload>.<signature> | ||
// \______ ________/ | ||
// \/ | ||
// message | ||
fn split_token(token: &str) -> (String, String) { | ||
let elems = token.split('.').collect::<Vec<&str>>(); | ||
let message = format!("{}.{}", elems[0], elems[1]); | ||
let signature = elems[2]; | ||
(message, signature.into()) | ||
} | ||
|
||
#[test] | ||
fn validate_test() { | ||
let token_file = std::env::current_dir() | ||
.expect("Failed to get current directory") | ||
.join("authentication") | ||
.join("token"); | ||
let mut token = std::fs::read_to_string(token_file) | ||
.expect("Failed to get bearer token"); | ||
let jwk_file = std::env::current_dir() | ||
.expect("Failed to get current directory") | ||
.join("authentication") | ||
.join("jwk"); | ||
let jwk = init(Some(jwk_file.to_str().unwrap().into())); | ||
|
||
validate(&token, &jwk).expect("Validation should pass"); | ||
// create invalid token | ||
token.push_str("invalid"); | ||
validate(&token, &jwk) | ||
.expect_err("Validation should fail with an invalid token"); | ||
} |
Oops, something went wrong.