Merge pull request #15489 from rook/mergify/bp/release-1.16/pr-15426

object: allow overriding rgw config value from secret (backport #15426)
This commit is contained in:
Travis Nielsen authored and GitHub committed 2025-03-05 13:51:01 -07:00
commit 2355fddf4d
8 files changed
+204 -3

No files matched your search

@@ -390,6 +390,10 @@ spec:
debug_rgw: "10" # int
# debug-rgw: "20" # equivalent config keys can have dashes or underscores
rgw_s3_auth_use_ldap: "true" # bool
rgwConfigFromSecret:
rgw_keystone_barbican_password: # name of rgw option with secret value
name: "barbican-secret" # name of K8s secret
key: "password" # key of secret value in K8s secret.Data map[string]string
rgwCommandFlags:
rgw_dmclock_auth_res: "100.0" # float
rgw_d4n_l1_datacache_persistent_path: /var/log/rook/rgwd4ncache # string
@@ -397,12 +401,13 @@ spec:
```
* `rgwConfig` - These configurations are applied and modified at runtime, without RGW restart.
* `rgwConfigFromSecret` - same as `rgwConfig` but config value is referenced from k8s secret.
* `rgwCommandFlags` - These configurations are applied as CLI arguments and result in RGW daemons
restarting when updates are applied. Restarts are desired behavior for some RGW configs.
!!! note
Once an `rgwConfig` is set, it will not be removed from Ceph's central config store when removed
from the `rgwConfig` spec. Be sure to specifically set values back to their defaults once done.
Once an `rgwConfig` or `rgwConfigFromSecret` is set, it will not be removed from Ceph's central config store when removed
from the `rgwConfig` or `rgwConfigFromSecret` spec. Be sure to specifically set values back to their defaults once done.
With this in mind, `rgwCommandFlags` may be a better choice for temporary config values like
debug levels.
@@ -455,6 +460,34 @@ data:
"bindpass.secret": aGVsbG8ud29ybGQK # hello.world
```
### Example - usage with `rgwConfigFromSecret`
The sample configuration below demonstrates how to securely handle secret configuration parameters when setting up RGW for [Barbican integration](https://docs.ceph.com/en/latest/radosgw/config-ref/#barbican-settings).
```yaml
# ...
gateway:
# ...
rgwConfig:
rgw_barbican_url: "http://barbican.example.com:9311"
rgw_keystone_barbican_domain: "domain"
rgw_keystone_barbican_project: "project"
rgw_keystone_barbican_user: "user"
rgwConfigFromSecret:
rgw_keystone_barbican_password:
name: "barbican-secret"
key: "password"
---
apiVersion: v1
kind: Secret
metadata:
name: barbican-secret
namespace: rook-ceph
type: Opaque
data:
"password": aGVsbG8ud29ybGQK # hello.world
```
## Deleting a CephObjectStore
During deletion of a CephObjectStore resource, Rook protects against accidental or premature
+17
View File
@@ -7203,6 +7203,23 @@ applied. Use with caution.</p>
</tr>
<tr>
<td>
<code>rgwConfigFromSecret</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secretkeyselector-v1-core">
map[string]k8s.io/api/core/v1.SecretKeySelector
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
Values are modified at runtime without RGW restart.
This feature is intended for advanced users. It allows breaking configurations to be easily
applied. Use with caution.</p>
</td>
</tr>
<tr>
<td>
<code>rgwCommandFlags</code><br/>
<em>
map[string]string
@@ -11953,6 +11953,36 @@ spec:
applied. Use with caution.
nullable: true
type: object
rgwConfigFromSecret:
additionalProperties:
description: SecretKeySelector selects a key of a Secret.
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
x-kubernetes-map-type: atomic
description: |-
RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
Values are modified at runtime without RGW restart.
This feature is intended for advanced users. It allows breaking configurations to be easily
applied. Use with caution.
nullable: true
type: object
securePort:
description: The port the rgw service will be listening on (https)
format: int32
+30
View File
@@ -11944,6 +11944,36 @@ spec:
applied. Use with caution.
nullable: true
type: object
rgwConfigFromSecret:
additionalProperties:
description: SecretKeySelector selects a key of a Secret.
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
x-kubernetes-map-type: atomic
description: |-
RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
Values are modified at runtime without RGW restart.
This feature is intended for advanced users. It allows breaking configurations to be easily
applied. Use with caution.
nullable: true
type: object
securePort:
description: The port the rgw service will be listening on (https)
format: int32
+8
View File
@@ -1748,6 +1748,14 @@ type GatewaySpec struct {
// +optional
RgwConfig map[string]string `json:"rgwConfig,omitempty"`
// RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
// Values are modified at runtime without RGW restart.
// This feature is intended for advanced users. It allows breaking configurations to be easily
// applied. Use with caution.
// +nullable
// +optional
RgwConfigFromSecret map[string]v1.SecretKeySelector `json:"rgwConfigFromSecret,omitempty"`
// RgwCommandFlags sets Ceph RGW config values for the gateway clients that serve this object
// store. Values are modified at RGW startup, resulting in RGW pod restarts.
// This feature is intended for advanced users. It allows breaking configurations to be easily
@@ -2743,6 +2743,13 @@ func (in *GatewaySpec) DeepCopyInto(out *GatewaySpec) {
(*out)[key] = val
}
}
if in.RgwConfigFromSecret != nil {
in, out := &in.RgwConfigFromSecret, &out.RgwConfigFromSecret
*out = make(map[string]corev1.SecretKeySelector, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.RgwCommandFlags != nil {
in, out := &in.RgwCommandFlags, &out.RgwCommandFlags
*out = make(map[string]string, len(*in))
+28 -1
View File
@@ -27,6 +27,7 @@ import (
cephconfig "github.com/rook/rook/pkg/operator/ceph/config"
"github.com/rook/rook/pkg/operator/ceph/config/keyring"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
@@ -215,12 +216,38 @@ func (c *clusterConfig) generateMonConfigOptions(rgwConfig *rgwConfig) (map[stri
for flag, val := range c.store.Spec.Gateway.RgwConfig {
if currVal, ok := configOptions[flag]; ok {
// RGW might break with some user-specified config overrides; log clearly to help triage
logger.Infof("overriding object store %q RGW config option %q (current value %q) with user-specified rgwConfig %q",
logger.Infof("overriding object store %q RGW config option %q with user-specified rgwConfig",
fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag)
logger.Tracef("overriding object store %q RGW config option %q (current value %q) with user-specified rgwConfig %q",
fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, currVal, val)
}
configOptions[flag] = val
}
for flag, val := range c.store.Spec.Gateway.RgwConfigFromSecret {
if val.Key == "" || val.Name == "" {
return nil, fmt.Errorf("invalid object store %q rgwConfigFromSecret value %q=%+v", fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, val)
}
secret, err := c.context.Clientset.CoreV1().Secrets(c.clusterInfo.Namespace).Get(c.clusterInfo.Context, val.Name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("unable to lookup secret for object store %q rgwConfigFromSecret %q=%+v", fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, val)
}
secretVal, ok := secret.Data[val.Key]
if !ok {
return nil, fmt.Errorf("unable to lookup secret for object store %q rgwConfigFromSecret %q: key %q not found in secret %q",
fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, val.Key, val.Name)
}
if currVal, ok := configOptions[flag]; ok {
// RGW might break with some user-specified config overrides; log clearly to help triage
logger.Infof("overriding object store %q RGW config option %q with user-specified rgwConfigFromSecret",
fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag)
logger.Tracef("overriding object store %q RGW config option %q (current value %q) with user-specified rgwConfigFromSecret %q",
fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, currVal, secretVal)
}
configOptions[flag] = string(secretVal)
}
return configOptions, nil
}
+49
View File
@@ -17,6 +17,7 @@ limitations under the License.
package object
import (
"context"
"testing"
cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
@@ -25,6 +26,8 @@ import (
cephver "github.com/rook/rook/pkg/operator/ceph/version"
"github.com/rook/rook/pkg/operator/test"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func newConfig(t *testing.T) *clusterConfig {
@@ -182,3 +185,49 @@ func Test_clusterConfig_generateMonConfigOptions(t *testing.T) {
})
}
}
func TestRgwConfigFromSecret(t *testing.T) {
objContext := &Context{
Context: &clusterd.Context{
Clientset: test.New(t, 3),
},
clusterInfo: cephclient.AdminTestClusterInfo("rook-ceph"),
}
objectStore := simpleStore()
objectStore.Spec.Gateway.RgwConfigFromSecret = map[string]v1.SecretKeySelector{
"rgw_secret_conf_name": {
LocalObjectReference: v1.LocalObjectReference{
Name: "my-secret",
},
Key: "secKey",
},
}
c := &clusterConfig{
store: objectStore,
context: objContext.Context,
clusterInfo: objContext.clusterInfo,
}
rgwConfig := &rgwConfig{}
_, err := c.generateMonConfigOptions(rgwConfig)
assert.Error(t, err, "error if secret not exists")
s := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
Namespace: "rook-ceph",
},
Data: map[string][]byte{
"secKey": []byte("secVal"),
},
}
_, err = objContext.Context.Clientset.CoreV1().Secrets(objContext.clusterInfo.Namespace).Create(context.TODO(), s, metav1.CreateOptions{})
assert.NoError(t, err, "create secret")
got, err := c.generateMonConfigOptions(rgwConfig)
assert.NoError(t, err)
assert.EqualValues(t, "secVal", got["rgw_secret_conf_name"])
}