1
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
35 func ProbeVolumePlugins() []volume.VolumePlugin {
36 return []volume.VolumePlugin{&secretPlugin{}}
37 }
38
39 const (
40 secretPluginName = "kubernetes.io/secret"
41 )
42
43
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
157
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
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
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
249
250 return volume.SetVolumeOwnership(b, dir, mounterArgs.FsGroup, nil , 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
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
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