Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add auth and tls for mirroring connections #4002

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use tracing::instrument;
use async_trait::async_trait;
pub use policy::BasicRbacPolicy;

use fluvio_auth::{AuthContext, Authorization, TypeAction, InstanceAction, AuthError};
use crate::{AuthContext, Authorization, TypeAction, InstanceAction, AuthError};
use fluvio_controlplane_metadata::extended::ObjectType;
use fluvio_auth::x509::X509Identity;
use crate::x509::X509Identity;

#[derive(Debug, Clone)]
pub struct BasicAuthorization {
Expand Down Expand Up @@ -84,8 +84,8 @@ mod policy {
use tracing::debug;
use serde::{Serialize, Deserialize};

use fluvio_auth::{AuthError, TypeAction, InstanceAction};
use fluvio_auth::x509::X509Identity;
use crate::{AuthError, TypeAction, InstanceAction};
use crate::x509::X509Identity;

use super::ObjectType;

Expand All @@ -105,6 +105,7 @@ mod policy {
match action {
TypeAction::Create => Action::Create,
TypeAction::Read => Action::Read,
TypeAction::Update => Action::Update,
}
}
}
Expand Down Expand Up @@ -147,6 +148,7 @@ mod policy {
// let (action,object,_instance) = request;
// For each scope provided in the identity,
// check if there is a match;
//
let is_allowed = identity.scopes().iter().any(|scope| {
self.0
.get(scope)
Expand Down Expand Up @@ -179,6 +181,7 @@ mod policy {
root_policy.insert(ObjectType::Partition, vec![Action::All]);
root_policy.insert(ObjectType::TableFormat, vec![Action::All]);
root_policy.insert(ObjectType::Mirror, vec![Action::All]);
root_policy.insert(ObjectType::RemoteToHomeConnection, vec![Action::All]);

let mut policy = HashMap::new();

Expand All @@ -197,7 +200,7 @@ mod test {
use std::convert::TryFrom;
use std::collections::HashMap;

use fluvio_auth::x509::X509Identity;
use crate::x509::X509Identity;

use super::policy::*;
use super::ObjectType;
Expand Down
3 changes: 3 additions & 0 deletions crates/fluvio-auth/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod policy;
mod error;

pub mod basic;
pub mod root;

pub mod x509;

pub use policy::*;
Expand Down
3 changes: 2 additions & 1 deletion crates/fluvio-auth/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ use super::AuthError;
pub enum TypeAction {
Create,
Read,
Update,
}

pub enum InstanceAction {
Delete,
}

#[async_trait]
pub trait AuthContext: Debug {
pub trait AuthContext: Debug + Send + Sync + 'static {
/// check if any allow type specific action can be allowed
async fn allow_type_action(
&self,
Expand Down
154 changes: 154 additions & 0 deletions crates/fluvio-auth/src/root.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use std::fmt::Debug;

use async_trait::async_trait;

use crate::{AuthContext, Authorization, TypeAction, InstanceAction, AuthError};
use fluvio_socket::FluvioSocket;
use fluvio_controlplane_metadata::extended::ObjectType;

/// Authorization that allows anything
/// Used for personal development
#[derive(Debug, Clone, Default)]
pub struct RootAuthorization {}

#[async_trait]
impl Authorization for RootAuthorization {
type Context = RootAuthContext;

async fn create_auth_context(
&self,
_socket: &mut FluvioSocket,
) -> Result<Self::Context, AuthError> {
Ok(RootAuthContext {})
}
}

impl RootAuthorization {
pub fn new() -> Self {
Self {}
}
}

#[derive(Debug)]
pub struct RootAuthContext {}

#[async_trait]
impl AuthContext for RootAuthContext {
async fn allow_type_action(
&self,
_ty: ObjectType,
_action: TypeAction,
) -> Result<bool, AuthError> {
Ok(true)
}

/// check if specific instance of spec can be deleted
async fn allow_instance_action(
&self,
_ty: ObjectType,
_action: InstanceAction,
_key: &str,
) -> Result<bool, AuthError> {
Ok(true)
}
}

/// Authorization that allows only read only ops
#[derive(Debug, Clone, Default)]
pub struct ReadOnlyAuthorization {}

#[async_trait]
impl Authorization for ReadOnlyAuthorization {
type Context = ReadOnlyAuthContext;

async fn create_auth_context(
&self,
_socket: &mut FluvioSocket,
) -> Result<Self::Context, AuthError> {
Ok(ReadOnlyAuthContext {})
}
}

impl ReadOnlyAuthorization {
pub fn new() -> Self {
Self {}
}
}

#[derive(Debug)]
pub struct ReadOnlyAuthContext {}

#[async_trait]
impl AuthContext for ReadOnlyAuthContext {
async fn allow_type_action(
&self,
ty: ObjectType,
action: TypeAction,
) -> Result<bool, AuthError> {
Ok(matches!(action, TypeAction::Read) || matches!(ty, ObjectType::CustomSpu))
}

/// check if specific instance of spec can be deleted
async fn allow_instance_action(
&self,
_ty: ObjectType,
_action: InstanceAction,
_key: &str,
) -> Result<bool, AuthError> {
Ok(true)
}
}

#[cfg(test)]
mod test {
use crate::AuthContext;

use super::{ObjectType, ReadOnlyAuthContext, RootAuthContext, TypeAction};

/// test read only context
/// read only context allows read on everything
/// and create on spu
#[fluvio_future::test]
async fn test_read_only_context() {
let auth_context = ReadOnlyAuthContext {};
assert!(auth_context
.allow_type_action(ObjectType::Spu, TypeAction::Read)
.await
.unwrap());
assert!(!auth_context
.allow_type_action(ObjectType::Spu, TypeAction::Create)
.await
.unwrap());
assert!(auth_context
.allow_type_action(ObjectType::Topic, TypeAction::Read)
.await
.unwrap());
assert!(!auth_context
.allow_type_action(ObjectType::Topic, TypeAction::Create)
.await
.unwrap());
}

/// test root context
/// root context allows everything
#[fluvio_future::test]
async fn test_root_context() {
let auth_context = RootAuthContext {};
assert!(auth_context
.allow_type_action(ObjectType::Spu, TypeAction::Read)
.await
.unwrap());
assert!(auth_context
.allow_type_action(ObjectType::Spu, TypeAction::Create)
.await
.unwrap());
assert!(auth_context
.allow_type_action(ObjectType::Topic, TypeAction::Read)
.await
.unwrap());
assert!(auth_context
.allow_type_action(ObjectType::Topic, TypeAction::Create)
.await
.unwrap());
}
}
28 changes: 15 additions & 13 deletions crates/fluvio-auth/src/x509/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ impl ScopeBindings {

#[derive(Debug)]
pub struct X509Authenticator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial scope of the authenticator was for only for SC. Should not overload these with two different purpose. Probably simpler rename original X509Authenticator as X509AuthenticatorSC and move to SC. And use this crate only for common auth utilities and libraries.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on other conversation, this should need to be changed since remote can be added to scope

scope_bindings: ScopeBindings,
scope_bindings: Option<ScopeBindings>,
}

impl X509Authenticator {
pub fn new(scope_binding_file_path: &Path) -> Self {
Self {
scope_bindings: ScopeBindings::load(scope_binding_file_path)
.expect("unable to create ScopeBindings"),
}
pub fn new(scope_binding_file_path: Option<&Path>) -> Self {
let scope_bindings = scope_binding_file_path.map(|scope_binding_file_path| {
ScopeBindings::load(scope_binding_file_path).expect("unable to create ScopeBindings")
});
Self { scope_bindings }
}

async fn send_authorization_request(
Expand Down Expand Up @@ -79,13 +79,9 @@ impl X509Authenticator {
fn principal_from_tls_stream(tls_stream: &DefaultServerTlsStream) -> Result<String, IoError> {
trace!("tls_stream {:?}", tls_stream);

let peer_certificate = tls_stream.peer_certificate();

trace!("peer_certificate {:?}", peer_certificate);

let client_certificate = tls_stream.peer_certificate().ok_or(IoErrorKind::NotFound)?;

trace!("client_certificate {:?}", tls_stream);
trace!("client_certificate {:?}", client_certificate);

let principal = Self::principal_from_raw_certificate(
&client_certificate
Expand All @@ -96,7 +92,7 @@ impl X509Authenticator {
Ok(principal)
}

fn principal_from_raw_certificate(certificate_bytes: &[u8]) -> Result<String, IoError> {
pub fn principal_from_raw_certificate(certificate_bytes: &[u8]) -> Result<String, IoError> {
parse_x509_certificate(certificate_bytes)
.map_err(|err| IoError::new(IoErrorKind::InvalidData, err))
.and_then(|(_, parsed_cert)| Self::common_name_from_parsed_certificate(parsed_cert))
Expand Down Expand Up @@ -131,7 +127,13 @@ impl Authenticator for X509Authenticator {
target_tcp_stream: &TcpStream,
) -> Result<bool, IoError> {
let principal = Self::principal_from_tls_stream(incoming_tls_stream)?;
let scopes = self.scope_bindings.get_scopes(&principal);

let scopes = if let Some(scope_bindings) = &self.scope_bindings {
scope_bindings.get_scopes(&principal)
} else {
Vec::new()
};

let authorization_request = AuthRequest::new(principal, scopes);
let success =
Self::send_authorization_request(target_tcp_stream, authorization_request).await?;
Expand Down
5 changes: 3 additions & 2 deletions crates/fluvio-auth/src/x509/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ impl X509Identity {
principal: req_msg.request.principal,
},
},
Err(_e) => {
Err(err) => {
tracing::error!(%err, "error decoding auth request");
return Err(std::io::Error::new(
std::io::ErrorKind::Interrupted,
"connection closed",
))
));
}
}
} else {
Expand Down
1 change: 1 addition & 0 deletions crates/fluvio-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ k8-types = { workspace = true , features = ["core"] }
fluvio-cluster = { path = "../fluvio-cluster", default-features = false, features = ["cli"], optional = true }

fluvio = { workspace = true }
fluvio-auth = { workspace = true }
fluvio-socket = { workspace = true }
fluvio-command = { workspace = true }
fluvio-package-index = { workspace = true }
Expand Down