Support protected directories
by setting the sticky bit to all explicitly requested directories.
This commit is contained in:

committed by
Sebastian Serth

parent
b1de6faa03
commit
b3eee17846
@ -404,7 +404,7 @@ paths:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
path:
|
path:
|
||||||
description: Location where the file should be placed. Can be absolute (starting with /) or relative to the workspace directory. Missing parent directories are created. If this ends with a /, the path is interpreted as a directory and content is ignored
|
description: Location where the file should be placed. Can be absolute (starting with /) or relative to the workspace directory. Missing parent directories are created. If this ends with a /, the path is interpreted as a directory and content is ignored. Currently, every file/directory is owned by root but the directories have the sticky bit set to allow unprivileged file creation.
|
||||||
type: string
|
type: string
|
||||||
example: /etc/passwd
|
example: /etc/passwd
|
||||||
content:
|
content:
|
||||||
|
@ -342,7 +342,7 @@ func tarHeader(file dto.File) *tar.Header {
|
|||||||
return &tar.Header{
|
return &tar.Header{
|
||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
Name: file.CleanedPath(),
|
Name: file.CleanedPath(),
|
||||||
Mode: 0o755,
|
Mode: 0o1777, // See #236. Sticky bit is to allow creating files next to write-protected files.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return &tar.Header{
|
return &tar.Header{
|
||||||
|
@ -325,6 +325,100 @@ func (s *E2ETestSuite) TestCopyFilesRoute_PermissionDenied() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *E2ETestSuite) TestCopyFilesRoute_ProtectedFolders() {
|
||||||
|
runnerID, err := ProvideRunner(&dto.RunnerRequest{ExecutionEnvironmentID: tests.DefaultEnvironmentIDAsInteger})
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
// Initialization of protected folder
|
||||||
|
newFileContent := []byte("New content")
|
||||||
|
protectedFolderPath := dto.FilePath("protectedFolder/")
|
||||||
|
copyFilesRequestByteString, err := json.Marshal(&dto.UpdateFileSystemRequest{
|
||||||
|
Copy: []dto.File{
|
||||||
|
{Path: protectedFolderPath},
|
||||||
|
{Path: protectedFolderPath + tests.DefaultFileName, Content: newFileContent},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
resp, err := helpers.HTTPPatch(helpers.BuildURL(api.BasePath, api.RunnersPath, runnerID, api.UpdateFileSystemPath),
|
||||||
|
"application/json", bytes.NewReader(copyFilesRequestByteString))
|
||||||
|
s.NoError(err)
|
||||||
|
s.Equal(http.StatusNoContent, resp.StatusCode)
|
||||||
|
|
||||||
|
// User manipulates protected folder
|
||||||
|
s.Run("User can create files", func() {
|
||||||
|
webSocketURL, err := ProvideWebSocketURL(&s.Suite, runnerID, &dto.ExecutionRequest{
|
||||||
|
Command: fmt.Sprintf("touch %s/userfile", protectedFolderPath),
|
||||||
|
TimeLimit: int(tests.DefaultTestTimeout.Seconds()),
|
||||||
|
PrivilegedExecution: false,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
connection, err := ConnectToWebSocket(webSocketURL)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messages, err := helpers.ReceiveAllWebSocketMessages(connection)
|
||||||
|
s.Require().Error(err)
|
||||||
|
s.Equal(&websocket.CloseError{Code: websocket.CloseNormalClosure}, err)
|
||||||
|
stdout, stderr, _ := helpers.WebSocketOutputMessages(messages)
|
||||||
|
s.Empty(stdout)
|
||||||
|
s.Empty(stderr)
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("User can not delete protected folder", func() {
|
||||||
|
webSocketURL, err := ProvideWebSocketURL(&s.Suite, runnerID, &dto.ExecutionRequest{
|
||||||
|
Command: fmt.Sprintf("rm -fr %s", protectedFolderPath),
|
||||||
|
TimeLimit: int(tests.DefaultTestTimeout.Seconds()),
|
||||||
|
PrivilegedExecution: false,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
connection, err := ConnectToWebSocket(webSocketURL)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messages, err := helpers.ReceiveAllWebSocketMessages(connection)
|
||||||
|
s.Require().Error(err)
|
||||||
|
s.Equal(&websocket.CloseError{Code: websocket.CloseNormalClosure}, err)
|
||||||
|
stdout, stderr, _ := helpers.WebSocketOutputMessages(messages)
|
||||||
|
s.Empty(stdout)
|
||||||
|
s.Contains(stderr, "Operation not permitted")
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("User can not delete protected file", func() {
|
||||||
|
webSocketURL, err := ProvideWebSocketURL(&s.Suite, runnerID, &dto.ExecutionRequest{
|
||||||
|
Command: fmt.Sprintf("rm -f %s", protectedFolderPath+tests.DefaultFileName),
|
||||||
|
TimeLimit: int(tests.DefaultTestTimeout.Seconds()),
|
||||||
|
PrivilegedExecution: false,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
connection, err := ConnectToWebSocket(webSocketURL)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messages, err := helpers.ReceiveAllWebSocketMessages(connection)
|
||||||
|
s.Require().Error(err)
|
||||||
|
s.Equal(&websocket.CloseError{Code: websocket.CloseNormalClosure}, err)
|
||||||
|
stdout, stderr, _ := helpers.WebSocketOutputMessages(messages)
|
||||||
|
s.Empty(stdout)
|
||||||
|
s.Contains(stderr, "Operation not permitted")
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("User can not write protected file", func() {
|
||||||
|
webSocketURL, err := ProvideWebSocketURL(&s.Suite, runnerID, &dto.ExecutionRequest{
|
||||||
|
Command: fmt.Sprintf("echo Hi >> %s", protectedFolderPath+tests.DefaultFileName),
|
||||||
|
TimeLimit: int(tests.DefaultTestTimeout.Seconds()),
|
||||||
|
PrivilegedExecution: false,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
connection, err := ConnectToWebSocket(webSocketURL)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messages, err := helpers.ReceiveAllWebSocketMessages(connection)
|
||||||
|
s.Require().Error(err)
|
||||||
|
s.Equal(&websocket.CloseError{Code: websocket.CloseNormalClosure}, err)
|
||||||
|
stdout, stderr, _ := helpers.WebSocketOutputMessages(messages)
|
||||||
|
s.Empty(stdout)
|
||||||
|
s.Contains(stderr, "Permission denied")
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("File content is not manipulated", func() {
|
||||||
|
s.assertFileContent(runnerID, string(protectedFolderPath+tests.DefaultFileName), string(newFileContent))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *E2ETestSuite) TestGetFileContent_Nomad() {
|
func (s *E2ETestSuite) TestGetFileContent_Nomad() {
|
||||||
runnerID, err := ProvideRunner(&dto.RunnerRequest{
|
runnerID, err := ProvideRunner(&dto.RunnerRequest{
|
||||||
ExecutionEnvironmentID: tests.DefaultEnvironmentIDAsInteger,
|
ExecutionEnvironmentID: tests.DefaultEnvironmentIDAsInteger,
|
||||||
|
Reference in New Issue
Block a user