Skip to content

Commit ca2c9d0

Browse files
committed
compiling - without tls
1 parent 4d3dc09 commit ca2c9d0

6 files changed

Lines changed: 273 additions & 8 deletions

File tree

deploy/helm/hive-operator/crds/crds.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ spec:
4949
required:
5050
- kerberos
5151
type: object
52+
authorization:
53+
description: |-
54+
Authorization options for Hive.
55+
Learn more in the [Hive authorization usage guide](https://docs.stackable.tech/home/nightly/hive/usage-guide/security#authorization).
56+
nullable: true
57+
properties:
58+
opa:
59+
description: |-
60+
Configure the OPA stacklet [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery)
61+
and the name of the Rego package containing your authorization rules.
62+
Consult the [OPA authorization documentation](https://docs.stackable.tech/home/nightly/concepts/opa)
63+
to learn how to deploy Rego authorization rules with OPA.
64+
nullable: true
65+
properties:
66+
configMapName:
67+
description: |-
68+
The [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery)
69+
for the OPA stacklet that should be used for authorization requests.
70+
type: string
71+
package:
72+
description: The name of the Rego package containing the Rego rules for the product.
73+
nullable: true
74+
type: string
75+
required:
76+
- configMapName
77+
type: object
78+
type: object
5279
database:
5380
description: Database connection specification for the metadata database.
5481
properties:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod jvm;
2+
pub mod opa;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use std::collections::BTreeMap;
2+
3+
use stackable_operator::{
4+
client::Client,
5+
commons::opa::{OpaApiVersion, OpaConfig},
6+
k8s_openapi::api::core::v1::ConfigMap,
7+
kube::ResourceExt,
8+
};
9+
10+
use crate::crd::v1alpha1::HiveCluster;
11+
12+
const HIVE_METASTORE_PRE_EVENT_LISTENERS: &str = "hive.metastore.pre.event.listeners";
13+
const HIVE_SECURITY_METASTORE_AUTHORIZATION_MANAGER: &str =
14+
"hive.security.metastore.authorization.manager";
15+
16+
const OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V3: &str =
17+
"com.bosch.bdps.hms3.OpaAuthorizationPreEventListener";
18+
const OPA_BASED_AUTHORIZATION_PROVIDER_V3: &str =
19+
"com.bosch.bdps.hms3.OpaBasedAuthorizationProvider";
20+
const OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V4: &str =
21+
"com.bosch.bdps.hms4.OpaAuthorizationPreEventListener";
22+
const OPA_BASED_AUTHORIZATION_PROVIDER_V4: &str =
23+
"com.bosch.bdps.hms4.OpaBasedAuthorizationProvider";
24+
25+
const OPA_AUTHORIZATION_BASE_ENDPOINT: &str = "com.bosch.bdps.opa.authorization.base.endpoint";
26+
const OPA_AUTHORIZATION_POLICY_URL_DATA_BASE: &str =
27+
"com.bosch.bdps.opa.authorization.policy.url.database";
28+
const OPA_AUTHORIZATION_POLICY_URL_TABLE: &str =
29+
"com.bosch.bdps.opa.authorization.policy.url.table";
30+
const OPA_AUTHORIZATION_POLICY_URL_COLUMN: &str =
31+
"com.bosch.bdps.opa.authorization.policy.url.column";
32+
const OPA_AUTHORIZATION_POLICY_URL_PARTITION: &str =
33+
"com.bosch.bdps.opa.authorization.policy.url.partition";
34+
const OPA_AUTHORIZATION_POLICY_URL_USER: &str = "com.bosch.bdps.opa.authorization.policy.url.user";
35+
36+
pub const OPA_TLS_VOLUME_NAME: &str = "opa-tls";
37+
38+
pub struct HiveOpaConfig {
39+
/// Endpoint for OPA, e.g.
40+
/// `http://localhost:8081/v1/data/hms/allow`
41+
pub(crate) base_endpoint: String,
42+
/// Policy to check database authorization, e.g.
43+
/// `http://localhost:8081/v1/data/hms/database_allow`
44+
pub(crate) policy_url_database: String,
45+
/// Policy to check table authorization, e.g.
46+
/// `http://localhost:8081/v1/data/hms/table_allow`
47+
pub(crate) policy_url_table: String,
48+
/// Policy to check column authorization, e.g.
49+
/// `http://localhost:8081/v1/data/hms/column_allow`
50+
pub(crate) policy_url_column: String,
51+
/// Policy to check partition authorization, e.g.
52+
/// `http://localhost:8081/v1/data/hms/partition_allow`
53+
pub(crate) policy_url_partition: String,
54+
/// Policy to check user authorization, e.g.
55+
/// `http://localhost:8081/v1/data/hms/user_allow`
56+
pub(crate) policy_url_user: String,
57+
/// Optional TLS secret class for OPA communication.
58+
/// If set, the CA certificate from this secret class will be added
59+
/// to hive's truststore to make it trust OPA's TLS certificate.
60+
pub(crate) tls_secret_class: Option<String>,
61+
}
62+
63+
impl HiveOpaConfig {
64+
pub async fn from_opa_config(
65+
client: &Client,
66+
hive: &HiveCluster,
67+
opa_config: &OpaConfig,
68+
) -> Result<Self, stackable_operator::commons::opa::Error> {
69+
// See: https://github.com/boschglobal/hive-metastore-opa-authorizer?tab=readme-ov-file#configuration
70+
// TODO: get document root once (client call) and build the other strings
71+
let base_endpoint = opa_config
72+
.full_document_url_from_config_map(client, hive, Some("allow"), OpaApiVersion::V1)
73+
.await?;
74+
75+
let policy_url_database = opa_config
76+
.full_document_url_from_config_map(
77+
client,
78+
hive,
79+
Some("database_allow"),
80+
OpaApiVersion::V1,
81+
)
82+
.await?;
83+
let policy_url_table = opa_config
84+
.full_document_url_from_config_map(client, hive, Some("table_allow"), OpaApiVersion::V1)
85+
.await?;
86+
let policy_url_column = opa_config
87+
.full_document_url_from_config_map(
88+
client,
89+
hive,
90+
Some("column_allow"),
91+
OpaApiVersion::V1,
92+
)
93+
.await?;
94+
let policy_url_partition = opa_config
95+
.full_document_url_from_config_map(
96+
client,
97+
hive,
98+
Some("partition_allow"),
99+
OpaApiVersion::V1,
100+
)
101+
.await?;
102+
let policy_url_user = opa_config
103+
.full_document_url_from_config_map(client, hive, Some("user_allow"), OpaApiVersion::V1)
104+
.await?;
105+
106+
let tls_secret_class = client
107+
.get::<ConfigMap>(
108+
&opa_config.config_map_name,
109+
hive.namespace().as_deref().unwrap_or("default"),
110+
)
111+
.await
112+
.ok()
113+
.and_then(|cm| cm.data)
114+
.and_then(|mut data| data.remove("OPA_SECRET_CLASS"));
115+
116+
Ok(HiveOpaConfig {
117+
base_endpoint,
118+
policy_url_database,
119+
policy_url_table,
120+
policy_url_column,
121+
policy_url_partition,
122+
policy_url_user,
123+
tls_secret_class,
124+
})
125+
}
126+
127+
pub fn as_config(&self, product_version: &str) -> BTreeMap<String, String> {
128+
let (pre_event_listener, authorization_provider) = if product_version.starts_with("3.") {
129+
(
130+
OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V3,
131+
OPA_BASED_AUTHORIZATION_PROVIDER_V3,
132+
)
133+
} else {
134+
(
135+
OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V4,
136+
OPA_BASED_AUTHORIZATION_PROVIDER_V4,
137+
)
138+
};
139+
140+
BTreeMap::from([
141+
(
142+
HIVE_METASTORE_PRE_EVENT_LISTENERS.to_string(),
143+
pre_event_listener.to_string(),
144+
),
145+
(
146+
HIVE_SECURITY_METASTORE_AUTHORIZATION_MANAGER.to_string(),
147+
authorization_provider.to_string(),
148+
),
149+
(
150+
OPA_AUTHORIZATION_BASE_ENDPOINT.to_string(),
151+
self.base_endpoint.to_owned(),
152+
),
153+
(
154+
OPA_AUTHORIZATION_POLICY_URL_DATA_BASE.to_string(),
155+
self.policy_url_database.to_owned(),
156+
),
157+
(
158+
OPA_AUTHORIZATION_POLICY_URL_TABLE.to_string(),
159+
self.policy_url_table.to_owned(),
160+
),
161+
(
162+
OPA_AUTHORIZATION_POLICY_URL_COLUMN.to_string(),
163+
self.policy_url_column.to_owned(),
164+
),
165+
(
166+
OPA_AUTHORIZATION_POLICY_URL_PARTITION.to_string(),
167+
self.policy_url_partition.to_owned(),
168+
),
169+
(
170+
OPA_AUTHORIZATION_POLICY_URL_USER.to_string(),
171+
self.policy_url_user.to_owned(),
172+
),
173+
])
174+
}
175+
176+
pub fn tls_mount_path(&self) -> Option<String> {
177+
self.tls_secret_class
178+
.as_ref()
179+
.map(|_| format!("/stackable/secrets/{OPA_TLS_VOLUME_NAME}"))
180+
}
181+
}

rust/operator-binary/src/controller.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ use stackable_operator::{
3636
commons::{
3737
product_image_selection::{self, ResolvedProductImage},
3838
rbac::build_rbac_resources,
39-
tls_verification::TlsClientDetailsError,
4039
},
4140
crd::{listener::v1alpha1::Listener, s3},
4241
k8s_openapi::{
@@ -85,7 +84,10 @@ use tracing::warn;
8584
use crate::{
8685
OPERATOR_NAME,
8786
command::build_container_command_args,
88-
config::jvm::{construct_hadoop_heapsize_env, construct_non_heap_jvm_args},
87+
config::{
88+
jvm::{construct_hadoop_heapsize_env, construct_non_heap_jvm_args},
89+
opa::HiveOpaConfig,
90+
},
8991
crd::{
9092
APP_NAME, CORE_SITE_XML, Container, DB_PASSWORD_ENV, DB_USERNAME_ENV, HIVE_PORT,
9193
HIVE_PORT_NAME, HIVE_SITE_XML, HiveClusterStatus, HiveRole, JVM_SECURITY_PROPERTIES_FILE,
@@ -236,6 +238,9 @@ pub enum Error {
236238
source: stackable_operator::commons::rbac::Error,
237239
},
238240

241+
#[snafu(display("internal operator failure"))]
242+
InternalOperatorFailure { source: crate::crd::Error },
243+
239244
#[snafu(display(
240245
"failed to serialize [{JVM_SECURITY_PROPERTIES_FILE}] for {}",
241246
rolegroup
@@ -313,6 +318,11 @@ pub enum Error {
313318
ResolveProductImage {
314319
source: product_image_selection::Error,
315320
},
321+
322+
#[snafu(display("invalid OpaConfig"))]
323+
InvalidOpaConfig {
324+
source: stackable_operator::commons::opa::Error,
325+
},
316326
}
317327
type Result<T, E = Error> = std::result::Result<T, E>;
318328

@@ -416,6 +426,15 @@ pub async fn reconcile_hive(
416426
.await
417427
.context(ApplyRoleBindingSnafu)?;
418428

429+
let hive_opa_config = match hive.get_opa_config() {
430+
Some(opa_config) => Some(
431+
HiveOpaConfig::from_opa_config(client, hive, opa_config)
432+
.await
433+
.context(InvalidOpaConfigSnafu)?,
434+
),
435+
None => None,
436+
};
437+
419438
let mut ss_cond_builder = StatefulSetConditionBuilder::default();
420439

421440
for (rolegroup_name, rolegroup_config) in metastore_config.iter() {
@@ -442,6 +461,7 @@ pub async fn reconcile_hive(
442461
s3_connection_spec.as_ref(),
443462
&config,
444463
&client.kubernetes_cluster_info,
464+
hive_opa_config.as_ref(),
445465
)?;
446466
let rg_statefulset = build_metastore_rolegroup_statefulset(
447467
hive,
@@ -567,6 +587,7 @@ fn build_metastore_rolegroup_config_map(
567587
s3_connection_spec: Option<&s3::v1alpha1::ConnectionSpec>,
568588
merged_config: &MetaStoreConfig,
569589
cluster_info: &KubernetesClusterInfo,
590+
hive_opa_config: Option<&HiveOpaConfig>,
570591
) -> Result<ConfigMap> {
571592
let mut hive_site_data = String::new();
572593

@@ -623,6 +644,17 @@ fn build_metastore_rolegroup_config_map(
623644
data.insert(property_name.to_string(), Some(property_value.to_string()));
624645
}
625646

647+
// OPA settings
648+
if let Some(opa_config) = hive_opa_config {
649+
data.extend(
650+
opa_config
651+
.as_config(&resolved_product_image.product_version)
652+
.into_iter()
653+
.map(|(k, v)| (k, Some(v)))
654+
.collect::<BTreeMap<String, Option<String>>>(),
655+
);
656+
}
657+
626658
// overrides
627659
for (property_name, property_value) in config {
628660
data.insert(property_name.to_string(), Some(property_value.to_string()));
@@ -711,10 +743,10 @@ fn build_metastore_rolegroup_statefulset(
711743
merged_config: &MetaStoreConfig,
712744
sa_name: &str,
713745
) -> Result<StatefulSet> {
714-
let role = hive.role(hive_role).context(InternalOperatorSnafu)?;
746+
let role = hive.role(hive_role).context(InternalOperatorFailureSnafu)?;
715747
let rolegroup = hive
716748
.rolegroup(rolegroup_ref)
717-
.context(InternalOperatorSnafu)?;
749+
.context(InternalOperatorFailureSnafu)?;
718750

719751
let mut container_builder =
720752
ContainerBuilder::new(APP_NAME).context(FailedToCreateHiveContainerSnafu {

rust/operator-binary/src/crd/mod.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use stackable_operator::{
77
commons::{
88
affinity::StackableAffinity,
99
cluster_operation::ClusterOperation,
10+
opa::OpaConfig,
1011
product_image_selection::ProductImage,
1112
resources::{
1213
CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment,
@@ -151,6 +152,13 @@ pub mod versioned {
151152
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
152153
#[serde(rename_all = "camelCase")]
153154
pub struct HiveClusterConfig {
155+
/// Settings related to user [authentication](DOCS_BASE_URL_PLACEHOLDER/usage-guide/security).
156+
pub authentication: Option<AuthenticationConfig>,
157+
158+
/// Authorization options for Hive.
159+
/// Learn more in the [Hive authorization usage guide](DOCS_BASE_URL_PLACEHOLDER/hive/usage-guide/security#authorization).
160+
pub authorization: Option<security::AuthorizationConfig>,
161+
154162
// no doc - docs in DatabaseConnectionSpec struct.
155163
pub database: DatabaseConnectionSpec,
156164

@@ -169,9 +177,6 @@ pub mod versioned {
169177
/// to learn how to configure log aggregation with Vector.
170178
#[serde(skip_serializing_if = "Option::is_none")]
171179
pub vector_aggregator_config_map_name: Option<String>,
172-
173-
/// Settings related to user [authentication](DOCS_BASE_URL_PLACEHOLDER/usage-guide/security).
174-
pub authentication: Option<AuthenticationConfig>,
175180
}
176181
}
177182

@@ -289,6 +294,14 @@ impl v1alpha1::HiveCluster {
289294
&self.spec.cluster_config.database.db_type
290295
}
291296

297+
pub fn get_opa_config(&self) -> Option<&OpaConfig> {
298+
self.spec
299+
.cluster_config
300+
.authorization
301+
.as_ref()
302+
.and_then(|a| a.opa.as_ref())
303+
}
304+
292305
/// Retrieve and merge resource configs for role and role groups
293306
pub fn merged_config(
294307
&self,

rust/operator-binary/src/crd/security.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use serde::{Deserialize, Serialize};
2-
use stackable_operator::schemars::{self, JsonSchema};
2+
use stackable_operator::{
3+
commons::opa::OpaConfig,
4+
schemars::{self, JsonSchema},
5+
};
36

47
#[derive(Clone, Debug, Deserialize, Eq, Hash, JsonSchema, PartialEq, Serialize)]
58
#[serde(rename_all = "camelCase")]
@@ -8,6 +11,14 @@ pub struct AuthenticationConfig {
811
pub kerberos: KerberosConfig,
912
}
1013

14+
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
15+
#[serde(rename_all = "camelCase")]
16+
pub struct AuthorizationConfig {
17+
// no doc - it's in the struct.
18+
#[serde(default, skip_serializing_if = "Option::is_none")]
19+
pub opa: Option<OpaConfig>,
20+
}
21+
1122
#[derive(Clone, Debug, Deserialize, Eq, Hash, JsonSchema, PartialEq, Serialize)]
1223
#[serde(rename_all = "camelCase")]
1324
pub struct KerberosConfig {

0 commit comments

Comments
 (0)