mirror of
https://github.com/restic/rest-server.git
synced 2025-10-19 15:43:21 +00:00
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
This commit is contained in:
parent
e35c6e39d9
commit
92216a10ba
3 changed files with 62 additions and 38 deletions
|
@ -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.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.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.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 read repo files")
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
}
|
}
|
||||||
|
@ -147,6 +148,12 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error {
|
||||||
log.Println("Private repositories disabled")
|
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()
|
enabledTLS, privateKey, publicKey, err := app.tlsSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
51
handlers.go
51
handlers.go
|
@ -15,23 +15,24 @@ import (
|
||||||
|
|
||||||
// Server encapsulates the rest-server's settings and repo management logic
|
// Server encapsulates the rest-server's settings and repo management logic
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Path string
|
Path string
|
||||||
HtpasswdPath string
|
HtpasswdPath string
|
||||||
Listen string
|
Listen string
|
||||||
Log string
|
Log string
|
||||||
CPUProfile string
|
CPUProfile string
|
||||||
TLSKey string
|
TLSKey string
|
||||||
TLSCert string
|
TLSCert string
|
||||||
TLS bool
|
TLS bool
|
||||||
NoAuth bool
|
NoAuth bool
|
||||||
AppendOnly bool
|
AppendOnly bool
|
||||||
PrivateRepos bool
|
PrivateRepos bool
|
||||||
Prometheus bool
|
Prometheus bool
|
||||||
PrometheusNoAuth bool
|
PrometheusNoAuth bool
|
||||||
Debug bool
|
Debug bool
|
||||||
MaxRepoSize int64
|
MaxRepoSize int64
|
||||||
PanicOnError bool
|
PanicOnError bool
|
||||||
NoVerifyUpload bool
|
NoVerifyUpload bool
|
||||||
|
GroupAccessibleRepos bool
|
||||||
|
|
||||||
htpasswdFile *HtpasswdFile
|
htpasswdFile *HtpasswdFile
|
||||||
quotaManager *quota.Manager
|
quotaManager *quota.Manager
|
||||||
|
@ -88,12 +89,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Pass the request to the repo.Handler
|
// Pass the request to the repo.Handler
|
||||||
opt := repo.Options{
|
opt := repo.Options{
|
||||||
AppendOnly: s.AppendOnly,
|
AppendOnly: s.AppendOnly,
|
||||||
Debug: s.Debug,
|
Debug: s.Debug,
|
||||||
QuotaManager: s.quotaManager, // may be nil
|
QuotaManager: s.quotaManager, // may be nil
|
||||||
PanicOnError: s.PanicOnError,
|
PanicOnError: s.PanicOnError,
|
||||||
NoVerifyUpload: s.NoVerifyUpload,
|
NoVerifyUpload: s.NoVerifyUpload,
|
||||||
FsyncWarning: &s.fsyncWarning,
|
FsyncWarning: &s.fsyncWarning,
|
||||||
|
GroupAccessible: s.GroupAccessibleRepos,
|
||||||
}
|
}
|
||||||
if s.Prometheus {
|
if s.Prometheus {
|
||||||
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
|
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
|
||||||
|
@ -158,7 +160,8 @@ func join(base string, names ...string) (string, error) {
|
||||||
// splitURLPath splits the URL path into a folderPath of the subrepo, and
|
// splitURLPath splits the URL path into a folderPath of the subrepo, and
|
||||||
// a remainder that can be passed to repo.Handler.
|
// a remainder that can be passed to repo.Handler.
|
||||||
// Example: /foo/bar/locks/0123... will be split into:
|
// Example: /foo/bar/locks/0123... will be split into:
|
||||||
// ["foo", "bar"] and "/locks/0123..."
|
//
|
||||||
|
// ["foo", "bar"] and "/locks/0123..."
|
||||||
func splitURLPath(urlPath string, maxDepth int) (folderPath []string, remainder string) {
|
func splitURLPath(urlPath string, maxDepth int) (folderPath []string, remainder string) {
|
||||||
if !strings.HasPrefix(urlPath, "/") {
|
if !strings.HasPrefix(urlPath, "/") {
|
||||||
// Really should start with "/"
|
// Really should start with "/"
|
||||||
|
|
42
repo/repo.go
42
repo/repo.go
|
@ -29,8 +29,6 @@ import (
|
||||||
type Options struct {
|
type Options struct {
|
||||||
AppendOnly bool // if set, delete actions are not allowed
|
AppendOnly bool // if set, delete actions are not allowed
|
||||||
Debug bool
|
Debug bool
|
||||||
DirMode os.FileMode
|
|
||||||
FileMode os.FileMode
|
|
||||||
NoVerifyUpload bool
|
NoVerifyUpload bool
|
||||||
|
|
||||||
// If set, we will panic when an internal server error happens. This
|
// If set, we will panic when an internal server error happens. This
|
||||||
|
@ -40,6 +38,13 @@ type Options struct {
|
||||||
BlobMetricFunc BlobMetricFunc
|
BlobMetricFunc BlobMetricFunc
|
||||||
QuotaManager *quota.Manager
|
QuotaManager *quota.Manager
|
||||||
FsyncWarning *sync.Once
|
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
|
// DefaultDirMode is the file mode used for directory creation if not
|
||||||
|
@ -50,6 +55,12 @@ const DefaultDirMode os.FileMode = 0700
|
||||||
// overridden in the Options
|
// overridden in the Options
|
||||||
const DefaultFileMode os.FileMode = 0600
|
const DefaultFileMode os.FileMode = 0600
|
||||||
|
|
||||||
|
// File mode used for directory creation when group access is enabled
|
||||||
|
const GroupAccessibleDirMode os.FileMode = 0770
|
||||||
|
|
||||||
|
// 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.
|
// New creates a new Handler for a single Restic backup repo.
|
||||||
// path is the full filesystem path to this repo directory.
|
// path is the full filesystem path to this repo directory.
|
||||||
// opt is a set of options.
|
// opt is a set of options.
|
||||||
|
@ -57,12 +68,15 @@ func New(path string, opt Options) (*Handler, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, fmt.Errorf("path is required")
|
return nil, fmt.Errorf("path is required")
|
||||||
}
|
}
|
||||||
if opt.DirMode == 0 {
|
|
||||||
opt.DirMode = DefaultDirMode
|
opt.dirMode = DefaultDirMode
|
||||||
}
|
opt.fileMode = DefaultFileMode
|
||||||
if opt.FileMode == 0 {
|
|
||||||
opt.FileMode = DefaultFileMode
|
if opt.GroupAccessible {
|
||||||
|
opt.dirMode = GroupAccessibleDirMode
|
||||||
|
opt.fileMode = GroupAccessibleFileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
h := Handler{
|
h := Handler{
|
||||||
path: path,
|
path: path,
|
||||||
opt: opt,
|
opt: opt,
|
||||||
|
@ -289,7 +303,7 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
cfg := h.getSubPath("config")
|
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 err != nil && os.IsExist(err) {
|
||||||
if h.opt.Debug {
|
if h.opt.Debug {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
|
@ -545,15 +559,15 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpFn := filepath.Join(filepath.Dir(path), objectID+".rest-server-temp")
|
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) {
|
if os.IsNotExist(err) {
|
||||||
// the error is caused by a missing directory, create it and retry
|
// 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 {
|
if mkdirErr != nil {
|
||||||
log.Print(mkdirErr)
|
log.Print(mkdirErr)
|
||||||
} else {
|
} else {
|
||||||
// try again
|
// try again
|
||||||
tf, err = tempFile(tmpFn, h.opt.FileMode)
|
tf, err = tempFile(tmpFn, h.opt.fileMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -750,13 +764,13 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
log.Printf("Creating repository directories in %s\n", h.path)
|
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)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range ObjectTypes {
|
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)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -764,7 +778,7 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", 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)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue