Move upload auth from nginx into the app
Add HTTP Basic auth on /upload, reading an Apache htpasswd file (bcrypt only, `htpasswd -B`) via a new -htpasswd flag. This lets nginx drop to a plain TLS-terminating reverse proxy with no auth of its own; the existing bcrypt htpasswd file is reused unchanged. auth.go: - LoadHtpasswd parses user:hash lines, keeps bcrypt entries ($2a/$2b/$2y, so Apache's $2y$ works), skips MD5/SHA/crypt with a warning. - Unknown users still pay for one bcrypt comparison against a dummy hash, so response timing can't enumerate valid usernames. - Failed attempts draw from a token-bucket limiter (x/time/rate, ~1/s, burst 10) returning 429 once drained; successful and bulk uploads never touch it. server.go guards both /upload routes (nil authenticator = passthrough for local --serve). /health stays open. Empty -htpasswd disables auth and logs a warning. module.nix: new top-level services.photogallery.htpasswdFile, delivered to the service via systemd LoadCredential (so a root-owned 0400 secret works). Removed nginx.htpasswdFile and basic_auth from the vhost. Both uploader apps already send Basic credentials, so no client change. Deps: golang.org/x/crypto/bcrypt, golang.org/x/time/rate (vendorHash bump).
This commit is contained in:
1 parent
3291ed67d5
commit
434d2437bf
9 files changed
+342
-27
No files matched your search
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
@@ -19,22 +20,37 @@ type Server struct {
|
||||
outputDir string
|
||||
serve bool
|
||||
generator *Generator
|
||||
auth *Authenticator // nil disables auth (dev only)
|
||||
authLimit *rate.Limiter
|
||||
}
|
||||
|
||||
func NewServer(port, contentDir, outputDir string, serve bool, g *Generator) *Server {
|
||||
func NewServer(port, contentDir, outputDir string, serve bool, g *Generator, auth *Authenticator) *Server {
|
||||
return &Server{
|
||||
port: port,
|
||||
contentDir: contentDir,
|
||||
outputDir: outputDir,
|
||||
serve: serve,
|
||||
generator: g,
|
||||
auth: auth,
|
||||
// Brute-force throttle: ~1 failed auth/sec steady, burst of 10. Only
|
||||
// failures draw from it, so legitimate bulk uploads are never slowed.
|
||||
authLimit: rate.NewLimiter(rate.Every(time.Second), 10),
|
||||
}
|
||||
}
|
||||
|
||||
// guard applies HTTP Basic auth to h when an Authenticator is configured, and
|
||||
// passes it through untouched otherwise (local --serve development).
|
||||
func (s *Server) guard(h http.HandlerFunc) http.HandlerFunc {
|
||||
if s.auth == nil {
|
||||
return h
|
||||
}
|
||||
return s.auth.basicAuth(s.authLimit, h)
|
||||
}
|
||||
|
||||
func (s *Server) Listen() error {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /upload", s.handleUploadPage)
|
||||
mux.HandleFunc("POST /upload", s.handleUpload)
|
||||
mux.HandleFunc("GET /upload", s.guard(s.handleUploadPage))
|
||||
mux.HandleFunc("POST /upload", s.guard(s.handleUpload))
|
||||
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "ok")
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user