From 9fd5004f35cd6d01a9cb2ebb95668044f0f9d34c Mon Sep 17 00:00:00 2001 From: Yan Song Date: Sat, 6 May 2023 11:16:25 +0000 Subject: [PATCH 1/8] solver: fix unnecessary force compression for provenance Signed-off-by: Yan Song --- solver/llbsolver/proc/provenance.go | 5 +++-- solver/llbsolver/proc/sbom.go | 3 ++- solver/llbsolver/provenance.go | 4 +++- solver/llbsolver/solver.go | 6 +++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/solver/llbsolver/proc/provenance.go b/solver/llbsolver/proc/provenance.go index 1af3af196..2edbdc39e 100644 --- a/solver/llbsolver/proc/provenance.go +++ b/solver/llbsolver/proc/provenance.go @@ -11,11 +11,12 @@ import ( "github.com/moby/buildkit/solver" "github.com/moby/buildkit/solver/llbsolver" "github.com/moby/buildkit/solver/result" + "github.com/moby/buildkit/util/compression" "github.com/pkg/errors" ) func ProvenanceProcessor(attrs map[string]string) llbsolver.Processor { - return func(ctx context.Context, res *llbsolver.Result, s *llbsolver.Solver, j *solver.Job) (*llbsolver.Result, error) { + return func(ctx context.Context, res *llbsolver.Result, s *llbsolver.Solver, j *solver.Job, comp compression.Config) (*llbsolver.Result, error) { ps, err := exptypes.ParsePlatforms(res.Metadata) if err != nil { return nil, err @@ -41,7 +42,7 @@ func ProvenanceProcessor(attrs map[string]string) llbsolver.Processor { return nil, errors.Errorf("could not find ref %s", p.ID) } - pc, err := llbsolver.NewProvenanceCreator(ctx, cp, ref, attrs, j) + pc, err := llbsolver.NewProvenanceCreator(ctx, cp, ref, attrs, j, comp) if err != nil { return nil, err } diff --git a/solver/llbsolver/proc/sbom.go b/solver/llbsolver/proc/sbom.go index 2d7e969ba..c0837218a 100644 --- a/solver/llbsolver/proc/sbom.go +++ b/solver/llbsolver/proc/sbom.go @@ -10,11 +10,12 @@ import ( "github.com/moby/buildkit/solver" "github.com/moby/buildkit/solver/llbsolver" "github.com/moby/buildkit/solver/result" + "github.com/moby/buildkit/util/compression" "github.com/pkg/errors" ) func SBOMProcessor(scannerRef string, useCache bool) llbsolver.Processor { - return func(ctx context.Context, res *llbsolver.Result, s *llbsolver.Solver, j *solver.Job) (*llbsolver.Result, error) { + return func(ctx context.Context, res *llbsolver.Result, s *llbsolver.Solver, j *solver.Job, comp compression.Config) (*llbsolver.Result, error) { // skip sbom generation if we already have an sbom if sbom.HasSBOM(res.Result) { return res, nil diff --git a/solver/llbsolver/provenance.go b/solver/llbsolver/provenance.go index b30581c85..f02e958e7 100644 --- a/solver/llbsolver/provenance.go +++ b/solver/llbsolver/provenance.go @@ -20,6 +20,7 @@ import ( "github.com/moby/buildkit/solver/llbsolver/provenance" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/source" + "github.com/moby/buildkit/util/compression" "github.com/moby/buildkit/worker" digest "github.com/opencontainers/go-digest" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" @@ -372,7 +373,7 @@ type ProvenanceCreator struct { addLayers func() error } -func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solver.ResultProxy, attrs map[string]string, j *solver.Job) (*ProvenanceCreator, error) { +func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solver.ResultProxy, attrs map[string]string, j *solver.Job, comp compression.Config) (*ProvenanceCreator, error) { var reproducible bool if v, ok := attrs["reproducible"]; ok { b, err := strconv.ParseBool(v) @@ -449,6 +450,7 @@ func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solve ResolveRemotes: resolveRemotes, Mode: solver.CacheExportModeRemoteOnly, ExportRoots: true, + CompressionOpt: &comp, }); err != nil { return err } diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 2f7ba61e5..ffe7ed570 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -93,7 +93,7 @@ type Solver struct { // Processor defines a processing function to be applied after solving, but // before exporting -type Processor func(ctx context.Context, result *Result, s *Solver, j *solver.Job) (*Result, error) +type Processor func(ctx context.Context, result *Result, s *Solver, j *solver.Job, comp compression.Config) (*Result, error) func New(opt Opt) (*Solver, error) { s := &Solver{ @@ -200,7 +200,7 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend } makeProvenance := func(res solver.ResultProxy, cap *provenance.Capture) (*controlapi.Descriptor, func(), error) { - prc, err := NewProvenanceCreator(ctx2, cap, res, attrs, j) + prc, err := NewProvenanceCreator(ctx2, cap, res, attrs, j, exp.Exporter.Config().Compression()) if err != nil { return nil, nil, err } @@ -501,7 +501,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro } for _, post := range post { - res2, err := post(ctx, resProv, s, j) + res2, err := post(ctx, resProv, s, j, exp.Exporter.Config().Compression()) if err != nil { return nil, err } From 76aeaa55ff4e1c575644bffbf0a43bea92215fbe Mon Sep 17 00:00:00 2001 From: Yan Song Date: Sat, 6 May 2023 11:22:27 +0000 Subject: [PATCH 2/8] nydus: set default compression option to avoid recomputing Signed-off-by: Yan Song --- nydus/util/util.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nydus/util/util.go b/nydus/util/util.go index f65cfe520..6524ebf78 100644 --- a/nydus/util/util.go +++ b/nydus/util/util.go @@ -15,12 +15,14 @@ type compressorKey struct{} type chunkDictDigestKey struct{} func WithContext(ctx context.Context, fsVersion string, compressor string, chunkDictDigest digest.Digest) context.Context { - if fsVersion == "" { - fsVersion = "5" + if fsVersion != "" { + ctx = context.WithValue(ctx, fsVersionKey{}, fsVersion) + } + + if compressor != "" { + ctx = context.WithValue(ctx, compressorKey{}, compressor) } - ctx = context.WithValue(ctx, fsVersionKey{}, fsVersion) - ctx = context.WithValue(ctx, compressorKey{}, compressor) if chunkDictDigest != "" { ctx = context.WithValue(ctx, chunkDictDigestKey{}, chunkDictDigest.String()) } @@ -29,8 +31,8 @@ func WithContext(ctx context.Context, fsVersion string, compressor string, chunk } func GetContext(ctx context.Context) (string, string, string) { - fsVersion := "" - compressor := "" + fsVersion := "5" + compressor := "lz4_block" chunkDictDigest := "" ctxValue := ctx.Value(fsVersionKey{}) From b2ef881dd4b2201bc5b630a0af91ea33c13d45bc Mon Sep 17 00:00:00 2001 From: Yan Song Date: Thu, 25 May 2023 09:18:46 +0000 Subject: [PATCH 3/8] nydus: use rafs v6 as default fs version - Use `--nydus-fs-version=6` as default; - Use `--nydus-compressor=zstd` as default; - Upgrade suggested nydus version to v2.1.6; Signed-off-by: Yan Song --- Dockerfile | 2 +- docs/nydus.md | 4 ++-- nydus/nydus.go | 4 ++-- nydus/util/util.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8869138b5..9c8a54f19 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ ARG CNI_VERSION=v1.1.1 ARG STARGZ_SNAPSHOTTER_VERSION=v0.13.0 ARG NERDCTL_VERSION=v1.0.0 ARG DNSNAME_VERSION=v1.3.1 -ARG NYDUS_VERSION=v2.1.0 +ARG NYDUS_VERSION=v2.1.6 ARG ALPINE_VERSION=3.17 diff --git a/docs/nydus.md b/docs/nydus.md index 0334c76c5..80c39a8ef 100644 --- a/docs/nydus.md +++ b/docs/nydus.md @@ -16,7 +16,7 @@ go build -tags=nydus -o ./bin/buildkitd ./cmd/buildkitd ### Building Nydus with BuildKit -Download `nydus-image` binary from [nydus release page](https://github.com/dragonflyoss/image-service/releases) (require v2.1.1 or higher), then put the `nydus-image` binary path into $PATH or specifying it on `NYDUS_BUILDER` environment variable for buildkitd: +Download `nydus-image` binary from [nydus release page](https://github.com/dragonflyoss/image-service/releases) (require v2.1.6 or higher), then put the `nydus-image` binary path into $PATH or specifying it on `NYDUS_BUILDER` environment variable for buildkitd: ``` env NYDUS_BUILDER=/path/to/nydus-image buildkitd ... @@ -33,7 +33,7 @@ buildctl build ... \ Available options: -- `nydus-fs-version`: Specify nydus image filesystem version, possible values: `5`, `6`, default `5`; +- `nydus-fs-version`: Specify nydus image filesystem version, possible values: `5`, `6`, default `6`; - `nydus-compressor`: Specify nydus image compressor, possible values: `none`, `lz4_block`, `zstd`, default `lz4_block`; - `nydus-chunk-dict-image`: Specify nydus chunk dict image reference for data de-duplication; diff --git a/nydus/nydus.go b/nydus/nydus.go index 2d808d2f0..5e80b6671 100644 --- a/nydus/nydus.go +++ b/nydus/nydus.go @@ -170,10 +170,10 @@ func loadChunkDict(ctx context.Context, registryHosts docker.RegistryHosts, sm * } if bootstrapDesc.Annotations[nydusify.LayerAnnotationFSVersion] == "" { - bootstrapDesc.Annotations[nydusify.LayerAnnotationFSVersion] = "5" + bootstrapDesc.Annotations[nydusify.LayerAnnotationFSVersion] = "6" } if nydusFSVersion == "" { - nydusFSVersion = "5" + nydusFSVersion = "6" } if bootstrapDesc.Annotations[nydusify.LayerAnnotationFSVersion] != nydusFSVersion { diff --git a/nydus/util/util.go b/nydus/util/util.go index 6524ebf78..22f58f641 100644 --- a/nydus/util/util.go +++ b/nydus/util/util.go @@ -31,8 +31,8 @@ func WithContext(ctx context.Context, fsVersion string, compressor string, chunk } func GetContext(ctx context.Context) (string, string, string) { - fsVersion := "5" - compressor := "lz4_block" + fsVersion := "6" + compressor := "zstd" chunkDictDigest := "" ctxValue := ctx.Value(fsVersionKey{}) From d0807534a9c34f3991b851800d23832e3fbc0f41 Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Fri, 26 May 2023 18:43:12 -0400 Subject: [PATCH 4/8] authprovider: fix a bug where registry-1.docker.io auth was always a cache miss Signed-off-by: Nick Santos --- session/auth/authprovider/authprovider.go | 10 ++++-- .../auth/authprovider/authprovider_test.go | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 session/auth/authprovider/authprovider_test.go diff --git a/session/auth/authprovider/authprovider.go b/session/auth/authprovider/authprovider.go index d77aaa96b..56c4fdced 100644 --- a/session/auth/authprovider/authprovider.go +++ b/session/auth/authprovider/authprovider.go @@ -29,6 +29,8 @@ import ( ) const defaultExpiration = 60 +const dockerIndexConfigfileKey = "https://index.docker.io/v1/" +const dockerRegistryHost = "registry-1.docker.io" func NewDockerAuthProvider(cfg *configfile.ConfigFile) session.Attachable { return &authProvider{ @@ -183,10 +185,12 @@ func (ap *authProvider) VerifyTokenAuthority(ctx context.Context, req *auth.Veri func (ap *authProvider) getAuthConfig(host string) (*types.AuthConfig, error) { ap.mu.Lock() defer ap.mu.Unlock() + + if host == dockerRegistryHost { + host = dockerIndexConfigfileKey + } + if _, exists := ap.authConfigCache[host]; !exists { - if host == "registry-1.docker.io" { - host = "https://index.docker.io/v1/" - } ac, err := ap.config.GetAuthConfig(host) if err != nil { return nil, err diff --git a/session/auth/authprovider/authprovider_test.go b/session/auth/authprovider/authprovider_test.go new file mode 100644 index 000000000..1602cdb80 --- /dev/null +++ b/session/auth/authprovider/authprovider_test.go @@ -0,0 +1,31 @@ +package authprovider + +import ( + "context" + "testing" + + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/types" + "github.com/moby/buildkit/session/auth" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFetchTokenCaching(t *testing.T) { + cfg := &configfile.ConfigFile{ + AuthConfigs: map[string]types.AuthConfig{ + dockerIndexConfigfileKey: {Username: "user", RegistryToken: "hunter2"}, + }, + } + p := NewDockerAuthProvider(cfg).(*authProvider) + res, err := p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerRegistryHost}) + require.NoError(t, err) + assert.Equal(t, "hunter2", res.Token) + + cfg.AuthConfigs[dockerIndexConfigfileKey] = types.AuthConfig{Username: "user", RegistryToken: "hunter3"} + res, err = p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerRegistryHost}) + require.NoError(t, err) + + // Verify that we cached the result instead of returning hunter3. + assert.Equal(t, "hunter2", res.Token) +} From 41843442c8254aa31368bbd85969f199c834e914 Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Tue, 30 May 2023 08:01:56 -0400 Subject: [PATCH 5/8] response to comments Signed-off-by: Nick Santos --- session/auth/authprovider/authprovider.go | 8 ++++---- session/auth/authprovider/authprovider_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/session/auth/authprovider/authprovider.go b/session/auth/authprovider/authprovider.go index 56c4fdced..045185d6b 100644 --- a/session/auth/authprovider/authprovider.go +++ b/session/auth/authprovider/authprovider.go @@ -29,8 +29,8 @@ import ( ) const defaultExpiration = 60 -const dockerIndexConfigfileKey = "https://index.docker.io/v1/" -const dockerRegistryHost = "registry-1.docker.io" +const dockerHubConfigfileKey = "https://index.docker.io/v1/" +const dockerHubRegistryHost = "registry-1.docker.io" func NewDockerAuthProvider(cfg *configfile.ConfigFile) session.Attachable { return &authProvider{ @@ -186,8 +186,8 @@ func (ap *authProvider) getAuthConfig(host string) (*types.AuthConfig, error) { ap.mu.Lock() defer ap.mu.Unlock() - if host == dockerRegistryHost { - host = dockerIndexConfigfileKey + if host == dockerHubRegistryHost { + host = dockerHubConfigfileKey } if _, exists := ap.authConfigCache[host]; !exists { diff --git a/session/auth/authprovider/authprovider_test.go b/session/auth/authprovider/authprovider_test.go index 1602cdb80..28b9bcfd1 100644 --- a/session/auth/authprovider/authprovider_test.go +++ b/session/auth/authprovider/authprovider_test.go @@ -14,16 +14,16 @@ import ( func TestFetchTokenCaching(t *testing.T) { cfg := &configfile.ConfigFile{ AuthConfigs: map[string]types.AuthConfig{ - dockerIndexConfigfileKey: {Username: "user", RegistryToken: "hunter2"}, + dockerHubConfigfileKey: {Username: "user", RegistryToken: "hunter2"}, }, } p := NewDockerAuthProvider(cfg).(*authProvider) - res, err := p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerRegistryHost}) + res, err := p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerHubRegistryHost}) require.NoError(t, err) assert.Equal(t, "hunter2", res.Token) - cfg.AuthConfigs[dockerIndexConfigfileKey] = types.AuthConfig{Username: "user", RegistryToken: "hunter3"} - res, err = p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerRegistryHost}) + cfg.AuthConfigs[dockerHubConfigfileKey] = types.AuthConfig{Username: "user", RegistryToken: "hunter3"} + res, err = p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerHubRegistryHost}) require.NoError(t, err) // Verify that we cached the result instead of returning hunter3. From 24257ef6379a9535045486a189d7a2b175276bb2 Mon Sep 17 00:00:00 2001 From: njucjc Date: Sat, 19 Aug 2023 03:37:53 +0800 Subject: [PATCH 6/8] buildctl: Add configured TLS certificate to trust store when making calls to registry auth Signed-off-by: njucjc --- cmd/buildctl/build.go | 10 +- cmd/buildctl/build/registryauthtlscontext.go | 84 ++++++++++++++ .../build/registryauthtlscontext_test.go | 109 ++++++++++++++++++ session/auth/authprovider/authconfig.go | 11 ++ session/auth/authprovider/authprovider.go | 61 +++++++++- .../auth/authprovider/authprovider_test.go | 2 +- 6 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 cmd/buildctl/build/registryauthtlscontext.go create mode 100644 cmd/buildctl/build/registryauthtlscontext_test.go create mode 100644 session/auth/authprovider/authconfig.go diff --git a/cmd/buildctl/build.go b/cmd/buildctl/build.go index e7cb1f770..241cc1840 100644 --- a/cmd/buildctl/build.go +++ b/cmd/buildctl/build.go @@ -105,6 +105,10 @@ var buildCommand = cli.Command{ Name: "ref-file", Usage: "Write build ref to a file", }, + cli.StringSliceFlag{ + Name: "registry-auth-tlscontext", + Usage: "Overwrite TLS configuration when authenticating with registries, e.g. --registry-auth-tlscontext host=https://myserver:2376,ca=/path/to/my/ca.crt,cert=/path/to/my/cert.crt,key=/path/to/my/key.crt", + }, }, } @@ -158,7 +162,11 @@ func buildAction(clicontext *cli.Context) error { } dockerConfig := config.LoadDefaultConfigFile(os.Stderr) - attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig)} + tlsConfigs, err := build.ParseRegistryAuthTLSContext(clicontext.StringSlice("registry-auth-tlscontext")) + if err != nil { + return err + } + attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig, tlsConfigs)} if ssh := clicontext.StringSlice("ssh"); len(ssh) > 0 { configs, err := build.ParseSSH(ssh) diff --git a/cmd/buildctl/build/registryauthtlscontext.go b/cmd/buildctl/build/registryauthtlscontext.go new file mode 100644 index 000000000..5e958b3b0 --- /dev/null +++ b/cmd/buildctl/build/registryauthtlscontext.go @@ -0,0 +1,84 @@ +package build + +import ( + "encoding/csv" + "strings" + + "github.com/moby/buildkit/session/auth/authprovider" + "github.com/pkg/errors" +) + +type authTLSContextEntry struct { + Host string + CA string + Cert string + Key string +} + +func parseRegistryAuthTLSContextCSV(s string) (authTLSContextEntry, error) { + authTLSContext := authTLSContextEntry{} + csvReader := csv.NewReader(strings.NewReader(s)) + fields, err := csvReader.Read() + if err != nil { + return authTLSContext, err + } + for _, field := range fields { + key, value, ok := strings.Cut(field, "=") + if !ok { + return authTLSContext, errors.Errorf("invalid value %s", field) + } + key = strings.ToLower(key) + switch key { + case "host": + authTLSContext.Host = value + case "ca": + authTLSContext.CA = value + case "cert": + authTLSContext.Cert = value + case "key": + authTLSContext.Key = value + } + } + if authTLSContext.Host == "" { + return authTLSContext, errors.New("--registry-auth-tlscontext requires host=") + } + if authTLSContext.CA == "" { + if authTLSContext.Cert == "" || authTLSContext.Key == "" { + return authTLSContext, errors.New("--registry-auth-tlscontext requires ca= or cert=,key=") + } + } else { + if (authTLSContext.Cert != "" && authTLSContext.Key == "") || (authTLSContext.Cert == "" && authTLSContext.Key != "") { + return authTLSContext, errors.New("--registry-auth-tlscontext requires cert=,key=") + } + } + return authTLSContext, nil +} + +func ParseRegistryAuthTLSContext(registryAuthTLSContext []string) (map[string]*authprovider.AuthTLSConfig, error) { + var tlsContexts []authTLSContextEntry + for _, c := range registryAuthTLSContext { + authTLSContext, err := parseRegistryAuthTLSContextCSV(c) + if err != nil { + return nil, err + } + tlsContexts = append(tlsContexts, authTLSContext) + } + + authConfigs := make(map[string]*authprovider.AuthTLSConfig) + for _, c := range tlsContexts { + _, ok := authConfigs[c.Host] + if !ok { + authConfigs[c.Host] = &authprovider.AuthTLSConfig{} + } + if c.CA != "" { + authConfigs[c.Host].RootCAs = append(authConfigs[c.Host].RootCAs, c.CA) + } + if c.Cert != "" && c.Key != "" { + authConfigs[c.Host].KeyPairs = append(authConfigs[c.Host].KeyPairs, authprovider.TLSKeyPair{ + Key: c.Key, + Certificate: c.Cert, + }) + } + } + return authConfigs, nil +} diff --git a/cmd/buildctl/build/registryauthtlscontext_test.go b/cmd/buildctl/build/registryauthtlscontext_test.go new file mode 100644 index 000000000..06822eea9 --- /dev/null +++ b/cmd/buildctl/build/registryauthtlscontext_test.go @@ -0,0 +1,109 @@ +package build + +import ( + "testing" + + "github.com/moby/buildkit/session/auth/authprovider" + "github.com/stretchr/testify/require" +) + +func TestParseRegistryAuthTLSContext(t *testing.T) { + type testCase struct { + registryAuthTLSContext []string //--registry-auth-tlscontext + expected map[string]*authprovider.AuthTLSConfig + expectedErr string + } + testCases := []testCase{ + { + registryAuthTLSContext: []string{ + "host=tcp://myserver:2376,ca=/home/admin/ca-file,cert=/home/admin/cert-file,key=/home/admin/key-file", + }, + expected: map[string]*authprovider.AuthTLSConfig{ + "tcp://myserver:2376": { + RootCAs: []string{ + "/home/admin/ca-file", + }, + KeyPairs: []authprovider.TLSKeyPair{ + { + Key: "/home/admin/key-file", + Certificate: "/home/admin/cert-file", + }, + }, + }, + }, + }, + { + registryAuthTLSContext: []string{ + "host=tcp://myserver:2376,cert=/home/admin/cert-file,key=/home/admin/key-file", + }, + expected: map[string]*authprovider.AuthTLSConfig{ + "tcp://myserver:2376": { + KeyPairs: []authprovider.TLSKeyPair{ + { + Key: "/home/admin/key-file", + Certificate: "/home/admin/cert-file", + }, + }, + }, + }, + }, + { + registryAuthTLSContext: []string{ + "host=tcp://myserver:2376,ca=/home/admin/ca-file", + }, + expected: map[string]*authprovider.AuthTLSConfig{ + "tcp://myserver:2376": { + RootCAs: []string{ + "/home/admin/ca-file", + }, + }, + }, + }, + { + registryAuthTLSContext: []string{ + "host=tcp://myserver:2376,ca=/home/admin/ca-file,key=/home/admin/key-file", + }, + expectedErr: "--registry-auth-tlscontext requires cert=,key=", + }, + { + registryAuthTLSContext: []string{ + "host=tcp://myserver:2376,ca=/home/admin/ca-file,cert=/home/admin/cert-file,key=/home/admin/key-file", + "host=https://myserver:2376,ca=/path/to/my/ca.crt,cert=/path/to/my/cert.crt,key=/path/to/my/key.crt", + }, + expected: map[string]*authprovider.AuthTLSConfig{ + "tcp://myserver:2376": { + RootCAs: []string{ + "/home/admin/ca-file", + }, + KeyPairs: []authprovider.TLSKeyPair{ + { + Key: "/home/admin/key-file", + Certificate: "/home/admin/cert-file", + }, + }, + }, + "https://myserver:2376": { + RootCAs: []string{ + "/path/to/my/ca.crt", + }, + KeyPairs: []authprovider.TLSKeyPair{ + { + Key: "/path/to/my/key.crt", + Certificate: "/path/to/my/cert.crt", + }, + }, + }, + }, + }, + } + + for _, tc := range testCases { + im, err := ParseRegistryAuthTLSContext(tc.registryAuthTLSContext) + if tc.expectedErr == "" { + require.EqualValues(t, tc.expected, im) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + } + } +} diff --git a/session/auth/authprovider/authconfig.go b/session/auth/authprovider/authconfig.go new file mode 100644 index 000000000..911e13483 --- /dev/null +++ b/session/auth/authprovider/authconfig.go @@ -0,0 +1,11 @@ +package authprovider + +type AuthTLSConfig struct { + RootCAs []string + KeyPairs []TLSKeyPair +} + +type TLSKeyPair struct { + Key string + Certificate string +} diff --git a/session/auth/authprovider/authprovider.go b/session/auth/authprovider/authprovider.go index 045185d6b..f71bac9bb 100644 --- a/session/auth/authprovider/authprovider.go +++ b/session/auth/authprovider/authprovider.go @@ -5,9 +5,11 @@ import ( "crypto/ed25519" "crypto/hmac" "crypto/sha256" + "crypto/tls" + "crypto/x509" "fmt" - "net/http" "os" + "runtime" "strconv" "strings" "sync" @@ -18,6 +20,7 @@ import ( "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/types" + http "github.com/hashicorp/go-cleanhttp" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/auth" "github.com/moby/buildkit/util/progress/progresswriter" @@ -32,12 +35,13 @@ const defaultExpiration = 60 const dockerHubConfigfileKey = "https://index.docker.io/v1/" const dockerHubRegistryHost = "registry-1.docker.io" -func NewDockerAuthProvider(cfg *configfile.ConfigFile) session.Attachable { +func NewDockerAuthProvider(cfg *configfile.ConfigFile, tlsConfigs map[string]*AuthTLSConfig) session.Attachable { return &authProvider{ authConfigCache: map[string]*types.AuthConfig{}, config: cfg, seeds: &tokenSeeds{dir: config.Dir()}, loggerCache: map[string]struct{}{}, + tlsConfigs: tlsConfigs, } } @@ -47,6 +51,7 @@ type authProvider struct { seeds *tokenSeeds logger progresswriter.Logger loggerCache map[string]struct{} + tlsConfigs map[string]*AuthTLSConfig // The need for this mutex is not well understood. // Without it, the docker cli on OS X hangs when @@ -89,6 +94,13 @@ func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequ Secret: creds.Secret, } + var httpClient = http.DefaultClient() + if tc, err := ap.tlsConfig(req.Host); err == nil && tc != nil { + transport := http.DefaultTransport() + transport.TLSClientConfig = tc + httpClient.Transport = transport + } + if creds.Secret != "" { done := func(progresswriter.SubLogger) error { return err @@ -103,7 +115,7 @@ func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequ } ap.mu.Unlock() // credential information is provided, use oauth POST endpoint - resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to) + resp, err := authutil.FetchTokenWithOAuth(ctx, httpClient, nil, "buildkit-client", to) if err != nil { var errStatus remoteserrors.ErrUnexpectedStatus if errors.As(err, &errStatus) { @@ -111,7 +123,7 @@ func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequ // As of September 2017, GCR is known to return 404. // As of February 2018, JFrog Artifactory is known to return 401. if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 { - resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to) + resp, err := authutil.FetchToken(ctx, httpClient, nil, to) if err != nil { return nil, err } @@ -123,13 +135,52 @@ func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequ return toTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil } // do request anonymously - resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to) + resp, err := authutil.FetchToken(ctx, httpClient, nil, to) if err != nil { return nil, errors.Wrap(err, "failed to fetch anonymous token") } return toTokenResponse(resp.Token, resp.IssuedAt, resp.ExpiresIn), nil } +func (ap *authProvider) tlsConfig(host string) (*tls.Config, error) { + if ap.tlsConfigs == nil { + return nil, nil + } + c, ok := ap.tlsConfigs[host] + if !ok { + return nil, nil + } + tc := &tls.Config{} + if len(c.RootCAs) > 0 { + systemPool, err := x509.SystemCertPool() + if err != nil { + if runtime.GOOS == "windows" { + systemPool = x509.NewCertPool() + } else { + return nil, errors.Wrapf(err, "unable to get system cert pool") + } + } + tc.RootCAs = systemPool + } + + for _, p := range c.RootCAs { + dt, err := os.ReadFile(p) + if err != nil { + return nil, errors.Wrapf(err, "failed to read %s", p) + } + tc.RootCAs.AppendCertsFromPEM(dt) + } + + for _, kp := range c.KeyPairs { + cert, err := tls.LoadX509KeyPair(kp.Certificate, kp.Key) + if err != nil { + return nil, errors.Wrapf(err, "failed to load keypair for %s", kp.Certificate) + } + tc.Certificates = append(tc.Certificates, cert) + } + return tc, nil +} + func (ap *authProvider) credentials(host string) (*auth.CredentialsResponse, error) { ac, err := ap.getAuthConfig(host) if err != nil { diff --git a/session/auth/authprovider/authprovider_test.go b/session/auth/authprovider/authprovider_test.go index 28b9bcfd1..69e67f882 100644 --- a/session/auth/authprovider/authprovider_test.go +++ b/session/auth/authprovider/authprovider_test.go @@ -17,7 +17,7 @@ func TestFetchTokenCaching(t *testing.T) { dockerHubConfigfileKey: {Username: "user", RegistryToken: "hunter2"}, }, } - p := NewDockerAuthProvider(cfg).(*authProvider) + p := NewDockerAuthProvider(cfg, nil).(*authProvider) res, err := p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerHubRegistryHost}) require.NoError(t, err) assert.Equal(t, "hunter2", res.Token) From 2f84195ae1dd5ecd3918d6daa5b89e7ee11418f6 Mon Sep 17 00:00:00 2001 From: Qinqi Qu Date: Thu, 9 Nov 2023 11:10:17 +0800 Subject: [PATCH 7/8] action: use nydus insead of image-service to fix broken CI Also update top 50 image list. Signed-off-by: Qinqi Qu --- .github/workflows/nydus.yml | 4 ++-- nydus-test/top_images/image_list.txt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nydus.yml b/.github/workflows/nydus.yml index 81f673eb8..5f91e39f2 100644 --- a/.github/workflows/nydus.yml +++ b/.github/workflows/nydus.yml @@ -20,8 +20,8 @@ jobs: uses: actions/checkout@v2 - name: Install Nydus binaries run: | - NYDUS_VERSION=$(curl --silent "https://api.github.com/repos/dragonflyoss/image-service/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")') - wget https://github.com/dragonflyoss/image-service/releases/download/$NYDUS_VERSION/nydus-static-$NYDUS_VERSION-linux-amd64.tgz + NYDUS_VERSION=$(curl --silent "https://api.github.com/repos/dragonflyoss/nydus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")') + wget https://github.com/dragonflyoss/nydus/releases/download/$NYDUS_VERSION/nydus-static-$NYDUS_VERSION-linux-amd64.tgz tar xzf nydus-static-$NYDUS_VERSION-linux-amd64.tgz sudo cp nydus-static/nydusify nydus-static/nydus-image /usr/local/bin/ sudo cp nydus-static/nydusd /usr/local/bin/nydusd diff --git a/nydus-test/top_images/image_list.txt b/nydus-test/top_images/image_list.txt index f96349211..5560bcac5 100644 --- a/nydus-test/top_images/image_list.txt +++ b/nydus-test/top_images/image_list.txt @@ -31,7 +31,7 @@ php bash caddy telegraf -vault +hashicorp/vault couchdb eclipse-mosquitto cassandra @@ -43,3 +43,4 @@ kong solr sentry zookeeper +ghcr.io/dragonflyoss/image-service/pax-uid-test \ No newline at end of file From c6dc69366adb2a7115bb78f7887f7542b41d000b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:32:52 +0000 Subject: [PATCH 8/8] build(deps): bump actions/github-script from 6 to 7 Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- .github/workflows/dockerd.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8d97ec4e..af6279577 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -284,7 +284,7 @@ jobs: path: ./coverage - name: List coverage files - uses: actions/github-script@v6 + uses: actions/github-script@v7 id: files with: result-encoding: string diff --git a/.github/workflows/dockerd.yml b/.github/workflows/dockerd.yml index f760f55e6..9cff92654 100644 --- a/.github/workflows/dockerd.yml +++ b/.github/workflows/dockerd.yml @@ -30,7 +30,7 @@ jobs: echo "DOCKER_VERSION=$version" >> $GITHUB_ENV - name: Check build - uses: actions/github-script@v6 + uses: actions/github-script@v7 id: build with: result-encoding: string