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

Typing #41

Closed
wants to merge 7 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::glob::GlobPattern;
use crate::policy_index::PolicyIndex;
use crate::typing::TypedProperty;
use log::warn;
use serde::Deserialize;

Expand Down Expand Up @@ -54,36 +55,36 @@ pub enum WhenConditionOperator {
Matches,
}

impl WhenConditionOperator {
pub fn eval(&self, value: &str, attr_value: &str) -> bool {
match *self {
WhenConditionOperator::Equal => value.eq(attr_value),
WhenConditionOperator::NotEqual => !value.eq(attr_value),
WhenConditionOperator::StartsWith => attr_value.starts_with(value),
WhenConditionOperator::EndsWith => attr_value.ends_with(value),
WhenConditionOperator::Matches => match GlobPattern::try_from(value) {
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PatternExpression {
pub selector: String,
pub operator: WhenConditionOperator,
pub value: String,
}

impl PatternExpression {
pub fn eval(&self, value: &TypedProperty) -> bool {
match self.operator {
WhenConditionOperator::Equal => value.eq(&self.value),
WhenConditionOperator::NotEqual => value.ne(&self.value),
WhenConditionOperator::StartsWith => value.as_string().starts_with(&self.value),
WhenConditionOperator::EndsWith => value.as_string().ends_with(&self.value),
WhenConditionOperator::Matches => match GlobPattern::try_from(self.value.as_str()) {
// TODO(eastizle): regexp being compiled and validated at request time.
// Validations and possibly regexp compilation should happen at boot time instead.
// In addition, if the regexp is not valid, the only consequence is that
// the current condition would not apply
Ok(glob_pattern) => glob_pattern.is_match(attr_value),
Ok(glob_pattern) => glob_pattern.is_match(&value.as_string()),
Err(e) => {
warn!("failed to parse regexp: {value}, error: {e:?}");
warn!("failed to parse regexp: {}, error: {e:?}", self.value);
false
}
},
}
}
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PatternExpression {
pub selector: String,
pub operator: WhenConditionOperator,
pub value: String,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Condition {
Expand Down
22 changes: 5 additions & 17 deletions src/envoy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,12 @@ mod timestamp;
mod token_bucket;
mod value;

pub mod properties;
Copy link
Contributor

Choose a reason for hiding this comment

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

the code from src/envoy has been copied&pasted from somewhere else. What if we "upgrade" this dependency?


pub use {
address::{
Address, Address_oneof_address, SocketAddress, SocketAddress_Protocol,
SocketAddress_oneof_port_specifier as SocketAddress_port,
},
attribute_context::{
AttributeContext, AttributeContext_HttpRequest, AttributeContext_Peer,
AttributeContext_Request,
},
base::HeaderValue,
base::Metadata,
external_auth::{CheckRequest, CheckResponse},
ratelimit::{RateLimitDescriptor, RateLimitDescriptor_Entry},
rls::{RateLimitRequest, RateLimitResponse, RateLimitResponse_Code},
route_components::{
HeaderMatcher, HeaderMatcher_oneof_header_match_specifier as HeaderMatcher_specifier,
RateLimit_Action, RateLimit_Action_oneof_action_specifier as RLA_action_specifier,
},
string::StringMatcher_oneof_match_pattern as StringMatcher_pattern,
timestamp::Timestamp,
};

#[cfg(test)]
pub use base::HeaderValue;
201 changes: 201 additions & 0 deletions src/envoy/properties.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
use crate::typing::TypedProperty;
use std::collections::BTreeMap;

type MapperFn = dyn Fn(Vec<u8>) -> TypedProperty;

pub struct EnvoyTypeMapper {
known_properties: BTreeMap<String, Box<MapperFn>>,
}

impl EnvoyTypeMapper {
pub fn new() -> Self {
let mut properties: BTreeMap<String, Box<MapperFn>> = BTreeMap::new();
properties.insert(
"request.time".to_string(),
Box::new(TypedProperty::timestamp),
);

properties.insert("request.id".to_string(), Box::new(TypedProperty::string));
properties.insert(
"request.protocol".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"request.scheme".to_string(),
Box::new(TypedProperty::string),
);
properties.insert("request.host".to_string(), Box::new(TypedProperty::string));
properties.insert(
"request.method".to_string(),
Box::new(TypedProperty::string),
);
properties.insert("request.path".to_string(), Box::new(TypedProperty::string));
properties.insert(
"request.url_path".to_string(),
Box::new(TypedProperty::string),
);
properties.insert("request.query".to_string(), Box::new(TypedProperty::string));
properties.insert(
"request.referer".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"request.useragent".to_string(),
Box::new(TypedProperty::string),
);
properties.insert("request.body".to_string(), Box::new(TypedProperty::string));
properties.insert(
"source.address".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"source.service".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"source.principal".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"source.certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"destination.address".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"destination.service".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"destination.principal".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"destination.certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.requested_server_name".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.tls_session.sni".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.tls_version".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.subject_local_certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.subject_peer_certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.dns_san_local_certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.dns_san_peer_certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.uri_san_local_certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.uri_san_peer_certificate".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"connection.sha256_peer_certificate_digest".to_string(),
Box::new(TypedProperty::string),
);
properties.insert(
"ratelimit.domain".to_string(),
Box::new(TypedProperty::string),
);

properties.insert("request.size".to_string(), Box::new(TypedProperty::integer));
properties.insert("source.port".to_string(), Box::new(TypedProperty::integer));
properties.insert(
"destination.port".to_string(),
Box::new(TypedProperty::integer),
);
properties.insert(
"connection.id".to_string(),
Box::new(TypedProperty::integer),
);
properties.insert(
"ratelimit.hits_addend".to_string(),
Box::new(TypedProperty::integer),
);

properties.insert("metadata".to_string(), Box::new(TypedProperty::metadata));

properties.insert(
"request.headers".to_string(),
Box::new(TypedProperty::string_map),
);
properties.insert(
"request.context_extensions".to_string(),
Box::new(TypedProperty::string_map),
);
properties.insert(
"source.labels".to_string(),
Box::new(TypedProperty::string_map),
);
properties.insert(
"destination.labels".to_string(),
Box::new(TypedProperty::string_map),
);
properties.insert(
"filter_state".to_string(),
Box::new(TypedProperty::string_map),
);

properties.insert(
"auth.metadata".to_string(),
Box::new(TypedProperty::complex_map),
);
properties.insert(
"auth.authorization".to_string(),
Box::new(TypedProperty::complex_map),
);
properties.insert(
"auth.response".to_string(),
Box::new(TypedProperty::complex_map),
);
properties.insert(
"auth.callbacks".to_string(),
Box::new(TypedProperty::complex_map),
);
Comment on lines +162 to +177

Choose a reason for hiding this comment

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

auth.* are not properties that can be requested directly to the host through the ABI, right? For that reason they were unchecked for "RL" in RFC 0002 for now. Seeing them here makes me think we may want to progress to the point where these attributes are abstractions that translate to metadata.filter_metadata.envoy\.filters\.http\.ext_authz.* perhaps?

Do we need a special TypedProperty function to handle them maybe?

Another option would be the "translation" to happen in the policy controller, and therefore the wasm shim won't ever even need to know about them.

Copy link
Member Author

@alexsnaps alexsnaps Jan 11, 2024

Choose a reason for hiding this comment

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

This actually merely defines the typing... but you are right that I conflated the typing with the host. Filter::get_typed_property is actually what decides where the property is to be sourced.

Maybe I should treat all auth related ones differently... tho they'd still use some typed mapper function just as the envoy properties do... 🤔


properties.insert(
"connection.mtls".to_string(),
Box::new(TypedProperty::boolean),
);

properties.insert(
"request.raw_body".to_string(),
Box::new(TypedProperty::bytes),
);
properties.insert("auth.identity".to_string(), Box::new(TypedProperty::bytes));

Choose a reason for hiding this comment

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


Self {
known_properties: properties,
}
}

pub fn typed(&self, path: &str, raw: Vec<u8>) -> Result<TypedProperty, Vec<u8>> {
match self.known_properties.get(path) {
None => Err(raw),
Some(mapper) => Ok(mapper(raw)),
}
}
}
3 changes: 3 additions & 0 deletions src/filter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::envoy::properties::EnvoyTypeMapper;

mod http_context;
mod root_context;

Expand Down Expand Up @@ -35,6 +37,7 @@ extern "C" fn start() {
Box::new(FilterRoot {
context_id,
config: Rc::new(FilterConfig::new()),
property_mapper: Rc::new(EnvoyTypeMapper::new()),
})
});
}
Loading
Loading