Skip to content
Open
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ All notable changes to this project will be documented in this file.

- Support objectOverrides using `.spec.objectOverrides`.
See [objectOverrides concepts page](https://docs.stackable.tech/home/nightly/concepts/overrides/#object-overrides) for details ([#831]).
- Add `enabledColumnMasking` field to `opa` configuration in `authorization` ([#827]).
- Support batched column masks in Rego rules ([#827]).

### Changed

- Pin k8s-openapi to `0.26.0` ([#831]).
- BREAKING: The field `opa` in `authorization` is now a mandatory enum variant instead of being optional ([#827]).
- BREAKING: The operator no longer sets `opa.policy.column-masking-uri` in `access-control.properties` but
`opa.policy.batch-column-masking-uri` instead, allowing Trino to fetch multiple column masks in a single request ([#827]).

[#831]: https://github.com/stackabletech/trino-operator/pull/831
[#827]: https://github.com/stackabletech/trino-operator/pull/827

## [25.11.0] - 2025-11-07

Expand Down
8 changes: 7 additions & 1 deletion deploy/helm/trino-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,26 @@ spec:
Authorization options for Trino.
Learn more in the [Trino authorization usage guide](https://docs.stackable.tech/home/nightly/trino/usage-guide/security#authorization).
nullable: true
oneOf:
- required:
- opa
properties:
opa:
description: |-
Configure the OPA stacklet [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery)
and the name of the Rego package containing your authorization rules.
Consult the [OPA authorization documentation](https://docs.stackable.tech/home/nightly/concepts/opa)
to learn how to deploy Rego authorization rules with OPA.
nullable: true
properties:
configMapName:
description: |-
The [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery)
for the OPA stacklet that should be used for authorization requests.
type: string
enableColumnMasking:
default: true
description: Set the OPA batched column masking uri for Trino queries or not. Defaults to true.
type: boolean
package:
description: The name of the Rego package containing the Rego rules for the product.
nullable: true
Expand Down
39 changes: 39 additions & 0 deletions docs/modules/trino/pages/reference/security.adoc
Copy link
Member

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.

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
Copy link
Member

Choose a reason for hiding this comment

The 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`:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
In `access-control.properties` the following value is set, when `enableColumnMasking` is set to `true`:
In the `access-control.properties` file, the following value is set when `enableColumnMasking` is set to `true`:

(or just fix the position of the comma)


[.wrap,source]
Copy link
Member

Choose a reason for hiding this comment

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

Is .wrap valid AsciiDoc? Where is it documented?

----
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.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
If there is no such rule defined, Trino queries, using that column masking endpoint, will fail.
If no such rule is defined, Trino queries that utilize the column masking endpoint will fail.

(or just fix the commas)

5 changes: 5 additions & 0 deletions docs/modules/trino/pages/usage-guide/overrides.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Copy link
Member

Choose a reason for hiding this comment

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

Does the file password-authenticator.properties exist and can it really be overridden?

* `security.properties`
* `exchange-manager.properties`
* `spooling-manager.properties`

=== Configuration overrides in the TrinoCatalog

Expand Down
1 change: 1 addition & 0 deletions docs/modules/trino/partials/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
** xref:trino:reference/crds.adoc[]
*** {crd-docs}/trino.stackable.tech/trinocluster/v1alpha1/[TrinoCluster {external-link-icon}^]
*** {crd-docs}/trino.stackable.tech/trinocatalog/v1alpha1/[TrinoCatalog {external-link-icon}^]
** xref:trino:reference/security.adoc[]
** xref:trino:reference/commandline-parameters.adoc[]
** xref:trino:reference/environment-variables.adoc[]
57 changes: 34 additions & 23 deletions rust/operator-binary/src/authorization/opa.rs
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";

Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

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

column-masking-uri is not set, so it will not be used.

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
Expand All @@ -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,
Expand All @@ -57,6 +57,7 @@ impl TrinoOpaConfig {
)
.await?;
let row_filters_connection_string = opa_config
.opa
.full_document_url_from_config_map(
client,
trino,
Expand All @@ -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() {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let batched_column_masking_connection_string = if trino.column_masking_enabled() {
let batched_column_masking_connection_string = if opa_config.enable_column_masking {

... and delete the function colum_masking_enabled().

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
Expand All @@ -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,
})
Expand All @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions rust/operator-binary/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"#));
Copy link
Member

Choose a reason for hiding this comment

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

This test case tests configOverrides. Perhaps you could override the batch-column-masking-uri?

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"#));
}
Expand Down
39 changes: 34 additions & 5 deletions rust/operator-binary/src/crd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure if this is better:

Suggested change
/// Set the OPA batched column masking uri for Trino queries or not. Defaults to true.
/// Whether to set the OPA batched column masking URI for Trino queries; defaults to true

#[serde(default = "TrinoAuthorizationOpaConfig::enabled_column_masking_default")]
pub enable_column_masking: bool,
}

#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions rust/operator-binary/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,10 @@ fn references_config_map(
};

match &trino.spec.cluster_config.authorization {
Some(trino_authorization) => match &trino_authorization.opa {
Some(opa_config) => opa_config.config_map_name == config_map.name_any(),
None => false,
Some(trino_authorization) => match &trino_authorization {
v1alpha1::TrinoAuthorization::Opa { config } => {
config.opa.config_map_name == config_map.name_any()
}
},
None => false,
}
Expand Down
Loading