From 7bb72a0171ad6126f3d38b55df524581e475b4eb Mon Sep 17 00:00:00 2001 From: Blaine Gardner Date: Thu, 10 Oct 2024 16:15:28 -0600 Subject: [PATCH] object: also use system certs for validating RGW cert When generating the HTTP client used for RGW admin ops, use both system certs as well as the user-given cert. As a real world example, admins may use ACME to rotate Letsencrypt certs every 2 months. For an external CephObjectStore, the cert used by Rook and RGW may not be rotated at the same time. This can cause the Rook operator to fail CephObjectStore reconciliation until both certs agree. When Rook also relies on system certs in the container, Rook's reconciliation will not have reconciliation failures because Letsencrypt's well-known and trusted root certificates can be loaded from the system to validate the RGW's newly-rotated cert. Signed-off-by: Blaine Gardner --- .../Cluster/external-cluster/provider-export.md | 2 +- .../create-external-cluster-resources.py | 2 +- pkg/operator/ceph/object/s3-handlers.go | 16 +++++++++++----- pkg/operator/ceph/object/s3-handlers_test.go | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Documentation/CRDs/Cluster/external-cluster/provider-export.md b/Documentation/CRDs/Cluster/external-cluster/provider-export.md index db64ab958..c8e2195a8 100644 --- a/Documentation/CRDs/Cluster/external-cluster/provider-export.md +++ b/Documentation/CRDs/Cluster/external-cluster/provider-export.md @@ -16,7 +16,7 @@ python3 create-external-cluster-resources.py --rbd-data-pool-name -- * `--alias-rbd-data-pool-name`: Provides an alias for the RBD data pool name, necessary if a special character is present in the pool name such as a period or underscore * `--rgw-endpoint`: (optional) The RADOS Gateway endpoint in the format `:` or `:`. * `--rgw-pool-prefix`: (optional) The prefix of the RGW pools. If not specified, the default prefix is `default` -* `--rgw-tls-cert-path`: (optional) RADOS Gateway endpoint TLS certificate file path +* `--rgw-tls-cert-path`: (optional) RADOS Gateway endpoint TLS certificate (or intermediate signing certificate) file path * `--rgw-skip-tls`: (optional) Ignore TLS certification validation when a self-signed certificate is provided (NOT RECOMMENDED) * `--rbd-metadata-ec-pool-name`: (optional) Provides the name of erasure coded RBD metadata pool, used for creating ECRBDStorageClass. * `--monitoring-endpoint`: (optional) Ceph Manager prometheus exporter endpoints (comma separated list of IP entries of active and standby mgrs) diff --git a/deploy/examples/create-external-cluster-resources.py b/deploy/examples/create-external-cluster-resources.py index 268176c4f..d059a1456 100644 --- a/deploy/examples/create-external-cluster-resources.py +++ b/deploy/examples/create-external-cluster-resources.py @@ -377,7 +377,7 @@ class RadosJSON: "--rgw-tls-cert-path", default="", required=False, - help="RADOS Gateway endpoint TLS certificate", + help="RADOS Gateway endpoint TLS certificate (or intermediate signing certificate)", ) output_group.add_argument( "--rgw-skip-tls", diff --git a/pkg/operator/ceph/object/s3-handlers.go b/pkg/operator/ceph/object/s3-handlers.go index a40614c70..13eccb826 100644 --- a/pkg/operator/ceph/object/s3-handlers.go +++ b/pkg/operator/ceph/object/s3-handlers.go @@ -199,12 +199,18 @@ func (s *S3Agent) DeleteObjectInBucket(bucketname string, key string) (bool, err func BuildTransportTLS(tlsCert []byte, insecure bool) *http.Transport { //nolint:gosec // is enabled only for testing - tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12, InsecureSkipVerify: insecure} - if len(tlsCert) > 0 { - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(tlsCert) - tlsConfig.RootCAs = caCertPool + tlsConfig := &tls.Config{InsecureSkipVerify: insecure} + var caCertPool *x509.CertPool + var err error + caCertPool, err = x509.SystemCertPool() + if err != nil { + logger.Warningf("failed to load system cert pool; continuing without loading system certs") + caCertPool = x509.NewCertPool() // start with empty cert pool instead } + if len(tlsCert) > 0 { + caCertPool.AppendCertsFromPEM(tlsCert) + } + tlsConfig.RootCAs = caCertPool return &http.Transport{ TLSClientConfig: tlsConfig, diff --git a/pkg/operator/ceph/object/s3-handlers_test.go b/pkg/operator/ceph/object/s3-handlers_test.go index 308575397..f7f20fe0c 100644 --- a/pkg/operator/ceph/object/s3-handlers_test.go +++ b/pkg/operator/ceph/object/s3-handlers_test.go @@ -53,7 +53,7 @@ func TestNewS3Agent(t *testing.T) { insecure := true s3Agent, err := newS3Agent(accessKey, secretKey, endpoint, debug, nil, insecure) assert.NoError(t, err) - assert.Nil(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.RootCAs) + assert.NotNil(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.RootCAs) // still includes sys certs assert.True(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify) assert.False(t, *s3Agent.Client.Config.DisableSSL) })