Merge pull request #386 from MichaelEischer/misc-fixes
test / Linux Go 1.26.x (push) Canceled after 0s
test / Linux (race) Go 1.27.x (push) Canceled after 0s
test / Linux Go 1.27.x (push) Canceled after 0s
test / lint (push) Canceled after 0s
test / Analyze results (push) Canceled after 0s

Potential crash fix, correct htpasswd cache expiry and avoid log spam
This commit is contained in:
Michael Eischer
2026-10-12 21:57:27 +02:00
committed by GitHub
6 changed files with 17 additions and 7 deletions
+6
View File
@@ -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
+1
View File
@@ -206,6 +206,7 @@ func (app *restServerApp) runRoot(_ *cobra.Command, _ []string) error {
// run server in background // run server in background
go func() { go func() {
var err error
if !enabledTLS { if !enabledTLS {
err = srv.Serve(listener) err = srv.Serve(listener)
} else { } else {
+1 -1
View File
@@ -51,7 +51,7 @@ func httpDefaultError(w http.ResponseWriter, code int) {
http.Error(w, http.StatusText(code), code) 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 // part of the request (figuring out the filesystem location, performing
// authentication, etc) and then passes it on to repo.Handler for actual // authentication, etc) and then passes it on to repo.Handler for actual
// REST API processing. // REST API processing.
+1 -2
View File
@@ -123,7 +123,7 @@ func (h *HtpasswdFile) expiryTimer() {
var zeros [sha256.Size]byte var zeros [sha256.Size]byte
// try to wipe expired cache entries // try to wipe expired cache entries
for user, entry := range h.cache { for user, entry := range h.cache {
if entry.expiry.After(now) { if now.After(entry.expiry) {
copy(entry.verifier, zeros[:]) copy(entry.verifier, zeros[:])
delete(h.cache, user) delete(h.cache, user)
} }
@@ -251,7 +251,6 @@ func (h *HtpasswdFile) Validate(user string, password string) bool {
isValid := isMatchingHashAndPassword(hashedPassword, password) isValid := isMatchingHashAndPassword(hashedPassword, password)
if !isValid { if !isValid {
log.Printf("Invalid htpasswd entry for %s.", user)
return false return false
} }
+1 -1
View File
@@ -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 != "" { if contentLenStr := req.Header.Get("Content-Length"); contentLenStr != "" {
contentLen, err := strconv.ParseInt(contentLenStr, 10, 64) contentLen, err := strconv.ParseInt(contentLenStr, 10, 64)
if err != nil { if err != nil {
return nil, http.StatusLengthRequired, err return nil, http.StatusBadRequest, err
} }
if currentSize+contentLen > m.maxRepoSize { if currentSize+contentLen > m.maxRepoSize {
err := fmt.Errorf("incoming blob (%d bytes) would exceed maximum size of repository (%d bytes)", err := fmt.Errorf("incoming blob (%d bytes) would exceed maximum size of repository (%d bytes)",
+6 -2
View File
@@ -305,11 +305,15 @@ 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 {
if h.opt.Debug { if h.opt.Debug {
log.Print(err) log.Print(err)
} }
if os.IsExist(err) {
httpDefaultError(w, http.StatusForbidden) httpDefaultError(w, http.StatusForbidden)
} else {
h.internalServerError(w, err)
}
return return
} }
@@ -810,7 +814,7 @@ func (h *Handler) internalServerError(w http.ResponseWriter, err error) {
httpDefaultError(w, http.StatusInternalServerError) 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 // 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 // will be returned to the client. All other errors are passed on to
// internalServerError // internalServerError