secret.go 9.1 KB

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