Summary
docker-modem currently sets X-Registry-Config using standard base64 in buildImage requests.
Code path:
https://github.com/apocas/docker-modem/blob/v5.0.6/lib/modem.js#L191-L193
optionsf.headers['X-Registry-Config'] = options.registryconfig.base64 ||
Buffer.from(JSON.stringify(options.registryconfig)).toString('base64');
However, Docker daemon build route decodes X-Registry-Config with URL-safe base64 (base64.URLEncoding) and ignores decode errors:
https://github.com/moby/moby/blob/59995f1b91d135ce740a7e4a9b801a748d1f7848/daemon/server/router/build/build_routes.go#L336-L348
func getAuthConfigs(header http.Header) map[string]registry.AuthConfig {
authConfigs := map[string]registry.AuthConfig{}
authConfigsEncoded := header.Get("X-Registry-Config")
if authConfigsEncoded == "" {
return authConfigs
}
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
// Pulling an image does not error when no auth is provided so to remain
// consistent with the existing api decode errors are ignored
_ = json.NewDecoder(authConfigsJSON).Decode(&authConfigs)
return authConfigs
}
So if the encoded header contains + or /, decode can fail and auth configs become empty.
Expected behavior
X-Registry-Config should be URL-safe base64 encoded (compatible with daemon decode path), like the Moby client does:
https://github.com/moby/moby/blob/59995f1b91d135ce740a7e4a9b801a748d1f7848/client/image_build.go#L31-L33
headers := http.Header{}
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
headers.Set("Content-Type", "application/x-tar")
Actual behavior
docker-modem sends standard base64.
For payloads where standard base64 output contains + or /, daemon-side decode fails and auth map is empty.
Repro
Using a payload that produces + in standard base64:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
)
type authConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
func decodeLikeMoby(header string) (map[string]authConfig, error) {
m := map[string]authConfig{}
err := json.NewDecoder(base64.NewDecoder(base64.URLEncoding, strings.NewReader(header))).Decode(&m)
return m, err
}
func main() {
// This is the JSON docker-modem would base64-encode into X-Registry-Config.
payload := `{"https://index.docker.io/v1/":{"username":"u","password":">"}}`
// docker-modem behavior today (standard base64):
std := base64.StdEncoding.EncodeToString([]byte(payload))
// Docker/Moby client behavior (URL-safe base64):
url := base64.URLEncoding.EncodeToString([]byte(payload))
fmt.Printf("payload=%s\n", payload)
fmt.Printf("std_has_plus=%v\n", strings.Contains(std, "+"))
fmt.Printf("std_has_slash=%v\n", strings.Contains(std, "/"))
stdM, stdErr := decodeLikeMoby(std)
urlM, urlErr := decodeLikeMoby(url)
fmt.Printf("decode_std_err=%v\n", stdErr)
fmt.Printf("decode_std_entries=%d\n", len(stdM))
fmt.Printf("decode_url_err=%v\n", urlErr)
fmt.Printf("decode_url_entries=%d\n", len(urlM))
}
Real-world impact
Downstream users can see authenticated pull working for normal pulls but Dockerfile FROM pull during build behaving as unauthenticated, causing Docker Hub rate-limit failures (toomanyrequests) despite valid credentials.
Proposed fix
In docker-modem, for X-Registry-Config default encoding, use URL-safe base64 (preserve padding), e.g. standard base64 + replace + -> -, / -> _.
Summary
docker-modem currently sets
X-Registry-Configusing standard base64 inbuildImagerequests.Code path:
https://github.com/apocas/docker-modem/blob/v5.0.6/lib/modem.js#L191-L193
However, Docker daemon build route decodes
X-Registry-Configwith URL-safe base64 (base64.URLEncoding) and ignores decode errors:https://github.com/moby/moby/blob/59995f1b91d135ce740a7e4a9b801a748d1f7848/daemon/server/router/build/build_routes.go#L336-L348
So if the encoded header contains + or /, decode can fail and auth configs become empty.
Expected behavior
X-Registry-Configshould be URL-safe base64 encoded (compatible with daemon decode path), like the Moby client does:https://github.com/moby/moby/blob/59995f1b91d135ce740a7e4a9b801a748d1f7848/client/image_build.go#L31-L33
Actual behavior
docker-modem sends standard base64.
For payloads where standard base64 output contains + or /, daemon-side decode fails and auth map is empty.
Repro
Using a payload that produces + in standard base64:
Real-world impact
Downstream users can see authenticated pull working for normal pulls but Dockerfile FROM pull during build behaving as unauthenticated, causing Docker Hub rate-limit failures (toomanyrequests) despite valid credentials.
Proposed fix
In docker-modem, for
X-Registry-Configdefault encoding, use URL-safe base64 (preserve padding), e.g. standard base64 + replace + -> -, / -> _.