...

Source file src/k8s.io/kubernetes/pkg/volume/secret/secret.go

Documentation: k8s.io/kubernetes/pkg/volume/secret

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package secret
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"k8s.io/klog/v2"
    23  	"k8s.io/mount-utils"
    24  	utilstrings "k8s.io/utils/strings"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	"k8s.io/kubernetes/pkg/volume"
    31  	volumeutil "k8s.io/kubernetes/pkg/volume/util"
    32  )
    33  
    34  // ProbeVolumePlugins is the entry point for plugin detection in a package.
    35  func ProbeVolumePlugins() []volume.VolumePlugin {
    36  	return []volume.VolumePlugin{&secretPlugin{}}
    37  }
    38  
    39  const (
    40  	secretPluginName = "kubernetes.io/secret"
    41  )
    42  
    43  // secretPlugin implements the VolumePlugin interface.
    44  type secretPlugin struct {
    45  	host      volume.VolumeHost
    46  	getSecret func(namespace, name string) (*v1.Secret, error)
    47  }
    48  
    49  var _ volume.VolumePlugin = &secretPlugin{}
    50  
    51  func wrappedVolumeSpec() volume.Spec {
    52  	return volume.Spec{
    53  		Volume: &v1.Volume{VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{Medium: v1.StorageMediumMemory}}},
    54  	}
    55  }
    56  
    57  func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
    58  	return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(secretPluginName), volName)
    59  }
    60  
    61  func (plugin *secretPlugin) Init(host volume.VolumeHost) error {
    62  	plugin.host = host
    63  	plugin.getSecret = host.GetSecretFunc()
    64  	return nil
    65  }
    66  
    67  func (plugin *secretPlugin) GetPluginName() string {
    68  	return secretPluginName
    69  }
    70  
    71  func (plugin *secretPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
    72  	volumeSource, _ := getVolumeSource(spec)
    73  	if volumeSource == nil {
    74  		return "", fmt.Errorf("Spec does not reference a Secret volume type")
    75  	}
    76  
    77  	return volumeSource.SecretName, nil
    78  }
    79  
    80  func (plugin *secretPlugin) CanSupport(spec *volume.Spec) bool {
    81  	return spec.Volume != nil && spec.Volume.Secret != nil
    82  }
    83  
    84  func (plugin *secretPlugin) RequiresRemount(spec *volume.Spec) bool {
    85  	return true
    86  }
    87  
    88  func (plugin *secretPlugin) SupportsMountOption() bool {
    89  	return false
    90  }
    91  
    92  func (plugin *secretPlugin) SupportsBulkVolumeVerification() bool {
    93  	return false
    94  }
    95  
    96  func (plugin *secretPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
    97  	return false, nil
    98  }
    99  
   100  func (plugin *secretPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
   101  	return &secretVolumeMounter{
   102  		secretVolume: &secretVolume{
   103  			spec.Name(),
   104  			pod.UID,
   105  			plugin,
   106  			plugin.host.GetMounter(plugin.GetPluginName()),
   107  			volume.NewCachedMetrics(volume.NewMetricsDu(getPath(pod.UID, spec.Name(), plugin.host))),
   108  		},
   109  		source:    *spec.Volume.Secret,
   110  		pod:       *pod,
   111  		opts:      &opts,
   112  		getSecret: plugin.getSecret,
   113  	}, nil
   114  }
   115  
   116  func (plugin *secretPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
   117  	return &secretVolumeUnmounter{
   118  		&secretVolume{
   119  			volName,
   120  			podUID,
   121  			plugin,
   122  			plugin.host.GetMounter(plugin.GetPluginName()),
   123  			volume.NewCachedMetrics(volume.NewMetricsDu(getPath(podUID, volName, plugin.host))),
   124  		},
   125  	}, nil
   126  }
   127  
   128  func (plugin *secretPlugin) ConstructVolumeSpec(volName, mountPath string) (volume.ReconstructedVolume, error) {
   129  	secretVolume := &v1.Volume{
   130  		Name: volName,
   131  		VolumeSource: v1.VolumeSource{
   132  			Secret: &v1.SecretVolumeSource{
   133  				SecretName: volName,
   134  			},
   135  		},
   136  	}
   137  	return volume.ReconstructedVolume{
   138  		Spec: volume.NewSpecFromVolume(secretVolume),
   139  	}, nil
   140  }
   141  
   142  type secretVolume struct {
   143  	volName string
   144  	podUID  types.UID
   145  	plugin  *secretPlugin
   146  	mounter mount.Interface
   147  	volume.MetricsProvider
   148  }
   149  
   150  var _ volume.Volume = &secretVolume{}
   151  
   152  func (sv *secretVolume) GetPath() string {
   153  	return getPath(sv.podUID, sv.volName, sv.plugin.host)
   154  }
   155  
   156  // secretVolumeMounter handles retrieving secrets from the API server
   157  // and placing them into the volume on the host.
   158  type secretVolumeMounter struct {
   159  	*secretVolume
   160  
   161  	source    v1.SecretVolumeSource
   162  	pod       v1.Pod
   163  	opts      *volume.VolumeOptions
   164  	getSecret func(namespace, name string) (*v1.Secret, error)
   165  }
   166  
   167  var _ volume.Mounter = &secretVolumeMounter{}
   168  
   169  func (sv *secretVolume) GetAttributes() volume.Attributes {
   170  	return volume.Attributes{
   171  		ReadOnly:       true,
   172  		Managed:        true,
   173  		SELinuxRelabel: true,
   174  	}
   175  }
   176  
   177  func (b *secretVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
   178  	return b.SetUpAt(b.GetPath(), mounterArgs)
   179  }
   180  
   181  func (b *secretVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
   182  	klog.V(3).Infof("Setting up volume %v for pod %v at %v", b.volName, b.pod.UID, dir)
   183  
   184  	// Wrap EmptyDir, let it do the setup.
   185  	wrapped, err := b.plugin.host.NewWrapperMounter(b.volName, wrappedVolumeSpec(), &b.pod, *b.opts)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	optional := b.source.Optional != nil && *b.source.Optional
   191  	secret, err := b.getSecret(b.pod.Namespace, b.source.SecretName)
   192  	if err != nil {
   193  		if !(errors.IsNotFound(err) && optional) {
   194  			klog.Errorf("Couldn't get secret %v/%v: %v", b.pod.Namespace, b.source.SecretName, err)
   195  			return err
   196  		}
   197  		secret = &v1.Secret{
   198  			ObjectMeta: metav1.ObjectMeta{
   199  				Namespace: b.pod.Namespace,
   200  				Name:      b.source.SecretName,
   201  			},
   202  		}
   203  	}
   204  
   205  	totalBytes := totalSecretBytes(secret)
   206  	klog.V(3).Infof("Received secret %v/%v containing (%v) pieces of data, %v total bytes",
   207  		b.pod.Namespace,
   208  		b.source.SecretName,
   209  		len(secret.Data),
   210  		totalBytes)
   211  
   212  	payload, err := MakePayload(b.source.Items, secret, b.source.DefaultMode, optional)
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	setupSuccess := false
   218  	if err := wrapped.SetUpAt(dir, mounterArgs); err != nil {
   219  		return err
   220  	}
   221  	if err := volumeutil.MakeNestedMountpoints(b.volName, dir, b.pod); err != nil {
   222  		return err
   223  	}
   224  
   225  	defer func() {
   226  		// Clean up directories if setup fails
   227  		if !setupSuccess {
   228  			unmounter, unmountCreateErr := b.plugin.NewUnmounter(b.volName, b.podUID)
   229  			if unmountCreateErr != nil {
   230  				klog.Errorf("error cleaning up mount %s after failure. Create unmounter failed with %v", b.volName, unmountCreateErr)
   231  				return
   232  			}
   233  			tearDownErr := unmounter.TearDown()
   234  			if tearDownErr != nil {
   235  				klog.Errorf("error tearing down volume %s with : %v", b.volName, tearDownErr)
   236  			}
   237  		}
   238  	}()
   239  
   240  	writerContext := fmt.Sprintf("pod %v/%v volume %v", b.pod.Namespace, b.pod.Name, b.volName)
   241  	writer, err := volumeutil.NewAtomicWriter(dir, writerContext)
   242  	if err != nil {
   243  		klog.Errorf("Error creating atomic writer: %v", err)
   244  		return err
   245  	}
   246  
   247  	setPerms := func(_ string) error {
   248  		// This may be the first time writing and new files get created outside the timestamp subdirectory:
   249  		// change the permissions on the whole volume and not only in the timestamp directory.
   250  		return volume.SetVolumeOwnership(b, dir, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
   251  	}
   252  	err = writer.Write(payload, setPerms)
   253  	if err != nil {
   254  		klog.Errorf("Error writing payload to dir: %v", err)
   255  		return err
   256  	}
   257  
   258  	setupSuccess = true
   259  	return nil
   260  }
   261  
   262  // MakePayload function is exported so that it can be called from the projection volume driver
   263  func MakePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
   264  	if defaultMode == nil {
   265  		return nil, fmt.Errorf("no defaultMode used, not even the default value for it")
   266  	}
   267  
   268  	payload := make(map[string]volumeutil.FileProjection, len(secret.Data))
   269  	var fileProjection volumeutil.FileProjection
   270  
   271  	if len(mappings) == 0 {
   272  		for name, data := range secret.Data {
   273  			fileProjection.Data = []byte(data)
   274  			fileProjection.Mode = *defaultMode
   275  			payload[name] = fileProjection
   276  		}
   277  	} else {
   278  		for _, ktp := range mappings {
   279  			content, ok := secret.Data[ktp.Key]
   280  			if !ok {
   281  				if optional {
   282  					continue
   283  				}
   284  				errMsg := fmt.Sprintf("references non-existent secret key: %s", ktp.Key)
   285  				klog.Errorf(errMsg)
   286  				return nil, fmt.Errorf(errMsg)
   287  			}
   288  
   289  			fileProjection.Data = []byte(content)
   290  			if ktp.Mode != nil {
   291  				fileProjection.Mode = *ktp.Mode
   292  			} else {
   293  				fileProjection.Mode = *defaultMode
   294  			}
   295  			payload[ktp.Path] = fileProjection
   296  		}
   297  	}
   298  	return payload, nil
   299  }
   300  
   301  func totalSecretBytes(secret *v1.Secret) int {
   302  	totalSize := 0
   303  	for _, bytes := range secret.Data {
   304  		totalSize += len(bytes)
   305  	}
   306  
   307  	return totalSize
   308  }
   309  
   310  // secretVolumeUnmounter handles cleaning up secret volumes.
   311  type secretVolumeUnmounter struct {
   312  	*secretVolume
   313  }
   314  
   315  var _ volume.Unmounter = &secretVolumeUnmounter{}
   316  
   317  func (c *secretVolumeUnmounter) TearDown() error {
   318  	return c.TearDownAt(c.GetPath())
   319  }
   320  
   321  func (c *secretVolumeUnmounter) TearDownAt(dir string) error {
   322  	return volumeutil.UnmountViaEmptyDir(dir, c.plugin.host, c.volName, wrappedVolumeSpec(), c.podUID)
   323  }
   324  
   325  func getVolumeSource(spec *volume.Spec) (*v1.SecretVolumeSource, bool) {
   326  	var readOnly bool
   327  	var volumeSource *v1.SecretVolumeSource
   328  
   329  	if spec.Volume != nil && spec.Volume.Secret != nil {
   330  		volumeSource = spec.Volume.Secret
   331  		readOnly = spec.ReadOnly
   332  	}
   333  
   334  	return volumeSource, readOnly
   335  }
   336  

View as plain text