From 024ff561e8ecaf17c1f5803b07bd7e51c019810d Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 9 May 2026 23:59:31 +0200 Subject: [PATCH 1/7] avoid potential data race for err variable --- cmd/rest-server/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index 12fd6b6..9194693 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -206,6 +206,7 @@ func (app *restServerApp) runRoot(_ *cobra.Command, _ []string) error { // run server in background go func() { + var err error if !enabledTLS { err = srv.Serve(listener) } else { From 1141a8db34a19e0d82fb8ee2732c47351f8e98e1 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 9 May 2026 23:59:47 +0200 Subject: [PATCH 2/7] fix comment typos --- handlers.go | 2 +- repo/repo.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/handlers.go b/handlers.go index 691afa8..784fdd4 100644 --- a/handlers.go +++ b/handlers.go @@ -51,7 +51,7 @@ func httpDefaultError(w http.ResponseWriter, code int) { http.Error(w, http.StatusText(code), code) } -// ServeHTTP makes this server an http.Handler. It handlers the administrative +// ServeHTTP makes this server an http.Handler. It handles the administrative // part of the request (figuring out the filesystem location, performing // authentication, etc) and then passes it on to repo.Handler for actual // REST API processing. diff --git a/repo/repo.go b/repo/repo.go index adc4a55..0fb09d2 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -810,7 +810,7 @@ func (h *Handler) internalServerError(w http.ResponseWriter, err error) { httpDefaultError(w, http.StatusInternalServerError) } -// internalServerError is called to report an error that occurred while +// fileAccessError is called to report an error that occurred while // accessing a file. If the does not exist, the corresponding http status code // will be returned to the client. All other errors are passed on to // internalServerError From d9feb75bc519db24f75d7f19fe3f6d5e4d0719be Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 10 May 2026 00:00:43 +0200 Subject: [PATCH 3/7] correct htpasswd cache expiry was 0-5 seconds instead of the intended one minute. --- htpasswd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htpasswd.go b/htpasswd.go index ad67a1e..46a8c72 100644 --- a/htpasswd.go +++ b/htpasswd.go @@ -123,7 +123,7 @@ func (h *HtpasswdFile) expiryTimer() { var zeros [sha256.Size]byte // try to wipe expired cache entries for user, entry := range h.cache { - if entry.expiry.After(now) { + if now.After(entry.expiry) { copy(entry.verifier, zeros[:]) delete(h.cache, user) } From 4e1be9e94b172e3708364f73c7feacffbb4decbe Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 10 May 2026 00:01:21 +0200 Subject: [PATCH 4/7] return 400 bad request if content-length is invalid --- quota/quota.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quota/quota.go b/quota/quota.go index 8437f65..ee243c6 100644 --- a/quota/quota.go +++ b/quota/quota.go @@ -70,7 +70,7 @@ func (m *Manager) WrapWriter(req *http.Request, w io.Writer) (io.Writer, int, er if contentLenStr := req.Header.Get("Content-Length"); contentLenStr != "" { contentLen, err := strconv.ParseInt(contentLenStr, 10, 64) if err != nil { - return nil, http.StatusLengthRequired, err + return nil, http.StatusBadRequest, err } if currentSize+contentLen > m.maxRepoSize { err := fmt.Errorf("incoming blob (%d bytes) would exceed maximum size of repository (%d bytes)", From b74306f0964fcca044db8c2ec2c6fbc61535955f Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 10 May 2026 00:01:57 +0200 Subject: [PATCH 5/7] don't panic if saveConfig fails to open file --- repo/repo.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/repo/repo.go b/repo/repo.go index 0fb09d2..52806aa 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -305,11 +305,15 @@ 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) - if err != nil && os.IsExist(err) { + if err != nil { if h.opt.Debug { log.Print(err) } - httpDefaultError(w, http.StatusForbidden) + if os.IsExist(err) { + httpDefaultError(w, http.StatusForbidden) + } else { + h.internalServerError(w, err) + } return } From e559c3760a0047b24bdb50e3e07fa771759bbb5b Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 10 May 2026 00:03:48 +0200 Subject: [PATCH 6/7] avoid log spam if user password is invalid --- htpasswd.go | 1 - 1 file changed, 1 deletion(-) diff --git a/htpasswd.go b/htpasswd.go index 46a8c72..430c991 100644 --- a/htpasswd.go +++ b/htpasswd.go @@ -251,7 +251,6 @@ func (h *HtpasswdFile) Validate(user string, password string) bool { isValid := isMatchingHashAndPassword(hashedPassword, password) if !isValid { - log.Printf("Invalid htpasswd entry for %s.", user) return false } From 7ee6edc0e403f2e0fd97f4a6101c106e9acbce3d Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Mon, 12 Oct 2026 21:52:02 +0200 Subject: [PATCH 7/7] add changelog entry for htpasswd cache --- changelog/unreleased/pull-386 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/pull-386 diff --git a/changelog/unreleased/pull-386 b/changelog/unreleased/pull-386 new file mode 100644 index 0000000..95d4a07 --- /dev/null +++ b/changelog/unreleased/pull-386 @@ -0,0 +1,6 @@ +Bugfix: Keep cached htpasswd credentials for one minute + +The password cache removed entries withing seconds instead of after +one minute as intended. This has been fixed. + +https://github.com/restic/rest-server/pull/386