mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-18 17:01:07 +00:00
add support for pkg build authentication (#4137)
Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
parent
940c1b7b3b
commit
2b4687338b
@ -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(®istryCreds, "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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
3
test/cases/040_packages/025_auth/.gitignore
vendored
Normal file
3
test/cases/040_packages/025_auth/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Dockerfile
|
||||
buildkitd.toml
|
||||
docker-config/
|
2
test/cases/040_packages/025_auth/Dockerfile.base
Normal file
2
test/cases/040_packages/025_auth/Dockerfile.base
Normal file
@ -0,0 +1,2 @@
|
||||
FROM alpine:3.21
|
||||
RUN echo hi
|
2
test/cases/040_packages/025_auth/build.yml
Normal file
2
test/cases/040_packages/025_auth/build.yml
Normal file
@ -0,0 +1,2 @@
|
||||
org: linuxkit
|
||||
image: auth-registry
|
129
test/cases/040_packages/025_auth/test.sh
Normal file
129
test/cases/040_packages/025_auth/test.sh
Normal 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
|
Loading…
Reference in New Issue
Block a user