mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #20164 from tonistiigi/verify-tarsteam-on-creation
Verify layer tarstream
This commit is contained in:
commit
c4756a0c36
4 changed files with 126 additions and 14 deletions
|
@ -6,6 +6,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
@ -56,7 +57,7 @@ func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestStore(t *testing.T) (Store, func()) {
|
func newTestStore(t *testing.T) (Store, string, func()) {
|
||||||
td, err := ioutil.TempDir("", "layerstore-")
|
td, err := ioutil.TempDir("", "layerstore-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -72,7 +73,7 @@ func newTestStore(t *testing.T) (Store, func()) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ls, func() {
|
return ls, td, func() {
|
||||||
graphcleanup()
|
graphcleanup()
|
||||||
os.RemoveAll(td)
|
os.RemoveAll(td)
|
||||||
}
|
}
|
||||||
|
@ -265,7 +266,7 @@ func assertLayerEqual(t *testing.T, l1, l2 Layer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMountAndRegister(t *testing.T) {
|
func TestMountAndRegister(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
li := initWithFiles(newTestFile("testfile.txt", []byte("some test data"), 0644))
|
li := initWithFiles(newTestFile("testfile.txt", []byte("some test data"), 0644))
|
||||||
|
@ -306,7 +307,7 @@ func TestMountAndRegister(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLayerRelease(t *testing.T) {
|
func TestLayerRelease(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
layer1, err := createLayer(ls, "", initWithFiles(newTestFile("layer1.txt", []byte("layer 1 file"), 0644)))
|
layer1, err := createLayer(ls, "", initWithFiles(newTestFile("layer1.txt", []byte("layer 1 file"), 0644)))
|
||||||
|
@ -351,7 +352,7 @@ func TestLayerRelease(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStoreRestore(t *testing.T) {
|
func TestStoreRestore(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
layer1, err := createLayer(ls, "", initWithFiles(newTestFile("layer1.txt", []byte("layer 1 file"), 0644)))
|
layer1, err := createLayer(ls, "", initWithFiles(newTestFile("layer1.txt", []byte("layer 1 file"), 0644)))
|
||||||
|
@ -472,7 +473,7 @@ func TestStoreRestore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTarStreamStability(t *testing.T) {
|
func TestTarStreamStability(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
files1 := []FileApplier{
|
files1 := []FileApplier{
|
||||||
|
@ -668,7 +669,7 @@ func assertActivityCount(t *testing.T, l RWLayer, expected int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegisterExistingLayer(t *testing.T) {
|
func TestRegisterExistingLayer(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
baseFiles := []FileApplier{
|
baseFiles := []FileApplier{
|
||||||
|
@ -713,7 +714,7 @@ func graphDiffSize(ls Store, l Layer) (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLayerSize(t *testing.T) {
|
func TestLayerSize(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
content1 := []byte("Base contents")
|
content1 := []byte("Base contents")
|
||||||
|
@ -765,3 +766,69 @@ func TestLayerSize(t *testing.T) {
|
||||||
t.Fatalf("Unexpected size %d, expected %d", layer2Size, expected)
|
t.Fatalf("Unexpected size %d, expected %d", layer2Size, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTarStreamVerification(t *testing.T) {
|
||||||
|
ls, tmpdir, cleanup := newTestStore(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
files1 := []FileApplier{
|
||||||
|
newTestFile("/foo", []byte("abc"), 0644),
|
||||||
|
newTestFile("/bar", []byte("def"), 0644),
|
||||||
|
}
|
||||||
|
files2 := []FileApplier{
|
||||||
|
newTestFile("/foo", []byte("abc"), 0644),
|
||||||
|
newTestFile("/bar", []byte("def"), 0600), // different perm
|
||||||
|
}
|
||||||
|
|
||||||
|
tar1, err := tarFromFiles(files1...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tar2, err := tarFromFiles(files2...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
layer1, err := ls.Register(bytes.NewReader(tar1), "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
layer2, err := ls.Register(bytes.NewReader(tar2), "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
id1 := digest.Digest(layer1.ChainID())
|
||||||
|
id2 := digest.Digest(layer2.ChainID())
|
||||||
|
|
||||||
|
// Replace tar data files
|
||||||
|
src, err := os.Open(filepath.Join(tmpdir, id1.Algorithm().String(), id1.Hex(), "tar-split.json.gz"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, err := os.Create(filepath.Join(tmpdir, id2.Algorithm().String(), id2.Hex(), "tar-split.json.gz"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(dst, src); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src.Close()
|
||||||
|
dst.Close()
|
||||||
|
|
||||||
|
ts, err := layer2.TarStream()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(ioutil.Discard, ts)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected data verification to fail")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "could not verify layer data") {
|
||||||
|
t.Fatalf("wrong error returned from tarstream: %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -268,7 +268,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMountMigration(t *testing.T) {
|
func TestMountMigration(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
baseFiles := []FileApplier{
|
baseFiles := []FileApplier{
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMountInit(t *testing.T) {
|
func TestMountInit(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
basefile := newTestFile("testfile.txt", []byte("base data!"), 0644)
|
basefile := newTestFile("testfile.txt", []byte("base data!"), 0644)
|
||||||
|
@ -63,7 +63,7 @@ func TestMountInit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMountSize(t *testing.T) {
|
func TestMountSize(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
content1 := []byte("Base contents")
|
content1 := []byte("Base contents")
|
||||||
|
@ -105,7 +105,7 @@ func TestMountSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMountChanges(t *testing.T) {
|
func TestMountChanges(t *testing.T) {
|
||||||
ls, cleanup := newTestStore(t)
|
ls, _, cleanup := newTestStore(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
basefiles := []FileApplier{
|
basefiles := []FileApplier{
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package layer
|
package layer
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
)
|
||||||
|
|
||||||
type roLayer struct {
|
type roLayer struct {
|
||||||
chainID ChainID
|
chainID ChainID
|
||||||
|
@ -29,7 +34,11 @@ func (rl *roLayer) TarStream() (io.ReadCloser, error) {
|
||||||
pw.Close()
|
pw.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return pr, nil
|
rc, err := newVerifiedReadCloser(pr, digest.Digest(rl.diffID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rl *roLayer) ChainID() ChainID {
|
func (rl *roLayer) ChainID() ChainID {
|
||||||
|
@ -117,3 +126,39 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newVerifiedReadCloser(rc io.ReadCloser, dgst digest.Digest) (io.ReadCloser, error) {
|
||||||
|
verifier, err := digest.NewDigestVerifier(dgst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &verifiedReadCloser{
|
||||||
|
rc: rc,
|
||||||
|
dgst: dgst,
|
||||||
|
verifier: verifier,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type verifiedReadCloser struct {
|
||||||
|
rc io.ReadCloser
|
||||||
|
dgst digest.Digest
|
||||||
|
verifier digest.Verifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vrc *verifiedReadCloser) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = vrc.rc.Read(p)
|
||||||
|
if n > 0 {
|
||||||
|
if n, err := vrc.verifier.Write(p[:n]); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
if !vrc.verifier.Verified() {
|
||||||
|
err = fmt.Errorf("could not verify layer data for: %s. This may be because internal files in the layer store were modified. Re-pulling or rebuilding this image may resolve the issue", vrc.dgst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (vrc *verifiedReadCloser) Close() error {
|
||||||
|
return vrc.rc.Close()
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue