Skip to content

Commit

Permalink
Support insecure registries (#1140)
Browse files Browse the repository at this point in the history
* Add the support to the new --insecure-registry parameter

Signed-off-by: Domenico Luciani <[email protected]>

* Add the support to the new --insecure-registry parameter in the creator command

Signed-off-by: Domenico Luciani <[email protected]>

* Add keychain mock and handler test

Signed-off-by: Domenico Luciani <[email protected]>

* Add support for a single insecure registry

Signed-off-by: Domenico Luciani <[email protected]>

* Add support to multiple insecure registries

Signed-off-by: Domenico Luciani <[email protected]>

* Adjusted flag name

Signed-off-by: Domenico Luciani <[email protected]>

* Fix problem with the mock

Signed-off-by: Domenico Luciani <[email protected]>

* InsecureRegistry to InsecureRegistries

Signed-off-by: Domenico Luciani <[email protected]>

* Address comment on FlagTags parity

Signed-off-by: Domenico Luciani <[email protected]>

* Parse the InsecureRegistry env variable with comma separated values

Signed-off-by: Domenico Luciani <[email protected]>

* Changed InsecureRegistry to InsecureRegistries

Signed-off-by: Domenico Luciani <[email protected]>

* Changing name at the env variable

Signed-off-by: Domenico Luciani <[email protected]>

* Exporter now accept insecure registries

Signed-off-by: Domenico Luciani <[email protected]>

* Bump up toward the latest version of imgutil

Signed-off-by: Domenico Luciani <[email protected]>

* Remove legacy guard

Signed-off-by: Domenico Luciani <[email protected]>

* Add WithRegistrySetting with insecure registries into restorer

Signed-off-by: Domenico Luciani <[email protected]>

* Add support for insecure registries to the rebaser

Signed-off-by: Domenico Luciani <[email protected]>

* Add rebaser testdata directory to .gitignore

Signed-off-by: Domenico Luciani <[email protected]>

* Cleaned testdata directory

Signed-off-by: Domenico Luciani <[email protected]>

* Remove testdata rebaser entries from .gitignore

Signed-off-by: Domenico Luciani <[email protected]>

* Add support to insecure registies for the read-write registry check

Signed-off-by: Domenico Luciani <[email protected]>

* Move registryHandler into its own file into the image package

Signed-off-by: Domenico Luciani <[email protected]>

* Add dockerfile.windows

Signed-off-by: Domenico Luciani <[email protected]>

* Introducing GetInsecureRegistryOptions

Signed-off-by: Domenico Luciani <[email protected]>

* Fix linter problems

Signed-off-by: Domenico Luciani <[email protected]>

* Remove legacy guards and add test support to arm64

Signed-off-by: Domenico Luciani <[email protected]>

* Remove duplication and utilize new GetInsecureRegistryOptions function

Signed-off-by: Domenico Luciani <[email protected]>

* Extract common code for getting insecure registry options from imageRef

Signed-off-by: Domenico Luciani <[email protected]>

* Added cli flags behind proper guards

Signed-off-by: Domenico Luciani <[email protected]>

* Bumped up to the imgutil latest version with the insecure adjustment and renamed the getInsecureRegistryOptions function

Signed-off-by: Domenico Luciani <[email protected]>

* Make the getInsecureOptions a static method as a temporary solution to remove duplications

Signed-off-by: Domenico Luciani <[email protected]>

* Added insecure registry to the runImage in the rebaser

Signed-off-by: Domenico Luciani <[email protected]>

* Bump up to the latest version of imgutil with the multiple registries fix

Signed-off-by: Domenico Luciani <[email protected]>

* Go mod tidy

Signed-off-by: Domenico Luciani <[email protected]>

* Remove insecure-registry filter based on the imageRef

Signed-off-by: Domenico Luciani <[email protected]>

* Remove reduntant for loop and added a test for multiple GetInsecureOptions

Signed-off-by: Domenico Luciani <[email protected]>

* Don't remove whitespaces between buildpacks names

Signed-off-by: Domenico Luciani <[email protected]>

* Add FlagInsecureRegistries behind 0.13 guards

Signed-off-by: Domenico Luciani <[email protected]>

---------

Signed-off-by: Domenico Luciani <[email protected]>
  • Loading branch information
Domenico Luciani authored Sep 22, 2023
1 parent 5b02ac1 commit 7ffcd58
Show file tree
Hide file tree
Showing 33 changed files with 531 additions and 104 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
.tool-versions
/out
.vscode

acceptance/testdata/*/**/container/cnb/lifecycle/*
acceptance/testdata/*/**/container/docker-config/*

acceptance/testdata/exporter/container/cnb/run.toml
acceptance/testdata/exporter/container/layers/*analyzed.toml
acceptance/testdata/exporter/container/other_layers/*analyzed.toml

acceptance/testdata/restorer/container/layers/*analyzed.toml
3 changes: 2 additions & 1 deletion acceptance/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ var (

func TestBuilder(t *testing.T) {
h.SkipIf(t, runtime.GOOS == "windows", "Builder acceptance tests are not yet supported on Windows")
h.SkipIf(t, runtime.GOARCH != "amd64", "Builder acceptance tests are not yet supported on non-amd64")

info, err := h.DockerCli(t).Info(context.TODO())
h.AssertNil(t, err)
Expand All @@ -39,6 +38,8 @@ func TestBuilder(t *testing.T) {
builderDaemonArch = info.Architecture
if builderDaemonArch == "x86_64" {
builderDaemonArch = "amd64"
} else if builderDaemonArch == "aarch64" {
builderDaemonArch = "arm64"
}

h.MakeAndCopyLifecycle(t, builderDaemonOS, builderDaemonArch, builderBinaryDir)
Expand Down
25 changes: 19 additions & 6 deletions acceptance/detector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package acceptance

import (
"context"
"fmt"
"os"
"os/exec"
Expand All @@ -22,17 +23,29 @@ import (
)

var (
detectDockerContext = filepath.Join("testdata", "detector")
detectorBinaryDir = filepath.Join("testdata", "detector", "container", "cnb", "lifecycle")
detectImage = "lifecycle/acceptance/detector"
userID = "1234"
detectDockerContext = filepath.Join("testdata", "detector")
detectorBinaryDir = filepath.Join("testdata", "detector", "container", "cnb", "lifecycle")
detectImage = "lifecycle/acceptance/detector"
userID = "1234"
detectorDaemonOS, detectorDaemonArch string
)

func TestDetector(t *testing.T) {
h.SkipIf(t, runtime.GOOS == "windows", "Detector acceptance tests are not yet supported on Windows")
h.SkipIf(t, runtime.GOARCH != "amd64", "Detector acceptance tests are not yet supported on non-amd64")

h.MakeAndCopyLifecycle(t, "linux", "amd64", detectorBinaryDir)
info, err := h.DockerCli(t).Info(context.TODO())
h.AssertNil(t, err)

detectorDaemonOS = info.OSType
detectorDaemonArch = info.Architecture
if detectorDaemonArch == "x86_64" {
detectorDaemonArch = "amd64"
}
if detectorDaemonArch == "aarch64" {
detectorDaemonArch = "arm64"
}

h.MakeAndCopyLifecycle(t, detectorDaemonOS, detectorDaemonArch, detectorBinaryDir)
h.DockerBuild(t,
detectImage,
detectDockerContext,
Expand Down
27 changes: 27 additions & 0 deletions acceptance/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,33 @@ func testExporterFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})
})

when("app using insecure registry", func() {
it.Before(func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.12"), "")
})

it("does an http request", func() {
var exportFlags []string
exportArgs := append([]string{ctrPath(exporterPath)}, exportFlags...)
exportedImageName = exportTest.RegRepoName("some-insecure-exported-image-" + h.RandString(10))
exportArgs = append(exportArgs, exportedImageName)
insecureRegistry := "host.docker.internal/bar"
insecureAnalyzed := "/layers/analyzed_insecure.toml"

_, _, err := h.DockerRunWithError(t,
exportImage,
h.WithFlags(
"--env", "CNB_PLATFORM_API="+platformAPI,
"--env", "CNB_INSECURE_REGISTRIES="+insecureRegistry,
"--env", "CNB_ANALYZED_PATH="+insecureAnalyzed,
"--network", exportRegNetwork,
),
h.WithArgs(exportArgs...),
)
h.AssertStringContains(t, err.Error(), "http://host.docker.internal")
})
})

when("SOURCE_DATE_EPOCH is set", func() {
it("Image CreatedAt is set to SOURCE_DATE_EPOCH", func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.9"), "SOURCE_DATE_EPOCH support added in 0.9")
Expand Down
59 changes: 59 additions & 0 deletions acceptance/rebaser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//go:build acceptance
// +build acceptance

package acceptance

import (
"path/filepath"
"testing"

"github.com/sclevine/spec"
"github.com/sclevine/spec/report"

"github.com/buildpacks/lifecycle/api"
h "github.com/buildpacks/lifecycle/testhelpers"
)

var (
rebaserTest *PhaseTest
rebaserPath string
rebaserImage string
)

func TestRebaser(t *testing.T) {
testImageDockerContextFolder := filepath.Join("testdata", "rebaser")
rebaserTest = NewPhaseTest(t, "rebaser", testImageDockerContextFolder)
rebaserTest.Start(t, updateTOMLFixturesWithTestRegistry)
defer rebaserTest.Stop(t)

rebaserImage = rebaserTest.testImageRef
rebaserPath = rebaserTest.containerBinaryPath

for _, platformAPI := range api.Platform.Supported {
spec.Run(t, "acceptance-rebaser/"+platformAPI.String(), testRebaser(platformAPI.String()), spec.Sequential(), spec.Report(report.Terminal{}))
}
}

func testRebaser(platformAPI string) func(t *testing.T, when spec.G, it spec.S) {
return func(t *testing.T, when spec.G, it spec.S) {
when("called with insecure registry flag", func() {
it.Before(func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.12"), "")
})
it("should do an http request", func() {
insecureRegistry := "host.docker.internal"
rebaserOutputImageName := insecureRegistry + "/bar"
_, _, err := h.DockerRunWithError(t,
rebaserImage,
h.WithFlags(
"--env", "CNB_PLATFORM_API="+platformAPI,
"--env", "CNB_INSECURE_REGISTRIES="+insecureRegistry,
),
h.WithArgs(ctrPath(rebaserPath), rebaserOutputImageName),
)

h.AssertStringContains(t, err.Error(), "http://host.docker.internal")
})
})
}
}
22 changes: 21 additions & 1 deletion acceptance/restorer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ var (

func TestRestorer(t *testing.T) {
h.SkipIf(t, runtime.GOOS == "windows", "Restorer acceptance tests are not yet supported on Windows")
h.SkipIf(t, runtime.GOARCH != "amd64", "Restorer acceptance tests are not yet supported on non-amd64")

testImageDockerContext := filepath.Join("testdata", "restorer")
restoreTest = NewPhaseTest(t, "restorer", testImageDockerContext)
Expand Down Expand Up @@ -106,6 +105,27 @@ func testRestorerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe

h.AssertStringContains(t, output, "Restoring metadata for \"some-buildpack-id:launch-layer\"")
})

when("restores app metadata using an insecure registry", func() {
it.Before(func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.12"), "")
})
it("does an http request ", func() {
insecureRegistry := "host.docker.internal"

_, _, err := h.DockerRunWithError(t,
restoreImage,
h.WithFlags(append(
dockerSocketMount,
"--env", "CNB_PLATFORM_API="+platformAPI,
"--env", "CNB_INSECURE_REGISTRIES="+insecureRegistry,
"--env", "CNB_BUILD_IMAGE="+insecureRegistry+"/bar",
)...),
)

h.AssertStringContains(t, err.Error(), "http://host.docker.internal")
})
})
})

when("using cache-dir", func() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[run-image]
reference = "host.docker.internal/bar"
3 changes: 3 additions & 0 deletions acceptance/testdata/rebaser/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM ubuntu:bionic

COPY ./container/ /
10 changes: 10 additions & 0 deletions acceptance/testdata/rebaser/Dockerfile.windows
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM mcr.microsoft.com/windows/nanoserver:1809
USER ContainerAdministrator

COPY container /

ENV CNB_USER_ID=1

ENV CNB_GROUP_ID=1

ENV CNB_PLATFORM_API=${cnb_platform_api}
4 changes: 2 additions & 2 deletions analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type AnalyzerFactory struct {
cacheHandler CacheHandler
configHandler ConfigHandler
imageHandler image.Handler
registryHandler RegistryHandler
registryHandler image.RegistryHandler
}

func NewAnalyzerFactory(
Expand All @@ -29,7 +29,7 @@ func NewAnalyzerFactory(
cacheHandler CacheHandler,
configHandler ConfigHandler,
imageHandler image.Handler,
registryHandler RegistryHandler,
registryHandler image.RegistryHandler,
) *AnalyzerFactory {
return &AnalyzerFactory{
platformAPI: platformAPI,
Expand Down
7 changes: 5 additions & 2 deletions cmd/lifecycle/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func (a *analyzeCmd) DefineFlags() {
cli.FlagStackPath(&a.StackPath)
}
switch {
case a.PlatformAPI.AtLeast("0.13"):
cli.FlagInsecureRegistries(&a.InsecureRegistries)
fallthrough
case a.PlatformAPI.AtLeast("0.12"):
cli.FlagLayoutDir(&a.LayoutDir)
cli.FlagUseLayout(&a.UseLayout)
Expand Down Expand Up @@ -99,8 +102,8 @@ func (a *analyzeCmd) Exec() error {
&cmd.BuildpackAPIVerifier{},
NewCacheHandler(a.keychain),
lifecycle.NewConfigHandler(),
image.NewHandler(a.docker, a.keychain, a.LayoutDir, a.UseLayout),
NewRegistryHandler(a.keychain),
image.NewHandler(a.docker, a.keychain, a.LayoutDir, a.UseLayout, a.InsecureRegistries),
image.NewRegistryHandler(a.keychain, a.InsecureRegistries),
)
analyzer, err := factory.NewAnalyzer(a.AdditionalTags, a.CacheImageRef, a.LaunchCacheDir, a.LayersDir, a.OutputImageRef, a.PreviousImageRef, a.RunImageRef, a.SkipLayers, cmd.DefaultLogger)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions cmd/lifecycle/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ func FlagForceRebase(force *bool) {
flagSet.BoolVar(force, "force", *force, "execute rebase even if operation is unsafe")
}

// FlagInsecureRegistries sets insecure-registry parameter as available
func FlagInsecureRegistries(insecureRegistries *str.Slice) {
flagSet.Var(insecureRegistries, "insecure-registry", "insecure registries")
}

// deprecated

func DeprecatedFlagRunImage(deprecatedRunImage *string) {
Expand Down
9 changes: 7 additions & 2 deletions cmd/lifecycle/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ func (c *createCmd) DefineFlags() {
cli.FlagUseLayout(&c.UseLayout)
cli.FlagRunPath(&c.RunPath)
}

if c.PlatformAPI.AtLeast("0.13") {
cli.FlagInsecureRegistries(&c.InsecureRegistries)
}

if c.PlatformAPI.AtLeast("0.11") {
cli.FlagBuildConfigDir(&c.BuildConfigDir)
cli.FlagLauncherSBOMDir(&c.LauncherSBOMDir)
Expand Down Expand Up @@ -124,8 +129,8 @@ func (c *createCmd) Exec() error {
&cmd.BuildpackAPIVerifier{},
NewCacheHandler(c.keychain),
lifecycle.NewConfigHandler(),
image.NewHandler(c.docker, c.keychain, c.LayoutDir, c.UseLayout),
NewRegistryHandler(c.keychain),
image.NewHandler(c.docker, c.keychain, c.LayoutDir, c.UseLayout, c.InsecureRegistries),
image.NewRegistryHandler(c.keychain, c.InsecureRegistries),
)
analyzer, err := analyzerFactory.NewAnalyzer(c.AdditionalTags, c.CacheImageRef, c.LaunchCacheDir, c.LayersDir, c.OutputImageRef, c.PreviousImageRef, c.RunImageRef, c.SkipLayers, cmd.DefaultLogger)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions cmd/lifecycle/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func (e *exportCmd) DefineFlags() {
} else {
cli.FlagStackPath(&e.StackPath)
}

if e.PlatformAPI.AtLeast("0.13") {
cli.FlagInsecureRegistries(&e.InsecureRegistries)
}

if e.PlatformAPI.AtLeast("0.11") {
cli.FlagLauncherSBOMDir(&e.LauncherSBOMDir)
}
Expand Down Expand Up @@ -340,6 +345,7 @@ func (e *exportCmd) initRemoteAppImage(analyzedMD files.Analyzed) (imgutil.Image
var opts = []remote.ImageOption{
remote.FromBaseImage(e.RunImageRef),
}

if e.supportsRunImageExtension() {
extendedConfig, err := e.getExtendedConfig(analyzedMD.RunImage)
if err != nil {
Expand All @@ -355,6 +361,8 @@ func (e *exportCmd) initRemoteAppImage(analyzedMD files.Analyzed) (imgutil.Image
opts = append(opts, remote.WithHistory())
}

opts = append(opts, image.GetInsecureOptions(e.InsecureRegistries)...)

if analyzedMD.PreviousImageRef() != "" {
cmd.DefaultLogger.Infof("Reusing layers from image '%s'", analyzedMD.PreviousImageRef())
opts = append(opts, remote.WithPreviousImage(analyzedMD.PreviousImageRef()))
Expand Down
55 changes: 0 additions & 55 deletions cmd/lifecycle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"path/filepath"
"strings"

"github.com/buildpacks/imgutil/remote"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/pkg/errors"

Expand Down Expand Up @@ -112,60 +111,6 @@ func (ch *DefaultCacheHandler) InitCache(cacheImageRef string, cacheDir string,
return cacheStore, nil
}

type DefaultRegistryHandler struct {
keychain authn.Keychain
}

func NewRegistryHandler(keychain authn.Keychain) *DefaultRegistryHandler {
return &DefaultRegistryHandler{
keychain: keychain,
}
}

func (rv *DefaultRegistryHandler) EnsureReadAccess(imageRefs ...string) error {
for _, imageRef := range imageRefs {
if err := verifyReadAccess(imageRef, rv.keychain); err != nil {
return err
}
}
return nil
}

func (rv *DefaultRegistryHandler) EnsureWriteAccess(imageRefs ...string) error {
for _, imageRef := range imageRefs {
if err := verifyReadWriteAccess(imageRef, rv.keychain); err != nil {
return err
}
}
return nil
}

func verifyReadAccess(imageRef string, keychain authn.Keychain) error {
if imageRef == "" {
return nil
}
img, _ := remote.NewImage(imageRef, keychain)
canRead, err := img.CheckReadAccess()
if !canRead {
cmd.DefaultLogger.Debugf("Error checking read access: %s", err)
return errors.Errorf("ensure registry read access to %s", imageRef)
}
return nil
}

func verifyReadWriteAccess(imageRef string, keychain authn.Keychain) error {
if imageRef == "" {
return nil
}
img, _ := remote.NewImage(imageRef, keychain)
canReadWrite, err := img.CheckReadWriteAccess()
if !canReadWrite {
cmd.DefaultLogger.Debugf("Error checking read/write access: %s", err)
return errors.Errorf("ensure registry read/write access to %s", imageRef)
}
return nil
}

// helpers

func initCache(cacheImageTag, cacheDir string, keychain authn.Keychain, deletionEnabled bool) (lifecycle.Cache, error) {
Expand Down
Loading

0 comments on commit 7ffcd58

Please sign in to comment.