forked from rook/rook
Given that Ceph Quincy (v17) is past end of life, remove Quincy from the supported Ceph versions, examples, and documentation. Supported versions now include only Reef and Squid. Signed-off-by: Travis Nielsen <tnielsen@redhat.com>
295 lines
9.5 KiB
Go
295 lines
9.5 KiB
Go
/*
|
||
Copyright 2019 The Rook Authors. All rights reserved.
|
||
|
||
Licensed under the Apache License, Version 2.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
You may obtain a copy of the License at
|
||
|
||
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
Unless required by applicable law or agreed to in writing, software
|
||
distributed under the License is distributed on an "AS IS" BASIS,
|
||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
*/
|
||
|
||
package object
|
||
|
||
import (
|
||
"fmt"
|
||
"path"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/pkg/errors"
|
||
cephconfig "github.com/rook/rook/pkg/operator/ceph/config"
|
||
"github.com/rook/rook/pkg/operator/ceph/config/keyring"
|
||
v1 "k8s.io/api/core/v1"
|
||
)
|
||
|
||
const (
|
||
keyringTemplate = `
|
||
[%s]
|
||
key = %s
|
||
caps mon = "allow rw"
|
||
caps osd = "allow rwx"
|
||
`
|
||
|
||
caBundleVolumeName = "rook-ceph-custom-ca-bundle"
|
||
caBundleUpdatedVolumeName = "rook-ceph-ca-bundle-updated"
|
||
caBundleTrustedDir = "/etc/pki/ca-trust/"
|
||
caBundleSourceCustomDir = caBundleTrustedDir + "source/anchors/"
|
||
caBundleExtractedDir = caBundleTrustedDir + "extracted/"
|
||
caBundleKeyName = "cabundle"
|
||
caBundleFileName = "custom-ca-bundle.crt"
|
||
certVolumeName = "rook-ceph-rgw-cert"
|
||
certDir = "/etc/ceph/private"
|
||
certKeyName = "cert"
|
||
certFilename = "rgw-cert.pem"
|
||
certKeyFileName = "rgw-key.pem"
|
||
rgwPortInternalPort int32 = 8080
|
||
ServiceServingCertCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt"
|
||
HttpTimeOut = time.Second * 15
|
||
rgwVaultVolumeName = "rgw-vault-volume"
|
||
rgwVaultDirName = "/etc/vault/rgw/"
|
||
)
|
||
|
||
var (
|
||
rgwFrontendName = "beast"
|
||
)
|
||
|
||
func (c *clusterConfig) portString() string {
|
||
var portString string
|
||
|
||
port := c.store.Spec.Gateway.Port
|
||
if port != 0 {
|
||
if !c.store.Spec.IsHostNetwork(c.clusterSpec) {
|
||
port = rgwPortInternalPort
|
||
}
|
||
portString = fmt.Sprintf("port=%s", strconv.Itoa(int(port)))
|
||
}
|
||
if c.store.Spec.IsTLSEnabled() {
|
||
certPath := path.Join(certDir, certFilename)
|
||
// This is the beast backend
|
||
// Config is: http://docs.ceph.com/docs/master/radosgw/frontends/#id3
|
||
if port != 0 {
|
||
portString = fmt.Sprintf("%s ssl_port=%d ssl_certificate=%s",
|
||
portString, c.store.Spec.Gateway.SecurePort, certPath)
|
||
} else {
|
||
portString = fmt.Sprintf("ssl_port=%d ssl_certificate=%s",
|
||
c.store.Spec.Gateway.SecurePort, certPath)
|
||
}
|
||
secretType, _ := c.rgwTLSSecretType(c.store.Spec.Gateway.SSLCertificateRef)
|
||
if c.store.Spec.GetServiceServingCert() != "" || secretType == v1.SecretTypeTLS {
|
||
privateKey := path.Join(certDir, certKeyFileName)
|
||
portString = fmt.Sprintf("%s ssl_private_key=%s", portString, privateKey)
|
||
}
|
||
}
|
||
return portString
|
||
}
|
||
|
||
func generateCephXUser(name string) string {
|
||
user := strings.TrimPrefix(name, AppName)
|
||
return "client.rgw" + strings.Replace(user, "-", ".", -1)
|
||
}
|
||
|
||
func (c *clusterConfig) generateKeyring(rgwConfig *rgwConfig) (string, error) {
|
||
user := generateCephXUser(rgwConfig.ResourceName)
|
||
/* TODO: this says `osd allow rwx` while template says `osd allow *`; which is correct? */
|
||
access := []string{"osd", "allow rwx", "mon", "allow rw"}
|
||
s := keyring.GetSecretStore(c.context, c.clusterInfo, c.ownerInfo)
|
||
|
||
key, err := s.GenerateKey(user, access)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
keyring := fmt.Sprintf(keyringTemplate, user, key)
|
||
return keyring, s.CreateOrUpdate(rgwConfig.ResourceName, keyring)
|
||
}
|
||
|
||
func mapKeystoneSecretToConfig(cfg map[string]string, secret *v1.Secret) (map[string]string, error) {
|
||
|
||
requiredKeys := []string{"OS_PROJECT_DOMAIN_NAME",
|
||
"OS_USER_DOMAIN_NAME",
|
||
"OS_PROJECT_DOMAIN_NAME",
|
||
"OS_USER_DOMAIN_NAME",
|
||
"OS_PROJECT_NAME",
|
||
"OS_USERNAME",
|
||
"OS_PASSWORD"}
|
||
|
||
data := make(map[string]string)
|
||
for key, value := range secret.Data {
|
||
data[key] = string(value[:])
|
||
}
|
||
|
||
for _, key := range requiredKeys {
|
||
if value, ok := data[key]; !ok || value == "" {
|
||
return nil, errors.New(fmt.Sprintf("Missing or empty %s", key))
|
||
}
|
||
}
|
||
|
||
if authType, ok := data["OS_AUTH_TYPE"]; ok && authType != "password" {
|
||
return nil, errors.New(fmt.Sprintf("OS_AUTHTYPE %s is not supported. Only OS_AUTH_TYPE password is supported!", authType))
|
||
}
|
||
|
||
if apiVersion, ok := data["OS_IDENTITY_API_VERSION"]; ok && apiVersion != "3" {
|
||
return nil, errors.New(fmt.Sprintf("OS_IDENTITY_API_VERSION %s is not supported! Only OS_IDENTITY_API_VERSION 3 is supported!", apiVersion))
|
||
}
|
||
|
||
if data["OS_PROJECT_DOMAIN_NAME"] != data["OS_USER_DOMAIN_NAME"] {
|
||
return nil, errors.New("The user domain name does not match the project domain name.")
|
||
}
|
||
|
||
cfg["rgw_keystone_api_version"] = data["OS_IDENTITY_API_VERSION"]
|
||
cfg["rgw_keystone_admin_domain"] = data["OS_PROJECT_DOMAIN_NAME"]
|
||
cfg["rgw_keystone_admin_project"] = data["OS_PROJECT_NAME"]
|
||
cfg["rgw_keystone_admin_user"] = data["OS_USERNAME"]
|
||
cfg["rgw_keystone_admin_password"] = data["OS_PASSWORD"]
|
||
|
||
return cfg, nil
|
||
}
|
||
|
||
func (c *clusterConfig) setFlagsMonConfigStore(rgwConfig *rgwConfig) error {
|
||
|
||
monStore := cephconfig.GetMonStore(c.context, c.clusterInfo)
|
||
who := generateCephXUser(rgwConfig.ResourceName)
|
||
configOptions := make(map[string]string)
|
||
|
||
configOptions["rgw_run_sync_thread"] = "true"
|
||
if c.store.Spec.Gateway.DisableMultisiteSyncTraffic {
|
||
configOptions["rgw_run_sync_thread"] = "false"
|
||
}
|
||
|
||
configOptions["rgw_log_nonexistent_bucket"] = "true"
|
||
configOptions["rgw_log_object_name_utc"] = "true"
|
||
configOptions["rgw_enable_usage_log"] = "true"
|
||
configOptions["rgw_zone"] = rgwConfig.Zone
|
||
configOptions["rgw_zonegroup"] = rgwConfig.ZoneGroup
|
||
|
||
configOptions, err := configureKeystoneAuthentication(rgwConfig, configOptions)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
s3disabled := false
|
||
if s3 := rgwConfig.Protocols.S3; s3 != nil {
|
||
if s3.Enabled != nil && !*s3.Enabled {
|
||
s3disabled = true
|
||
}
|
||
|
||
if s3.AuthUseKeystone != nil {
|
||
configOptions["rgw_s3_auth_use_keystone"] = fmt.Sprintf("%t", *s3.AuthUseKeystone)
|
||
}
|
||
|
||
}
|
||
|
||
if swift := rgwConfig.Protocols.Swift; swift != nil {
|
||
|
||
if swift.AccountInUrl != nil {
|
||
configOptions["rgw_swift_account_in_url"] = fmt.Sprintf("%t", *swift.AccountInUrl)
|
||
}
|
||
|
||
if swift.UrlPrefix != nil {
|
||
configOptions["rgw_swift_url_prefix"] = *swift.UrlPrefix
|
||
|
||
if configOptions["rgw_swift_url_prefix"] == "/" {
|
||
logger.Warning("Forcefully disabled S3 as the swift prefix is given as a slash /. Ignoring any S3 options (including Enabled=true)!")
|
||
// this will later on disable the s3 api using the rgw_enable_apis setting
|
||
s3disabled = true
|
||
}
|
||
|
||
}
|
||
if swift.VersioningEnabled != nil {
|
||
configOptions["rgw_swift_versioning_enabled"] = fmt.Sprintf("%t", *swift.VersioningEnabled)
|
||
}
|
||
|
||
}
|
||
|
||
if s3disabled {
|
||
// XXX: how to handle enabled APIs? We only configure s3 and
|
||
// swift in the resource, `admin` is required for the operator to
|
||
// work, `swift_auth` is required to access swift without keystone
|
||
// – not sure about the additional APIs
|
||
// https://docs.ceph.com/en/latest/radosgw/config-ref/#confval-rgw_enable_apis
|
||
// see also https://docs.ceph.com/en/octopus/radosgw/config-ref/#swift-settings on disabling s3
|
||
// when using '/' as prefix
|
||
|
||
// Swift was enabled so far already by default, so perhaps better
|
||
// not change that if someone relies on it.
|
||
|
||
configOptions["rgw_enable_apis"] = "s3website, swift, swift_auth, admin, sts, iam, notifications"
|
||
}
|
||
|
||
for flag, val := range configOptions {
|
||
err := monStore.Set(who, flag, val)
|
||
if err != nil {
|
||
return errors.Wrapf(err, "failed to set %q to %q on %q", flag, val, who)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func configureKeystoneAuthentication(rgwConfig *rgwConfig, configOptions map[string]string) (map[string]string, error) {
|
||
|
||
keystone := rgwConfig.Auth.Keystone
|
||
if keystone == nil {
|
||
logger.Debug("Authentication with keystone is disabled")
|
||
return configOptions, nil
|
||
}
|
||
|
||
logger.Info("Configuring authentication with keystone")
|
||
|
||
configOptions["rgw_keystone_url"] = keystone.Url
|
||
configOptions["rgw_keystone_accepted_roles"] = strings.Join(keystone.AcceptedRoles, ",")
|
||
if keystone.ImplicitTenants != "" {
|
||
lc := strings.ToLower(string(keystone.ImplicitTenants))
|
||
|
||
// only four values are valid here (swift, s3, true and false)
|
||
//
|
||
// https://docs.ceph.com/en/latest/radosgw/keystone/#integrating-with-openstack-keystone
|
||
if lc != "true" &&
|
||
lc != "false" &&
|
||
lc != "swift" &&
|
||
lc != "s3" {
|
||
|
||
return nil, errors.New(fmt.Sprintf("ImplicitTenantSetting can only be 'swift', 's3', 'true' or 'false', not %q", string(keystone.ImplicitTenants)))
|
||
|
||
}
|
||
|
||
configOptions["rgw_keystone_implicit_tenants"] = lc
|
||
|
||
}
|
||
|
||
if keystone.TokenCacheSize != nil {
|
||
configOptions["rgw_keystone_token_cache_size"] = fmt.Sprintf("%d", *keystone.TokenCacheSize)
|
||
}
|
||
|
||
if rgwConfig.KeystoneSecret == nil {
|
||
return nil, errors.New("Cannot find keystone secret")
|
||
}
|
||
|
||
var err error
|
||
configOptions, err = mapKeystoneSecretToConfig(configOptions, rgwConfig.KeystoneSecret)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, fmt.Sprintf("error mapping keystone secret %s to config", rgwConfig.KeystoneSecret.Name))
|
||
}
|
||
|
||
return configOptions, nil
|
||
}
|
||
|
||
func (c *clusterConfig) deleteFlagsMonConfigStore(rgwName string) error {
|
||
monStore := cephconfig.GetMonStore(c.context, c.clusterInfo)
|
||
who := generateCephXUser(rgwName)
|
||
err := monStore.DeleteDaemon(who)
|
||
if err != nil {
|
||
return errors.Wrapf(err, "failed to delete rgw config for %q in mon configuration database", who)
|
||
}
|
||
|
||
logger.Infof("successfully deleted rgw config for %q in mon configuration database", who)
|
||
return nil
|
||
}
|