detached 0c26242

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) {
6666 log.Debug().Str("data_dir", cfg.Server.DataDir).Msg("Config had empty data_dir, using default")
6767 }
6868
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+
6975 // Get registry auth config
7076 if err := viper.UnmarshalKey("registry_auth", &cfg.RegistryAuth); err != nil {
7177 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 config75 // 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 {
4141 func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string) bool {
4242 username, password, ok := r.BasicAuth()
4343 if !ok {
44+ log.Debug().
45+ Str("method", r.Method).
46+ Str("path", r.URL.Path).
47+ Msg("No basic auth provided")
4448 return false
4549 }
4650
@@ -48,5 +52,15 @@ func isAuthenticated(r *http.Request, expectedUsername, expectedPassword string)
4852 usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(expectedUsername)) == 1
4953 passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(expectedPassword)) == 1
5054
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
5266 }⏎̸
@@ -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 false48 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)) == 152 usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(expectedUsername)) == 1
49 passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(expectedPassword)) == 153 passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(expectedPassword)) == 1
50 54
51- return usernameMatch && passwordMatch55+ 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) {
6565 s.handleGetManifest(w, r)
6666 case method == "PUT" && matches(path, "/v2/.*/manifests/.*"):
6767 s.handlePutManifest(w, r)
68- case method == "GET" && matches(path, "/v2/.*/blobs/.*"):
68+ case (method == "GET" || method == "HEAD") && matches(path, "/v2/.*/blobs/.*"):
6969 s.handleGetBlob(w, r)
7070 case method == "POST" && matches(path, "/v2/.*/blobs/uploads/"):
7171 s.handleStartBlobUpload(w, r)
@@ -268,7 +268,7 @@ func (s *Server) handlePutManifest(w http.ResponseWriter, r *http.Request) {
268268 func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) {
269269 name, digest := s.parseBlobPath(r.URL.Path)
270270
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")
272272
273273 reader, err := s.storage.GetBlob(digest)
274274 if err != nil {
@@ -281,6 +281,11 @@ func (s *Server) handleGetBlob(w http.ResponseWriter, r *http.Request) {
281281 w.Header().Set("Content-Type", "application/octet-stream")
282282 w.WriteHeader(http.StatusOK)
283283
284+ // For HEAD requests, don't send the body
285+ if r.Method == "HEAD" {
286+ return
287+ }
288+
284289 _, err = io.Copy(w, reader)
285290 if err != nil {
286291 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")

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close