Skip to content

Commit e1ff8d5

Browse files
authored
Merge pull request #291 from carolynvs/fix-skip-tls
Support insecure registries during archive
2 parents 2837523 + acf11e3 commit e1ff8d5

17 files changed

+434
-8
lines changed

imagestore/construction/construction.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func NewLocatingConstructor() imagestore.Constructor {
2525
if thin(parms.ArchiveDir) {
2626
return remote.Create()
2727
}
28-
return ocilayout.LocateOciLayout(parms.ArchiveDir)
28+
return ocilayout.LocateOciLayout(parms)
2929
}
3030
}
3131

imagestore/ocilayout/ocilayout.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func Create(options ...imagestore.Option) (imagestore.Store, error) {
2727
return nil, err
2828
}
2929

30-
layout, err := ggcr.NewRegistryClient().NewLayout(layoutDir)
30+
layout, err := ggcr.NewRegistryClient(parms.BuildRegistryOptions()...).NewLayout(layoutDir)
3131
if err != nil {
3232
return nil, err
3333
}
@@ -38,12 +38,12 @@ func Create(options ...imagestore.Option) (imagestore.Store, error) {
3838
}, nil
3939
}
4040

41-
func LocateOciLayout(archiveDir string) (imagestore.Store, error) {
42-
layoutDir := filepath.Join(archiveDir, "artifacts", "layout")
41+
func LocateOciLayout(parms imagestore.Parameters) (imagestore.Store, error) {
42+
layoutDir := filepath.Join(parms.ArchiveDir, "artifacts", "layout")
4343
if _, err := os.Stat(layoutDir); os.IsNotExist(err) {
4444
return nil, err
4545
}
46-
layout, err := ggcr.NewRegistryClient().ReadLayout(layoutDir)
46+
layout, err := ggcr.NewRegistryClient(parms.BuildRegistryOptions()...).ReadLayout(layoutDir)
4747
if err != nil {
4848
return nil, err
4949
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package ocilayout
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"os/exec"
7+
"testing"
8+
9+
"github.com/pivotal/image-relocation/pkg/image"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/cnabio/cnab-go/imagestore"
14+
"github.com/cnabio/cnab-go/imagestore/tests"
15+
)
16+
17+
func TestOCILayout_PushToInsecureRegistry(t *testing.T) {
18+
// Start an insecure registry and get the port that it's running on
19+
regPort := tests.StartTestRegistry(t)
20+
registry := fmt.Sprintf("localhost:%s", regPort)
21+
22+
// Using hello-world
23+
srcDigest, err := image.NewDigest("sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4")
24+
require.NoError(t, err, "image.NewDigest failed")
25+
sourceImg, err := image.NewName(fmt.Sprintf("docker.io/library/hello-world@%s", srcDigest.String()))
26+
require.NoError(t, err, "image.NewName failed")
27+
destImg, err := image.NewName(fmt.Sprintf("%s/hello-world:latest", registry))
28+
require.NoError(t, err, "image.NewName failed")
29+
30+
t.Run("failed push", func(t *testing.T) {
31+
// Try to push without passing in a http client with skipTLS set to true
32+
store, err := Create(imagestore.WithArchiveDir(t.TempDir()))
33+
require.NoError(t, err, "ocilayout.Create failed")
34+
35+
// Add the source image to our registry
36+
dig, err := store.Add(sourceImg.String())
37+
require.NoError(t, err, "Add failed")
38+
assert.Equal(t, srcDigest.String(), dig, "incorrect content digest returned by Add")
39+
40+
err = store.Push(srcDigest, sourceImg, destImg)
41+
require.Errorf(t, err, "expected push to fail because skipTLS was not specified")
42+
assert.Contains(t, err.Error(), "Client sent an HTTP request to an HTTPS server", "expected push to fail because TLS wasn't configured properly")
43+
})
44+
45+
t.Run("successful push", func(t *testing.T) {
46+
// Create a http transport that allows connecting to our insecure registry
47+
skipTLS := http.DefaultTransport.(*http.Transport).Clone()
48+
skipTLS.TLSClientConfig.InsecureSkipVerify = true
49+
50+
store, err := Create(imagestore.WithTransport(skipTLS), imagestore.WithArchiveDir(t.TempDir()))
51+
require.NoError(t, err, "ocilayout.Create failed")
52+
53+
// Add the source image to our registry
54+
dig, err := store.Add(sourceImg.String())
55+
require.NoError(t, err, "Add failed")
56+
assert.Equal(t, srcDigest.String(), dig, "incorrect content digest returned by Add")
57+
58+
// Push a test container (hello-world) into our registry
59+
err = store.Push(srcDigest, sourceImg, destImg)
60+
require.NoError(t, err, "Push failed")
61+
62+
// Validate the image was copied to the new location
63+
err = exec.Command("docker", "pull", destImg.String()).Run()
64+
require.NoError(t, err, "the image was not present in the destination registry")
65+
})
66+
67+
t.Run("push from archive", func(t *testing.T) {
68+
// Create a http transport that allows connecting to our insecure registry
69+
skipTLS := http.DefaultTransport.(*http.Transport).Clone()
70+
skipTLS.TLSClientConfig.InsecureSkipVerify = true
71+
72+
parms := imagestore.Create(
73+
imagestore.WithArchiveDir("testdata"),
74+
imagestore.WithTransport(skipTLS))
75+
layout, err := LocateOciLayout(parms)
76+
require.NoError(t, err, "LocateOciLayout failed")
77+
78+
//
79+
// Add the image from the archive into the index
80+
//
81+
img, err := image.NewName("docker.io/library/hello-world@sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4")
82+
require.NoError(t, err, "NewName failed")
83+
84+
dig, err := layout.Add(img.String())
85+
require.NoError(t, err, "Add failed")
86+
assert.Equal(t, "sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4", dig, "incorrect content digest returned by Add")
87+
88+
imgDig, err := image.NewDigest(dig)
89+
require.NoError(t, err, "NewDigest failed")
90+
91+
// Push the image
92+
err = layout.Push(imgDig, img, destImg)
93+
require.NoError(t, err, "Push failed")
94+
95+
// Validate the image was copied to the new location
96+
err = exec.Command("docker", "pull", destImg.String()).Run()
97+
require.NoError(t, err, "the image was not present in the destination registry")
98+
})
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"schemaVersion": 2,
3+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
4+
"config": {
5+
"mediaType": "application/vnd.docker.container.image.v1+json",
6+
"size": 1469,
7+
"digest": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412"
8+
},
9+
"layers": [
10+
{
11+
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
12+
"size": 2479,
13+
"digest": "sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54"
14+
}
15+
]
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/hello"],"Image":"sha256:b9935d4e8431fb1a7f0989304ec86b3329a99a25f5efdc7f09f3f8c41434ca6d","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"8746661ca3c2f215da94e6d3f7dfdcafaff5ec0b21c9aff6af3dc379a82fbc72","container_config":{"Hostname":"8746661ca3c2","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/hello\"]"],"Image":"sha256:b9935d4e8431fb1a7f0989304ec86b3329a99a25f5efdc7f09f3f8c41434ca6d","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2021-09-23T23:47:57.442225064Z","docker_version":"20.10.7","history":[{"created":"2021-09-23T23:47:57.098990892Z","created_by":"/bin/sh -c #(nop) COPY file:50563a97010fd7ce1ceebd1fa4f4891ac3decdf428333fb2683696f4358af6c2 in / "},{"created":"2021-09-23T23:47:57.442225064Z","created_by":"/bin/sh -c #(nop) CMD [\"/hello\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359"]}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"schemaVersion": 2,
3+
"manifests": [
4+
{
5+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
6+
"size": 525,
7+
"digest": "sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4",
8+
"annotations": {
9+
"org.opencontainers.image.ref.name": "docker.io/library/hello-world@sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4"
10+
}
11+
},
12+
{
13+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
14+
"size": 525,
15+
"digest": "sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4",
16+
"annotations": {
17+
"org.opencontainers.image.ref.name": "docker.io/library/hello-world@sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4"
18+
}
19+
},
20+
{
21+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
22+
"size": 525,
23+
"digest": "sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4",
24+
"annotations": {
25+
"org.opencontainers.image.ref.name": "docker.io/library/hello-world@sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4"
26+
}
27+
}
28+
]
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"imageLayoutVersion": "1.0.0"
3+
}

imagestore/remote/remote.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ type remote struct {
1515
registryClient registry.Client
1616
}
1717

18-
func Create(...imagestore.Option) (imagestore.Store, error) {
18+
func Create(options ...imagestore.Option) (imagestore.Store, error) {
19+
parms := imagestore.Create(options...)
1920
return &remote{
20-
registryClient: ggcr.NewRegistryClient(),
21+
registryClient: ggcr.NewRegistryClient(parms.BuildRegistryOptions()...),
2122
}, nil
2223
}
2324

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package remote
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"os/exec"
7+
"testing"
8+
9+
"github.com/pivotal/image-relocation/pkg/image"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/cnabio/cnab-go/imagestore"
14+
"github.com/cnabio/cnab-go/imagestore/tests"
15+
)
16+
17+
func TestRemote_PushToInsecureRegistry(t *testing.T) {
18+
// Start an insecure registry and get the port that it's running on
19+
regPort := tests.StartTestRegistry(t)
20+
registry := fmt.Sprintf("localhost:%s", regPort)
21+
22+
// Using hello-world
23+
srcDigest, err := image.NewDigest("sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4")
24+
require.NoError(t, err, "image.NewDigest failed")
25+
sourceImg, err := image.NewName(fmt.Sprintf("docker.io/library/hello-world@%s", srcDigest.String()))
26+
require.NoError(t, err, "image.NewName failed")
27+
destImg, err := image.NewName(fmt.Sprintf("%s/hello-world:latest", registry))
28+
require.NoError(t, err, "image.NewName failed")
29+
30+
t.Run("failed push", func(t *testing.T) {
31+
// Try to push without passing in a http client with skipTLS set to true
32+
store, err := Create()
33+
require.NoError(t, err, "remote.Create failed")
34+
35+
err = store.Push(srcDigest, sourceImg, destImg)
36+
require.Errorf(t, err, "expected push to fail because skipTLS was not specified")
37+
assert.Contains(t, err.Error(), "Client sent an HTTP request to an HTTPS server", "expected push to fail because TLS wasn't configured properly")
38+
})
39+
40+
t.Run("successful push", func(t *testing.T) {
41+
// Create a http transport that allows connecting to our insecure registry
42+
skipTLS := http.DefaultTransport.(*http.Transport).Clone()
43+
skipTLS.TLSClientConfig.InsecureSkipVerify = true
44+
45+
// Push a test container (hello-world) into our registry
46+
store, err := Create(imagestore.WithTransport(skipTLS))
47+
require.NoError(t, err, "remote.Create failed")
48+
49+
// Remote doesn't implement add so we arne't calling it
50+
51+
err = store.Push(srcDigest, sourceImg, destImg)
52+
require.NoError(t, err, "Push failed")
53+
54+
// Validate the image was copied to the new location
55+
err = exec.Command("docker", "pull", destImg.String()).Run()
56+
require.NoError(t, err, "the image was not present in the destination registry")
57+
})
58+
}

imagestore/store.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package imagestore
33
import (
44
"io"
55
"io/ioutil"
6+
"net/http"
67

78
"github.com/pivotal/image-relocation/pkg/image"
9+
"github.com/pivotal/image-relocation/pkg/registry/ggcr"
810
)
911

1012
// Store is an abstract image store.
@@ -24,6 +26,19 @@ type Constructor func(...Option) (Store, error)
2426
type Parameters struct {
2527
ArchiveDir string
2628
Logs io.Writer
29+
30+
// Transport is http.Transport to use when communicating with an OCI registry.
31+
Transport *http.Transport
32+
}
33+
34+
// BuildRegistryOptions returns a list of applicable ggcr.Option values
35+
// to use when calling ggcr.NewRegistryClient().
36+
func (p Parameters) BuildRegistryOptions() []ggcr.Option {
37+
var regOpts []ggcr.Option
38+
if p.Transport != nil {
39+
regOpts = append(regOpts, ggcr.WithTransport(p.Transport))
40+
}
41+
return regOpts
2742
}
2843

2944
// Options is a function which returns updated parameters.
@@ -45,16 +60,29 @@ func WithArchiveDir(archiveDir string) Option {
4560
return Parameters{
4661
ArchiveDir: archiveDir,
4762
Logs: b.Logs,
63+
Transport: b.Transport,
4864
}
4965
}
5066
}
5167

52-
// WithArchiveDir return an option to set the logs parameter.
68+
// WithLogs return an option to set the logs parameter.
5369
func WithLogs(logs io.Writer) Option {
5470
return func(b Parameters) Parameters {
5571
return Parameters{
5672
ArchiveDir: b.ArchiveDir,
5773
Logs: logs,
74+
Transport: b.Transport,
75+
}
76+
}
77+
}
78+
79+
// WithTransport returns an option with the Transport parameter set.
80+
func WithTransport(transport *http.Transport) Option {
81+
return func(b Parameters) Parameters {
82+
return Parameters{
83+
ArchiveDir: b.ArchiveDir,
84+
Logs: b.Logs,
85+
Transport: transport,
5886
}
5987
}
6088
}

imagestore/tests/doc.go

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package tests is a set of test helpers that other tests in the imagestore
2+
// package tree may use for common test setup
3+
package tests

0 commit comments

Comments
 (0)