diff options
Diffstat (limited to 'internal/httpfs/http_fs.go')
-rw-r--r-- | internal/httpfs/http_fs.go | 66 |
1 files changed, 59 insertions, 7 deletions
diff --git a/internal/httpfs/http_fs.go b/internal/httpfs/http_fs.go index cd2edb83..3578352f 100644 --- a/internal/httpfs/http_fs.go +++ b/internal/httpfs/http_fs.go @@ -8,6 +8,7 @@ package httpfs import ( "errors" + "fmt" "net/http" "os" "path" @@ -24,15 +25,28 @@ var ( // fileSystemPaths implements the http.FileSystem interface type fileSystemPaths struct { allowedPaths []string + // workaround for https://gitlab.com/gitlab-org/gitlab/-/issues/326117#note_546346101 + // where daemon-inplace-chroot=true fails to serve zip archives when + // pages_serve_with_zip_file_protocol is enabled + // TODO: evaluate if we need to remove this field when we remove + // chroot https://gitlab.com/gitlab-org/gitlab-pages/-/issues/561 + chrootPath string } // NewFileSystemPath creates a new fileSystemPaths that can be used to register -// a file:// protocol with an http.Transport -func NewFileSystemPath(allowedPaths []string) (http.FileSystem, error) { - for k, path := range allowedPaths { - var err error +// a file:// protocol with an http.Transport. +// When the daemon runs inside a chroot we need to strip chrootPath out of each +// of the allowedPaths so that we are able to find the file correctly inside +// the chroot. When Open is called, the same chrootPath will be stripped out of +// the full filepath. +func NewFileSystemPath(allowedPaths []string, chrootPath string) (http.FileSystem, error) { + for k, allowedPath := range allowedPaths { + strippedPath, err := stripChrootPath(ensureEndingSlash(allowedPath), chrootPath) + if err != nil { + return nil, err + } - allowedPaths[k], err = filepath.Abs(path) + allowedPaths[k], err = filepath.Abs(strippedPath) if err != nil { return nil, err } @@ -40,6 +54,7 @@ func NewFileSystemPath(allowedPaths []string) (http.FileSystem, error) { return &fileSystemPaths{ allowedPaths: allowedPaths, + chrootPath: chrootPath, }, nil } @@ -50,12 +65,26 @@ func (p *fileSystemPaths) Open(name string) (http.File, error) { return nil, errInvalidChar } - absPath, err := filepath.Abs(filepath.FromSlash(path.Clean("/" + name))) + cleanedPath := filepath.FromSlash(path.Clean("/" + name)) + + // since deamon can run in a chroot, we allow to define a chroot path that will be stripped from + // the FS location + // TODO: evaluate if we need to remove this check when we remove chroot + // https://gitlab.com/gitlab-org/gitlab-pages/-/issues/561 + strippedPath, err := stripChrootPath(cleanedPath, p.chrootPath) + if err != nil { + log.WithError(err).Error(os.ErrPermission) + + return nil, os.ErrPermission + } + + absPath, err := filepath.Abs(strippedPath) if err != nil { return nil, err } for _, allowedPath := range p.allowedPaths { - if strings.HasPrefix(absPath, allowedPath+"/") { + // allowedPath may be a single / in chroot so we need to ensure it's not double slash + if strings.HasPrefix(absPath, ensureEndingSlash(allowedPath)) { return os.Open(absPath) } } @@ -67,3 +96,26 @@ func (p *fileSystemPaths) Open(name string) (http.File, error) { // https://github.com/golang/go/blob/release-branch.go1.15/src/net/http/fs.go#L635 return nil, os.ErrPermission } + +func ensureEndingSlash(path string) string { + if strings.HasSuffix(path, "/") { + return path + } + + return path + "/" +} + +func stripChrootPath(path, chrootPath string) (string, error) { + if chrootPath == "" { + return path, nil + } + + if !strings.HasPrefix(path, chrootPath+"/") { + return "", fmt.Errorf("allowed path %q is not in chroot path %q", path, chrootPath) + } + + // path will contain a leading `/` + path = path[len(chrootPath):] + + return path, nil +} |