secret.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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/klog"
  17. "k8s.io/utils/mount"
  18. utilstrings "k8s.io/utils/strings"
  19. v1 "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. "k8s.io/kubernetes/pkg/volume"
  24. volumeutil "k8s.io/kubernetes/pkg/volume/util"
  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) RequiresRemount() bool {
  66. return true
  67. }
  68. func (plugin *secretPlugin) SupportsMountOption() bool {
  69. return false
  70. }
  71. func (plugin *secretPlugin) SupportsBulkVolumeVerification() bool {
  72. return false
  73. }
  74. func (plugin *secretPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
  75. return &secretVolumeMounter{
  76. secretVolume: &secretVolume{
  77. spec.Name(),
  78. pod.UID,
  79. plugin,
  80. plugin.host.GetMounter(plugin.GetPluginName()),
  81. volume.NewCachedMetrics(volume.NewMetricsDu(getPath(pod.UID, spec.Name(), plugin.host))),
  82. },
  83. source: *spec.Volume.Secret,
  84. pod: *pod,
  85. opts: &opts,
  86. getSecret: plugin.getSecret,
  87. }, nil
  88. }
  89. func (plugin *secretPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  90. return &secretVolumeUnmounter{
  91. &secretVolume{
  92. volName,
  93. podUID,
  94. plugin,
  95. plugin.host.GetMounter(plugin.GetPluginName()),
  96. volume.NewCachedMetrics(volume.NewMetricsDu(getPath(podUID, volName, plugin.host))),
  97. },
  98. }, nil
  99. }
  100. func (plugin *secretPlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
  101. secretVolume := &v1.Volume{
  102. Name: volName,
  103. VolumeSource: v1.VolumeSource{
  104. Secret: &v1.SecretVolumeSource{
  105. SecretName: volName,
  106. },
  107. },
  108. }
  109. return volume.NewSpecFromVolume(secretVolume), nil
  110. }
  111. type secretVolume struct {
  112. volName string
  113. podUID types.UID
  114. plugin *secretPlugin
  115. mounter mount.Interface
  116. volume.MetricsProvider
  117. }
  118. var _ volume.Volume = &secretVolume{}
  119. func (sv *secretVolume) GetPath() string {
  120. return getPath(sv.podUID, sv.volName, sv.plugin.host)
  121. }
  122. // secretVolumeMounter handles retrieving secrets from the API server
  123. // and placing them into the volume on the host.
  124. type secretVolumeMounter struct {
  125. *secretVolume
  126. source v1.SecretVolumeSource
  127. pod v1.Pod
  128. opts *volume.VolumeOptions
  129. getSecret func(namespace, name string) (*v1.Secret, error)
  130. }
  131. var _ volume.Mounter = &secretVolumeMounter{}
  132. func (sv *secretVolume) GetAttributes() volume.Attributes {
  133. return volume.Attributes{
  134. ReadOnly: true,
  135. Managed: true,
  136. SupportsSELinux: true,
  137. }
  138. }
  139. // Checks prior to mount operations to verify that the required components (binaries, etc.)
  140. // to mount the volume are available on the underlying node.
  141. // If not, it returns an error
  142. func (b *secretVolumeMounter) CanMount() error {
  143. return nil
  144. }
  145. func (b *secretVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
  146. return b.SetUpAt(b.GetPath(), mounterArgs)
  147. }
  148. func (b *secretVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  149. klog.V(3).Infof("Setting up volume %v for pod %v at %v", b.volName, b.pod.UID, dir)
  150. // Wrap EmptyDir, let it do the setup.
  151. wrapped, err := b.plugin.host.NewWrapperMounter(b.volName, wrappedVolumeSpec(), &b.pod, *b.opts)
  152. if err != nil {
  153. return err
  154. }
  155. optional := b.source.Optional != nil && *b.source.Optional
  156. secret, err := b.getSecret(b.pod.Namespace, b.source.SecretName)
  157. if err != nil {
  158. if !(errors.IsNotFound(err) && optional) {
  159. klog.Errorf("Couldn't get secret %v/%v: %v", b.pod.Namespace, b.source.SecretName, err)
  160. return err
  161. }
  162. secret = &v1.Secret{
  163. ObjectMeta: metav1.ObjectMeta{
  164. Namespace: b.pod.Namespace,
  165. Name: b.source.SecretName,
  166. },
  167. }
  168. }
  169. totalBytes := totalSecretBytes(secret)
  170. klog.V(3).Infof("Received secret %v/%v containing (%v) pieces of data, %v total bytes",
  171. b.pod.Namespace,
  172. b.source.SecretName,
  173. len(secret.Data),
  174. totalBytes)
  175. payload, err := MakePayload(b.source.Items, secret, b.source.DefaultMode, optional)
  176. if err != nil {
  177. return err
  178. }
  179. setupSuccess := false
  180. if err := wrapped.SetUpAt(dir, mounterArgs); err != nil {
  181. return err
  182. }
  183. if err := volumeutil.MakeNestedMountpoints(b.volName, dir, b.pod); err != nil {
  184. return err
  185. }
  186. defer func() {
  187. // Clean up directories if setup fails
  188. if !setupSuccess {
  189. unmounter, unmountCreateErr := b.plugin.NewUnmounter(b.volName, b.podUID)
  190. if unmountCreateErr != nil {
  191. klog.Errorf("error cleaning up mount %s after failure. Create unmounter failed with %v", b.volName, unmountCreateErr)
  192. return
  193. }
  194. tearDownErr := unmounter.TearDown()
  195. if tearDownErr != nil {
  196. klog.Errorf("error tearing down volume %s with : %v", b.volName, tearDownErr)
  197. }
  198. }
  199. }()
  200. writerContext := fmt.Sprintf("pod %v/%v volume %v", b.pod.Namespace, b.pod.Name, b.volName)
  201. writer, err := volumeutil.NewAtomicWriter(dir, writerContext)
  202. if err != nil {
  203. klog.Errorf("Error creating atomic writer: %v", err)
  204. return err
  205. }
  206. err = writer.Write(payload)
  207. if err != nil {
  208. klog.Errorf("Error writing payload to dir: %v", err)
  209. return err
  210. }
  211. err = volume.SetVolumeOwnership(b, mounterArgs.FsGroup)
  212. if err != nil {
  213. klog.Errorf("Error applying volume ownership settings for group: %v", mounterArgs.FsGroup)
  214. return err
  215. }
  216. setupSuccess = true
  217. return nil
  218. }
  219. // MakePayload function is exported so that it can be called from the projection volume driver
  220. func MakePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
  221. if defaultMode == nil {
  222. return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
  223. }
  224. payload := make(map[string]volumeutil.FileProjection, len(secret.Data))
  225. var fileProjection volumeutil.FileProjection
  226. if len(mappings) == 0 {
  227. for name, data := range secret.Data {
  228. fileProjection.Data = []byte(data)
  229. fileProjection.Mode = *defaultMode
  230. payload[name] = fileProjection
  231. }
  232. } else {
  233. for _, ktp := range mappings {
  234. content, ok := secret.Data[ktp.Key]
  235. if !ok {
  236. if optional {
  237. continue
  238. }
  239. errMsg := fmt.Sprintf("references non-existent secret key: %s", ktp.Key)
  240. klog.Errorf(errMsg)
  241. return nil, fmt.Errorf(errMsg)
  242. }
  243. fileProjection.Data = []byte(content)
  244. if ktp.Mode != nil {
  245. fileProjection.Mode = *ktp.Mode
  246. } else {
  247. fileProjection.Mode = *defaultMode
  248. }
  249. payload[ktp.Path] = fileProjection
  250. }
  251. }
  252. return payload, nil
  253. }
  254. func totalSecretBytes(secret *v1.Secret) int {
  255. totalSize := 0
  256. for _, bytes := range secret.Data {
  257. totalSize += len(bytes)
  258. }
  259. return totalSize
  260. }
  261. // secretVolumeUnmounter handles cleaning up secret volumes.
  262. type secretVolumeUnmounter struct {
  263. *secretVolume
  264. }
  265. var _ volume.Unmounter = &secretVolumeUnmounter{}
  266. func (c *secretVolumeUnmounter) TearDown() error {
  267. return c.TearDownAt(c.GetPath())
  268. }
  269. func (c *secretVolumeUnmounter) TearDownAt(dir string) error {
  270. return volumeutil.UnmountViaEmptyDir(dir, c.plugin.host, c.volName, wrappedVolumeSpec(), c.podUID)
  271. }
  272. func getVolumeSource(spec *volume.Spec) (*v1.SecretVolumeSource, bool) {
  273. var readOnly bool
  274. var volumeSource *v1.SecretVolumeSource
  275. if spec.Volume != nil && spec.Volume.Secret != nil {
  276. volumeSource = spec.Volume.Secret
  277. readOnly = spec.ReadOnly
  278. }
  279. return volumeSource, readOnly
  280. }