Skip to content

Commit 519eea7

Browse files
gituashutosh-narkar
authored andcommitted
remove not required basedir for oci bundles & add test to verify signature verification
Signed-off-by: Florian Schrag <[email protected]>
1 parent f8e1e4b commit 519eea7

File tree

9 files changed

+128
-52
lines changed

9 files changed

+128
-52
lines changed

download/oci_download.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func (d *OCIDownloader) download(ctx context.Context, m metrics.Metrics) (*downl
253253
return nil, err
254254
}
255255
loader := bundle.NewTarballLoaderWithBaseURL(fileReader, d.localStorePath)
256-
reader := bundle.NewCustomReader(loader).WithBaseDir(d.localStorePath).
256+
reader := bundle.NewCustomReader(loader).
257257
WithMetrics(m).
258258
WithBundleVerificationConfig(d.bvc).
259259
WithBundleEtag(etag)

download/oci_download_test.go

+43
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"encoding/base64"
99
"fmt"
10+
"github.com/open-policy-agent/opa/bundle"
1011
"net/http"
1112
"strings"
1213
"testing"
@@ -16,6 +17,48 @@ import (
1617
"github.com/open-policy-agent/opa/plugins/rest"
1718
)
1819

20+
// when changed the layer hash & size should be updated in signed.manifest
21+
//go:generate go run github.com/open-policy-agent/opa build -b --signing-alg HS256 --signing-key secret testdata/signed_bundle_data --output testdata/signed.tar.gz
22+
23+
func TestOCIDownloaderWithBundleVerificationConfig(t *testing.T) {
24+
vc := bundle.NewVerificationConfig(map[string]*bundle.KeyConfig{"default": {Key: "secret", Algorithm: "HS256"}}, "", "", nil)
25+
ctx := context.Background()
26+
fixture := newTestFixture(t)
27+
fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff"
28+
29+
updates := make(chan *Update)
30+
31+
config := Config{}
32+
if err := config.ValidateAndInjectDefaults(); err != nil {
33+
t.Fatal(err)
34+
}
35+
36+
d := NewOCI(config, fixture.client, "ghcr.io/org/repo:signed", "/tmp/opa/").WithCallback(func(_ context.Context, u Update) {
37+
if u.Error != nil {
38+
t.Fatalf("expected no error but got: %v", u.Error)
39+
}
40+
updates <- &u
41+
}).WithBundleVerificationConfig(vc)
42+
43+
d.Start(ctx)
44+
45+
// Give time for some download events to occur
46+
time.Sleep(1 * time.Second)
47+
48+
u1 := <-updates
49+
50+
if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 {
51+
t.Fatal("expected bundle with at least one module but got:", u1)
52+
}
53+
54+
if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) {
55+
t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path)
56+
}
57+
58+
d.Stop(ctx)
59+
60+
}
61+
1962
func TestOCIStartStop(t *testing.T) {
2063
ctx := context.Background()
2164
fixture := newTestFixture(t)
File renamed without changes.
File renamed without changes.

download/testdata/signed.manifest

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"schemaVersion":2,
3+
"config":{
4+
"mediaType":"application/vnd.oci.image.config.v1+json",
5+
"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
6+
"size":2
7+
},
8+
"layers":[
9+
{
10+
"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip",
11+
"digest":"sha256:e060c7b9558fad3ec85df5ffa19d0d019f839c36d7ec146977c871dcbc70885e",
12+
"size":629,
13+
"annotations":{
14+
"org.opencontainers.image.created":"2022-02-11T09:00:07Z",
15+
"org.opencontainers.image.title":"dani/testpol"
16+
}
17+
}
18+
]
19+
}

download/testdata/signed.tar.gz

629 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[1,2,3]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package example

download/testharness.go

+63-51
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ package download
66
import (
77
"bytes"
88
"context"
9+
"crypto/sha256"
910
"encoding/json"
1011
"errors"
1112
"fmt"
13+
"io"
1214
"net/http"
1315
"net/http/httptest"
1416
"os"
@@ -246,6 +248,11 @@ func (t *testFixture) oneShot(ctx context.Context, u Update) {
246248
t.etags["test/bundle1"] = u.ETag
247249
}
248250

251+
type fileInfo struct {
252+
name string
253+
length int64
254+
}
255+
249256
type testServer struct {
250257
t *testing.T
251258
customAuth func(http.ResponseWriter, *http.Request) error
@@ -256,6 +263,7 @@ type testServer struct {
256263
server *httptest.Server
257264
etagInResponse bool
258265
longPoll bool
266+
testdataHashes map[string]fileInfo
259267
}
260268

261269
func newTestServer(t *testing.T) *testServer {
@@ -339,68 +347,58 @@ func (t *testServer) handle(w http.ResponseWriter, r *http.Request) {
339347

340348
var buf bytes.Buffer
341349

342-
if r.URL.Path == "/v2/org/repo/manifests/latest" {
343-
w.Header().Add("Content-Length", "596")
344-
w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json")
345-
w.Header().Add("Docker-Content-Digest", "sha256:fe9c2930b6d8cc1bf3fa0c560996a95c75f0d0668bee71138355d9784c8c99b8")
346-
w.WriteHeader(200)
347-
return
348-
}
349-
if r.URL.Path == "/v2/org/repo/manifests/sha256:fe9c2930b6d8cc1bf3fa0c560996a95c75f0d0668bee71138355d9784c8c99b8" {
350-
w.Header().Add("Content-Length", "596")
351-
w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json")
352-
w.Header().Add("Docker-Content-Digest", "sha256:fe9c2930b6d8cc1bf3fa0c560996a95c75f0d0668bee71138355d9784c8c99b8")
353-
w.WriteHeader(200)
354-
bs, err := os.ReadFile("testdata/manifest.layer")
355-
if err != nil {
356-
w.WriteHeader(404)
357-
return
350+
if strings.HasPrefix(r.URL.Path, "/v2/org/repo/") {
351+
// build test data to hash map to serve testdata files by hash
352+
if t.testdataHashes == nil {
353+
t.testdataHashes = make(map[string]fileInfo)
354+
files, err := os.ReadDir("testdata")
355+
if err != nil {
356+
t.t.Fatalf("failed to read testdata directory: %s", err)
357+
}
358+
for _, file := range files {
359+
if file.IsDir() {
360+
continue
361+
}
362+
hash, length, err := getFileSHAandSize("testdata/" + file.Name())
363+
if err != nil {
364+
t.t.Fatalf("failed to read testdata file: %s", err)
365+
}
366+
t.testdataHashes[fmt.Sprintf("%x", hash)] = fileInfo{name: file.Name(), length: length}
367+
}
358368
}
359-
buf.WriteString(string(bs))
360-
w.Write(buf.Bytes())
361-
return
362369
}
363-
if r.URL.Path == "/v2/org/repo/blobs/sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" {
364-
w.Header().Add("Content-Length", "464")
365-
w.Header().Add("Content-Type", "application/vnd.oci.image.layer.v1.tar+gzip,application/vnd.oci.image.config.v1+json")
366-
w.Header().Add("Docker-Content-Digest", "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff")
367-
w.WriteHeader(200)
368-
bs, err := os.ReadFile("testdata/manifest.layer")
369-
if err != nil {
370-
w.WriteHeader(404)
371-
return
372-
}
373-
buf.WriteString(string(bs))
374-
buf.WriteTo(w)
375370

376-
return
377-
}
378-
if r.URL.Path == "/v2/org/repo/blobs/sha256:b206ac766b0f3f880f6a62c4bb5ba5192d29deaefd989a1961603346a7555bdd" {
379-
w.Header().Add("Content-Length", "568")
380-
w.Header().Add("Content-Type", "application/vnd.oci.image.layer.v1.tar+gzip")
381-
w.Header().Add("Docker-Content-Digest", "sha256:b206ac766b0f3f880f6a62c4bb5ba5192d29deaefd989a1961603346a7555bdd")
382-
w.WriteHeader(200)
383-
bs, err := os.ReadFile("testdata/tar.layer")
384-
if err != nil {
385-
w.WriteHeader(404)
371+
if strings.HasPrefix(r.URL.Path, "/v2/org/repo/blobs/sha256:") || strings.HasPrefix(r.URL.Path, "/v2/org/repo/manifests/sha256:") {
372+
sha := strings.TrimPrefix(strings.TrimPrefix(r.URL.Path, "/v2/org/repo/blobs/sha256:"), "/v2/org/repo/manifests/sha256:")
373+
if fileInfo, ok := t.testdataHashes[sha]; ok {
374+
w.Header().Add("Content-Length", strconv.Itoa(int(fileInfo.length)))
375+
w.Header().Add("Content-Type", "application/gzip")
376+
w.Header().Add("Docker-Content-Digest", "sha256:"+sha)
377+
w.WriteHeader(200)
378+
bs, err := os.ReadFile("testdata/" + fileInfo.name)
379+
if err != nil {
380+
w.WriteHeader(404)
381+
return
382+
}
383+
buf.WriteString(string(bs))
384+
w.Write(buf.Bytes())
386385
return
387386
}
388-
buf.WriteString(string(bs))
389-
w.Write(buf.Bytes())
387+
w.WriteHeader(404)
390388
return
391389
}
392-
if r.URL.Path == "/v2/org/repo/blobs/sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a" {
393-
w.Header().Add("Content-Length", "2")
394-
w.Header().Add("Content-Type", "application/vnd.oci.image.config.v1+json")
395-
w.Header().Add("Docker-Content-Digest", "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a")
396-
w.WriteHeader(200)
397-
bs, err := os.ReadFile("testdata/config.layer")
390+
391+
if strings.HasPrefix(r.URL.Path, "/v2/org/repo/manifests/") {
392+
sha, size, err := getFileSHAandSize("testdata/" + strings.TrimPrefix(r.URL.Path, "/v2/org/repo/manifests/") + ".manifest")
398393
if err != nil {
399394
w.WriteHeader(404)
400395
return
401396
}
402-
buf.WriteString(string(bs))
403-
w.Write(buf.Bytes())
397+
398+
w.Header().Add("Content-Length", strconv.Itoa(int(size)))
399+
w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json")
400+
w.Header().Add("Docker-Content-Digest", "sha256:"+fmt.Sprintf("%x", sha))
401+
w.WriteHeader(200)
404402
return
405403
}
406404
name := strings.TrimPrefix(r.URL.Path, "/bundles/")
@@ -487,3 +485,17 @@ func getPreferHeaderField(r *http.Request, field string) string {
487485
}
488486
return ""
489487
}
488+
489+
func getFileSHAandSize(filePath string) ([]byte, int64, error) {
490+
f, err := os.Open(filePath)
491+
if err != nil {
492+
return nil, 0, err
493+
}
494+
defer f.Close()
495+
hash := sha256.New()
496+
w, err := io.Copy(hash, f)
497+
if err != nil {
498+
return nil, w, err
499+
}
500+
return hash.Sum(nil), w, nil
501+
}

0 commit comments

Comments
 (0)