Skip to content

Commit

Permalink
feat: redesign the public key ref (#325)
Browse files Browse the repository at this point in the history
* feat: redesign the public key ref

* rename secretref to more align with purpose

* named it back :D

* adding base64 encode and decode
  • Loading branch information
Skarlso authored Dec 5, 2023
1 parent cb04700 commit 69d819a
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 53 deletions.
36 changes: 28 additions & 8 deletions api/v1alpha1/componentversion_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
package v1alpha1

import (
"bytes"
"encoding/base64"
"fmt"
"io"
"time"

"github.com/fluxcd/pkg/apis/meta"
Expand Down Expand Up @@ -77,18 +80,35 @@ type Signature struct {
// signatures.
Name string `json:"name"`

// PublicKey provides a reference to a Kubernetes Secret that contains a public key
// PublicKey provides a reference to a Kubernetes Secret of contain a blob of a public key that
// which will be used to validate the named signature.
PublicKey SecretRef `json:"publicKey"`
PublicKey PublicKey `json:"publicKey"`
}

// PublicKey specifies access to a public key for verification.
type PublicKey struct {
// SecretRef is a reference to a Secret that contains a public key.
// +optional
SecretRef *v1.LocalObjectReference `json:"secretRef,omitempty"`

// PublicKeyBlob defines an inlined public key.
//+optional
PublicKeyBlob []byte `json:"publicKeyBlob,omitempty"`
// Value defines a PEM/base64 encoded public key value.
// +optional
Value []byte `json:"value,omitempty"`
}

// SecretRef specifies a reference to a Secret.
type SecretRef struct {
SecretRef v1.LocalObjectReference `json:"secretRef"`
func (p *PublicKey) DecodePublicValue() ([]byte, error) {
if len(p.Value) == 0 {
return nil, fmt.Errorf("key value not provided")
}

decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(p.Value))

content, err := io.ReadAll(decoder)
if err != nil {
return nil, fmt.Errorf("failed to decode public key pem: %w", err)
}

return content, nil
}

// Version specifies version information that can be used to resolve a Component Version.
Expand Down
48 changes: 26 additions & 22 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 9 additions & 11 deletions config/crd/bases/delivery.ocm.software_componentversions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,24 @@ spec:
type: string
publicKey:
description: PublicKey provides a reference to a Kubernetes
Secret that contains a public key which will be used to validate
the named signature.
Secret of contain a blob of a public key that which will be
used to validate the named signature.
properties:
secretRef:
description: LocalObjectReference contains enough information
to let you locate the referenced object inside the same
namespace.
description: SecretRef is a reference to a Secret that contains
a public key.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
required:
- secretRef
value:
description: Value defines a PEM/base64 encoded public key
value.
format: byte
type: string
type: object
publicKeyBlob:
description: PublicKeyBlob defines an inlined public key.
format: byte
type: string
required:
- name
- publicKey
Expand Down
10 changes: 5 additions & 5 deletions pkg/fakes/ocm.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (c *Context) constructComponentDescriptor(
return compd
}

// ************** Mock Attribute Values **************
// ************** Mock Attribute Value **************

type mockAttribute struct {
datacontext.Attributes
Expand All @@ -243,7 +243,7 @@ func (m *mockAttribute) GetAttribute(name string, def ...any) any { //nolint:rev
return nil
}

// ************** Mock Repository Values and Functions **************
// ************** Mock Repository Value and Functions **************

type mockRepository struct {
ocm.Repository
Expand Down Expand Up @@ -299,7 +299,7 @@ func (m *mockRepository) GetVersion() string {

var _ ocm.Repository = &mockRepository{}

// ************** Mock Component Access Values and Functions **************
// ************** Mock Component Access Value and Functions **************

type mockComponentAccess struct {
ocm.ComponentAccess
Expand All @@ -324,7 +324,7 @@ func (m *mockComponentAccess) ListVersions() ([]string, error) {

var _ ocm.ComponentAccess = &mockComponentAccess{}

// ************** Mock Component Version Access Values and Functions **************
// ************** Mock Component Version Access Value and Functions **************

var _ ocm.ComponentVersionAccess = &Component{}

Expand Down Expand Up @@ -377,7 +377,7 @@ func (c *Component) GetVersion() string {
return c.Version
}

// ************** Mock Resource Access Values and Functions **************
// ************** Mock Resource Access Value and Functions **************

var _ ocm.ResourceAccess = &Resource{}

Expand Down
9 changes: 7 additions & 2 deletions pkg/ocm/ocm.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,14 @@ func (c *Client) VerifyComponent(
cert []byte
err error
)
if signature.PublicKeyBlob != nil {
cert = signature.PublicKeyBlob

if signature.PublicKey.Value != nil {
cert, err = signature.PublicKey.DecodePublicValue()
} else {
if signature.PublicKey.SecretRef == nil {
return false, fmt.Errorf("kubernetes secret reference not provided")
}

cert, err = c.getPublicKey(
ctx,
obj.Namespace,
Expand Down
120 changes: 116 additions & 4 deletions pkg/ocm/ocm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package ocm
import (
"bytes"
"context"
"encoding/base64"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -572,8 +573,8 @@ func TestClient_VerifyComponent(t *testing.T) {
Verify: []v1alpha1.Signature{
{
Name: Signature,
PublicKey: v1alpha1.SecretRef{
SecretRef: corev1.LocalObjectReference{
PublicKey: v1alpha1.PublicKey{
SecretRef: &corev1.LocalObjectReference{
Name: secretName,
},
},
Expand All @@ -587,6 +588,117 @@ func TestClient_VerifyComponent(t *testing.T) {
assert.True(t, verified, "verified should have been true, but it did not")
}

func TestClient_VerifyComponentWithValueKey(t *testing.T) {
publicKey1, err := os.ReadFile(filepath.Join("testdata", "public1_key.pem"))
require.NoError(t, err)
privateKey, err := os.ReadFile(filepath.Join("testdata", "private_key.pem"))
require.NoError(t, err)

fakeKubeClient := env.FakeKubeClient()
cache := &fakes.FakeCache{}
ocmClient := NewClient(fakeKubeClient, cache)
component := "github.com/skarlso/ocm-demo-index"

octx := fakeocm.NewFakeOCMContext()

c := &fakeocm.Component{
Name: component,
Version: "v0.0.1",
Sign: &fakeocm.Sign{
Name: Signature,
PrivKey: privateKey,
PubKey: publicKey1,
Digest: "3d879ecdea45acb7f8d85b89fd653288d84af4476eac4141822142ec59c13745",
},
}
require.NoError(t, octx.AddComponent(c))
//var buffer []byte
buf := bytes.Buffer{}
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
_, err = encoder.Write(publicKey1)
require.NoError(t, encoder.Close())
require.NoError(t, err)
cv := &v1alpha1.ComponentVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "default",
},
Spec: v1alpha1.ComponentVersionSpec{
Component: component,
Version: v1alpha1.Version{
Semver: "v0.0.1",
},
Repository: v1alpha1.Repository{
URL: "localhost",
},
Verify: []v1alpha1.Signature{
{
Name: Signature,
PublicKey: v1alpha1.PublicKey{
Value: buf.Bytes(),
},
},
},
},
}

verified, err := ocmClient.VerifyComponent(context.Background(), octx, cv, "v0.0.1")
require.NoError(t, err)
assert.True(t, verified, "verified should have been true, but it did not")
}

func TestClient_VerifyComponentWithValueKeyFailsIfValueIsEmpty(t *testing.T) {
publicKey1, err := os.ReadFile(filepath.Join("testdata", "public1_key.pem"))
require.NoError(t, err)
privateKey, err := os.ReadFile(filepath.Join("testdata", "private_key.pem"))
require.NoError(t, err)

fakeKubeClient := env.FakeKubeClient()
cache := &fakes.FakeCache{}
ocmClient := NewClient(fakeKubeClient, cache)
component := "github.com/skarlso/ocm-demo-index"

octx := fakeocm.NewFakeOCMContext()

c := &fakeocm.Component{
Name: component,
Version: "v0.0.1",
Sign: &fakeocm.Sign{
Name: Signature,
PrivKey: privateKey,
PubKey: publicKey1,
Digest: "3d879ecdea45acb7f8d85b89fd653288d84af4476eac4141822142ec59c13745",
},
}
require.NoError(t, octx.AddComponent(c))
cv := &v1alpha1.ComponentVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "default",
},
Spec: v1alpha1.ComponentVersionSpec{
Component: component,
Version: v1alpha1.Version{
Semver: "v0.0.1",
},
Repository: v1alpha1.Repository{
URL: "localhost",
},
Verify: []v1alpha1.Signature{
{
Name: Signature,
PublicKey: v1alpha1.PublicKey{
Value: []byte{},
},
},
},
},
}

_, err = ocmClient.VerifyComponent(context.Background(), octx, cv, "v0.0.1")
assert.EqualError(t, err, "failed to get public key for verification: key value not provided")
}

func TestClient_VerifyComponentDifferentPublicKey(t *testing.T) {
publicKey2, err := os.ReadFile(filepath.Join("testdata", "public2_key.pem"))
require.NoError(t, err)
Expand Down Expand Up @@ -638,8 +750,8 @@ func TestClient_VerifyComponentDifferentPublicKey(t *testing.T) {
Verify: []v1alpha1.Signature{
{
Name: Signature,
PublicKey: v1alpha1.SecretRef{
SecretRef: corev1.LocalObjectReference{
PublicKey: v1alpha1.PublicKey{
SecretRef: &corev1.LocalObjectReference{
Name: secretName,
},
},
Expand Down
2 changes: 1 addition & 1 deletion pkg/snapshot/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func buildTar(artifactPath, sourceDir string) error {
}
// The name needs to be modified to maintain directory structure
// as tar.FileInfoHeader only has access to the base name of the file.
// Ref: https://golang.org/src/archive/tar/common.go?#L626
// SecretRef: https://golang.org/src/archive/tar/common.go?#L626
relFilePath := p
if filepath.IsAbs(sourceDir) {
relFilePath, err = filepath.Rel(sourceDir, p)
Expand Down

0 comments on commit 69d819a

Please sign in to comment.