add support for pkg build authentication (#4137)

Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
Avi Deitcher 2025-07-02 18:52:05 +03:00 committed by GitHub
parent 940c1b7b3b
commit 2b4687338b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 341 additions and 56 deletions

View File

@ -7,7 +7,10 @@ import (
"github.com/spf13/cobra"
)
var pkglibConfig pkglib.PkglibConfig
var (
pkglibConfig pkglib.PkglibConfig
registryCreds []string
)
func pkgCmd() *cobra.Command {
var (
@ -96,5 +99,6 @@ func pkgCmd() *cobra.Command {
cmd.PersistentFlags().BoolVar(&dirty, "force-dirty", false, "Force the pkg(s) to be considered dirty")
cmd.PersistentFlags().BoolVar(&devMode, "dev", false, "Force org and hash to $USER and \"dev\" respectively")
cmd.PersistentFlags().StringSliceVar(&registryCreds, "registry-creds", nil, "Registry auths to use for building images, format is <registry>=<username>:<password> OR <registry>=<registry-token>. If no username is provided, it is treated as a registry token. <registry> must be a URL, e.g. 'https://index.docker.io/'. May be provided as many times as desired. Will override anything in your default.")
return cmd
}

View File

@ -8,6 +8,7 @@ import (
"strings"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/pkglib"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
)
@ -40,6 +41,7 @@ func addCmdRunPkgBuildPush(cmd *cobra.Command, withPush bool) *cobra.Command {
skipPlatforms string
builders string
builderImage string
builderConfig string
builderRestart bool
release string
nobuild bool
@ -159,6 +161,13 @@ func addCmdRunPkgBuildPush(cmd *cobra.Command, withPush bool) *cobra.Command {
if err != nil {
return fmt.Errorf("error in --builders flag: %w", err)
}
if builderConfig != "" {
if _, err := os.Stat(builderConfig); err != nil {
return fmt.Errorf("error reading builder config file %s: %w", builderConfig, err)
}
opts = append(opts, pkglib.WithBuildBuilderConfig(builderConfig))
}
opts = append(opts, pkglib.WithBuildBuilders(buildersMap))
opts = append(opts, pkglib.WithBuildBuilderImage(builderImage))
opts = append(opts, pkglib.WithBuildBuilderRestart(builderRestart))
@ -166,6 +175,46 @@ func addCmdRunPkgBuildPush(cmd *cobra.Command, withPush bool) *cobra.Command {
if len(ssh) > 0 {
opts = append(opts, pkglib.WithSSH(ssh))
}
if len(registryCreds) > 0 {
registryCredMap := make(map[string]spec.RegistryAuth)
for _, cred := range registryCreds {
parts := strings.SplitN(cred, "=", 2)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return fmt.Errorf("invalid registry auth specification '%s'", cred)
}
registryPart := strings.TrimSpace(parts[0])
authPart := strings.TrimSpace(parts[1])
var auth spec.RegistryAuth
// if the auth is a token, we don't need a username
credParts := strings.SplitN(authPart, ":", 2)
var userPart, credPart string
userPart = strings.TrimSpace(credParts[0])
if len(credParts) == 2 {
credPart = strings.TrimSpace(credParts[1])
}
switch {
case len(registryPart) == 0:
return fmt.Errorf("invalid registry auth specification '%s', registry must not be blank", cred)
case len(credParts) == 2 && (len(userPart) == 0 || len(credPart) == 0):
return fmt.Errorf("invalid registry auth specification '%s', username and password must not be blank", cred)
case len(credParts) == 1 && len(userPart) == 0:
return fmt.Errorf("invalid registry auth specification '%s', token must not be blank", cred)
case len(credParts) == 2:
auth = spec.RegistryAuth{
Username: userPart,
Password: credPart,
}
case len(credParts) == 1:
auth = spec.RegistryAuth{
RegistryToken: authPart,
}
default:
return fmt.Errorf("invalid registry auth specification '%s'", cred)
}
registryCredMap[registryPart] = auth
}
opts = append(opts, pkglib.WithRegistryAuth(registryCredMap))
}
for _, p := range pkgs {
// things we need our own copies of
@ -224,6 +273,7 @@ func addCmdRunPkgBuildPush(cmd *cobra.Command, withPush bool) *cobra.Command {
cmd.Flags().StringVar(&skipPlatforms, "skip-platforms", "", "Platforms that should be skipped, even if present in build.yml")
cmd.Flags().StringVar(&builders, "builders", "", "Which builders to use for which platforms, e.g. linux/arm64=docker-context-arm64, overrides defaults and environment variables, see https://github.com/linuxkit/linuxkit/blob/master/docs/packages.md#Providing-native-builder-nodes")
cmd.Flags().StringVar(&builderImage, "builder-image", defaultBuilderImage, "buildkit builder container image to use")
cmd.Flags().StringVar(&builderConfig, "builder-config", "", "path to buildkit builder config.toml file to use, overrides the default config.toml in the builder image; USE WITH CAUTION")
cmd.Flags().BoolVar(&builderRestart, "builder-restart", false, "force restarting builder, even if container with correct name and image exists")
cmd.Flags().Var(&cacheDir, "cache", fmt.Sprintf("Directory for caching and finding cached image, overrides env var %s", envVarCacheDir))
cmd.Flags().StringVar(&release, "release", "", "Release the given version")

View File

@ -12,9 +12,10 @@ import (
func pkgBuilderCmd() *cobra.Command {
var (
builders string
platforms string
builderImage string
builders string
platforms string
builderImage string
builderConfigPath string
)
cmd := &cobra.Command{
Use: "builder",
@ -40,11 +41,11 @@ func pkgBuilderCmd() *cobra.Command {
platformsToClean := strings.Split(platforms, ",")
switch command {
case "du":
if err := pkglib.DiskUsage(buildersMap, builderImage, platformsToClean, verbose); err != nil {
if err := pkglib.DiskUsage(buildersMap, builderImage, builderConfigPath, platformsToClean, verbose); err != nil {
return fmt.Errorf("unable to print disk usage of builder: %w", err)
}
case "prune":
if err := pkglib.PruneBuilder(buildersMap, builderImage, platformsToClean, verbose); err != nil {
if err := pkglib.PruneBuilder(buildersMap, builderImage, builderConfigPath, platformsToClean, verbose); err != nil {
return fmt.Errorf("unable to prune builder: %w", err)
}
default:
@ -57,6 +58,7 @@ func pkgBuilderCmd() *cobra.Command {
cmd.PersistentFlags().StringVar(&builders, "builders", "", "Which builders to use for which platforms, e.g. linux/arm64=docker-context-arm64, overrides defaults and environment variables, see https://github.com/linuxkit/linuxkit/blob/master/docs/packages.md#Providing-native-builder-nodes")
cmd.PersistentFlags().StringVar(&platforms, "platforms", fmt.Sprintf("linux/%s", runtime.GOARCH), "Which platforms we built images for")
cmd.PersistentFlags().StringVar(&builderImage, "builder-image", defaultBuilderImage, "buildkit builder container image to use")
cmd.Flags().StringVar(&builderConfigPath, "builder-config", "", "path to buildkit builder config.toml file to use, overrides the default config.toml in the builder image; USE WITH CAUTION")
return cmd
}

View File

@ -24,28 +24,30 @@ import (
)
type buildOpts struct {
skipBuild bool
force bool
pull bool
ignoreCache bool
push bool
release string
manifest bool
targetDocker bool
cacheDir string
cacheProvider spec.CacheProvider
platforms []imagespec.Platform
builders map[string]string
runner dockerRunner
writer io.Writer
builderImage string
builderRestart bool
sbomScan bool
sbomScannerImage string
dockerfile string
buildArgs []string
progress string
ssh []string
skipBuild bool
force bool
pull bool
ignoreCache bool
push bool
release string
manifest bool
targetDocker bool
cacheDir string
cacheProvider spec.CacheProvider
platforms []imagespec.Platform
builders map[string]string
runner dockerRunner
writer io.Writer
builderImage string
builderConfigPath string
builderRestart bool
sbomScan bool
sbomScannerImage string
dockerfile string
buildArgs []string
progress string
ssh []string
registryAuth map[string]spec.RegistryAuth
}
// BuildOpt allows callers to specify options to Build
@ -164,6 +166,14 @@ func WithBuildBuilderImage(image string) BuildOpt {
}
}
// WithBuildBuilderConfig set the contents of the
func WithBuildBuilderConfig(builderConfigPath string) BuildOpt {
return func(bo *buildOpts) error {
bo.builderConfigPath = builderConfigPath
return nil
}
}
// WithBuildBuilderRestart restart the builder container even if it already is running with the correct image version
func WithBuildBuilderRestart(restart bool) BuildOpt {
return func(bo *buildOpts) error {
@ -223,6 +233,14 @@ func WithSSH(ssh []string) BuildOpt {
}
}
// WithRegistryAuth stores registry credentials
func WithRegistryAuth(creds map[string]spec.RegistryAuth) BuildOpt {
return func(bo *buildOpts) error {
bo.registryAuth = creds
return nil
}
}
// Build builds the package
func (p Pkg) Build(bos ...BuildOpt) error {
var bo buildOpts
@ -449,10 +467,11 @@ func (p Pkg) Build(bos ...BuildOpt) error {
}
imageBuildOpts.SSH = bo.ssh
imageBuildOpts.RegistryAuths = bo.registryAuth
// build for each arch and save in the linuxkit cache
for _, platform := range platformsToBuild {
builtDescs, err := p.buildArch(ctx, d, c, bo.builderImage, platform.Architecture, bo.builderRestart, writer, bo, imageBuildOpts)
builtDescs, err := p.buildArch(ctx, d, c, bo.builderImage, bo.builderConfigPath, platform.Architecture, bo.builderRestart, writer, bo, imageBuildOpts)
if err != nil {
return fmt.Errorf("error building for arch %s: %v", platform.Architecture, err)
}
@ -602,7 +621,7 @@ func (p Pkg) Build(bos ...BuildOpt) error {
// C - manifest, saved in cache as is, referenced by the index (E), and returned as a descriptor
// D - attestations (if any), saved in cache as is, referenced by the index (E), and returned as a descriptor
// E - index, saved in cache as is, stored in cache as tag "image:tag-arch", *not* returned as a descriptor
func (p Pkg) buildArch(ctx context.Context, d dockerRunner, c spec.CacheProvider, builderImage, arch string, restart bool, writer io.Writer, bo buildOpts, imageBuildOpts spec.ImageBuildOptions) ([]registry.Descriptor, error) {
func (p Pkg) buildArch(ctx context.Context, d dockerRunner, c spec.CacheProvider, builderImage, builderConfigPath, arch string, restart bool, writer io.Writer, bo buildOpts, imageBuildOpts spec.ImageBuildOptions) ([]registry.Descriptor, error) {
var (
tagArch string
tag = p.FullTag()
@ -671,7 +690,7 @@ func (p Pkg) buildArch(ctx context.Context, d dockerRunner, c spec.CacheProvider
imageBuildOpts.Dockerfile = bo.dockerfile
if err := d.build(ctx, tagArch, p.path, builderName, builderImage, platform, restart, passCache, buildCtx.Reader(), stdout, bo.sbomScan, bo.sbomScannerImage, bo.progress, imageBuildOpts); err != nil {
if err := d.build(ctx, tagArch, p.path, builderName, builderImage, builderConfigPath, platform, restart, passCache, buildCtx.Reader(), stdout, bo.sbomScan, bo.sbomScannerImage, bo.progress, imageBuildOpts); err != nil {
stdoutCloser()
if strings.Contains(err.Error(), "executor failed running [/dev/.buildkit_qemu_emulator") {
return nil, fmt.Errorf("buildkit was unable to emulate %s. check binfmt has been set up and works for this platform: %v", platform, err)

View File

@ -53,10 +53,10 @@ func (d *dockerMocker) contextSupportCheck() error {
}
return errors.New("contexts not supported")
}
func (d *dockerMocker) builder(_ context.Context, _, _, _ string, _ bool) (*buildkitClient.Client, error) {
func (d *dockerMocker) builder(_ context.Context, _, _, _, _ string, _ bool) (*buildkitClient.Client, error) {
return nil, fmt.Errorf("not implemented")
}
func (d *dockerMocker) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, builderRestart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, sbomScan bool, sbomScannerImage, progress string, imageBuildOpts spec.ImageBuildOptions) error {
func (d *dockerMocker) build(ctx context.Context, tag, pkg, dockerContext, builderImage, builderConfigPath, platform string, builderRestart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, sbomScan bool, sbomScannerImage, progress string, imageBuildOpts spec.ImageBuildOptions) error {
if !d.enableBuild {
return errors.New("build disabled")
}

View File

@ -37,12 +37,16 @@ import (
// golint requires comments on non-main(test)
// package for blank import
dockerconfig "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
dockerconfigtypes "github.com/docker/cli/cli/config/types"
_ "github.com/moby/buildkit/client/connhelper/dockercontainer"
_ "github.com/moby/buildkit/client/connhelper/ssh"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/buildkit/frontend/dockerfile/linter"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/moby/buildkit/frontend/dockerfile/shell"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/session/sshforward/sshprovider"
"github.com/moby/buildkit/session/upload/uploadprovider"
log "github.com/sirupsen/logrus"
@ -54,16 +58,17 @@ const (
buildkitWaitServer = 30 // seconds
buildkitCheckInterval = 1 // seconds
sbomFrontEndKey = "attest:sbom"
buildkitConfigPath = "/etc/buildkit/buildkitd.toml"
)
type dockerRunner interface {
tag(ref, tag string) error
build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, restart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, sbomScan bool, sbomScannerImage, platformType string, imageBuildOpts spec.ImageBuildOptions) error
build(ctx context.Context, tag, pkg, dockerContext, builderImage, builderConfigPath, platform string, restart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, sbomScan bool, sbomScannerImage, platformType string, imageBuildOpts spec.ImageBuildOptions) error
save(tgt string, refs ...string) error
load(src io.Reader) error
pull(img string) (bool, error)
contextSupportCheck() error
builder(ctx context.Context, dockerContext, builderImage, platform string, restart bool) (*buildkitClient.Client, error)
builder(ctx context.Context, dockerContext, builderImage, builderConfigPath, platform string, restart bool) (*buildkitClient.Client, error)
}
type dockerRunnerImpl struct {
@ -218,14 +223,14 @@ func (dr *dockerRunnerImpl) contextSupportCheck() error {
// 1. if dockerContext is provided, try to create a builder with that context; if it succeeds, we are done; if not, return an error.
// 2. try to find an existing named runner with the pattern; if it succeeds, we are done; if not, try next.
// 3. try to create a generic builder using the default context named "linuxkit".
func (dr *dockerRunnerImpl) builder(ctx context.Context, dockerContext, builderImage, platform string, restart bool) (*buildkitClient.Client, error) {
func (dr *dockerRunnerImpl) builder(ctx context.Context, dockerContext, builderImage, builderConfigPath, platform string, restart bool) (*buildkitClient.Client, error) {
// if we were given a context, we must find a builder and use it, or create one and use it
if dockerContext != "" {
// does the context exist?
if err := dr.command(nil, io.Discard, io.Discard, "context", "inspect", dockerContext); err != nil {
return nil, fmt.Errorf("provided docker context '%s' not found", dockerContext)
}
client, err := dr.builderEnsureContainer(ctx, buildkitBuilderName, builderImage, platform, dockerContext, restart)
client, err := dr.builderEnsureContainer(ctx, buildkitBuilderName, builderImage, builderConfigPath, platform, dockerContext, restart)
if err != nil {
return nil, fmt.Errorf("error preparing builder based on context '%s': %v", dockerContext, err)
}
@ -236,13 +241,13 @@ func (dr *dockerRunnerImpl) builder(ctx context.Context, dockerContext, builderI
dockerContext = fmt.Sprintf("%s-%s", "linuxkit", strings.ReplaceAll(platform, "/", "-"))
if err := dr.command(nil, io.Discard, io.Discard, "context", "inspect", dockerContext); err == nil {
// we found an appropriately named context, so let us try to use it or error out
if client, err := dr.builderEnsureContainer(ctx, buildkitBuilderName, builderImage, platform, dockerContext, restart); err == nil {
if client, err := dr.builderEnsureContainer(ctx, buildkitBuilderName, builderImage, builderConfigPath, platform, dockerContext, restart); err == nil {
return client, nil
}
}
// create a generic builder
client, err := dr.builderEnsureContainer(ctx, buildkitBuilderName, builderImage, "", "default", restart)
client, err := dr.builderEnsureContainer(ctx, buildkitBuilderName, builderImage, builderConfigPath, "", "default", restart)
if err != nil {
return nil, fmt.Errorf("error ensuring builder container in default context: %v", err)
}
@ -254,7 +259,7 @@ func (dr *dockerRunnerImpl) builder(ctx context.Context, dockerContext, builderI
// but has the wrong version of buildkit, or not running buildkit at all, remove it and create an appropriate
// one.
// Returns a network connection to the buildkit builder in the container.
func (dr *dockerRunnerImpl) builderEnsureContainer(ctx context.Context, name, image, platform, dockerContext string, forceRestart bool) (*buildkitClient.Client, error) {
func (dr *dockerRunnerImpl) builderEnsureContainer(ctx context.Context, name, image, configPath, platform, dockerContext string, forceRestart bool) (*buildkitClient.Client, error) {
// if no error, then we have a builder already
// inspect it to make sure it is of the right type
var (
@ -288,6 +293,30 @@ func (dr *dockerRunnerImpl) builderEnsureContainer(ctx context.Context, name, im
cid = containerJSON[0].ID
existingImage := containerJSON[0].Config.Image
isRunning := containerJSON[0].State.Status == "running"
// need to check for mounts, in case the builder-config is provided
// by default, we assume the configPath is correct
var configPathCorrect = true
if configPath != "" {
// if it is provided, we assume it is false until proven true
configPathCorrect = false
for _, mount := range containerJSON[0].Mounts {
// if this mount is not the buildkit config path, we can ignore it
if mount.Destination != buildkitConfigPath {
continue
}
// if the mount source does not match the provided configPath,
// we should restart it
// Just break. Since configPathCorrect is set to false, the switch statement below
// will catch it
if mount.Source != configPath {
fmt.Printf("existing container %s has config mounted from %s instead of expected %s, replacing\n", name, mount.Source, configPath)
} else {
configPathCorrect = true
}
// no need to cheak any more, we found the specific mount
break
}
}
switch {
case forceRestart:
@ -308,6 +337,11 @@ func (dr *dockerRunnerImpl) builderEnsureContainer(ctx context.Context, name, im
recreate = true
stop = isRunning
remove = true
case !configPathCorrect:
fmt.Printf("existing container has wrong configPath mount, restarting")
recreate = true
stop = isRunning
remove = true
case isRunning:
// if already running with the right image and permissions, just use it
fmt.Printf("using existing container %s\n", name)
@ -351,7 +385,17 @@ func (dr *dockerRunnerImpl) builderEnsureContainer(ctx context.Context, name, im
}
if recreate {
// create the builder
args := []string{"--context", dockerContext, "container", "run", "-d", "--name", name, "--privileged", image, "--allow-insecure-entitlement", "network.host", "--addr", fmt.Sprintf("unix://%s", buildkitSocketPath), "--debug"}
args := []string{"--context", dockerContext, "container", "run", "-d", "--name", name, "--privileged"}
// was a config file provided?
if configPath != "" {
// if so, we need to pass it as a buildkitd config file
args = append(args, "-v", fmt.Sprintf("%s:%s:ro", configPath, buildkitConfigPath))
}
args = append(args, image, "--allow-insecure-entitlement", "network.host", "--addr", fmt.Sprintf("unix://%s", buildkitSocketPath), "--debug")
if configPath != "" {
// set the config path explicitly
args = append(args, "--config", buildkitConfigPath)
}
msg := fmt.Sprintf("creating builder container '%s' in context '%s'", name, dockerContext)
fmt.Println(msg)
if err := dr.command(nil, nil, io.Discard, args...); err != nil {
@ -442,9 +486,9 @@ func (dr *dockerRunnerImpl) tag(ref, tag string) error {
return dr.command(nil, nil, nil, "image", "tag", ref, tag)
}
func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, restart bool, c spec.CacheProvider, stdin io.Reader, stdout io.Writer, sbomScan bool, sbomScannerImage, progressType string, imageBuildOpts spec.ImageBuildOptions) error {
func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext, builderImage, builderConfigPath, platform string, restart bool, c spec.CacheProvider, stdin io.Reader, stdout io.Writer, sbomScan bool, sbomScannerImage, progressType string, imageBuildOpts spec.ImageBuildOptions) error {
// ensure we have a builder
client, err := dr.builder(ctx, dockerContext, builderImage, platform, restart)
client, err := dr.builder(ctx, dockerContext, builderImage, builderConfigPath, platform, restart)
if err != nil {
return fmt.Errorf("unable to ensure builder container: %v", err)
}
@ -495,6 +539,7 @@ func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext,
attachable := []session.Attachable{}
localDirs := map[string]string{}
// Add SSH agent provider if needed
if len(imageBuildOpts.SSH) > 0 {
configs, err := build.ParseSSH(imageBuildOpts.SSH)
if err != nil {
@ -515,8 +560,30 @@ func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext,
} else {
localDirs[dockerui.DefaultLocalNameDockerfile] = pkg
localDirs[dockerui.DefaultLocalNameContext] = pkg
}
// add credentials
var cf *configfile.ConfigFile
if len(imageBuildOpts.RegistryAuths) > 0 {
// if static ones were provided, use those
cf = configfile.New("custom")
// merge imageBuildOpts.RegistryAuths into dockercfg
for registry, auth := range imageBuildOpts.RegistryAuths {
bareRegistry := strings.TrimPrefix(registry, "https://")
bareRegistry = strings.TrimPrefix(bareRegistry, "http://")
cf.AuthConfigs[bareRegistry] = dockerconfigtypes.AuthConfig{
ServerAddress: bareRegistry,
Username: auth.Username,
Password: auth.Password,
RegistryToken: auth.RegistryToken,
}
}
} else {
// Else use Docker authentication provider so BuildKit can use ~/.docker/config.json or OS-specific credential helpers.
cf = dockerconfig.LoadDefaultConfigFile(io.Discard)
}
attachable = append(attachable,
authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{ConfigFile: cf}),
)
solveOpts := buildkitClient.SolveOpt{
Frontend: "dockerfile.v0",

View File

@ -93,14 +93,14 @@ func printVerbose(tw *tabwriter.Writer, du []*buildkitClient.UsageInfo) {
_ = tw.Flush()
}
func getClientForPlatform(ctx context.Context, buildersMap map[string]string, builderImage, platform string) (*buildkitClient.Client, error) {
func getClientForPlatform(ctx context.Context, buildersMap map[string]string, builderImage, builderConfigPath, platform string) (*buildkitClient.Client, error) {
p, err := platforms.Parse(platform)
if err != nil {
return nil, fmt.Errorf("failed to parse platform: %s", err)
}
dr := newDockerRunner(false)
builderName := getBuilderForPlatform(p.Architecture, buildersMap)
client, err := dr.builder(ctx, builderName, builderImage, platform, false)
client, err := dr.builder(ctx, builderName, builderImage, builderConfigPath, platform, false)
if err != nil {
return nil, fmt.Errorf("unable to ensure builder container: %v", err)
}
@ -108,11 +108,11 @@ func getClientForPlatform(ctx context.Context, buildersMap map[string]string, bu
}
// DiskUsage of builder
func DiskUsage(buildersMap map[string]string, builderImage string, platformsToClean []string, verbose bool) error {
func DiskUsage(buildersMap map[string]string, builderImage, builderConfigPath string, platformsToClean []string, verbose bool) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for _, platform := range platformsToClean {
client, err := getClientForPlatform(ctx, buildersMap, builderImage, platform)
client, err := getClientForPlatform(ctx, buildersMap, builderImage, builderConfigPath, platform)
if err != nil {
return fmt.Errorf("cannot get client: %s", err)
}
@ -143,12 +143,12 @@ func DiskUsage(buildersMap map[string]string, builderImage string, platformsToCl
}
// PruneBuilder clean build cache of builder
func PruneBuilder(buildersMap map[string]string, builderImage string, platformsToClean []string, verbose bool) error {
func PruneBuilder(buildersMap map[string]string, builderImage, builderConfigPath string, platformsToClean []string, verbose bool) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
total := int64(0)
for _, platform := range platformsToClean {
client, err := getClientForPlatform(ctx, buildersMap, builderImage, platform)
client, err := getClientForPlatform(ctx, buildersMap, builderImage, builderConfigPath, platform)
if err != nil {
return fmt.Errorf("cannot get client: %s", err)
}

View File

@ -1,9 +1,16 @@
package spec
type ImageBuildOptions struct {
Labels map[string]string
BuildArgs map[string]*string
NetworkMode string
Dockerfile string
SSH []string
type RegistryAuth struct {
Username string
Password string
RegistryToken string // base64 encoded auth token
}
type ImageBuildOptions struct {
Labels map[string]string
BuildArgs map[string]*string
NetworkMode string
Dockerfile string
SSH []string
RegistryAuths map[string]RegistryAuth
}

View File

@ -0,0 +1,3 @@
Dockerfile
buildkitd.toml
docker-config/

View File

@ -0,0 +1,2 @@
FROM alpine:3.21
RUN echo hi

View File

@ -0,0 +1,2 @@
org: linuxkit
image: auth-registry

View File

@ -0,0 +1,129 @@
#!/bin/sh
# SUMMARY: Check that we can access a registry with auth
# LABELS:
set -e
# Source libraries. Uncomment if needed/defined
#. "${RT_LIB}"
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
clean_up() {
docker kill "${REGISTRY_NAME}" || true
DOCKER_CONFIG="${DOCKER_CONFIG}" docker buildx rm "${BUILDKIT_NAME}" || true
[ -n "${CACHDIR}" ] && rm -rf "${CACHDIR}"
[ -n "${DOCKER_CONFIG}" ] && rm -rf "${DOCKER_CONFIG}"
[ -n "${REGISTRY_DIR}" ] && rm -rf "${REGISTRY_DIR}"
}
trap clean_up EXIT
# determine platform
ARCH=$(uname -m)
if [ "${ARCH}" = "x86_64" ]; then
ARCH="amd64"
elif [ "${ARCH}" = "aarch64" ]; then
ARCH="arm64"
fi
PLATFORM="linux/${ARCH}"
# container names
REGISTRY_NAME="test-registry-$$"
BUILDKIT_NAME="test-buildkitd-$$"
# start a registry with auth
REGISTRY_USER="testuser"
REGISTRY_PASS="testpass"
REGISTRY_PORT="5000"
REGISTRY_DIR=$(mktemp -d)
mkdir -p "$REGISTRY_DIR/auth"
docker run --rm \
--entrypoint htpasswd \
httpd:2 -Bbn "${REGISTRY_USER}" "${REGISTRY_PASS}" > "$REGISTRY_DIR/auth/htpasswd"
# Start registry
REGISTRY_CID=$(docker run -d --rm \
-p ":${REGISTRY_PORT}" \
-v "$REGISTRY_DIR/auth:/auth" \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
--name "${REGISTRY_NAME}" \
registry:3)
REGISTRY_IP=$(docker inspect "${REGISTRY_NAME}" \
--format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
IMAGENAME="${REGISTRY_IP}:${REGISTRY_PORT}/myimage"
# start an insecure buildkit so we can load an image to the registry
cat > buildkitd.toml <<EOF
[registry."${REGISTRY_IP}:${REGISTRY_PORT}"]
insecure = true
http = true
EOF
# save the credentials
credsb64=$(printf "%s" "${REGISTRY_USER}:${REGISTRY_PASS}" | base64)
# DO NOT export DOCKER_CONFIG, as that will cause the thing we are testing to succeed.
# we need to be explicit about it.
DOCKER_CONFIG=$(pwd)/docker-config
rm -rf "${DOCKER_CONFIG}"
mkdir -p "${DOCKER_CONFIG}"
cat > "${DOCKER_CONFIG}/config.json" <<EOF
{
"auths": {
"${REGISTRY_IP}:5000": {
"auth": "${credsb64}"
}
}
}
EOF
DOCKER_CONFIG=${DOCKER_CONFIG} docker buildx create \
--name "${BUILDKIT_NAME}" \
--driver docker-container \
--buildkitd-config "$(pwd)/buildkitd.toml" \
--bootstrap
DOCKER_CONFIG=${DOCKER_CONFIG} docker buildx build \
--builder "${BUILDKIT_NAME}" \
--file Dockerfile.base \
--tag "${IMAGENAME}" \
--push \
--progress plain \
--platform "${PLATFORM}" \
.
# Generate Dockerfile for pkg with FROM
cat > Dockerfile <<EOF
FROM "${IMAGENAME}"
RUN echo SUCCESS
EOF
CACHEDIR=$(mktemp -d)
# 3 tests:
# 1. build a package with no auth - should fail
# 2. build a package with explicit auth - should succeed
# 3. build a package with auth in the config - should succeed
if linuxkit --cache "${CACHEDIR}" pkg build --platforms "${PLATFORM}" \
--builder-config "$(pwd)/buildkitd.toml" --force \
.; then
echo "Test 1 failed: build succeeded without auth"
exit 1
fi
linuxkit --cache "${CACHEDIR}" pkg build --platforms "${PLATFORM}" \
--builder-config "$(pwd)/buildkitd.toml" --force \
--registry-creds "${REGISTRY_IP}:${REGISTRY_PORT}=${REGISTRY_USER}:${REGISTRY_PASS}" \
.
DOCKER_CONFIG=${DOCKER_CONFIG} linuxkit --cache "${CACHEDIR}" pkg build --platforms "${PLATFORM}" \
--builder-config "$(pwd)/buildkitd.toml" --force \
.
exit 0