mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 02:41:25 +00:00
Merge pull request #114464 from Nordix/issue-114461
Set permissions for timestamp directory before publishing update to avoid permission denied
This commit is contained in:
commit
685d639cb5
@ -368,7 +368,7 @@ func (cephfsVolume *cephfs) execFuseMount(mountpoint string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writer.Write(payload)
|
err = writer.Write(payload, nil /*setPerms*/)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("failed to write payload to dir: %v", err)
|
klog.Errorf("failed to write payload to dir: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -249,17 +249,17 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterA
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writer.Write(payload)
|
setPerms := func(_ string) error {
|
||||||
|
// This may be the first time writing and new files get created outside the timestamp subdirectory:
|
||||||
|
// change the permissions on the whole volume and not only in the timestamp directory.
|
||||||
|
return volume.SetVolumeOwnership(b, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
|
||||||
|
}
|
||||||
|
err = writer.Write(payload, setPerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Error writing payload to dir: %v", err)
|
klog.Errorf("Error writing payload to dir: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = volume.SetVolumeOwnership(b, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Error applying volume ownership settings for group: %v", mounterArgs.FsGroup)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
setupSuccess = true
|
setupSuccess = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -220,18 +220,17 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, mounterArgs volume.Mounte
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writer.Write(data)
|
setPerms := func(_ string) error {
|
||||||
|
// This may be the first time writing and new files get created outside the timestamp subdirectory:
|
||||||
|
// change the permissions on the whole volume and not only in the timestamp directory.
|
||||||
|
return volume.SetVolumeOwnership(b, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
|
||||||
|
}
|
||||||
|
err = writer.Write(data, setPerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Error writing payload to dir: %v", err)
|
klog.Errorf("Error writing payload to dir: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = volume.SetVolumeOwnership(b, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Error applying volume ownership settings for group: %v", mounterArgs.FsGroup)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
setupSuccess = true
|
setupSuccess = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -230,17 +230,17 @@ func (s *projectedVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterA
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writer.Write(data)
|
setPerms := func(_ string) error {
|
||||||
|
// This may be the first time writing and new files get created outside the timestamp subdirectory:
|
||||||
|
// change the permissions on the whole volume and not only in the timestamp directory.
|
||||||
|
return volume.SetVolumeOwnership(s, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(s.plugin, nil))
|
||||||
|
}
|
||||||
|
err = writer.Write(data, setPerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Error writing payload to dir: %v", err)
|
klog.Errorf("Error writing payload to dir: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = volume.SetVolumeOwnership(s, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(s.plugin, nil))
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Error applying volume ownership settings for group: %v", mounterArgs.FsGroup)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
setupSuccess = true
|
setupSuccess = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -244,17 +244,17 @@ func (b *secretVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writer.Write(payload)
|
setPerms := func(_ string) error {
|
||||||
|
// This may be the first time writing and new files get created outside the timestamp subdirectory:
|
||||||
|
// change the permissions on the whole volume and not only in the timestamp directory.
|
||||||
|
return volume.SetVolumeOwnership(b, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
|
||||||
|
}
|
||||||
|
err = writer.Write(payload, setPerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Error writing payload to dir: %v", err)
|
klog.Errorf("Error writing payload to dir: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = volume.SetVolumeOwnership(b, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Error applying volume ownership settings for group: %v", mounterArgs.FsGroup)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
setupSuccess = true
|
setupSuccess = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,16 @@ const (
|
|||||||
|
|
||||||
// Write does an atomic projection of the given payload into the writer's target
|
// Write does an atomic projection of the given payload into the writer's target
|
||||||
// directory. Input paths must not begin with '..'.
|
// directory. Input paths must not begin with '..'.
|
||||||
|
// setPerms is an optional pointer to a function that caller can provide to set the
|
||||||
|
// permissions of the newly created files before they are published. The function is
|
||||||
|
// passed subPath which is the name of the timestamped directory that was created
|
||||||
|
// under target directory.
|
||||||
//
|
//
|
||||||
// The Write algorithm is:
|
// The Write algorithm is:
|
||||||
//
|
//
|
||||||
// 1. The payload is validated; if the payload is invalid, the function returns
|
// 1. The payload is validated; if the payload is invalid, the function returns
|
||||||
// 2. The current timestamped directory is detected by reading the data directory
|
//
|
||||||
|
// 2. The current timestamped directory is detected by reading the data directory
|
||||||
// symlink
|
// symlink
|
||||||
//
|
//
|
||||||
// 3. The old version of the volume is walked to determine whether any
|
// 3. The old version of the volume is walked to determine whether any
|
||||||
@ -98,13 +103,19 @@ const (
|
|||||||
//
|
//
|
||||||
// 4. The data in the current timestamped directory is compared to the projected
|
// 4. The data in the current timestamped directory is compared to the projected
|
||||||
// data to determine if an update is required.
|
// data to determine if an update is required.
|
||||||
// 5. A new timestamped dir is created
|
|
||||||
//
|
//
|
||||||
// 6. The payload is written to the new timestamped directory
|
// 5. A new timestamped dir is created.
|
||||||
// 7. A symlink to the new timestamped directory ..data_tmp is created that will
|
//
|
||||||
// become the new data directory
|
// 6. The payload is written to the new timestamped directory.
|
||||||
// 8. The new data directory symlink is renamed to the data directory; rename is atomic
|
//
|
||||||
// 9. Symlinks and directory for new user-visible files are created (if needed).
|
// 7. Permissions are set (if setPerms is not nil) on the new timestamped directory and files.
|
||||||
|
//
|
||||||
|
// 8. A symlink to the new timestamped directory ..data_tmp is created that will
|
||||||
|
// become the new data directory.
|
||||||
|
//
|
||||||
|
// 9. The new data directory symlink is renamed to the data directory; rename is atomic.
|
||||||
|
//
|
||||||
|
// 10. Symlinks and directory for new user-visible files are created (if needed).
|
||||||
//
|
//
|
||||||
// For example, consider the files:
|
// For example, consider the files:
|
||||||
// <target-dir>/podName
|
// <target-dir>/podName
|
||||||
@ -123,9 +134,10 @@ const (
|
|||||||
// linking everything else. On Windows, if a target does not exist, the created symlink
|
// linking everything else. On Windows, if a target does not exist, the created symlink
|
||||||
// will not work properly if the target ends up being a directory.
|
// will not work properly if the target ends up being a directory.
|
||||||
//
|
//
|
||||||
// 10. Old paths are removed from the user-visible portion of the target directory
|
// 11. Old paths are removed from the user-visible portion of the target directory.
|
||||||
// 11. The previous timestamped directory is removed, if it exists
|
//
|
||||||
func (w *AtomicWriter) Write(payload map[string]FileProjection) error {
|
// 12. The previous timestamped directory is removed, if it exists.
|
||||||
|
func (w *AtomicWriter) Write(payload map[string]FileProjection, setPerms func(subPath string) error) error {
|
||||||
// (1)
|
// (1)
|
||||||
cleanPayload, err := validatePayload(payload)
|
cleanPayload, err := validatePayload(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -185,6 +197,14 @@ func (w *AtomicWriter) Write(payload map[string]FileProjection) error {
|
|||||||
klog.V(4).Infof("%s: performed write of new data to ts data directory: %s", w.logContext, tsDir)
|
klog.V(4).Infof("%s: performed write of new data to ts data directory: %s", w.logContext, tsDir)
|
||||||
|
|
||||||
// (7)
|
// (7)
|
||||||
|
if setPerms != nil {
|
||||||
|
if err := setPerms(tsDirName); err != nil {
|
||||||
|
klog.Errorf("%s: error applying ownership settings: %v", w.logContext, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (8)
|
||||||
newDataDirPath := filepath.Join(w.targetDir, newDataDirName)
|
newDataDirPath := filepath.Join(w.targetDir, newDataDirName)
|
||||||
if err = os.Symlink(tsDirName, newDataDirPath); err != nil {
|
if err = os.Symlink(tsDirName, newDataDirPath); err != nil {
|
||||||
os.RemoveAll(tsDir)
|
os.RemoveAll(tsDir)
|
||||||
@ -192,7 +212,7 @@ func (w *AtomicWriter) Write(payload map[string]FileProjection) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// (8)
|
// (9)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
os.Remove(dataDirPath)
|
os.Remove(dataDirPath)
|
||||||
err = os.Symlink(tsDirName, dataDirPath)
|
err = os.Symlink(tsDirName, dataDirPath)
|
||||||
@ -207,19 +227,19 @@ func (w *AtomicWriter) Write(payload map[string]FileProjection) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// (9)
|
// (10)
|
||||||
if err = w.createUserVisibleFiles(cleanPayload); err != nil {
|
if err = w.createUserVisibleFiles(cleanPayload); err != nil {
|
||||||
klog.Errorf("%s: error creating visible symlinks in %s: %v", w.logContext, w.targetDir, err)
|
klog.Errorf("%s: error creating visible symlinks in %s: %v", w.logContext, w.targetDir, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// (10)
|
// (11)
|
||||||
if err = w.removeUserVisiblePaths(pathsToRemove); err != nil {
|
if err = w.removeUserVisiblePaths(pathsToRemove); err != nil {
|
||||||
klog.Errorf("%s: error removing old visible symlinks: %v", w.logContext, err)
|
klog.Errorf("%s: error removing old visible symlinks: %v", w.logContext, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// (11)
|
// (12)
|
||||||
if len(oldTsDir) > 0 {
|
if len(oldTsDir) > 0 {
|
||||||
if err = os.RemoveAll(oldTsPath); err != nil {
|
if err = os.RemoveAll(oldTsPath); err != nil {
|
||||||
klog.Errorf("%s: error removing old data directory %s: %v", w.logContext, oldTsDir, err)
|
klog.Errorf("%s: error removing old data directory %s: %v", w.logContext, oldTsDir, err)
|
||||||
|
@ -21,6 +21,7 @@ package util
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -229,7 +230,7 @@ func TestPathsToRemove(t *testing.T) {
|
|||||||
defer os.RemoveAll(targetDir)
|
defer os.RemoveAll(targetDir)
|
||||||
|
|
||||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||||
err = writer.Write(tc.payload1)
|
err = writer.Write(tc.payload1, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
||||||
continue
|
continue
|
||||||
@ -397,7 +398,7 @@ IAAAAAAAsDyZDwU=`
|
|||||||
defer os.RemoveAll(targetDir)
|
defer os.RemoveAll(targetDir)
|
||||||
|
|
||||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||||
err = writer.Write(tc.payload)
|
err = writer.Write(tc.payload, nil)
|
||||||
if err != nil && tc.success {
|
if err != nil && tc.success {
|
||||||
t.Errorf("%v: unexpected error writing payload: %v", tc.name, err)
|
t.Errorf("%v: unexpected error writing payload: %v", tc.name, err)
|
||||||
continue
|
continue
|
||||||
@ -574,7 +575,7 @@ func TestUpdate(t *testing.T) {
|
|||||||
|
|
||||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||||
|
|
||||||
err = writer.Write(tc.first)
|
err = writer.Write(tc.first, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
||||||
continue
|
continue
|
||||||
@ -585,7 +586,7 @@ func TestUpdate(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writer.Write(tc.next)
|
err = writer.Write(tc.next, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if tc.shouldWrite {
|
if tc.shouldWrite {
|
||||||
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
||||||
@ -743,7 +744,7 @@ func TestMultipleUpdates(t *testing.T) {
|
|||||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||||
|
|
||||||
for _, payload := range tc.payloads {
|
for _, payload := range tc.payloads {
|
||||||
writer.Write(payload)
|
writer.Write(payload, nil)
|
||||||
|
|
||||||
checkVolumeContents(targetDir, tc.name, payload, t)
|
checkVolumeContents(targetDir, tc.name, payload, t)
|
||||||
}
|
}
|
||||||
@ -984,3 +985,54 @@ func TestCreateUserVisibleFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetPerms(t *testing.T) {
|
||||||
|
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(targetDir)
|
||||||
|
|
||||||
|
// Test that setPerms() is called once and with valid timestamp directory.
|
||||||
|
payload1 := map[string]FileProjection{
|
||||||
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
|
}
|
||||||
|
|
||||||
|
var setPermsCalled int
|
||||||
|
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||||
|
err = writer.Write(payload1, func(subPath string) error {
|
||||||
|
fileInfo, err := os.Stat(filepath.Join(targetDir, subPath))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error getting file info: %v", err)
|
||||||
|
}
|
||||||
|
// Ensure that given timestamp directory really exists.
|
||||||
|
if !fileInfo.IsDir() {
|
||||||
|
t.Fatalf("subPath is not a directory: %v", subPath)
|
||||||
|
}
|
||||||
|
setPermsCalled++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error writing: %v", err)
|
||||||
|
}
|
||||||
|
if setPermsCalled != 1 {
|
||||||
|
t.Fatalf("unexpected number of calls to setPerms: %v", setPermsCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that errors from setPerms() are propagated.
|
||||||
|
payload2 := map[string]FileProjection{
|
||||||
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo2")},
|
||||||
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar2")},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writer.Write(payload2, func(_ string) error {
|
||||||
|
return fmt.Errorf("error in setPerms")
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error while writing but got nil")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "error in setPerms") {
|
||||||
|
t.Fatalf("unexpected error while writing: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user