From 6e44b7587593b165c1945747f6590156c727eb86 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Fri, 19 Aug 2022 15:56:21 +0200 Subject: [PATCH 1/2] Added Hbase service account. --- .../helm/hbase-operator/templates/roles.yaml | 83 +++++++++++++++++++ deploy/manifests/roles.yaml | 28 +++++++ rust/operator-binary/src/hbase_controller.rs | 47 ++++++++++- rust/operator-binary/src/main.rs | 1 + rust/operator-binary/src/rbac.rs | 44 ++++++++++ 5 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 rust/operator-binary/src/rbac.rs diff --git a/deploy/helm/hbase-operator/templates/roles.yaml b/deploy/helm/hbase-operator/templates/roles.yaml index a1965cdf..61af9599 100644 --- a/deploy/helm/hbase-operator/templates/roles.yaml +++ b/deploy/helm/hbase-operator/templates/roles.yaml @@ -76,3 +76,86 @@ rules: - {{ include "operator.name" . }}clusters/status verbs: - patch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + verbs: + - bind + resourceNames: + - {{ include "operator.name" . }}-clusterrole + +{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} +--- +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: hbase-scc + annotations: + kubernetes.io/description: |- + This resource is derived from hostmount-anyuid. It provides all the features of the + restricted SCC but allows host mounts and any UID by a pod. This is primarily + used by the persistent volume recycler. WARNING: this SCC allows host file + system access as any UID, including UID 0. Grant with caution. + release.openshift.io/create-only: "true" +allowHostDirVolumePlugin: true +allowHostIPC: false +allowHostNetwork: false +allowHostPID: false +allowHostPorts: false +allowPrivilegeEscalation: true +allowPrivilegedContainer: false +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: RunAsAny +groups: [] +priority: null +readOnlyRootFilesystem: false +runAsUser: + type: RunAsAny +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: RunAsAny +volumes: +- configMap +- downwardAPI +- emptyDir +- hostPath +- nfs +- persistentVolumeClaim +- projected +- secret +- ephemeral +{{ end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "operator.name" . }}-clusterrole +rules: + - apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + verbs: + - get + - apiGroups: + - events.k8s.io + resources: + - events + verbs: + - create +{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - hbase-scc + verbs: + - use +{{ end }} diff --git a/deploy/manifests/roles.yaml b/deploy/manifests/roles.yaml index 397ca5e5..3f71ce5e 100644 --- a/deploy/manifests/roles.yaml +++ b/deploy/manifests/roles.yaml @@ -77,3 +77,31 @@ rules: - hbaseclusters/status verbs: - patch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + verbs: + - bind + resourceNames: + - hbase-clusterrole +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: hbase-clusterrole +rules: + - apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + verbs: + - get + - apiGroups: + - events.k8s.io + resources: + - events + verbs: + - create diff --git a/rust/operator-binary/src/hbase_controller.rs b/rust/operator-binary/src/hbase_controller.rs index 5ee58a56..5f73020f 100644 --- a/rust/operator-binary/src/hbase_controller.rs +++ b/rust/operator-binary/src/hbase_controller.rs @@ -1,13 +1,16 @@ //! Ensures that `Pod`s are configured and running for each [`HbaseCluster`] -use crate::discovery::build_discovery_configmap; +use crate::{discovery::build_discovery_configmap, rbac}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_hbase_crd::{ HbaseCluster, HbaseConfig, HbaseRole, APP_NAME, HBASE_ENV_SH, HBASE_MASTER_PORT, HBASE_REGIONSERVER_PORT, HBASE_REST_PORT, HBASE_SITE_XML, HBASE_ZOOKEEPER_QUORUM, }; use stackable_operator::{ - builder::{ConfigMapBuilder, ContainerBuilder, ObjectMetaBuilder, PodBuilder}, + builder::{ + ConfigMapBuilder, ContainerBuilder, ObjectMetaBuilder, PodBuilder, + PodSecurityContextBuilder, + }, cluster_resources::ClusterResources, k8s_openapi::{ api::{ @@ -124,6 +127,16 @@ pub enum Error { entry: &'static str, cm_name: String, }, + #[snafu(display("failed to patch service account: {source}"))] + ApplyServiceAccount { + name: String, + source: stackable_operator::error::Error, + }, + #[snafu(display("failed to patch role binding: {source}"))] + ApplyRoleBinding { + name: String, + source: stackable_operator::error::Error, + }, } type Result = std::result::Result; @@ -182,6 +195,20 @@ pub async fn reconcile_hbase(hbase: Arc, ctx: Arc) -> Result< .await .context(ApplyDiscoveryConfigMapSnafu)?; + let (rbac_sa, rbac_rolebinding) = rbac::build_rbac_resources(hbase.as_ref(), "hbase"); + client + .apply_patch(CONTROLLER_NAME, &rbac_sa, &rbac_sa) + .await + .with_context(|_| ApplyServiceAccountSnafu { + name: rbac_sa.name_unchecked(), + })?; + client + .apply_patch(CONTROLLER_NAME, &rbac_rolebinding, &rbac_rolebinding) + .await + .with_context(|_| ApplyRoleBindingSnafu { + name: rbac_rolebinding.name_unchecked(), + })?; + for (role_name, group_config) in validated_config.iter() { for (rolegroup_name, rolegroup_config) in group_config.iter() { let rolegroup = hbase.server_rolegroup_ref(role_name, rolegroup_name); @@ -192,7 +219,12 @@ pub async fn reconcile_hbase(hbase: Arc, ctx: Arc) -> Result< rolegroup_config, &zk_connect_string, )?; - let rg_statefulset = build_rolegroup_statefulset(&hbase, &rolegroup, rolegroup_config)?; + let rg_statefulset = build_rolegroup_statefulset( + &hbase, + &rolegroup, + rolegroup_config, + &rbac_sa.name_unchecked(), + )?; cluster_resources .add(client, &rg_service) .await @@ -383,6 +415,7 @@ fn build_rolegroup_statefulset( hbase: &HbaseCluster, rolegroup_ref: &RoleGroupRef, _rolegroup_config: &HashMap>, + sa_name: &str, ) -> Result { let hbase_version = hbase_version(hbase)?; @@ -541,6 +574,14 @@ fn build_rolegroup_statefulset( }), ..Default::default() }) + .service_account_name(sa_name) + .security_context( + PodSecurityContextBuilder::new() + .run_as_user(rbac::HBASE_UID) + .run_as_group(0) + .fs_group(1000) // Needed for secret-operator + .build(), + ) .build_template(), ..StatefulSetSpec::default() }), diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index b747ab27..9a2da49b 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -1,5 +1,6 @@ mod discovery; mod hbase_controller; +mod rbac; use std::sync::Arc; diff --git a/rust/operator-binary/src/rbac.rs b/rust/operator-binary/src/rbac.rs new file mode 100644 index 00000000..9410c430 --- /dev/null +++ b/rust/operator-binary/src/rbac.rs @@ -0,0 +1,44 @@ +use stackable_operator::builder::ObjectMetaBuilder; +use stackable_operator::k8s_openapi::api::core::v1::ServiceAccount; +use stackable_operator::k8s_openapi::api::rbac::v1::{RoleBinding, RoleRef, Subject}; +use stackable_operator::kube::{Resource, ResourceExt}; + +/// Used as runAsUser in the pod security context. This is specified in the Hbase image file +pub const HBASE_UID: i64 = 1000; + +/// Build RBAC objects for the product workloads. +/// The `rbac_prefix` is meant to be the product name, for example: zookeeper, airflow, etc. +/// and it is a assumed that a ClusterRole named `{rbac_prefix}-clusterrole` exists. +pub fn build_rbac_resources( + resource: &T, + rbac_prefix: &str, +) -> (ServiceAccount, RoleBinding) { + let sa_name = format!("{rbac_prefix}-sa"); + let service_account = ServiceAccount { + metadata: ObjectMetaBuilder::new() + .name_and_namespace(resource) + .name(sa_name.clone()) + .build(), + ..ServiceAccount::default() + }; + + let role_binding = RoleBinding { + metadata: ObjectMetaBuilder::new() + .name_and_namespace(resource) + .name(format!("{rbac_prefix}-rolebinding")) + .build(), + role_ref: RoleRef { + kind: "ClusterRole".to_string(), + name: format!("{rbac_prefix}-clusterrole"), + api_group: "rbac.authorization.k8s.io".to_string(), + }, + subjects: Some(vec![Subject { + kind: "ServiceAccount".to_string(), + name: sa_name, + namespace: resource.namespace(), + ..Subject::default() + }]), + }; + + (service_account, role_binding) +} From 87d3a96107f374e995f85a62d978f9a21190f8f8 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Fri, 19 Aug 2022 16:45:18 +0200 Subject: [PATCH 2/2] Update kuttl tests and CHANGELOG. --- CHANGELOG.md | 2 ++ tests/templates/kuttl/smoke/03-install-hbase-test-runner.yaml | 2 +- tests/templates/kuttl/smoke/04-assert.yaml | 2 +- tests/templates/kuttl/smoke/04-test-hbase.yaml | 4 +--- tests/templates/kuttl/smoke/requirements.txt | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 tests/templates/kuttl/smoke/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index b86565c9..fe44df49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,14 @@ - Include chart name when installing with a custom release name ([#209], [#210]). - Orphaned resources are deleted ([#215]). - Fix HBase-shell start failure ([#218]). +- Added OpenShift compatiblity ([#232]) [#193]: https://github.com/stackabletech/hbase-operator/pull/193 [#209]: https://github.com/stackabletech/hbase-operator/pull/209 [#210]: https://github.com/stackabletech/hbase-operator/pull/210 [#215]: https://github.com/stackabletech/hbase-operator/pull/215 [#218]: https://github.com/stackabletech/hbase-operator/pull/218 +[#232]: https://github.com/stackabletech/hbase-operator/pull/232 ## [0.3.0] - 2022-06-30 diff --git a/tests/templates/kuttl/smoke/03-install-hbase-test-runner.yaml b/tests/templates/kuttl/smoke/03-install-hbase-test-runner.yaml index 5f4ae33d..b855771b 100644 --- a/tests/templates/kuttl/smoke/03-install-hbase-test-runner.yaml +++ b/tests/templates/kuttl/smoke/03-install-hbase-test-runner.yaml @@ -17,6 +17,6 @@ spec: spec: containers: - name: hbase-test-runner - image: python:3.10-slim + image: docker.stackable.tech/stackable/testing-tools:0.1.0-stackable0.1.0 stdin: true tty: true diff --git a/tests/templates/kuttl/smoke/04-assert.yaml b/tests/templates/kuttl/smoke/04-assert.yaml index 8e1c61c1..10b05f91 100644 --- a/tests/templates/kuttl/smoke/04-assert.yaml +++ b/tests/templates/kuttl/smoke/04-assert.yaml @@ -4,4 +4,4 @@ kind: TestAssert metadata: name: test-hbase commands: - - script: kubectl exec --namespace=$NAMESPACE hbase-test-runner-0 -- python /root/test-hbase.py http://test-hbase-restserver-default:8080 + - script: kubectl exec --namespace=$NAMESPACE hbase-test-runner-0 -- python /tmp/test-hbase.py http://test-hbase-restserver-default:8080 diff --git a/tests/templates/kuttl/smoke/04-test-hbase.yaml b/tests/templates/kuttl/smoke/04-test-hbase.yaml index 89c49ac5..6863fc61 100644 --- a/tests/templates/kuttl/smoke/04-test-hbase.yaml +++ b/tests/templates/kuttl/smoke/04-test-hbase.yaml @@ -2,6 +2,4 @@ apiVersion: kuttl.dev/v1beta1 kind: TestStep commands: - - script: kubectl cp --namespace=$NAMESPACE ./requirements.txt hbase-test-runner-0:/root - - script: kubectl cp --namespace=$NAMESPACE ./test-hbase.py hbase-test-runner-0:/root - - script: kubectl exec --namespace=$NAMESPACE hbase-test-runner-0 -- pip install --user --requirement /root/requirements.txt + - script: kubectl cp --namespace=$NAMESPACE ./test-hbase.py hbase-test-runner-0:/tmp diff --git a/tests/templates/kuttl/smoke/requirements.txt b/tests/templates/kuttl/smoke/requirements.txt deleted file mode 100644 index d15ce5a7..00000000 --- a/tests/templates/kuttl/smoke/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -requests==2.28.1