diff options
author | 2025-02-17 22:17:54 +0100 | |
---|---|---|
committer | 2025-02-17 22:17:54 +0100 | |
commit | f053e334865c5ec402c75d55bdf09b745342080d (patch) | |
tree | 98f43563d7d7b441c827f301677f325388ac9062 | |
parent | 10a06dcbf1b33242ce78d02fe8fcbfa32ee06026 (diff) | |
download | rest-server-f053e334865c5ec402c75d55bdf09b745342080d.tar.gz rest-server-f053e334865c5ec402c75d55bdf09b745342080d.tar.zst rest-server-f053e334865c5ec402c75d55bdf09b745342080d.zip |
Add group-accessible-repos option (#308)
* Add group-accessible-repos option
The group-accessible-repos option will let filesystem group id
be able to access files and dir within the restic repo
Default stick with old behaviour to be owner restricted
While here make dirMode and fileMode within Options struct
private
---------
Co-authored-by: Michael Eischer <michael.eischer@fau.de>
-rw-r--r-- | changelog/unreleased/issue-189 | 9 | ||||
-rw-r--r-- | cmd/rest-server/main.go | 7 | ||||
-rw-r--r-- | handlers.go | 48 | ||||
-rw-r--r-- | repo/repo.go | 44 |
4 files changed, 71 insertions, 37 deletions
diff --git a/changelog/unreleased/issue-189 b/changelog/unreleased/issue-189 new file mode 100644 index 0000000..f60bcd5 --- /dev/null +++ b/changelog/unreleased/issue-189 @@ -0,0 +1,9 @@ +Enhancement: Support group accessible repositories + +Rest-server now supports making repositories accessible to the filesystem group +by setting the `--group-accessible-repos` option. Note that permissions of +existing files are not modified. Use `chmod -R g+rwX /path/to/repo` to make +the repository group-accessible. + +https://github.com/restic/rest-server/issues/189 +https://github.com/restic/rest-server/pull/308 diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index aa921c9..cabc565 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -69,6 +69,7 @@ func newRestServerApp() *restServerApp { flags.BoolVar(&rv.Server.PrivateRepos, "private-repos", rv.Server.PrivateRepos, "users can only access their private repo") flags.BoolVar(&rv.Server.Prometheus, "prometheus", rv.Server.Prometheus, "enable Prometheus metrics") flags.BoolVar(&rv.Server.PrometheusNoAuth, "prometheus-no-auth", rv.Server.PrometheusNoAuth, "disable auth for Prometheus /metrics endpoint") + flags.BoolVar(&rv.Server.GroupAccessibleRepos, "group-accessible-repos", rv.Server.GroupAccessibleRepos, "let filesystem group be able to access repo files") return rv } @@ -149,6 +150,12 @@ func (app *restServerApp) runRoot(_ *cobra.Command, _ []string) error { log.Println("Private repositories disabled") } + if app.Server.GroupAccessibleRepos { + log.Println("Group accessible repos enabled") + } else { + log.Println("Group accessible repos disabled") + } + enabledTLS, privateKey, publicKey, err := app.tlsSettings() if err != nil { return err diff --git a/handlers.go b/handlers.go index 1bccc60..cde0637 100644 --- a/handlers.go +++ b/handlers.go @@ -15,23 +15,24 @@ import ( // Server encapsulates the rest-server's settings and repo management logic type Server struct { - Path string - HtpasswdPath string - Listen string - Log string - CPUProfile string - TLSKey string - TLSCert string - TLS bool - NoAuth bool - AppendOnly bool - PrivateRepos bool - Prometheus bool - PrometheusNoAuth bool - Debug bool - MaxRepoSize int64 - PanicOnError bool - NoVerifyUpload bool + Path string + HtpasswdPath string + Listen string + Log string + CPUProfile string + TLSKey string + TLSCert string + TLS bool + NoAuth bool + AppendOnly bool + PrivateRepos bool + Prometheus bool + PrometheusNoAuth bool + Debug bool + MaxRepoSize int64 + PanicOnError bool + NoVerifyUpload bool + GroupAccessibleRepos bool htpasswdFile *HtpasswdFile quotaManager *quota.Manager @@ -88,12 +89,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Pass the request to the repo.Handler opt := repo.Options{ - AppendOnly: s.AppendOnly, - Debug: s.Debug, - QuotaManager: s.quotaManager, // may be nil - PanicOnError: s.PanicOnError, - NoVerifyUpload: s.NoVerifyUpload, - FsyncWarning: &s.fsyncWarning, + AppendOnly: s.AppendOnly, + Debug: s.Debug, + QuotaManager: s.quotaManager, // may be nil + PanicOnError: s.PanicOnError, + NoVerifyUpload: s.NoVerifyUpload, + FsyncWarning: &s.fsyncWarning, + GroupAccessible: s.GroupAccessibleRepos, } if s.Prometheus { opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath) diff --git a/repo/repo.go b/repo/repo.go index cd84929..0e38bb6 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -28,8 +28,6 @@ import ( type Options struct { AppendOnly bool // if set, delete actions are not allowed Debug bool - DirMode os.FileMode - FileMode os.FileMode NoVerifyUpload bool // If set, we will panic when an internal server error happens. This @@ -39,6 +37,13 @@ type Options struct { BlobMetricFunc BlobMetricFunc QuotaManager *quota.Manager FsyncWarning *sync.Once + + // If set makes files group accessible + GroupAccessible bool + + // Defaults dir and file mode + dirMode os.FileMode + fileMode os.FileMode } // DefaultDirMode is the file mode used for directory creation if not @@ -49,6 +54,14 @@ const DefaultDirMode os.FileMode = 0700 // overridden in the Options const DefaultFileMode os.FileMode = 0600 +// GroupAccessibleDirMode is the file mode used for directory creation when +// group access is enabled +const GroupAccessibleDirMode os.FileMode = 0770 + +// GroupAccessibleFileMode is the file mode used for file creation when +// group access is enabled +const GroupAccessibleFileMode os.FileMode = 0660 + // New creates a new Handler for a single Restic backup repo. // path is the full filesystem path to this repo directory. // opt is a set of options. @@ -56,12 +69,15 @@ func New(path string, opt Options) (*Handler, error) { if path == "" { return nil, fmt.Errorf("path is required") } - if opt.DirMode == 0 { - opt.DirMode = DefaultDirMode - } - if opt.FileMode == 0 { - opt.FileMode = DefaultFileMode + + opt.dirMode = DefaultDirMode + opt.fileMode = DefaultFileMode + + if opt.GroupAccessible { + opt.dirMode = GroupAccessibleDirMode + opt.fileMode = GroupAccessibleFileMode } + h := Handler{ path: path, opt: opt, @@ -288,7 +304,7 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) { } cfg := h.getSubPath("config") - f, err := os.OpenFile(cfg, os.O_CREATE|os.O_WRONLY|os.O_EXCL, h.opt.FileMode) + f, err := os.OpenFile(cfg, os.O_CREATE|os.O_WRONLY|os.O_EXCL, h.opt.fileMode) if err != nil && os.IsExist(err) { if h.opt.Debug { log.Print(err) @@ -554,15 +570,15 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) { } tmpFn := filepath.Join(filepath.Dir(path), objectID+".rest-server-temp") - tf, err := tempFile(tmpFn, h.opt.FileMode) + tf, err := tempFile(tmpFn, h.opt.fileMode) if os.IsNotExist(err) { // the error is caused by a missing directory, create it and retry - mkdirErr := os.MkdirAll(filepath.Dir(path), h.opt.DirMode) + mkdirErr := os.MkdirAll(filepath.Dir(path), h.opt.dirMode) if mkdirErr != nil { log.Print(mkdirErr) } else { // try again - tf, err = tempFile(tmpFn, h.opt.FileMode) + tf, err = tempFile(tmpFn, h.opt.fileMode) } } if err != nil { @@ -759,13 +775,13 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) { log.Printf("Creating repository directories in %s\n", h.path) - if err := os.MkdirAll(h.path, h.opt.DirMode); err != nil { + if err := os.MkdirAll(h.path, h.opt.dirMode); err != nil { h.internalServerError(w, err) return } for _, d := range ObjectTypes { - if err := os.Mkdir(filepath.Join(h.path, d), h.opt.DirMode); err != nil && !os.IsExist(err) { + if err := os.Mkdir(filepath.Join(h.path, d), h.opt.dirMode); err != nil && !os.IsExist(err) { h.internalServerError(w, err) return } @@ -773,7 +789,7 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) { for i := 0; i < 256; i++ { dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i)) - if err := os.Mkdir(dirPath, h.opt.DirMode); err != nil && !os.IsExist(err) { + if err := os.Mkdir(dirPath, h.opt.dirMode); err != nil && !os.IsExist(err) { h.internalServerError(w, err) return } |