feat(config): Set default registry port and enhance authentication logging
- Implement logic to set a default registry port of 5000 if not specified in the configuration. - Add debug logging for cases where basic authentication is not provided and when authentication fails, improving traceability. - Update blob handling to log the request method for better context in debug messages.
- commit
- 0c262428814c88d3bb6198432cf30bc82cedd15f
- author
- bamen · · 2025-06-07 09:26 UTC
- parent
- fb6535b
- signature
- unsigned
3 files · +28 -3
M internal/config/config.go +6 -0
| @@ -66,6 +66,12 @@ func Load() (*Config, error) { | |||
| 66 | 66 | log.Debug().Str("data_dir", cfg.Server.DataDir).Msg("Config had empty data_dir, using default") | |
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | + | // If registry_port is 0 after loading config, use the default | |
| 70 | + | if cfg.Server.RegistryPort == 0 { | |
| 71 | + | cfg.Server.RegistryPort = 5000 | |
| 72 | + | log.Debug().Int("registry_port", cfg.Server.RegistryPort).Msg("Config had empty registry_port, using default") | |
| 73 | + | } | |
| 74 | + | ||
| 69 | 75 | // Get registry auth config | |
| 70 | 76 | if err := viper.UnmarshalKey("registry_auth", &cfg.RegistryAuth); err != nil { | |
| 71 | 77 | return nil, fmt.Errorf("unable to decode registry auth config: %v", err) | |
| @@ -66,6 +66,12 @@ func Load() (*Config, error) { | |||||
| 66 | log.Debug().Str("data_dir", cfg.Server.DataDir).Msg("Config had empty data_dir, using default") | 66 | log.Debug().Str("data_dir", cfg.Server.DataDir).Msg("Config had empty data_dir, using default") | ||
| 67 | } | 67 | } | ||
| 68 | 68 | ||||
| 69 | + | // If registry_port is 0 after loading config, use the default | |||
| 70 | + | if cfg.Server.RegistryPort == 0 { | |||
| 71 | + | cfg.Server.RegistryPort = 5000 | |||
| 72 | + | log.Debug().Int("registry_port", cfg.Server.RegistryPort).Msg("Config had empty registry_port, using default") | |||
| 73 | + | } | |||
| 74 | + | ||||
| 69 | // Get registry auth config | 75 | // Get registry auth config | ||
| 70 | if err := viper.UnmarshalKey("registry_auth", &cfg.RegistryAuth); err != nil { | 76 | if err := viper.UnmarshalKey("registry_auth", &cfg.RegistryAuth); err != nil { | ||
| 71 | return nil, fmt.Errorf("unable to decode registry auth config: %v", err) | 77 | return nil, fmt.Errorf("unable to decode registry auth config: %v", err) | ||
M internal/middleware/auth.go +15 -1
| @@ -41,6 +41,10 @@ func RegistryAuth(username, password string) func(http.Handler) http.Handler { | |||
| 41 | 41 | func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string) bool { | |
| 42 | 42 | username, password, ok := r.BasicAuth() | |
| 43 | 43 | if !ok { | |
| 44 | + | log.Debug(). | |
| 45 | + | Str("method", r.Method). | |
| 46 | + | Str("path", r.URL.Path). | |
| 47 | + | Msg("No basic auth provided") | |
| 44 | 48 | return false | |
| 45 | 49 | } | |
| 46 | 50 | ||
| @@ -48,5 +52,15 @@ func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string) | |||
| 48 | 52 | usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(expectedUsername)) == 1 | |
| 49 | 53 | passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(expectedPassword)) == 1 | |
| 50 | 54 | ||
| 51 | - | return usernameMatch && passwordMatch | |
| 55 | + | authenticated := usernameMatch && passwordMatch | |
| 56 | + | if !authenticated { | |
| 57 | + | log.Debug(). | |
| 58 | + | Str("provided_username", username). | |
| 59 | + | Str("expected_username", expectedUsername). | |
| 60 | + | Str("method", r.Method). | |
| 61 | + | Str("path", r.URL.Path). | |
| 62 | + | Msg("Authentication failed") | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | return authenticated | |
| 52 | 66 | }⏎̸ | |
| @@ -41,6 +41,10 @@ func RegistryAuth(username, password string) func(http.Handler) http.Handler { | |||||
| 41 | func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string) bool { | 41 | func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string) bool { | ||
| 42 | username, password, ok := r.BasicAuth() | 42 | username, password, ok := r.BasicAuth() | ||
| 43 | if !ok { | 43 | if !ok { | ||
| 44 | + | log.Debug(). | |||
| 45 | + | Str("method", r.Method). | |||
| 46 | + | Str("path", r.URL.Path). | |||
| 47 | + | Msg("No basic auth provided") | |||
| 44 | return false | 48 | return false | ||
| 45 | } | 49 | } | ||
| 46 | 50 | ||||
| @@ -48,5 +52,15 @@ func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string) | |||||
| 48 | usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(expectedUsername)) == 1 | 52 | usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(expectedUsername)) == 1 | ||
| 49 | passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(expectedPassword)) == 1 | 53 | passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(expectedPassword)) == 1 | ||
| 50 | 54 | ||||
| 51 | - | return usernameMatch && passwordMatch | 55 | + | authenticated := usernameMatch && passwordMatch |
| 56 | + | if !authenticated { | |||
| 57 | + | log.Debug(). | |||
| 58 | + | Str("provided_username", username). | |||
| 59 | + | Str("expected_username", expectedUsername). | |||
| 60 | + | Str("method", r.Method). | |||
| 61 | + | Str("path", r.URL.Path). | |||
| 62 | + | Msg("Authentication failed") | |||
| 63 | + | } | |||
| 64 | + | ||||
| 65 | + | return authenticated | |||
| 52 | }⏎̸ | 66 | }⏎̸ | ||
M internal/registry/server.go +7 -2
| @@ -65,7 +65,7 @@ func (s *Server) handleV2Routes(w http.ResponseWriter, r *http.Request) { | |||
| 65 | 65 | s.handleGetManifest(w, r) | |
| 66 | 66 | case method == "PUT" && matches(path, "/v2/.*/manifests/.*"): | |
| 67 | 67 | s.handlePutManifest(w, r) | |
| 68 | - | case method == "GET" && matches(path, "/v2/.*/blobs/.*"): | |
| 68 | + | case (method == "GET" || method == "HEAD") && matches(path, "/v2/.*/blobs/.*"): | |
| 69 | 69 | s.handleGetBlob(w, r) | |
| 70 | 70 | case method == "POST" && matches(path, "/v2/.*/blobs/uploads/"): | |
| 71 | 71 | s.handleStartBlobUpload(w, r) | |
| @@ -268,7 +268,7 @@ func (s *Server) handlePutManifest(w http.ResponseWriter, r *http.Request) { | |||
| 268 | 268 | func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) { | |
| 269 | 269 | name, digest := s.parseBlobPath(r.URL.Path) | |
| 270 | 270 | ||
| 271 | - | log.Debug().Str("name", name).Str("digest", digest).Msg("GET blob") | |
| 271 | + | log.Debug().Str("name", name).Str("digest", digest).Str("method", r.Method).Msg("Handle blob request") | |
| 272 | 272 | ||
| 273 | 273 | reader, err := s.storage.GetBlob(digest) | |
| 274 | 274 | if err != nil { | |
| @@ -281,6 +281,11 @@ func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) { | |||
| 281 | 281 | w.Header().Set("Content-Type", "application/octet-stream") | |
| 282 | 282 | w.WriteHeader(http.StatusOK) | |
| 283 | 283 | ||
| 284 | + | // For HEAD requests, don't send the body | |
| 285 | + | if r.Method == "HEAD" { | |
| 286 | + | return | |
| 287 | + | } | |
| 288 | + | ||
| 284 | 289 | _, err = io.Copy(w, reader) | |
| 285 | 290 | if err != nil { | |
| 286 | 291 | log.Error().Err(err).Str("digest", digest).Msg("Failed to serve blob") | |
| @@ -65,7 +65,7 @@ func (s *Server) handleV2Routes(w http.ResponseWriter, r *http.Request) { | |||||
| 65 | s.handleGetManifest(w, r) | 65 | s.handleGetManifest(w, r) | ||
| 66 | case method == "PUT" && matches(path, "/v2/.*/manifests/.*"): | 66 | case method == "PUT" && matches(path, "/v2/.*/manifests/.*"): | ||
| 67 | s.handlePutManifest(w, r) | 67 | s.handlePutManifest(w, r) | ||
| 68 | - | case method == "GET" && matches(path, "/v2/.*/blobs/.*"): | 68 | + | case (method == "GET" || method == "HEAD") && matches(path, "/v2/.*/blobs/.*"): |
| 69 | s.handleGetBlob(w, r) | 69 | s.handleGetBlob(w, r) | ||
| 70 | case method == "POST" && matches(path, "/v2/.*/blobs/uploads/"): | 70 | case method == "POST" && matches(path, "/v2/.*/blobs/uploads/"): | ||
| 71 | s.handleStartBlobUpload(w, r) | 71 | s.handleStartBlobUpload(w, r) | ||
| @@ -268,7 +268,7 @@ func (s *Server) handlePutManifest(w http.ResponseWriter, r *http.Request) { | |||||
| 268 | func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) { | 268 | func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) { | ||
| 269 | name, digest := s.parseBlobPath(r.URL.Path) | 269 | name, digest := s.parseBlobPath(r.URL.Path) | ||
| 270 | 270 | ||||
| 271 | - | log.Debug().Str("name", name).Str("digest", digest).Msg("GET blob") | 271 | + | log.Debug().Str("name", name).Str("digest", digest).Str("method", r.Method).Msg("Handle blob request") |
| 272 | 272 | ||||
| 273 | reader, err := s.storage.GetBlob(digest) | 273 | reader, err := s.storage.GetBlob(digest) | ||
| 274 | if err != nil { | 274 | if err != nil { | ||
| @@ -281,6 +281,11 @@ func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) { | |||||
| 281 | w.Header().Set("Content-Type", "application/octet-stream") | 281 | w.Header().Set("Content-Type", "application/octet-stream") | ||
| 282 | w.WriteHeader(http.StatusOK) | 282 | w.WriteHeader(http.StatusOK) | ||
| 283 | 283 | ||||
| 284 | + | // For HEAD requests, don't send the body | |||
| 285 | + | if r.Method == "HEAD" { | |||
| 286 | + | return | |||
| 287 | + | } | |||
| 288 | + | ||||
| 284 | _, err = io.Copy(w, reader) | 289 | _, err = io.Copy(w, reader) | ||
| 285 | if err != nil { | 290 | if err != nil { | ||
| 286 | log.Error().Err(err).Str("digest", digest).Msg("Failed to serve blob") | 291 | log.Error().Err(err).Str("digest", digest).Msg("Failed to serve blob") | ||