Headline
CVE-2022-41939: func/builder.go at 5ca77d38744d3481cc0b795f607c5859b19588fc · knative/func
knative.dev/func is is a client library and CLI enabling the development and deployment of Kubernetes functions. Developers using a malicious or compromised third-party buildpack could expose their registry credentials or local docker socket to a malicious lifecycle
container. This issues has been patched in PR #1442, and is part of release 1.8.1. This issue only affects users who are using function buildpacks from third-parties; pinning the builder image to a specific content-hash with a valid lifecycle
image will also mitigate the attack.
package buildpacks import ( “bytes” “context” “fmt” “io” “runtime” “strings” “github.com/Masterminds/semver” pack “github.com/buildpacks/pack/pkg/client” “github.com/buildpacks/pack/pkg/logging” “github.com/docker/docker/client” “github.com/heroku/color” fn “knative.dev/func” “knative.dev/func/builders” “knative.dev/func/docker” ) // DefaultName when no WithName option is provided to NewBuilder const DefaultName = builders.Pack var ( DefaultBuilderImages = map[string]string{ "node": "gcr.io/paketo-buildpacks/builder:base", "nodejs": "gcr.io/paketo-buildpacks/builder:base", "typescript": "gcr.io/paketo-buildpacks/builder:base", "go": "gcr.io/paketo-buildpacks/builder:base", "python": "gcr.io/paketo-buildpacks/builder:base", "quarkus": "gcr.io/paketo-buildpacks/builder:base", "rust": "gcr.io/paketo-buildpacks/builder:base", "springboot": "gcr.io/paketo-buildpacks/builder:base", } trustedBuilderImagePrefixes = []string{ "quay.io/boson", "gcr.io/paketo-buildpacks", "docker.io/paketobuildpacks", "ghcr.io/vmware-tanzu/function-buildpacks-for-knative", } v330 = semver.MustParse(“v3.3.0”) // for checking podman version ) // Builder will build Function using Pack. type Builder struct { name string verbose bool // in non-verbose mode contains std[err,out], so it can be printed on error outBuff bytes.Buffer logger logging.Logger impl Impl } // Impl allows for the underlying implementation to be mocked for tests. type Impl interface { Build(context.Context, pack.BuildOptions) error } // NewBuilder instantiates a Buildpack-based Builder func NewBuilder(options …Option) *Builder { b := &Builder{name: DefaultName} for _, o := range options { o(b) } // Stream logs to stdout or buffer only for display on error. if b.verbose { b.logger = logging.NewLogWithWriters(color.Stdout(), color.Stderr(), logging.WithVerbose()) } else { b.logger = logging.NewSimpleLogger(&b.outBuff) } return b } type Option func(*Builder) func WithName(n string) Option { return func(b *Builder) { b.name = n } } func WithVerbose(v bool) Option { return func(b *Builder) { b.verbose = v } } func WithImpl(i Impl) Option { return func(b *Builder) { b.impl = i } } var DefaultLifecycleImage = “quay.io/boson/lifecycle@sha256:79dac4658ea5e9b42c3aece456f8a9c20f9e1a91d9d4648717967d88eaa7d9ef” // Build the Function at path. func (b *Builder) Build(ctx context.Context, f fn.Function) (err error) { // Builder image from the function if defined, default otherwise. image, err := BuilderImage(f, b.name) if err != nil { return } // Pack build options opts := pack.BuildOptions{ AppPath: f.Root, Image: f.Image, LifecycleImage: DefaultLifecycleImage, Builder: image, Buildpacks: f.Build.Buildpacks, ContainerConfig: struct { Network string Volumes []string }{Network: "", Volumes: nil}, } if opts.Env, err = fn.Interpolate(f.Build.BuildEnvs); err != nil { return err } if runtime.GOOS == “linux” { opts.ContainerConfig.Network = “host” } var impl = b.impl // Instantiate the pack build client implementation // (and update build opts as necessary) if impl == nil { var ( cli client.CommonAPIClient dockerHost string ) cli, dockerHost, err = docker.NewClient(client.DefaultDockerHost) if err != nil { return fmt.Errorf("cannot create docker client: %w", err) } defer cli.Close() if impl, err = newImpl(ctx, cli, dockerHost, &opts, b.logger); err != nil { return fmt.Errorf("cannot create pack client: %w", err) } } // Perform the build if err = impl.Build(ctx, opts); err != nil { if ctx.Err() != nil { return // SIGINT } else if !b.verbose { err = fmt.Errorf("failed to build the function: %w", err) fmt.Fprintln(color.Stderr(), “”) _, _ = io.Copy(color.Stderr(), &b.outBuff) fmt.Fprintln(color.Stderr(), “”) } } return } // newImpl returns an instance of the builder implementation. Note that this // also mutates the provided options’ DockerHost and TrustBuilder. func newImpl(ctx context.Context, cli client.CommonAPIClient, dockerHost string, opts *pack.BuildOptions, logger logging.Logger) (impl Impl, err error) { opts.DockerHost = dockerHost daemonIsPodmanPreV330, err := podmanPreV330(ctx, cli) if err != nil { return } opts.TrustBuilder = func(_ string) bool { if daemonIsPodmanPreV330 { return false } for _, v := range trustedBuilderImagePrefixes { if strings.HasPrefix(opts.Builder, v) { return true } } return false } // Client with a logger which is enabled if in Verbose mode and a dockerClient that supports SSH docker daemon connection. return pack.NewClient(pack.WithLogger(logger), pack.WithDockerClient(cli)) } // Builder Image chooses the correct builder image or defaults. func BuilderImage(f fn.Function, builderName string) (string, error) { return builders.Image(f, builderName, DefaultBuilderImages) } // podmanPreV330 returns if the daemon is podman pre v330 or errors trying. func podmanPreV330(ctx context.Context, cli client.CommonAPIClient) (b bool, err error) { version, err := cli.ServerVersion(ctx) if err != nil { return } for _, component := range version.Components { if component.Name == “Podman Engine” { v := semver.MustParse(version.Version) if v.Compare(v330) < 0 { return true, nil } break } } return } // Errors type ErrRuntimeRequired struct{} func (e ErrRuntimeRequired) Error() string { return “Pack requires the Function define a language runtime” } type ErrRuntimeNotSupported struct { Runtime string } func (e ErrRuntimeNotSupported) Error() string { return fmt.Sprintf("Pack builder has no default builder image for the ‘%v’ language runtime. Please provide one.", e.Runtime) }