diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a2301..8ae6aec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Support hot-reloading of security configuration files ([#130]). + +[#130]: https://github.com/stackabletech/opensearch-operator/pull/130 + ## [26.3.0] - 2026-03-16 ## [26.3.0-rc1] - 2026-03-16 diff --git a/docs/modules/opensearch/examples/getting_started/opensearch-security-config.yaml b/docs/modules/opensearch/examples/getting_started/opensearch-security-config.yaml deleted file mode 100644 index dde39f1..0000000 --- a/docs/modules/opensearch/examples/getting_started/opensearch-security-config.yaml +++ /dev/null @@ -1,87 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: opensearch-security-config -stringData: - action_groups.yml: | - --- - _meta: - type: actiongroups - config_version: 2 - allowlist.yml: | - --- - _meta: - type: allowlist - config_version: 2 - config: - enabled: false - audit.yml: | - --- - _meta: - type: audit - config_version: 2 - config: - enabled: false - config.yml: | - --- - _meta: - type: config - config_version: 2 - config: - dynamic: - authc: - basic_internal_auth_domain: - description: Authenticate via HTTP Basic against internal users database - http_enabled: true - transport_enabled: true - order: 1 - http_authenticator: - type: basic - challenge: true - authentication_backend: - type: intern - authz: {} - internal_users.yml: | - --- - _meta: - type: internalusers - config_version: 2 - admin: - hash: $2y$10$xRtHZFJ9QhG9GcYhRpAGpufCZYsk//nxsuel5URh0GWEBgmiI4Q/e - reserved: true - backend_roles: - - admin - description: OpenSearch admin user - kibanaserver: - hash: $2y$10$vPgQ/6ilKDM5utawBqxoR.7euhVQ0qeGl8mPTeKhmFT475WUDrfQS - reserved: true - description: OpenSearch Dashboards user - nodes_dn.yml: | - --- - _meta: - type: nodesdn - config_version: 2 - roles.yml: | - --- - _meta: - type: roles - config_version: 2 - roles_mapping.yml: | - --- - _meta: - type: rolesmapping - config_version: 2 - all_access: - reserved: false - backend_roles: - - admin - kibana_server: - reserved: true - users: - - kibanaserver - tenants.yml: | - --- - _meta: - type: tenants - config_version: 2 diff --git a/docs/modules/opensearch/pages/usage-guide/monitoring.adoc b/docs/modules/opensearch/pages/usage-guide/monitoring.adoc index c628c44..2e7889a 100644 --- a/docs/modules/opensearch/pages/usage-guide/monitoring.adoc +++ b/docs/modules/opensearch/pages/usage-guide/monitoring.adoc @@ -29,10 +29,12 @@ To make the metrics accessible for all users, especially Prometheus, anonymous a ---- --- apiVersion: v1 -kind: Secret +kind: ConfigMap metadata: - name: opensearch-security-config -stringData: + name: custom-opensearch-security-config + annotations: + restarter.stackable.tech/ignore: "true" +data: config.yml: | --- _meta: diff --git a/docs/modules/opensearch/pages/usage-guide/security.adoc b/docs/modules/opensearch/pages/usage-guide/security.adoc index ab8076e..4dff514 100644 --- a/docs/modules/opensearch/pages/usage-guide/security.adoc +++ b/docs/modules/opensearch/pages/usage-guide/security.adoc @@ -144,6 +144,31 @@ spec: If this role group is not defined, it will be created by the operator. +[IMPORTANT] +==== +Settings managed by the operator are hot-reloaded when changed, i.e. without pod restarts. +However, if those settings are provided via ConfigMap or Secret, updates will normally trigger a restart. +To prevent that restart, add the following annotation: + +[source,yaml] +---- +--- +apiVersion: v1 +kind: Secret +metadata: + name: security-config + annotations: + restarter.stackable.tech/ignore: "true" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: security-config + annotations: + restarter.stackable.tech/ignore: "true" +---- +==== + == TLS TLS is also managed by the OpenSearch security plugin, therefore TLS is only available if the security plugin was not disabled. diff --git a/docs/modules/opensearch/pages/usage-guide/upgrade.adoc b/docs/modules/opensearch/pages/usage-guide/upgrade.adoc index 2b8485f..e3b6815 100644 --- a/docs/modules/opensearch/pages/usage-guide/upgrade.adoc +++ b/docs/modules/opensearch/pages/usage-guide/upgrade.adoc @@ -1,6 +1,35 @@ = SDP upgrade notes :description: Instructions for upgrading the SDP versions. +== Upgrade from SDP 26.3 to 26.7 + +=== Hot-reloading of security settings + +The security settings defined in the cluster specification are now stored in a separate ConfigMap named `-security-config`. +If you used this name for your custom security configuration, then you must rename it. +Otherwise the operator will override it. + +The operator now supports hot-reloading of security settings. +If those settings are provided via ConfigMap or Secret, then the annotation `restarter.stackable.tech/ignore: "true"` should be added to avoid restarts triggered by the restart controller: + +[source,yaml] +---- +--- +apiVersion: v1 +kind: Secret +metadata: + name: security-config + annotations: + restarter.stackable.tech/ignore: "true" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: security-config + annotations: + restarter.stackable.tech/ignore: "true" +---- + == Upgrade from SDP 25.11 to 26.3 When upgrading the OpenSearch operator from SDP 25.11 to 26.3, you may encounter several warnings and errors in the operator logs. diff --git a/rust/operator-binary/src/controller/build.rs b/rust/operator-binary/src/controller/build.rs index 5be50f3..479decc 100644 --- a/rust/operator-binary/src/controller/build.rs +++ b/rust/operator-binary/src/controller/build.rs @@ -33,9 +33,12 @@ pub fn build(names: &ContextNames, cluster: ValidatedCluster) -> KubernetesResou listeners.push(role_group_builder.build_listener()); } - if let Some(discovery_config_map) = role_builder.build_discovery_config_map() { + if let Some(discovery_config_map) = role_builder.build_maybe_discovery_config_map() { config_maps.push(discovery_config_map); } + if let Some(security_config_map) = role_builder.build_maybe_security_config_map() { + config_maps.push(security_config_map); + } services.push(role_builder.build_seed_nodes_service()); listeners.push(role_builder.build_discovery_service_listener()); @@ -90,7 +93,7 @@ mod tests { role_utils::GenericProductSpecificCommonConfig, types::{ common::Port, - kubernetes::{Hostname, ListenerClassName, NamespaceName}, + kubernetes::{Hostname, ListenerClassName, NamespaceName, SecretClassName}, operator::{ ClusterName, ControllerName, OperatorName, ProductName, ProductVersion, RoleGroupName, @@ -134,7 +137,8 @@ mod tests { "my-opensearch", "my-opensearch-nodes-cluster-manager", "my-opensearch-nodes-coordinating", - "my-opensearch-nodes-data" + "my-opensearch-nodes-data", + "my-opensearch-security-config" ], extract_resource_names(&resources.config_maps) ); @@ -209,7 +213,11 @@ mod tests { ), ] .into(), - ValidatedSecurity::Disabled, + ValidatedSecurity::ManagedByApi { + settings: v1alpha1::SecuritySettings::default(), + tls_server_secret_class: None, + tls_internal_secret_class: SecretClassName::from_str_unsafe("tls"), + }, vec![], Some(ValidatedDiscoveryEndpoint { hostname: Hostname::from_str_unsafe("1.2.3.4"), diff --git a/rust/operator-binary/src/controller/build/role_builder.rs b/rust/operator-binary/src/controller/build/role_builder.rs index 385e0fc..93de96d 100644 --- a/rust/operator-binary/src/controller/build/role_builder.rs +++ b/rust/operator-binary/src/controller/build/role_builder.rs @@ -1,6 +1,6 @@ //! Builder for role resources -use std::str::FromStr; +use std::{collections::BTreeMap, str::FromStr}; use stackable_operator::{ builder::meta::ObjectMetaBuilder, @@ -13,7 +13,6 @@ use stackable_operator::{ rbac::v1::{ClusterRole, RoleBinding, RoleRef, Subject}, }, }, - kube::api::ObjectMeta, kvp::{ Label, Labels, consts::{STACKABLE_VENDOR_KEY, STACKABLE_VENDOR_VALUE}, @@ -23,12 +22,14 @@ use stackable_operator::{ use crate::{ controller::{ ContextNames, HTTP_PORT, HTTP_PORT_NAME, TRANSPORT_PORT, TRANSPORT_PORT_NAME, - ValidatedCluster, build::role_group_builder::RoleGroupBuilder, + ValidatedCluster, ValidatedSecurity, build::role_group_builder::RoleGroupBuilder, }, + crd::v1alpha1, framework::{ NameIsValidLabelValue, builder::{ - meta::ownerreference_from_resource, pdb::pod_disruption_budget_builder_with_role, + meta::{annotation_ignore_restarter, ownerreference_from_resource}, + pdb::pod_disruption_budget_builder_with_role, }, role_utils::ResourceNames, types::{ @@ -80,7 +81,9 @@ impl<'a> RoleBuilder<'a> { /// Builds a ServiceAccount used by all role-groups pub fn build_service_account(&self) -> ServiceAccount { - let metadata = self.common_metadata(self.resource_names.service_account_name()); + let metadata = self + .common_metadata(self.resource_names.service_account_name()) + .build(); ServiceAccount { metadata, @@ -90,7 +93,9 @@ impl<'a> RoleBuilder<'a> { /// Builds a RoleBinding used by all role-groups pub fn build_role_binding(&self) -> RoleBinding { - let metadata = self.common_metadata(self.resource_names.role_binding_name()); + let metadata = self + .common_metadata(self.resource_names.role_binding_name()) + .build(); RoleBinding { metadata, @@ -116,7 +121,9 @@ impl<'a> RoleBuilder<'a> { ..ServicePort::default() }]; - let metadata = self.common_metadata(seed_nodes_service_name(&self.cluster.name)); + let metadata = self + .common_metadata(seed_nodes_service_name(&self.cluster.name)) + .build(); let service_selector = RoleGroupBuilder::cluster_manager_labels(&self.cluster, self.context_names); @@ -140,7 +147,9 @@ impl<'a> RoleBuilder<'a> { /// Builds a Listener whose status is used to populate the discovery ConfigMap. pub fn build_discovery_service_listener(&self) -> listener::v1alpha1::Listener { - let metadata = self.common_metadata(discovery_service_listener_name(&self.cluster.name)); + let metadata = self + .common_metadata(discovery_service_listener_name(&self.cluster.name)) + .build(); let listener_class = &self.cluster.role_config.discovery_service_listener_class; @@ -166,10 +175,12 @@ impl<'a> RoleBuilder<'a> { /// The discovery endpoint is derived from the status of the discovery service Listener. If the /// status is not set yet, the reconciliation process will occur again once the Listener status /// is updated, leading to the eventual creation of the discovery ConfigMap. - pub fn build_discovery_config_map(&self) -> Option { + pub fn build_maybe_discovery_config_map(&self) -> Option { let discovery_endpoint = self.cluster.discovery_endpoint.as_ref()?; - let metadata = self.common_metadata(discovery_config_map_name(&self.cluster.name)); + let metadata = self + .common_metadata(discovery_config_map_name(&self.cluster.name)) + .build(); let protocol = if self.cluster.is_server_tls_enabled() { "https" @@ -204,6 +215,43 @@ impl<'a> RoleBuilder<'a> { }) } + /// Builds the [`ConfigMap`] containing the security configuration files that were defined by + /// value. + /// + /// Returns `None` if the security plugin is disabled or all configuration files are + /// references. + pub fn build_maybe_security_config_map(&self) -> Option { + let metadata = self + .common_metadata(security_config_map_name(&self.cluster.name)) + .with_annotation(annotation_ignore_restarter()) + .build(); + + let mut data = BTreeMap::new(); + + if let ValidatedSecurity::ManagedByApi { settings, .. } + | ValidatedSecurity::ManagedByOperator { settings, .. } = &self.cluster.security + { + for file_type in settings { + if let v1alpha1::SecuritySettingsFileTypeContent::Value( + v1alpha1::SecuritySettingsFileTypeContentValue { value }, + ) = &file_type.content + { + data.insert(file_type.filename.to_owned(), value.to_string()); + } + } + } + + if data.is_empty() { + None + } else { + Some(ConfigMap { + metadata, + data: Some(data), + ..ConfigMap::default() + }) + } + } + /// Builds a [`PodDisruptionBudget`] used by all role-groups pub fn build_pdb(&self) -> Option { let pdb_config = &self.cluster.role_config.common.pod_disruption_budget; @@ -229,8 +277,10 @@ impl<'a> RoleBuilder<'a> { } /// Common metadata for role resources - fn common_metadata(&self, resource_name: impl Into) -> ObjectMeta { - ObjectMetaBuilder::new() + fn common_metadata(&self, resource_name: impl Into) -> ObjectMetaBuilder { + let mut builder = ObjectMetaBuilder::new(); + + builder .name(resource_name) .namespace(&self.cluster.namespace) .ownerreference(ownerreference_from_resource( @@ -238,8 +288,9 @@ impl<'a> RoleBuilder<'a> { None, Some(true), )) - .with_labels(self.labels()) - .build() + .with_labels(self.labels()); + + builder } /// Common labels for role resources @@ -297,6 +348,20 @@ fn discovery_config_map_name(cluster_name: &ClusterName) -> ConfigMapName { ConfigMapName::from_str(cluster_name.as_ref()).expect("should be a valid ConfigMap name") } +pub fn security_config_map_name(cluster_name: &ClusterName) -> ConfigMapName { + const SUFFIX: &str = "-security-config"; + + // compile-time checks + const _: () = assert!( + ClusterName::MAX_LENGTH + SUFFIX.len() <= ConfigMapName::MAX_LENGTH, + "The string `-security-config` must not exceed the limit of ConfigMap names." + ); + let _ = ClusterName::IS_RFC_1123_SUBDOMAIN_NAME; + + ConfigMapName::from_str(&format!("{}{SUFFIX}", cluster_name.as_ref())) + .expect("should be a valid ConfigMap name") +} + pub fn discovery_service_listener_name(cluster_name: &ClusterName) -> ListenerName { // compile-time checks const _: () = assert!( @@ -640,12 +705,13 @@ mod tests { } #[test] - fn test_build_discovery_config_map() { + fn test_build_maybe_discovery_config_map() { let context_names = context_names(); let role_builder = role_builder(&context_names); - let discovery_config_map = serde_json::to_value(role_builder.build_discovery_config_map()) - .expect("should be serializable"); + let discovery_config_map = + serde_json::to_value(role_builder.build_maybe_discovery_config_map()) + .expect("should be serializable"); assert_eq!( json!({ @@ -683,6 +749,59 @@ mod tests { ); } + #[test] + fn test_build_maybe_security_config_map() { + let context_names = context_names(); + let role_builder = role_builder(&context_names); + + let security_config_map = + serde_json::to_value(role_builder.build_maybe_security_config_map()) + .expect("should be serializable"); + + assert_eq!( + json!({ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "annotations": { + "restarter.stackable.tech/ignore": "true" + }, + "labels": { + "app.kubernetes.io/component": "nodes", + "app.kubernetes.io/instance": "my-opensearch-cluster", + "app.kubernetes.io/managed-by": "opensearch.stackable.tech_opensearchcluster", + "app.kubernetes.io/name": "opensearch", + "app.kubernetes.io/version": "3.4.0", + "stackable.tech/vendor": "Stackable", + }, + "name": "my-opensearch-cluster-security-config", + "namespace": "default", + "ownerReferences": [ + { + "apiVersion": "opensearch.stackable.tech/v1alpha1", + "controller": true, + "kind": "OpenSearchCluster", + "name": "my-opensearch-cluster", + "uid": "0b1e30e6-326e-4c1a-868d-ad6598b49e8b", + }, + ], + }, + "data": { + "action_groups.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"actiongroups\"}}", + "allowlist.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"allowlist\"},\"config\":{\"enabled\":false}}", + "audit.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"audit\"},\"config\":{\"enabled\":false}}", + "config.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"config\"},\"config\":{\"dynamic\":{\"authc\":{},\"authz\":{},\"http\":{}}}}", + "internal_users.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"internalusers\"}}", + "nodes_dn.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"nodesdn\"}}", + "roles.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"roles\"}}", + "roles_mapping.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"rolesmapping\"}}", + "tenants.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"tenants\"}}", + }, + }), + security_config_map + ); + } + #[test] fn test_build_pdb() { let context_names = context_names(); diff --git a/rust/operator-binary/src/controller/build/role_group_builder.rs b/rust/operator-binary/src/controller/build/role_group_builder.rs index 975efcd..f82634b 100644 --- a/rust/operator-binary/src/controller/build/role_group_builder.rs +++ b/rust/operator-binary/src/controller/build/role_group_builder.rs @@ -47,8 +47,11 @@ use crate::{ controller::{ ContextNames, HTTP_PORT, HTTP_PORT_NAME, OpenSearchRoleGroupConfig, TRANSPORT_PORT, TRANSPORT_PORT_NAME, ValidatedCluster, ValidatedNodeRole, ValidatedSecurity, - build::product_logging::config::{ - MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE, vector_config_file_extra_env_vars, + build::{ + product_logging::config::{ + MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE, vector_config_file_extra_env_vars, + }, + role_builder::security_config_map_name, }, }, crd::{ExtendedSecuritySettingsFileType, v1alpha1}, @@ -294,19 +297,6 @@ impl<'a> RoleGroupBuilder<'a> { data.insert(VECTOR_CONFIG_FILE.to_owned(), vector_config_file_content()); } - if let RoleGroupSecurityMode::Initializing { settings, .. } - | RoleGroupSecurityMode::Managing { settings, .. } = &self.security_mode - { - for file_type in settings { - if let v1alpha1::SecuritySettingsFileTypeContent::Value( - v1alpha1::SecuritySettingsFileTypeContentValue { value }, - ) = &file_type.content - { - data.insert(file_type.filename.to_owned(), value.to_string()); - } - } - } - ConfigMap { metadata, data: Some(data), @@ -725,7 +715,10 @@ impl<'a> RoleGroupBuilder<'a> { }; if let RoleGroupSecurityMode::Initializing { settings, .. } = &self.security_mode { - volume_mounts.extend(self.security_config_volume_mounts(settings)); + // Mount the security configuration files using `subPath`, because the configuration + // files are only used for initializing the security index and hot-reloading is not + // required. + volume_mounts.extend(self.security_config_volume_mounts(settings, true)); }; if !self.cluster.keystores.is_empty() { @@ -788,23 +781,45 @@ impl<'a> RoleGroupBuilder<'a> { /// Builds the security settings volume mounts for the [`v1alpha1::Container::OpenSearch`] /// container or the [`v1alpha1::Container::UpdateSecurityConfig`] container + /// + /// If `use_sub_path` is set to `true`, then the configuration files are directly mounted via + /// `subPath` into the opensearch-security configuration directory. If it is set to `false`, + /// then they are mounted into sub directories of the opensearch-security configuration + /// directory without using `subPath`. Files mounted via `subPath` are not updated on changes + /// in the ConfigMap or Secret volume. Therefore, hot-reloading works only without `subPath`, + /// but links from the configuration directory into the sub directories are required. fn security_config_volume_mounts( &self, settings: &v1alpha1::SecuritySettings, + use_sub_path: bool, ) -> Vec { let mut volume_mounts = vec![]; let opensearch_path_conf = self.node_config.opensearch_path_conf(); for file_type in settings { - volume_mounts.push(VolumeMount { - mount_path: format!( + let mount_path; + let sub_path; + + if use_sub_path { + mount_path = format!( "{opensearch_path_conf}/opensearch-security/{filename}", filename = file_type.filename.to_owned() - ), + ); + sub_path = Some(file_type.filename.to_owned()); + } else { + mount_path = format!( + "{opensearch_path_conf}/opensearch-security/{file_type}", + file_type = file_type.id + ); + sub_path = None; + } + + volume_mounts.push(VolumeMount { + mount_path, name: Self::security_settings_file_type_volume_name(&file_type).to_string(), read_only: Some(true), - sub_path: Some(file_type.filename.to_owned()), + sub_path, ..VolumeMount::default() }); } @@ -877,7 +892,10 @@ impl<'a> RoleGroupBuilder<'a> { ..VolumeMount::default() }, ]; - volume_mounts.extend(self.security_config_volume_mounts(settings)); + + // Mount the security configuration files without using `subPath`, so that hot-reloading + // works. + volume_mounts.extend(self.security_config_volume_mounts(settings, false)); let mut env_vars = EnvVarSet::new() .with_value( @@ -1125,7 +1143,7 @@ impl<'a> RoleGroupBuilder<'a> { mode: Some(0o660), path: file_type.filename.to_owned(), }]), - name: self.resource_names.role_group_config_map().to_string(), + name: security_config_map_name(&self.cluster.name).to_string(), ..Default::default() }), ..Volume::default() @@ -1628,12 +1646,8 @@ mod tests { } #[rstest] - #[case::security_mode_initializing(TestSecurityMode::Initializing)] - #[case::security_mode_managing(TestSecurityMode::Managing)] - #[case::security_mode_participating(TestSecurityMode::Participating)] - #[case::security_mode_disabled(TestSecurityMode::Disabled)] - fn test_build_config_map(#[case] security_mode: TestSecurityMode) { - let cluster = validated_cluster(security_mode); + fn test_build_config_map() { + let cluster = validated_cluster(TestSecurityMode::Disabled); let context_names = context_names(); let role_group_builder = role_group_builder(&cluster, &context_names); @@ -1649,26 +1663,6 @@ mod tests { // vector.yaml is a static file and does not have to be repeated here. config_map["data"]["vector.yaml"].take(); - let expected_data = match security_mode { - TestSecurityMode::Initializing | TestSecurityMode::Managing => json!({ - "action_groups.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"actiongroups\"}}", - "allowlist.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"allowlist\"},\"config\":{\"enabled\":false}}", - "audit.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"audit\"},\"config\":{\"enabled\":false}}", - "config.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"config\"},\"config\":{\"dynamic\":{\"authc\":{},\"authz\":{},\"http\":{}}}}", - "log4j2.properties": null, - "nodes_dn.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"nodesdn\"}}", - "opensearch.yml": null, - "roles_mapping.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"rolesmapping\"}}", - "tenants.yml": "{\"_meta\":{\"config_version\":2,\"type\":\"tenants\"}}", - "vector.yaml": null - }), - TestSecurityMode::Participating | TestSecurityMode::Disabled => json!({ - "log4j2.properties": null, - "opensearch.yml": null, - "vector.yaml": null - }), - }; - assert_eq!( json!({ "apiVersion": "v1", @@ -1695,7 +1689,11 @@ mod tests { } ] }, - "data": expected_data + "data": { + "log4j2.properties": null, + "opensearch.yml": null, + "vector.yaml": null + } }), config_map ); @@ -2297,58 +2295,49 @@ mod tests { "name": "log", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/action_groups.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/actiongroups", "name": "security-config-file-actiongroups", "readOnly": true, - "subPath": "action_groups.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/allowlist.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/allowlist", "name": "security-config-file-allowlist", "readOnly": true, - "subPath": "allowlist.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/audit.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/audit", "name": "security-config-file-audit", "readOnly": true, - "subPath": "audit.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/config.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/config", "name": "security-config-file-config", "readOnly": true, - "subPath": "config.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/internal_users.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/internalusers", "name": "security-config-file-internalusers", "readOnly": true, - "subPath": "internal_users.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/nodes_dn.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/nodesdn", "name": "security-config-file-nodesdn", "readOnly": true, - "subPath": "nodes_dn.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/roles.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/roles", "name": "security-config-file-roles", "readOnly": true, - "subPath": "roles.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/roles_mapping.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/rolesmapping", "name": "security-config-file-rolesmapping", "readOnly": true, - "subPath": "roles_mapping.yml", }, { - "mountPath": "/stackable/opensearch/config/opensearch-security/tenants.yml", + "mountPath": "/stackable/opensearch/config/opensearch-security/tenants", "name": "security-config-file-tenants", "readOnly": true, - "subPath": "tenants.yml", }, ], }); @@ -2546,7 +2535,7 @@ mod tests { "path": "action_groups.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-actiongroups" }, @@ -2559,7 +2548,7 @@ mod tests { "path": "allowlist.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-allowlist" }, @@ -2572,7 +2561,7 @@ mod tests { "path": "audit.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-audit" }, @@ -2585,7 +2574,7 @@ mod tests { "path": "config.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-config" }, @@ -2611,7 +2600,7 @@ mod tests { "path": "nodes_dn.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-nodesdn" }, @@ -2637,7 +2626,7 @@ mod tests { "path": "roles_mapping.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-rolesmapping" }, @@ -2650,7 +2639,7 @@ mod tests { "path": "tenants.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-tenants" }, @@ -2756,7 +2745,7 @@ mod tests { "path": "action_groups.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-actiongroups" }, @@ -2769,7 +2758,7 @@ mod tests { "path": "allowlist.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-allowlist" }, @@ -2782,7 +2771,7 @@ mod tests { "path": "audit.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-audit" }, @@ -2795,7 +2784,7 @@ mod tests { "path": "config.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-config" }, @@ -2821,7 +2810,7 @@ mod tests { "path": "nodes_dn.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-nodesdn" }, @@ -2847,7 +2836,7 @@ mod tests { "path": "roles_mapping.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-rolesmapping" }, @@ -2860,7 +2849,7 @@ mod tests { "path": "tenants.yml" } ], - "name": "my-opensearch-cluster-nodes-default" + "name": "my-opensearch-cluster-security-config" }, "name": "security-config-file-tenants" }, diff --git a/rust/operator-binary/src/controller/build/scripts/update-security-config.sh b/rust/operator-binary/src/controller/build/scripts/update-security-config.sh index d84a550..926433f 100644 --- a/rust/operator-binary/src/controller/build/scripts/update-security-config.sh +++ b/rust/operator-binary/src/controller/build/scripts/update-security-config.sh @@ -1,7 +1,51 @@ #!/usr/bin/env bash +# +# Required environment variables: +# - OPENSEARCH_PATH_CONF +# - POD_NAME +# - MANAGE_ACTIONGROUPS +# - MANAGE_ALLOWLIST +# - MANAGE_AUDIT +# - MANAGE_CONFIG +# - MANAGE_INTERNALUSERS +# - MANAGE_NODESDN +# - MANAGE_ROLES +# - MANAGE_ROLESMAPPING +# - MANAGE_TENANTS set -u -o pipefail +VECTOR_CONTROL_DIR=/stackable/log/_vector +SECURITY_CONFIG_DIR="$OPENSEARCH_PATH_CONF/opensearch-security" + +declare -a CONFIG_FILETYPES=( + actiongroups + allowlist + audit + config + internalusers + nodesdn + roles + rolesmapping + tenants +) + +declare -A CONFIG_FILENAME=( + [actiongroups]=action_groups.yml + [allowlist]=allowlist.yml + [audit]=audit.yml + [config]=config.yml + [internalusers]=internal_users.yml + [nodesdn]=nodes_dn.yml + [roles]=roles.yml + [rolesmapping]=roles_mapping.yml + [tenants]=tenants.yml +) + +declare -a managed_filetypes + +last_applied_config_hashes="" + function log () { level="$1" message="$2" @@ -10,6 +54,12 @@ function log () { echo "$timestamp [$level] $message" } +function debug () { + message="$*" + + log DEBUG "$message" +} + function info () { message="$*" @@ -22,33 +72,96 @@ function warn () { log WARN "$message" } -function wait_seconds () { +# Return the configuration file in SECURITY_CONFIG_DIR for the given file type +function config_file () { + filetype="$1" + + echo "$SECURITY_CONFIG_DIR/${CONFIG_FILENAME[$filetype]}" +} + +# Create a link for every configuration file in SECURITY_CONFIG_DIR +function symlink_config_files () { + for filetype in "${CONFIG_FILETYPES[@]}" + do + ln --force --symbolic \ + "$SECURITY_CONFIG_DIR/$filetype/${CONFIG_FILENAME[$filetype]}" \ + "$(config_file "$filetype")" + done +} + +# Initialize the variable managed_filetypes +function initialize_managed_config_filetypes () { + for filetype in "${CONFIG_FILETYPES[@]}" + do + envvar="MANAGE_${filetype^^}" + if test "${!envvar}" = "true" + then + info "Watch managed configuration type \"$filetype\"." + managed_filetypes+=("$filetype") + else + info "Skip unmanaged configuration type \"$filetype\"." + fi + done +} + +# Calculate the hashes of the managed configuration files +function calculate_config_hashes () { + for filetype in "${managed_filetypes[@]}" + do + file=$(config_file "$filetype") + sha256sum "$file" + done +} + +function wait_seconds_or_shutdown () { seconds="$1" - if test "$seconds" = 0 - then - info "Wait until pod is restarted..." - else - info "Wait for $seconds seconds..." - fi + debug "Wait for $seconds seconds..." - if test ! -e /stackable/log/_vector/shutdown + if test ! -e "$VECTOR_CONTROL_DIR/shutdown" then - mkdir --parents /stackable/log/_vector inotifywait \ --quiet --quiet \ --timeout "$seconds" \ --event create \ - /stackable/log/_vector + "$VECTOR_CONTROL_DIR" fi - if test -e /stackable/log/_vector/shutdown + # Only the file named "shutdown" should be created in VECTOR_CONTROL_DIR. If another file is + # created instead, this function will return early; this is acceptable and has no adverse + # effects. + if test -e "$VECTOR_CONTROL_DIR/shutdown" then info "Shut down" exit 0 fi } +function wait_for_configuration_changes_or_shutdown () { + info "Wait for security configuration changes..." + + while test "$(calculate_config_hashes)" = "$last_applied_config_hashes" + do + wait_seconds_or_shutdown 10 + done + + info "Configuration change detected" +} + +function wait_for_shutdown () { + until test ! -e "$VECTOR_CONTROL_DIR/shutdown" + do + inotifywait \ + --quiet --quiet \ + --event create \ + "$VECTOR_CONTROL_DIR" + done + + info "Shut down" + exit 0 +} + +# Return if this pod is responsible for managing the security configuration or wait for shutdown function check_pod () { POD_INDEX="${POD_NAME##*-}" @@ -62,34 +175,36 @@ function check_pod () { "configuration. The security configuration is managed by" \ "the pod \"$MANAGING_POD\"." - wait_seconds 0 + wait_for_shutdown fi } +# Initialize the security index with all (managed and unmanaged) configuration files function initialize_security_index() { info "Initialize the security index." + last_applied_config_hashes=$(calculate_config_hashes) + until plugins/opensearch-security/tools/securityadmin.sh \ - --configdir "$OPENSEARCH_PATH_CONF/opensearch-security" \ + --configdir "$SECURITY_CONFIG_DIR" \ --disable-host-name-verification \ -cacert "$OPENSEARCH_PATH_CONF/tls/ca.crt" \ -cert "$OPENSEARCH_PATH_CONF/tls/tls.crt" \ -key "$OPENSEARCH_PATH_CONF/tls/tls.key" do warn "Initializing the security index failed." - wait_seconds 10 + wait_seconds_or_shutdown 10 done } -function update_config () { - filetype="$1" - filename="$2" +# Update the security index with the managed configuration files +function update_security_index () { + last_applied_config_hashes=$(calculate_config_hashes) - file="$OPENSEARCH_PATH_CONF/opensearch-security/$filename" + for filetype in "${managed_filetypes[@]}" + do + file=$(config_file "$filetype") - envvar="MANAGE_${filetype^^}" - if test "${!envvar}" = "true" - then info "Update managed configuration type \"$filetype\"." until plugins/opensearch-security/tools/securityadmin.sh \ @@ -101,14 +216,13 @@ function update_config () { -key "$OPENSEARCH_PATH_CONF/tls/tls.key" do warn "Updating \"$filetype\" in the security index failed." - wait_seconds 10 + wait_seconds_or_shutdown 10 done - else - info "Skip unmanaged configuration type \"$filetype\"." - fi + done } -function update_security_index() { +# Initialize or update the security index +function apply_configuration_files() { info "Check the status of the security index." STATUS_CODE=$(curl \ @@ -122,30 +236,26 @@ function update_security_index() { if test "$STATUS_CODE" = "200" then info "The security index is already initialized." - - update_config actiongroups action_groups.yml - update_config allowlist allowlist.yml - update_config audit audit.yml - update_config config config.yml - update_config internalusers internal_users.yml - update_config nodesdn nodes_dn.yml - update_config roles roles.yml - update_config rolesmapping roles_mapping.yml - update_config tenants tenants.yml + update_security_index elif test "$STATUS_CODE" = "404" then initialize_security_index else warn "Checking the security index failed." - wait_seconds 10 - check_security_index + wait_seconds_or_shutdown 10 + update_security_index fi } -check_pod +# Ensure that VECTOR_CONTROL_DIR exists, so that calls to inotifywait do not fail +mkdir --parents "$VECTOR_CONTROL_DIR" -update_security_index +check_pod +symlink_config_files +initialize_managed_config_filetypes -info "Wait for security configuration changes..." -# Wait until the pod is restarted due to a change of the Secret. -wait_seconds 0 +while true +do + apply_configuration_files + wait_for_configuration_changes_or_shutdown +done diff --git a/rust/operator-binary/src/framework/builder/meta.rs b/rust/operator-binary/src/framework/builder/meta.rs index 5034004..45d85c3 100644 --- a/rust/operator-binary/src/framework/builder/meta.rs +++ b/rust/operator-binary/src/framework/builder/meta.rs @@ -1,6 +1,7 @@ use stackable_operator::{ builder::meta::OwnerReferenceBuilder, k8s_openapi::apimachinery::pkg::apis::meta::v1::OwnerReference, kube::Resource, + kvp::Annotation, }; use crate::framework::{HasName, HasUid}; @@ -28,6 +29,15 @@ pub fn ownerreference_from_resource( ) } +/// Annotation which signals the restarter to ignore this resource. +pub fn annotation_ignore_restarter() -> Annotation { + Annotation::try_from(( + "restarter.stackable.tech/ignore".to_owned(), + "true".to_owned(), + )) + .expect("should be a valid annotation") +} + #[cfg(test)] mod tests { use std::borrow::Cow; @@ -37,7 +47,10 @@ mod tests { kube::Resource, }; - use crate::framework::{HasName, HasUid, Uid, builder::meta::ownerreference_from_resource}; + use crate::framework::{ + HasName, HasUid, Uid, + builder::meta::{annotation_ignore_restarter, ownerreference_from_resource}, + }; struct Cluster { object_meta: ObjectMeta, @@ -121,4 +134,10 @@ mod tests { assert_eq!(expected_owner_reference, actual_owner_reference); } + + #[test] + fn test_annotation_ignore_restarter() { + // Test that the function does not panic + annotation_ignore_restarter(); + } } diff --git a/tests/templates/kuttl/security-config/10-security-config.yaml b/tests/templates/kuttl/security-config/10-security-config.yaml index a411ee8..91f5e63 100644 --- a/tests/templates/kuttl/security-config/10-security-config.yaml +++ b/tests/templates/kuttl/security-config/10-security-config.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: Secret metadata: name: security-config-file-internal-users + annotations: + restarter.stackable.tech/ignore: "true" stringData: internal_users.yml: | --- @@ -21,6 +23,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: security-config + annotations: + restarter.stackable.tech/ignore: "true" data: roles.yml: | --- diff --git a/tests/templates/kuttl/security-config/11-assert.yaml b/tests/templates/kuttl/security-config/11-assert.yaml index 11166f9..8084b58 100644 --- a/tests/templates/kuttl/security-config/11-assert.yaml +++ b/tests/templates/kuttl/security-config/11-assert.yaml @@ -30,7 +30,9 @@ status: apiVersion: v1 kind: ConfigMap metadata: - name: opensearch-nodes-security-config + name: opensearch-security-config + annotations: + restarter.stackable.tech/ignore: "true" data: action_groups.yml: '{"_meta":{"config_version":2,"type":"actiongroups"}}' allowlist.yml: '{"_meta":{"config_version":2,"type":"allowlist"},"config":{"enabled":false}}' diff --git a/tests/templates/kuttl/security-config/21-assert.yaml b/tests/templates/kuttl/security-config/21-assert.yaml index bffbbbc..9cab8a4 100644 --- a/tests/templates/kuttl/security-config/21-assert.yaml +++ b/tests/templates/kuttl/security-config/21-assert.yaml @@ -6,7 +6,7 @@ timeout: 120 apiVersion: v1 kind: ConfigMap metadata: - name: opensearch-nodes-security-config + name: opensearch-security-config data: action_groups.yml: '{"_meta":{"config_version":2,"type":"actiongroups"}}' allowlist.yml: '{"_meta":{"config_version":2,"type":"allowlist"},"config":{"enabled":false}}' diff --git a/tests/templates/kuttl/smoke/10-assert.yaml.j2 b/tests/templates/kuttl/smoke/10-assert.yaml.j2 index ec38871..b2e3124 100644 --- a/tests/templates/kuttl/smoke/10-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/10-assert.yaml.j2 @@ -358,7 +358,7 @@ spec: - key: action_groups.yml mode: 432 path: action_groups.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-actiongroups - configMap: defaultMode: 420 @@ -366,7 +366,7 @@ spec: - key: allowlist.yml mode: 432 path: allowlist.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-allowlist - configMap: defaultMode: 420 @@ -374,7 +374,7 @@ spec: - key: audit.yml mode: 432 path: audit.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-audit - configMap: defaultMode: 420 @@ -382,7 +382,7 @@ spec: - key: config.yml mode: 432 path: config.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-config - configMap: defaultMode: 420 @@ -390,7 +390,7 @@ spec: - key: internal_users.yml mode: 432 path: internal_users.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-internalusers - configMap: defaultMode: 420 @@ -398,7 +398,7 @@ spec: - key: nodes_dn.yml mode: 432 path: nodes_dn.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-nodesdn - configMap: defaultMode: 420 @@ -406,7 +406,7 @@ spec: - key: roles.yml mode: 432 path: roles.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-roles - configMap: defaultMode: 420 @@ -414,7 +414,7 @@ spec: - key: roles_mapping.yml mode: 432 path: roles_mapping.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-rolesmapping - configMap: defaultMode: 420 @@ -422,7 +422,7 @@ spec: - key: tenants.yml mode: 432 path: tenants.yml - name: opensearch-nodes-cluster-manager + name: opensearch-security-config name: security-config-file-tenants volumeClaimTemplates: - apiVersion: v1 @@ -834,7 +834,7 @@ spec: - key: action_groups.yml mode: 432 path: action_groups.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-actiongroups - configMap: defaultMode: 420 @@ -842,7 +842,7 @@ spec: - key: allowlist.yml mode: 432 path: allowlist.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-allowlist - configMap: defaultMode: 420 @@ -850,7 +850,7 @@ spec: - key: audit.yml mode: 432 path: audit.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-audit - configMap: defaultMode: 420 @@ -858,7 +858,7 @@ spec: - key: config.yml mode: 432 path: config.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-config - configMap: defaultMode: 420 @@ -866,7 +866,7 @@ spec: - key: internal_users.yml mode: 432 path: internal_users.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-internalusers - configMap: defaultMode: 420 @@ -874,7 +874,7 @@ spec: - key: nodes_dn.yml mode: 432 path: nodes_dn.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-nodesdn - configMap: defaultMode: 420 @@ -882,7 +882,7 @@ spec: - key: roles.yml mode: 432 path: roles.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-roles - configMap: defaultMode: 420 @@ -890,7 +890,7 @@ spec: - key: roles_mapping.yml mode: 432 path: roles_mapping.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-rolesmapping - configMap: defaultMode: 420 @@ -898,7 +898,7 @@ spec: - key: tenants.yml mode: 432 path: tenants.yml - name: opensearch-nodes-data + name: opensearch-security-config name: security-config-file-tenants volumeClaimTemplates: - apiVersion: v1 @@ -941,15 +941,16 @@ status: apiVersion: v1 kind: ConfigMap metadata: + annotations: + restarter.stackable.tech/ignore: "true" labels: app.kubernetes.io/component: nodes app.kubernetes.io/instance: opensearch app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch - app.kubernetes.io/role-group: cluster-manager app.kubernetes.io/version: {{ test_scenario['values']['opensearch'].split(',')[0] }} stackable.tech/vendor: Stackable - name: opensearch-nodes-cluster-manager + name: opensearch-security-config ownerReferences: - apiVersion: opensearch.stackable.tech/v1alpha1 controller: true @@ -962,6 +963,28 @@ data: config.yml: '{"_meta":{"config_version":2,"type":"config"},"config":{"dynamic":{"authc":{"basic_internal_auth_domain":{"authentication_backend":{"type":"intern"},"description":"Authenticate via HTTP Basic against internal users database","http_authenticator":{"challenge":true,"type":"basic"},"http_enabled":true,"order":1,"transport_enabled":true}},"authz":{}}}}' internal_users.yml: '{"_meta":{"config_version":2,"type":"internalusers"},"admin":{"backend_roles":["admin"],"description":"OpenSearch admin user","hash":"$2y$10$xRtHZFJ9QhG9GcYhRpAGpufCZYsk//nxsuel5URh0GWEBgmiI4Q/e","reserved":true}}' nodes_dn.yml: '{"_meta":{"config_version":2,"type":"nodesdn"}}' + roles.yml: '{"_meta":{"config_version":2,"type":"roles"}}' + roles_mapping.yml: '{"_meta":{"config_version":2,"type":"rolesmapping"},"all_access":{"backend_roles":["admin"],"reserved":false}}' + tenants.yml: '{"_meta":{"config_version":2,"type":"tenants"}}' +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: nodes + app.kubernetes.io/instance: opensearch + app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster + app.kubernetes.io/name: opensearch + app.kubernetes.io/role-group: cluster-manager + app.kubernetes.io/version: {{ test_scenario['values']['opensearch'].split(',')[0] }} + stackable.tech/vendor: Stackable + name: opensearch-nodes-cluster-manager + ownerReferences: + - apiVersion: opensearch.stackable.tech/v1alpha1 + controller: true + kind: OpenSearchCluster + name: opensearch +data: opensearch.yml: |- cluster.name: "opensearch" cluster.routing.allocation.disk.threshold_enabled: "false" @@ -984,9 +1007,6 @@ data: plugins.security.ssl.transport.pemcert_filepath: "{{ test_scenario['values']['opensearch_home'] }}/config/tls/internal/tls.crt" plugins.security.ssl.transport.pemkey_filepath: "{{ test_scenario['values']['opensearch_home'] }}/config/tls/internal/tls.key" plugins.security.ssl.transport.pemtrustedcas_filepath: "{{ test_scenario['values']['opensearch_home'] }}/config/tls/internal/ca.crt" - roles.yml: '{"_meta":{"config_version":2,"type":"roles"}}' - roles_mapping.yml: '{"_meta":{"config_version":2,"type":"rolesmapping"},"all_access":{"backend_roles":["admin"],"reserved":false}}' - tenants.yml: '{"_meta":{"config_version":2,"type":"tenants"}}' --- apiVersion: v1 kind: ConfigMap @@ -1006,12 +1026,6 @@ metadata: kind: OpenSearchCluster name: opensearch data: - action_groups.yml: '{"_meta":{"config_version":2,"type":"actiongroups"}}' - allowlist.yml: '{"_meta":{"config_version":2,"type":"allowlist"},"config":{"enabled":false}}' - audit.yml: '{"_meta":{"config_version":2,"type":"audit"},"config":{"enabled":false}}' - config.yml: '{"_meta":{"config_version":2,"type":"config"},"config":{"dynamic":{"authc":{"basic_internal_auth_domain":{"authentication_backend":{"type":"intern"},"description":"Authenticate via HTTP Basic against internal users database","http_authenticator":{"challenge":true,"type":"basic"},"http_enabled":true,"order":1,"transport_enabled":true}},"authz":{}}}}' - internal_users.yml: '{"_meta":{"config_version":2,"type":"internalusers"},"admin":{"backend_roles":["admin"],"description":"OpenSearch admin user","hash":"$2y$10$xRtHZFJ9QhG9GcYhRpAGpufCZYsk//nxsuel5URh0GWEBgmiI4Q/e","reserved":true}}' - nodes_dn.yml: '{"_meta":{"config_version":2,"type":"nodesdn"}}' opensearch.yml: |- cluster.name: "opensearch" cluster.routing.allocation.disk.threshold_enabled: "false" @@ -1034,9 +1048,6 @@ data: plugins.security.ssl.transport.pemcert_filepath: "{{ test_scenario['values']['opensearch_home'] }}/config/tls/internal/tls.crt" plugins.security.ssl.transport.pemkey_filepath: "{{ test_scenario['values']['opensearch_home'] }}/config/tls/internal/tls.key" plugins.security.ssl.transport.pemtrustedcas_filepath: "{{ test_scenario['values']['opensearch_home'] }}/config/tls/internal/ca.crt" - roles.yml: '{"_meta":{"config_version":2,"type":"roles"}}' - roles_mapping.yml: '{"_meta":{"config_version":2,"type":"rolesmapping"},"all_access":{"backend_roles":["admin"],"reserved":false}}' - tenants.yml: '{"_meta":{"config_version":2,"type":"tenants"}}' --- apiVersion: v1 kind: Service