WIP: docker: public.ecr.aws support #66

Closed
Rirmach wants to merge 2 commits from main into main
4 changed files with 62 additions and 10 deletions

View File

@@ -133,6 +133,12 @@ func DefaultConfig() *AppConfig {
AuthType: "anonymous",
Enabled: true,
},
"public.ecr.aws": {
Upstream: "public.ecr.aws",
AuthHost: "public.ecr.aws/token",
AuthType: "aws-ecr",
Enabled: true,
},
},
TokenCache: struct {
Enabled bool `toml:"enabled"`

View File

@@ -25,7 +25,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/json-iterator/go v1.1.12
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
@@ -37,6 +37,7 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect

View File

@@ -77,8 +77,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=

View File

@@ -3,17 +3,17 @@ package handlers
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
jsoniter "github.com/json-iterator/go"
"hubproxy/config"
"hubproxy/utils"
"io"
"net/http"
"strings"
"time"
)
// DockerProxy Docker代理配置
@@ -546,7 +546,7 @@ func handleUpstreamBlobRequest(c *gin.Context, imageRef, digest string, mapping
if err != nil {
fmt.Printf("获取layer大小失败: %v\n", err)
c.String(http.StatusInternalServerError, "Failed to get layer size")
return
// return
}
reader, err := layer.Compressed()
@@ -593,17 +593,62 @@ func handleUpstreamTagsRequest(c *gin.Context, imageRef string, mapping config.R
// createUpstreamOptions 创建上游Registry选项
func createUpstreamOptions(mapping config.RegistryMapping) []remote.Option {
options := []remote.Option{
remote.WithAuth(authn.Anonymous),
remote.WithUserAgent("hubproxy/go-containerregistry"),
remote.WithTransport(utils.GetGlobalHTTPClient().Transport),
}
useAnonymous := true
// 预留将来不同Registry的差异化认证逻辑扩展点
switch mapping.AuthType {
case "github":
case "google":
case "quay":
case "aws-ecr":
// Step 1: Get anonymous token from https://public.ecr.aws/token/
client := &http.Client{
Timeout: 10 * time.Second,
Transport: utils.GetGlobalHTTPClient().Transport,
}
authUrl := fmt.Sprint("https://", mapping.AuthHost)
resp, err := client.Get(authUrl)
if err != nil {
fmt.Printf("%s: Failed to get token, fallback to anonymous: %v\n", mapping.AuthType, err)
break
}
defer resp.Body.Close()
c, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("%s: Failed to read token, fallback to anonymous: %v\n", mapping.AuthType, err)
break
}
var tokenRet map[string]string
if err := jsoniter.Unmarshal(c, &tokenRet); err != nil {
fmt.Printf("%s: Failed to read token, fallback to anonymous: %v\n", mapping.AuthType, err)
break
}
// Step 2: Successfully got token like:
/*
{"token":"[Token here]"}
*/
if token, ok := tokenRet["token"]; !ok {
fmt.Printf("%s: token is missing in response, fallback to anonymous: %v\n", mapping.AuthType, err)
break
} else {
// Step 3: Use the token fetched as Bearer auth
auth := &authn.Bearer{
Token: token,
}
options = append(options, remote.WithAuth(auth))
useAnonymous = false
}
}
if useAnonymous {
options = append(options, remote.WithAuth(authn.Anonymous))
}
return options
}