Skip to content

Commit

Permalink
Add Alpine Package type (#337)
Browse files Browse the repository at this point in the history
This adds support for the alpine package format used by Alpine Linux,
which is the concatenation of three tgz files (signature, control data,
and then the actual package files).

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>
  • Loading branch information
bobcallaway authored Jun 23, 2021
1 parent 34caf45 commit 5fb05e1
Show file tree
Hide file tree
Showing 28 changed files with 2,578 additions and 12 deletions.
84 changes: 83 additions & 1 deletion cmd/rekor-cli/app/pflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (

"github.com/go-playground/validator"
"github.com/sigstore/rekor/pkg/generated/models"
alpine_v001 "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1"
intoto_v001 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1"
jar_v001 "github.com/sigstore/rekor/pkg/types/jar/v0.0.1"
rekord_v001 "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1"
Expand Down Expand Up @@ -335,6 +336,82 @@ func CreateRpmFromPFlags() (models.ProposedEntry, error) {
return &returnVal, nil
}

func CreateAlpineFromPFlags() (models.ProposedEntry, error) {
//TODO: how to select version of item to create
returnVal := models.Alpine{}
re := new(alpine_v001.V001Entry)

apk := viper.GetString("entry")
if apk != "" {
var apkBytes []byte
apkURL, err := url.Parse(apk)
if err == nil && apkURL.IsAbs() {
/* #nosec G107 */
apkResp, err := http.Get(apk)
if err != nil {
return nil, fmt.Errorf("error fetching 'alpine': %w", err)
}
defer apkResp.Body.Close()
apkBytes, err = ioutil.ReadAll(apkResp.Body)
if err != nil {
return nil, fmt.Errorf("error fetching 'alpine': %w", err)
}
} else {
apkBytes, err = ioutil.ReadFile(filepath.Clean(apk))
if err != nil {
return nil, fmt.Errorf("error processing 'alpine' file: %w", err)
}
}
if err := json.Unmarshal(apkBytes, &returnVal); err != nil {
return nil, fmt.Errorf("error parsing alpine file: %w", err)
}
} else {
// we will need artifact, public-key, signature
re.AlpineModel = models.AlpineV001Schema{}
re.AlpineModel.Package = &models.AlpineV001SchemaPackage{}

artifact := viper.GetString("artifact")
dataURL, err := url.Parse(artifact)
if err == nil && dataURL.IsAbs() {
re.AlpineModel.Package.URL = strfmt.URI(artifact)
} else {
artifactBytes, err := ioutil.ReadFile(filepath.Clean(artifact))
if err != nil {
return nil, fmt.Errorf("error reading artifact file: %w", err)
}
re.AlpineModel.Package.Content = strfmt.Base64(artifactBytes)
}

re.AlpineModel.PublicKey = &models.AlpineV001SchemaPublicKey{}
publicKey := viper.GetString("public-key")
keyURL, err := url.Parse(publicKey)
if err == nil && keyURL.IsAbs() {
re.AlpineModel.PublicKey.URL = strfmt.URI(publicKey)
} else {
keyBytes, err := ioutil.ReadFile(filepath.Clean(publicKey))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
re.AlpineModel.PublicKey.Content = strfmt.Base64(keyBytes)
}

if err := re.Validate(); err != nil {
return nil, err
}

if re.HasExternalEntities() {
if err := re.FetchExternalEntities(context.Background()); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %v", err)
}
}

returnVal.APIVersion = swag.String(re.APIVersion())
returnVal.Spec = re.AlpineModel
}

return &returnVal, nil
}

func CreateRekordFromPFlags() (models.ProposedEntry, error) {
//TODO: how to select version of item to create
returnVal := models.Rekord{}
Expand Down Expand Up @@ -481,12 +558,17 @@ func (t *typeFlag) Set(s string) error {
"jar": {},
"intoto": {},
"rfc3161": {},
"alpine": {},
}
if _, ok := set[s]; ok {
t.value = s
return nil
}
return fmt.Errorf("value specified is invalid: [%s] supported values are: [rekord, rpm, jar, intoto, rfc3161]", s)
var types []string
for typeStr := range set {
types = append(types, typeStr)
}
return fmt.Errorf("value specified is invalid: [%s] supported values are: [%v]", s, strings.Join(types, ", "))
}

type pkiFormatFlag struct {
Expand Down
39 changes: 39 additions & 0 deletions cmd/rekor-cli/app/pflags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ func TestArtifactPFlags(t *testing.T) {
file, err = ioutil.ReadFile("../../../tests/test.rpm")
case "/rpmPublicKey":
file, err = ioutil.ReadFile("../../../tests/test_rpm_public_key.key")
case "/alpine":
file, err = ioutil.ReadFile("../../../tests/test_alpine.apk")
case "/alpinePublicKey":
file, err = ioutil.ReadFile("../../../tests/test_alpine.pub")
case "/alpineEntry":
file, err = ioutil.ReadFile("../../../tests/alpine.json")
case "/not_found":
err = errors.New("file not found")
}
Expand Down Expand Up @@ -110,6 +116,13 @@ func TestArtifactPFlags(t *testing.T) {
expectParseSuccess: true,
expectValidateSuccess: true,
},
{
caseDesc: "valid alpine URL",
entry: testServer.URL + "/alpineEntry",
typeStr: "alpine",
expectParseSuccess: true,
expectValidateSuccess: true,
},
{
caseDesc: "valid rpm file, wrong type",
typeStr: "rekord",
Expand Down Expand Up @@ -145,6 +158,14 @@ func TestArtifactPFlags(t *testing.T) {
expectParseSuccess: true,
expectValidateSuccess: true,
},
{
caseDesc: "valid alpine - local artifact with required flags",
typeStr: "alpine",
artifact: "../../../tests/test_alpine.apk",
publicKey: "../../../tests/test_alpine.pub",
expectParseSuccess: true,
expectValidateSuccess: true,
},
{
caseDesc: "nonexistant local artifact",
artifact: "../../../tests/not_a_file",
Expand Down Expand Up @@ -231,6 +252,14 @@ func TestArtifactPFlags(t *testing.T) {
expectParseSuccess: true,
expectValidateSuccess: true,
},
{
caseDesc: "valid alpine - remote artifact with required flags",
typeStr: "alpine",
artifact: testServer.URL + "/alpine",
publicKey: "../../../tests/test_alpine.pub",
expectParseSuccess: true,
expectValidateSuccess: true,
},
{
caseDesc: "remote artifact with invalid URL",
artifact: "hteeteep%**/test_file.txt",
Expand Down Expand Up @@ -364,6 +393,16 @@ func TestArtifactPFlags(t *testing.T) {
createFn = CreateRekordFromPFlags
case "rpm":
createFn = CreateRpmFromPFlags
case "jar":
createFn = CreateJarFromPFlags
case "intoto":
createFn = CreateIntotoFromPFlags
case "rfc3161":
createFn = CreateRFC3161FromPFlags
case "alpine":
createFn = CreateAlpineFromPFlags
default:
t.Fatalf("type %v not implemented", tc.typeStr)
}
if _, err := createFn(); err != nil {
t.Errorf("unexpected result in '%v' building entry: %v", tc.caseDesc, err)
Expand Down
5 changes: 5 additions & 0 deletions cmd/rekor-cli/app/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ var uploadCmd = &cobra.Command{
if err != nil {
return nil, err
}
case "alpine":
entry, err = CreateAlpineFromPFlags()
if err != nil {
return nil, err
}
default:
return nil, errors.New("unknown type specified")
}
Expand Down
20 changes: 20 additions & 0 deletions cmd/rekor-cli/app/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ var verifyCmd = &cobra.Command{
if err != nil {
return nil, err
}
case "jar":
entry, err = CreateJarFromPFlags()
if err != nil {
return nil, err
}
case "intoto":
entry, err = CreateIntotoFromPFlags()
if err != nil {
return nil, err
}
case "rfc3161":
entry, err = CreateRFC3161FromPFlags()
if err != nil {
return nil, err
}
case "alpine":
entry, err = CreateAlpineFromPFlags()
if err != nil {
return nil, err
}
default:
return nil, errors.New("invalid type specified")
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/rekor-server/app/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/sigstore/rekor/pkg/generated/restapi"
"github.com/sigstore/rekor/pkg/generated/restapi/operations"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/types/alpine"
alpine_v001 "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1"
"github.com/sigstore/rekor/pkg/types/intoto"
intoto_v001 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1"
"github.com/sigstore/rekor/pkg/types/jar"
Expand Down Expand Up @@ -79,6 +81,7 @@ var serveCmd = &cobra.Command{
jar.KIND: jar_v001.APIVERSION,
intoto.KIND: intoto_v001.APIVERSION,
rfc3161.KIND: rfc3161_v001.APIVERSION,
alpine.KIND: alpine_v001.APIVERSION,
}

for k, v := range pluggableTypeMap {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ require (
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/ini.v1 v1.62.0
)
21 changes: 20 additions & 1 deletion openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ definitions:

rpm:
type: object
description: RPM object
description: RPM package
allOf:
- $ref: '#/definitions/ProposedEntry'
- properties:
Expand All @@ -328,6 +328,24 @@ definitions:
- apiVersion
- spec
additionalProperties: false

alpine:
type: object
description: Alpine package
allOf:
- $ref: '#/definitions/ProposedEntry'
- properties:
apiVersion:
type: string
pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
spec:
type: object
$ref: 'pkg/types/alpine/alpine_schema.json'
required:
- apiVersion
- spec
additionalProperties: false

intoto:
type: object
description: Intoto object
Expand All @@ -344,6 +362,7 @@ definitions:
- apiVersion
- spec
additionalProperties: false

jar:
type: object
description: Java Archive (JAR)
Expand Down
Loading

0 comments on commit 5fb05e1

Please sign in to comment.