Skip to content

Commit 1e3a74a

Browse files
authored
Merge pull request #316 from ionut-arm/admin
Add admin configuration
2 parents c22fe91 + deffa55 commit 1e3a74a

File tree

8 files changed

+236
-48
lines changed

8 files changed

+236
-48
lines changed

config.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ timeout = 200 # in milliseconds
6464
# https://parallaxsecond.github.io/parsec-book/parsec_security/secure_deployment.html
6565
auth_type = "UnixPeerCredentials"
6666

67+
# List of admins to be identified by the authenticator.
68+
# The "name" field of each entry in the list must contain the application name (as required by the
69+
# identifier in `auth_type`). For example, for `UnixPeerCredentials`, the names should be UIDs of
70+
# the admin users.
71+
# WARNING: Admins have special privileges and access to operations that are not permitted for normal
72+
# users of the service. Only enable this feature with some list of admins if you are confident
73+
# about the need for those permissions.
74+
# Read more here: https://parallaxsecond.github.io/parsec-book/parsec_client/operations/index.html#core-operations
75+
#admins = [ { name = "admin_1" }, { name = "admin_2" } ]
76+
6777
# (Required only for JwtSvid) Location of the Workload API endpoint
6878
# WARNING: only use this authenticator if the Workload API socket is TRUSTED. A malicious entity
6979
# owning that socket would have access to all the keys owned by clients using this authentication

e2e_tests/tests/per_provider/normal_tests/asym_sign_verify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ fn fail_verify_hash() -> Result<()> {
289289

290290
let mut signature = client.sign_with_rsa_sha256(key_name.clone(), hash.clone())?;
291291
// Modify signature
292-
signature[4] += 1;
292+
signature[4] ^= 1;
293293
let status = client
294294
.verify_with_rsa_sha256(key_name, hash, signature)
295295
.unwrap_err();

src/authenticators/direct_authenticator/mod.rs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
//! This authenticator does not offer any security value and should only be used in environments
99
//! where all the clients and the service are mutually trustworthy.
1010
11-
use super::ApplicationName;
12-
use super::Authenticate;
11+
use super::{Admin, AdminList, ApplicationName, Authenticate};
1312
use crate::front::listener::ConnectionMetadata;
1413
use log::error;
1514
use parsec_interface::operations::list_authenticators;
@@ -20,8 +19,19 @@ use parsec_interface::secrecy::ExposeSecret;
2019
use std::str;
2120

2221
/// Direct authentication authenticator implementation
23-
#[derive(Copy, Clone, Debug)]
24-
pub struct DirectAuthenticator;
22+
#[derive(Clone, Debug)]
23+
pub struct DirectAuthenticator {
24+
admins: AdminList,
25+
}
26+
27+
impl DirectAuthenticator {
28+
/// Create new direct authenticator
29+
pub fn new(admins: Vec<Admin>) -> Self {
30+
DirectAuthenticator {
31+
admins: admins.into(),
32+
}
33+
}
34+
}
2535

2636
impl Authenticate for DirectAuthenticator {
2737
fn describe(&self) -> Result<list_authenticators::AuthenticatorInfo> {
@@ -47,7 +57,11 @@ impl Authenticate for DirectAuthenticator {
4757
Err(ResponseStatus::AuthenticationError)
4858
} else {
4959
match str::from_utf8(auth.buffer.expose_secret()) {
50-
Ok(str) => Ok(ApplicationName(String::from(str))),
60+
Ok(str) => {
61+
let app_name = String::from(str);
62+
let is_admin = self.admins.is_admin(&app_name);
63+
Ok(ApplicationName::new(app_name, is_admin))
64+
}
5165
Err(_) => {
5266
error!("Error parsing the authentication value as a UTF-8 string.");
5367
Err(ResponseStatus::AuthenticationError)
@@ -59,14 +73,16 @@ impl Authenticate for DirectAuthenticator {
5973

6074
#[cfg(test)]
6175
mod test {
62-
use super::super::Authenticate;
76+
use super::super::{Admin, Authenticate};
6377
use super::DirectAuthenticator;
6478
use parsec_interface::requests::request::RequestAuth;
6579
use parsec_interface::requests::ResponseStatus;
6680

6781
#[test]
6882
fn successful_authentication() {
69-
let authenticator = DirectAuthenticator {};
83+
let authenticator = DirectAuthenticator {
84+
admins: Default::default(),
85+
};
7086

7187
let app_name = "app_name".to_string();
7288
let req_auth = RequestAuth::new(app_name.clone().into_bytes());
@@ -77,11 +93,14 @@ mod test {
7793
.expect("Failed to authenticate");
7894

7995
assert_eq!(auth_name.get_name(), app_name);
96+
assert_eq!(auth_name.is_admin, false);
8097
}
8198

8299
#[test]
83100
fn failed_authentication() {
84-
let authenticator = DirectAuthenticator {};
101+
let authenticator = DirectAuthenticator {
102+
admins: Default::default(),
103+
};
85104
let conn_metadata = None;
86105
let status = authenticator
87106
.authenticate(&RequestAuth::new(vec![0xff; 5]), conn_metadata)
@@ -92,12 +111,44 @@ mod test {
92111

93112
#[test]
94113
fn empty_auth() {
95-
let authenticator = DirectAuthenticator {};
114+
let authenticator = DirectAuthenticator {
115+
admins: Default::default(),
116+
};
96117
let conn_metadata = None;
97118
let status = authenticator
98119
.authenticate(&RequestAuth::new(Vec::new()), conn_metadata)
99120
.expect_err("Empty auth should have failed");
100121

101122
assert_eq!(status, ResponseStatus::AuthenticationError);
102123
}
124+
125+
#[test]
126+
fn admin_check() {
127+
let admin_name = String::from("admin_name");
128+
let authenticator = DirectAuthenticator {
129+
admins: vec![Admin {
130+
name: admin_name.clone(),
131+
}]
132+
.into(),
133+
};
134+
135+
let app_name = "app_name".to_string();
136+
let req_auth = RequestAuth::new(app_name.clone().into_bytes());
137+
let conn_metadata = None;
138+
139+
let auth_name = authenticator
140+
.authenticate(&req_auth, conn_metadata)
141+
.expect("Failed to authenticate");
142+
143+
assert_eq!(auth_name.get_name(), app_name);
144+
assert_eq!(auth_name.is_admin, false);
145+
146+
let req_auth = RequestAuth::new(admin_name.clone().into_bytes());
147+
let auth_name = authenticator
148+
.authenticate(&req_auth, conn_metadata)
149+
.expect("Failed to authenticate");
150+
151+
assert_eq!(auth_name.get_name(), admin_name);
152+
assert_eq!(auth_name.is_admin, true);
153+
}
103154
}

src/authenticators/jwt_svid_authenticator/mod.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
//! JWT SVID authenticator
44
5-
use super::ApplicationName;
6-
use super::Authenticate;
5+
use super::{Admin, AdminList, ApplicationName, Authenticate};
76
use crate::front::listener::ConnectionMetadata;
87
use log::error;
98
use parsec_interface::operations::list_authenticators;
@@ -19,13 +18,15 @@ use std::str;
1918
#[allow(missing_debug_implementations)]
2019
pub struct JwtSvidAuthenticator {
2120
jwt_client: JWTClient,
21+
admins: AdminList,
2222
}
2323

2424
impl JwtSvidAuthenticator {
2525
/// Create a new JWT-SVID authenticator with a specific path to the Workload API socket.
26-
pub fn new(workload_endpoint: String) -> Self {
26+
pub fn new(workload_endpoint: String, admins: Vec<Admin>) -> Self {
2727
JwtSvidAuthenticator {
2828
jwt_client: JWTClient::new(&workload_endpoint, None, None),
29+
admins: admins.into(),
2930
}
3031
}
3132
}
@@ -65,7 +66,8 @@ impl Authenticate for JwtSvidAuthenticator {
6566
error!("The validation of the JWT-SVID failed ({}).", e);
6667
ResponseStatus::AuthenticationError
6768
})?;
68-
69-
Ok(ApplicationName(validate_response.spiffe_id().to_string()))
69+
let app_name = validate_response.spiffe_id().to_string();
70+
let is_admin = self.admins.is_admin(&app_name);
71+
Ok(ApplicationName::new(app_name, is_admin))
7072
}
7173
}

src/authenticators/mod.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ use parsec_interface::operations::list_authenticators;
2020
use parsec_interface::requests::request::RequestAuth;
2121
use parsec_interface::requests::Result;
2222
use serde::Deserialize;
23+
use std::ops::Deref;
2324
use zeroize::Zeroize;
2425

2526
/// String wrapper for app names
2627
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
27-
pub struct ApplicationName(String);
28+
pub struct ApplicationName {
29+
name: String,
30+
is_admin: bool,
31+
}
2832

2933
/// Authentication interface
3034
///
@@ -52,20 +56,33 @@ pub trait Authenticate {
5256
}
5357

5458
impl ApplicationName {
55-
/// Create a new ApplicationName from a String
56-
pub fn new(name: String) -> ApplicationName {
57-
ApplicationName(name)
59+
/// Create a new ApplicationName
60+
fn new(name: String, is_admin: bool) -> ApplicationName {
61+
ApplicationName { name, is_admin }
62+
}
63+
64+
/// Create ApplicationName from name string only
65+
pub fn from_name(name: String) -> ApplicationName {
66+
ApplicationName {
67+
name,
68+
is_admin: false,
69+
}
5870
}
5971

6072
/// Get a reference to the inner string
6173
pub fn get_name(&self) -> &str {
62-
&self.0
74+
&self.name
75+
}
76+
77+
/// Check whether the application is an admin
78+
pub fn is_admin(&self) -> bool {
79+
self.is_admin
6380
}
6481
}
6582

6683
impl std::fmt::Display for ApplicationName {
6784
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68-
write!(f, "{}", self.0)
85+
write!(f, "{}", self.name)
6986
}
7087
}
7188

@@ -75,12 +92,56 @@ impl std::fmt::Display for ApplicationName {
7592
#[serde(tag = "auth_type")]
7693
pub enum AuthenticatorConfig {
7794
/// Direct authentication
78-
Direct,
79-
/// Unix Peer Credenditals authentication
80-
UnixPeerCredentials,
95+
Direct {
96+
/// List of service admins
97+
admins: Option<Vec<Admin>>,
98+
},
99+
/// Unix Peer Credentials authentication
100+
UnixPeerCredentials {
101+
/// List of service admins
102+
admins: Option<Vec<Admin>>,
103+
},
81104
/// JWT-SVID
82105
JwtSvid {
83106
/// Path to the Workload API socket
84107
workload_endpoint: String,
108+
/// List of service admins
109+
admins: Option<Vec<Admin>>,
85110
},
86111
}
112+
113+
/// Structure defining the properties of a service admin
114+
#[derive(Deserialize, Debug, Zeroize, Clone)]
115+
#[zeroize(drop)]
116+
pub struct Admin {
117+
name: String,
118+
}
119+
120+
impl Admin {
121+
fn name(&self) -> &str {
122+
&self.name
123+
}
124+
}
125+
126+
#[derive(Debug, Clone, Default)]
127+
struct AdminList(Vec<Admin>);
128+
129+
impl AdminList {
130+
fn is_admin(&self, app_name: &str) -> bool {
131+
self.iter().any(|admin| admin.name() == app_name)
132+
}
133+
}
134+
135+
impl Deref for AdminList {
136+
type Target = Vec<Admin>;
137+
138+
fn deref(&self) -> &Self::Target {
139+
&self.0
140+
}
141+
}
142+
143+
impl From<Vec<Admin>> for AdminList {
144+
fn from(admin_list: Vec<Admin>) -> Self {
145+
AdminList(admin_list)
146+
}
147+
}

0 commit comments

Comments
 (0)