mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 08:03:43 +08:00 
			
		
		
		
	Merge pull request #965 from tonistiigi/bake-context-validation
bake: additional support for named context on remote inputs
This commit is contained in:
		
							
								
								
									
										88
									
								
								bake/bake.go
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								bake/bake.go
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ import ( | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| @@ -715,6 +716,21 @@ func updateContext(t *build.Inputs, inp *Input) { | ||||
| 	if inp == nil || inp.State == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	for k, v := range t.NamedContexts { | ||||
| 		if v.Path == "." { | ||||
| 			t.NamedContexts[k] = build.NamedContext{Path: inp.URL} | ||||
| 		} | ||||
| 		if strings.HasPrefix(v.Path, "cwd://") || strings.HasPrefix(v.Path, "target:") || strings.HasPrefix(v.Path, "docker-image:") { | ||||
| 			continue | ||||
| 		} | ||||
| 		if IsRemoteURL(v.Path) { | ||||
| 			continue | ||||
| 		} | ||||
| 		st := llb.Scratch().File(llb.Copy(*inp.State, v.Path, "/"), llb.WithCustomNamef("set context %s to %s", k, v.Path)) | ||||
| 		t.NamedContexts[k] = build.NamedContext{State: &st} | ||||
| 	} | ||||
|  | ||||
| 	if t.ContextPath == "." { | ||||
| 		t.ContextPath = inp.URL | ||||
| 		return | ||||
| @@ -729,6 +745,59 @@ func updateContext(t *build.Inputs, inp *Input) { | ||||
| 	t.ContextState = &st | ||||
| } | ||||
|  | ||||
| // validateContextsEntitlements is a basic check to ensure contexts do not | ||||
| // escape local directories when loaded from remote sources. This is to be | ||||
| // replaced with proper entitlements support in the future. | ||||
| func validateContextsEntitlements(t build.Inputs, inp *Input) error { | ||||
| 	if inp == nil || inp.State == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if v, ok := os.LookupEnv("BAKE_ALLOW_REMOTE_FS_ACCESS"); ok { | ||||
| 		if vv, _ := strconv.ParseBool(v); vv { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	if t.ContextState == nil { | ||||
| 		if err := checkPath(t.ContextPath); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range t.NamedContexts { | ||||
| 		if v.State != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if err := checkPath(v.Path); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func checkPath(p string) error { | ||||
| 	if IsRemoteURL(p) || strings.HasPrefix(p, "target:") || strings.HasPrefix(p, "docker-image:") { | ||||
| 		return nil | ||||
| 	} | ||||
| 	p, err := filepath.EvalSymlinks(p) | ||||
| 	if err != nil { | ||||
| 		if os.IsNotExist(err) { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| 	wd, err := os.Getwd() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	rel, err := filepath.Rel(wd, p) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { | ||||
| 		return errors.Errorf("path %s is outside of the working directory, please set BAKE_ALLOW_REMOTE_FS_ACCESS=1", p) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { | ||||
| 	if v := t.Context; v != nil && *v == "-" { | ||||
| 		return nil, errors.Errorf("context from stdin not allowed in bake") | ||||
| @@ -769,7 +838,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { | ||||
| 	bi := build.Inputs{ | ||||
| 		ContextPath:    contextPath, | ||||
| 		DockerfilePath: dockerfilePath, | ||||
| 		NamedContexts:  t.Contexts, | ||||
| 		NamedContexts:  toNamedContexts(t.Contexts), | ||||
| 	} | ||||
| 	if t.DockerfileInline != nil { | ||||
| 		bi.DockerfileInline = *t.DockerfileInline | ||||
| @@ -778,6 +847,15 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { | ||||
| 	if strings.HasPrefix(bi.ContextPath, "cwd://") { | ||||
| 		bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://")) | ||||
| 	} | ||||
| 	for k, v := range bi.NamedContexts { | ||||
| 		if strings.HasPrefix(v.Path, "cwd://") { | ||||
| 			bi.NamedContexts[k] = build.NamedContext{Path: path.Clean(strings.TrimPrefix(v.Path, "cwd://"))} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := validateContextsEntitlements(bi, inp); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	t.Context = &bi.ContextPath | ||||
|  | ||||
| @@ -903,3 +981,11 @@ func sliceEqual(s1, s2 []string) bool { | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func toNamedContexts(m map[string]string) map[string]build.NamedContext { | ||||
| 	m2 := make(map[string]build.NamedContext, len(m)) | ||||
| 	for k, v := range m { | ||||
| 		m2[k] = build.NamedContext{Path: v} | ||||
| 	} | ||||
| 	return m2 | ||||
| } | ||||
|   | ||||
| @@ -386,8 +386,8 @@ func TestReadContexts(t *testing.T) { | ||||
| 	ctxs := bo["app"].Inputs.NamedContexts | ||||
| 	require.Equal(t, 2, len(ctxs)) | ||||
|  | ||||
| 	require.Equal(t, "baz", ctxs["foo"]) | ||||
| 	require.Equal(t, "def", ctxs["abc"]) | ||||
| 	require.Equal(t, "baz", ctxs["foo"].Path) | ||||
| 	require.Equal(t, "def", ctxs["abc"].Path) | ||||
|  | ||||
| 	m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo=bay", "base.contexts.ghi=jkl"}, nil) | ||||
| 	require.NoError(t, err) | ||||
| @@ -402,9 +402,9 @@ func TestReadContexts(t *testing.T) { | ||||
| 	ctxs = bo["app"].Inputs.NamedContexts | ||||
| 	require.Equal(t, 3, len(ctxs)) | ||||
|  | ||||
| 	require.Equal(t, "bay", ctxs["foo"]) | ||||
| 	require.Equal(t, "def", ctxs["abc"]) | ||||
| 	require.Equal(t, "jkl", ctxs["ghi"]) | ||||
| 	require.Equal(t, "bay", ctxs["foo"].Path) | ||||
| 	require.Equal(t, "def", ctxs["abc"].Path) | ||||
| 	require.Equal(t, "jkl", ctxs["ghi"].Path) | ||||
|  | ||||
| 	// test resetting base values | ||||
| 	m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo="}, nil) | ||||
| @@ -419,7 +419,7 @@ func TestReadContexts(t *testing.T) { | ||||
|  | ||||
| 	ctxs = bo["app"].Inputs.NamedContexts | ||||
| 	require.Equal(t, 1, len(ctxs)) | ||||
| 	require.Equal(t, "def", ctxs["abc"]) | ||||
| 	require.Equal(t, "def", ctxs["abc"].Path) | ||||
| } | ||||
|  | ||||
| func TestReadContextFromTargetUnknown(t *testing.T) { | ||||
|   | ||||
| @@ -84,7 +84,12 @@ type Inputs struct { | ||||
| 	InStream         io.Reader | ||||
| 	ContextState     *llb.State | ||||
| 	DockerfileInline string | ||||
| 	NamedContexts    map[string]string | ||||
| 	NamedContexts    map[string]NamedContext | ||||
| } | ||||
|  | ||||
| type NamedContext struct { | ||||
| 	Path  string | ||||
| 	State *llb.State | ||||
| } | ||||
|  | ||||
| type DriverInfo struct { | ||||
| @@ -1160,11 +1165,20 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr | ||||
|  | ||||
| 	for k, v := range inp.NamedContexts { | ||||
| 		target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward" | ||||
| 		if urlutil.IsGitURL(v) || urlutil.IsURL(v) || strings.HasPrefix(v, "docker-image://") || strings.HasPrefix(v, "target:") { | ||||
| 			target.FrontendAttrs["context:"+k] = v | ||||
| 		if v.State != nil { | ||||
| 			target.FrontendAttrs["context:"+k] = "input:" + k | ||||
| 			if target.FrontendInputs == nil { | ||||
| 				target.FrontendInputs = make(map[string]llb.State) | ||||
| 			} | ||||
| 			target.FrontendInputs[k] = *v.State | ||||
| 			continue | ||||
| 		} | ||||
| 		st, err := os.Stat(v) | ||||
|  | ||||
| 		if urlutil.IsGitURL(v.Path) || urlutil.IsURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") { | ||||
| 			target.FrontendAttrs["context:"+k] = v.Path | ||||
| 			continue | ||||
| 		} | ||||
| 		st, err := os.Stat(v.Path) | ||||
| 		if err != nil { | ||||
| 			return nil, errors.Wrapf(err, "failed to get build context %v", k) | ||||
| 		} | ||||
| @@ -1175,7 +1189,7 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr | ||||
| 		if k == "context" || k == "dockerfile" { | ||||
| 			localName = "_" + k // underscore to avoid collisions | ||||
| 		} | ||||
| 		target.LocalDirs[localName] = v | ||||
| 		target.LocalDirs[localName] = v.Path | ||||
| 		target.FrontendAttrs["context:"+k] = "local:" + localName | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -480,11 +480,11 @@ func listToMap(values []string, defaultEnv bool) map[string]string { | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func parseContextNames(values []string) (map[string]string, error) { | ||||
| func parseContextNames(values []string) (map[string]build.NamedContext, error) { | ||||
| 	if len(values) == 0 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	result := make(map[string]string, len(values)) | ||||
| 	result := make(map[string]build.NamedContext, len(values)) | ||||
| 	for _, value := range values { | ||||
| 		kv := strings.SplitN(value, "=", 2) | ||||
| 		if len(kv) != 2 { | ||||
| @@ -495,7 +495,7 @@ func parseContextNames(values []string) (map[string]string, error) { | ||||
| 			return nil, errors.Wrapf(err, "invalid context name %s", kv[0]) | ||||
| 		} | ||||
| 		name := strings.TrimSuffix(reference.FamiliarString(named), ":latest") | ||||
| 		result[name] = kv[1] | ||||
| 		result[name] = build.NamedContext{Path: kv[1]} | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Tõnis Tiigi
					Tõnis Tiigi