ceph-csi/internal/util/credentials.go
Humble Chirammal b49bf4b987 rbd: parse migration secret and set it for controller server operations
This commit adds a couple of helper functions to parse the migration
request secret and set it for further csi driver operations.

More details:

The intree secret has a data field called "key" which is the base64
admin secret key. The ceph CSI driver currently expect the secret to
contain data field "UserKey" for the equivalant. The CSI driver also
expect the "UserID" field which is not available in the in-tree secret
by deafult. This missing userID will be filled (if the username differ
than 'admin') in the migration secret as 'adminId' field in the
migration request, this commit adds the logic to parse this migration
secret as below:

"key" field value will be picked up from the migraion secret to "UserKey"
field.
"adminId" field value will be picked up from the migration secret to "UserID"
field

if `adminId` field is nil or not set, `UserID` field will be filled with
default value ie `admin`.The above logic get activated only when the secret
is a migration secret, otherwise skipped to the normal workflow as we have
today.

Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
2021-10-27 18:35:00 +00:00

156 lines
4.6 KiB
Go

/*
Copyright 2018 The Ceph-CSI Authors.
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 util
import (
"errors"
"fmt"
"io/ioutil"
"os"
)
const (
credUserID = "userID"
credUserKey = "userKey"
credAdminID = "adminID"
credAdminKey = "adminKey"
credMonitors = "monitors"
tmpKeyFileLocation = "/tmp/csi/keys"
tmpKeyFileNamePrefix = "keyfile-"
migUserName = "admin"
migUserID = "adminId"
migUserKey = "key"
)
// Credentials struct represents credentials to access the ceph cluster.
type Credentials struct {
ID string
KeyFile string
}
func storeKey(key string) (string, error) {
tmpfile, err := ioutil.TempFile(tmpKeyFileLocation, tmpKeyFileNamePrefix)
if err != nil {
return "", fmt.Errorf("error creating a temporary keyfile: %w", err)
}
defer func() {
if err != nil {
// don't complain about unhandled error
_ = os.Remove(tmpfile.Name())
}
}()
if _, err = tmpfile.Write([]byte(key)); err != nil {
return "", fmt.Errorf("error writing key to temporary keyfile: %w", err)
}
keyFile := tmpfile.Name()
if keyFile == "" {
err = fmt.Errorf("error reading temporary filename for key: %w", err)
return "", err
}
if err = tmpfile.Close(); err != nil {
return "", fmt.Errorf("error closing temporary filename: %w", err)
}
return keyFile, nil
}
func newCredentialsFromSecret(idField, keyField string, secrets map[string]string) (*Credentials, error) {
var (
c = &Credentials{}
ok bool
)
if len(secrets) == 0 {
return nil, errors.New("provided secret is empty")
}
if c.ID, ok = secrets[idField]; !ok {
return nil, fmt.Errorf("missing ID field '%s' in secrets", idField)
}
key := secrets[keyField]
if key == "" {
return nil, fmt.Errorf("missing key field '%s' in secrets", keyField)
}
keyFile, err := storeKey(key)
if err == nil {
c.KeyFile = keyFile
}
return c, err
}
// DeleteCredentials removes the KeyFile.
func (cr *Credentials) DeleteCredentials() {
// don't complain about unhandled error
_ = os.Remove(cr.KeyFile)
}
// NewUserCredentials creates new user credentials from secret.
func NewUserCredentials(secrets map[string]string) (*Credentials, error) {
return newCredentialsFromSecret(credUserID, credUserKey, secrets)
}
// NewAdminCredentials creates new admin credentials from secret.
func NewAdminCredentials(secrets map[string]string) (*Credentials, error) {
return newCredentialsFromSecret(credAdminID, credAdminKey, secrets)
}
// GetMonValFromSecret returns monitors from secret.
func GetMonValFromSecret(secrets map[string]string) (string, error) {
if mons, ok := secrets[credMonitors]; ok {
return mons, nil
}
return "", fmt.Errorf("missing %q", credMonitors)
}
// ParseAndSetSecretMapFromMigSecret parse the secretmap from the migration request and return
// newsecretmap with the userID and userKey fields set.
func ParseAndSetSecretMapFromMigSecret(secretmap map[string]string) (map[string]string, error) {
newSecretMap := make(map[string]string)
// parse and set userKey
if !IsMigrationSecret(secretmap) {
return nil, errors.New("passed secret map does not contain user key or it is nil")
}
newSecretMap[credUserKey] = secretmap[migUserKey]
// parse and set the userID
newSecretMap[credUserID] = migUserName
if secretmap[migUserID] != "" {
newSecretMap[credUserID] = secretmap[migUserID]
}
return newSecretMap, nil
}
// IsMigrationSecret validates if the passed in secretmap is a secret
// of a migration volume request. The migration secret carry a field
// called `key` which is the equivalent of `userKey` which is what we
// check here for identifying the secret.
func IsMigrationSecret(passedSecretMap map[string]string) bool {
// the below 'nil' check is an extra measure as the request validators like
// ValidateNodeStageVolumeRequest() already does the nil check, however considering
// this function can be called independently with a map of secret values
// it is good to have this check in place, also it gives clear error about this
// was hit on migration request compared to general one.
return len(passedSecretMap) != 0 && passedSecretMap[migUserKey] != ""
}