-
-
Notifications
You must be signed in to change notification settings - Fork 8
feat: Batch OPA column mask calls #827
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
base: main
Are you sure you want to change the base?
Changes from all commits
9b5eba6
76aeaf5
e2e563d
a607d95
de1b3af
b1addca
df98f6c
36319f9
1320aef
2d4c4ad
06b2878
34767d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||
| = Security | ||||||
|
|
||||||
| == Authorization | ||||||
|
|
||||||
| === OPA | ||||||
|
|
||||||
| ==== Column masking | ||||||
|
|
||||||
| ===== CRD configuration | ||||||
|
|
||||||
| [source,yaml] | ||||||
| ---- | ||||||
| apiVersion: trino.stackable.tech/v1alpha1 | ||||||
| kind: TrinoCluster # <1> | ||||||
| spec: | ||||||
| clusterConfig: | ||||||
| authorization: | ||||||
| opa: | ||||||
| enableColumnMasking: true # default | ||||||
| ---- | ||||||
|
|
||||||
| <1> Redundant fields for column masking reference configuration are omitted | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which fields do you mean? All other TrinoCluster fields? If yes, then this comment is not necessary because almost all code snippets in this documentation are incomplete or we would have to add such a comment to all other snippets for consistency. |
||||||
|
|
||||||
| ===== Result | ||||||
|
|
||||||
| In `access-control.properties` the following value is set, when `enableColumnMasking` is set to `true`: | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
(or just fix the position of the comma) |
||||||
|
|
||||||
| [.wrap,source] | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is |
||||||
| ---- | ||||||
| opa.policy.batch-column-masking-uri=<opa-url>/v1/data/<package>/batchColumnMasks # <1> <2> | ||||||
| ---- | ||||||
|
|
||||||
| <1> `<opa-url>` is read from the OPA discovery ConfigMap | ||||||
| <2> `<package>` is read from `spec.clusterConfig.authorization.opa.package` if set, otherwise defaults to the TrinoCluster name | ||||||
|
|
||||||
| ===== Considerations | ||||||
|
|
||||||
| The default setting for `enableColumnMasking` assumes a `batchColumnMasks` rule is defined in the Rego rules for the TrinoCluster. | ||||||
| If there is no such rule defined, Trino queries, using that column masking endpoint, will fail. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
(or just fix the commas) |
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,8 +27,13 @@ Confiuration overrides are applied like so: | |
|
|
||
| Configuration overrides can be applied to: | ||
|
|
||
| * `access-control.properties` | ||
| * `config.properties` | ||
| * `node.properties` | ||
| * `password-authenticator.properties` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the file |
||
| * `security.properties` | ||
| * `exchange-manager.properties` | ||
| * `spooling-manager.properties` | ||
|
|
||
| === Configuration overrides in the TrinoCatalog | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,13 +1,11 @@ | ||||||
| use std::collections::BTreeMap; | ||||||
|
|
||||||
| use stackable_operator::{ | ||||||
| client::Client, | ||||||
| commons::opa::{OpaApiVersion, OpaConfig}, | ||||||
| k8s_openapi::api::core::v1::ConfigMap, | ||||||
| client::Client, commons::opa::OpaApiVersion, k8s_openapi::api::core::v1::ConfigMap, | ||||||
| kube::ResourceExt, | ||||||
| }; | ||||||
|
|
||||||
| use crate::crd::v1alpha1::TrinoCluster; | ||||||
| use crate::crd::v1alpha1; | ||||||
|
|
||||||
| pub const OPA_TLS_VOLUME_NAME: &str = "opa-tls"; | ||||||
|
|
||||||
|
|
@@ -23,10 +21,10 @@ pub struct TrinoOpaConfig { | |||||
| /// `http://localhost:8081/v1/data/trino/rowFilters` - if not set, | ||||||
| /// no row filtering will be applied | ||||||
| pub(crate) row_filters_connection_string: Option<String>, | ||||||
| /// URI for fetching column masks, e.g. | ||||||
| /// `http://localhost:8081/v1/data/trino/columnMask` - if not set, | ||||||
| /// no masking will be applied | ||||||
| pub(crate) column_masking_connection_string: Option<String>, | ||||||
| /// URI for fetching columns masks in batches, e.g. | ||||||
| /// `http://localhost:8081/v1/data/trino/batchColumnMasks` - if not set, | ||||||
| /// column-masking-uri will be used for getting column masks in parallel | ||||||
|
Comment on lines
+24
to
+26
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| pub(crate) batched_column_masking_connection_string: Option<String>, | ||||||
| /// Whether to allow permission management (GRANT, DENY, ...) and | ||||||
| /// role management operations - OPA will not be queried for any | ||||||
| /// such operations, they will be bulk allowed or denied depending | ||||||
|
|
@@ -41,13 +39,15 @@ pub struct TrinoOpaConfig { | |||||
| impl TrinoOpaConfig { | ||||||
| pub async fn from_opa_config( | ||||||
| client: &Client, | ||||||
| trino: &TrinoCluster, | ||||||
| opa_config: &OpaConfig, | ||||||
| trino: &v1alpha1::TrinoCluster, | ||||||
| opa_config: &v1alpha1::TrinoAuthorizationOpaConfig, | ||||||
| ) -> Result<Self, stackable_operator::commons::opa::Error> { | ||||||
| let non_batched_connection_string = opa_config | ||||||
| .opa | ||||||
| .full_document_url_from_config_map(client, trino, Some("allow"), OpaApiVersion::V1) | ||||||
| .await?; | ||||||
| let batched_connection_string = opa_config | ||||||
| .opa | ||||||
| .full_document_url_from_config_map( | ||||||
| client, | ||||||
| trino, | ||||||
|
|
@@ -57,6 +57,7 @@ impl TrinoOpaConfig { | |||||
| ) | ||||||
| .await?; | ||||||
| let row_filters_connection_string = opa_config | ||||||
| .opa | ||||||
| .full_document_url_from_config_map( | ||||||
| client, | ||||||
| trino, | ||||||
|
|
@@ -65,19 +66,27 @@ impl TrinoOpaConfig { | |||||
| OpaApiVersion::V1, | ||||||
| ) | ||||||
| .await?; | ||||||
| let column_masking_connection_string = opa_config | ||||||
| .full_document_url_from_config_map( | ||||||
| client, | ||||||
| trino, | ||||||
| // Sticking to https://github.com/trinodb/trino/blob/455/plugin/trino-opa/src/test/java/io/trino/plugin/opa/TestOpaAccessControlDataFilteringSystem.java#L47 | ||||||
| Some("columnMask"), | ||||||
| OpaApiVersion::V1, | ||||||
|
|
||||||
| let batched_column_masking_connection_string = if trino.column_masking_enabled() { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
... and delete the function |
||||||
| Some( | ||||||
| opa_config | ||||||
| .opa | ||||||
| .full_document_url_from_config_map( | ||||||
| client, | ||||||
| trino, | ||||||
| // Sticking to https://github.com/trinodb/trino/blob/455/plugin/trino-opa/src/test/java/io/trino/plugin/opa/TestOpaAccessControlDataFilteringSystem.java#L48 | ||||||
| Some("batchColumnMasks"), | ||||||
| OpaApiVersion::V1, | ||||||
| ) | ||||||
| .await?, | ||||||
| ) | ||||||
| .await?; | ||||||
| } else { | ||||||
| None | ||||||
| }; | ||||||
|
|
||||||
| let tls_secret_class = client | ||||||
| .get::<ConfigMap>( | ||||||
| &opa_config.config_map_name, | ||||||
| &opa_config.opa.config_map_name, | ||||||
| trino.namespace().as_deref().unwrap_or("default"), | ||||||
| ) | ||||||
| .await | ||||||
|
|
@@ -89,7 +98,7 @@ impl TrinoOpaConfig { | |||||
| non_batched_connection_string, | ||||||
| batched_connection_string, | ||||||
| row_filters_connection_string: Some(row_filters_connection_string), | ||||||
| column_masking_connection_string: Some(column_masking_connection_string), | ||||||
| batched_column_masking_connection_string, | ||||||
| allow_permission_management_operations: true, | ||||||
| tls_secret_class, | ||||||
| }) | ||||||
|
|
@@ -113,10 +122,12 @@ impl TrinoOpaConfig { | |||||
| Some(row_filters_connection_string.clone()), | ||||||
| ); | ||||||
| } | ||||||
| if let Some(column_masking_connection_string) = &self.column_masking_connection_string { | ||||||
| if let Some(batched_column_masking_connection_string) = | ||||||
| &self.batched_column_masking_connection_string | ||||||
| { | ||||||
| config.insert( | ||||||
| "opa.policy.column-masking-uri".to_string(), | ||||||
| Some(column_masking_connection_string.clone()), | ||||||
| "opa.policy.batch-column-masking-uri".to_string(), | ||||||
| Some(batched_column_masking_connection_string.clone()), | ||||||
| ); | ||||||
| } | ||||||
| if self.allow_permission_management_operations { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2065,8 +2065,8 @@ mod tests { | |
| "http://simple-opa.default.svc.cluster.local:8081/v1/data/my-product/rowFilters" | ||
| .to_string(), | ||
| ), | ||
| column_masking_connection_string: Some( | ||
| "http://simple-opa.default.svc.cluster.local:8081/v1/data/my-product/columnMask" | ||
| batched_column_masking_connection_string: Some( | ||
| "http://simple-opa.default.svc.cluster.local:8081/v1/data/my-product/batchColumnMasks" | ||
| .to_string(), | ||
| ), | ||
| allow_permission_management_operations: true, | ||
|
|
@@ -2168,7 +2168,7 @@ mod tests { | |
| assert!(access_control_config.contains("foo.bar=true")); | ||
| assert!(access_control_config.contains("opa.allow-permission-management-operations=false")); | ||
| assert!(access_control_config.contains(r#"opa.policy.batched-uri=http\://simple-opa.default.svc.cluster.local\:8081/v1/data/my-product/batch-new"#)); | ||
| assert!(access_control_config.contains(r#"opa.policy.column-masking-uri=http\://simple-opa.default.svc.cluster.local\:8081/v1/data/my-product/columnMask"#)); | ||
| assert!(access_control_config.contains(r#"opa.policy.batch-column-masking-uri=http\://simple-opa.default.svc.cluster.local\:8081/v1/data/my-product/batchColumnMasks"#)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test case tests configOverrides. Perhaps you could override the |
||
| assert!(access_control_config.contains(r#"opa.policy.row-filters-uri=http\://simple-opa.default.svc.cluster.local\:8081/v1/data/my-product/rowFilters"#)); | ||
| assert!(access_control_config.contains(r#"opa.policy.uri=http\://simple-opa.default.svc.cluster.local\:8081/v1/data/my-product/allow"#)); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -317,10 +317,24 @@ pub mod versioned { | |||||
|
|
||||||
| #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] | ||||||
| #[serde(rename_all = "camelCase")] | ||||||
| pub struct TrinoAuthorization { | ||||||
| pub enum TrinoAuthorization { | ||||||
| Opa { | ||||||
| // no doc - it's in the struct. | ||||||
| #[serde(default, flatten)] | ||||||
| config: TrinoAuthorizationOpaConfig, | ||||||
| }, | ||||||
| } | ||||||
|
|
||||||
| #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] | ||||||
| #[serde(rename_all = "camelCase")] | ||||||
| pub struct TrinoAuthorizationOpaConfig { | ||||||
| // no doc - it's in the struct. | ||||||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||||||
| pub opa: Option<OpaConfig>, | ||||||
| #[serde(flatten)] | ||||||
| pub opa: OpaConfig, | ||||||
|
|
||||||
| /// Set the OPA batched column masking uri for Trino queries or not. Defaults to true. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if this is better:
Suggested change
|
||||||
| #[serde(default = "TrinoAuthorizationOpaConfig::enabled_column_masking_default")] | ||||||
| pub enable_column_masking: bool, | ||||||
| } | ||||||
|
|
||||||
| #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] | ||||||
|
|
@@ -370,6 +384,12 @@ pub mod versioned { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| impl v1alpha1::TrinoAuthorizationOpaConfig { | ||||||
| pub fn enabled_column_masking_default() -> bool { | ||||||
| true | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| impl Default for v1alpha1::TrinoCoordinatorRoleConfig { | ||||||
| fn default() -> Self { | ||||||
| v1alpha1::TrinoCoordinatorRoleConfig { | ||||||
|
|
@@ -882,12 +902,21 @@ impl v1alpha1::TrinoCluster { | |||||
| !spec.cluster_config.authentication.is_empty() | ||||||
| } | ||||||
|
|
||||||
| pub fn get_opa_config(&self) -> Option<&OpaConfig> { | ||||||
| pub fn column_masking_enabled(&self) -> bool { | ||||||
| match self.get_opa_config() { | ||||||
| Some(a) => a.enable_column_masking, | ||||||
| None => v1alpha1::TrinoAuthorizationOpaConfig::enabled_column_masking_default(), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| pub fn get_opa_config(&self) -> Option<&v1alpha1::TrinoAuthorizationOpaConfig> { | ||||||
| self.spec | ||||||
| .cluster_config | ||||||
| .authorization | ||||||
| .as_ref() | ||||||
| .and_then(|a| a.opa.as_ref()) | ||||||
| .map(|a| match a { | ||||||
| v1alpha1::TrinoAuthorization::Opa { config } => config, | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| /// Return user provided server TLS settings | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why haven't you added this information to
usage-guide/security.adoc? I would not have expected a second security page.