Skip to content

Commit

Permalink
feat: add support for configuring signature verification pubkeys inli…
Browse files Browse the repository at this point in the history
…ne (#289)

## Issue
Resolves
#279

## Description
- Adds the `PublicKeys` field to the `SignatureVerifcation` struct.
- Defaults to first checking a secret for public keys, if no secret name
is provided the `PublicKeys` field is checked.
- Moves `BasicAuthsDirect` and `AllPubKeysDirect` from validatorctl to
the plugin.
- Fixes issue in `AllPublicKeysDirect` where we were using an array
instead of a map
  • Loading branch information
ahmad-ibra authored Sep 4, 2024
1 parent e4d1fa8 commit a9c17b5
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 5 deletions.
36 changes: 36 additions & 0 deletions api/v1alpha1/ocivalidator_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ type SignatureVerification struct {
// SecretName is the name of the Kubernetes Secret that exists in the same namespace as the OciValidator
// and that contains the trusted public keys used to sign artifacts in the OciRegistryRule.
SecretName string `json:"secretName" yaml:"secretName"`

// PublicKeys is a slice of public keys used to verify the signatures of artifacts in the OciRegistryRule.
PublicKeys []string `json:"publicKeys,omitempty" yaml:"publicKeys,omitempty"`
}

// OciValidatorStatus defines the observed state of OciValidator.
Expand Down Expand Up @@ -217,3 +220,36 @@ type OciValidatorList struct {
func init() {
SchemeBuilder.Register(&OciValidator{}, &OciValidatorList{})
}

// BasicAuthsDirect returns a map of basic authentication details for each rule when invoked directly.
func (s *OciValidatorSpec) BasicAuthsDirect() map[string][]string {
auths := make(map[string][]string)

for _, r := range s.OciRegistryRules {
if r.Auth.Basic != nil {
auths[r.Name()] = []string{r.Auth.Basic.Username, r.Auth.Basic.Password}
continue
}
}

return auths
}

// AllPubKeysDirect returns a map of public keys for each rule when invoked directly.
func (s *OciValidatorSpec) AllPubKeysDirect() map[string][][]byte {
pubKeysMap := make(map[string][][]byte)

for _, r := range s.OciRegistryRules {
if r.SignatureVerification.PublicKeys == nil || len(r.SignatureVerification.PublicKeys) == 0 {
continue
}

pubKeys := make([][]byte, len(r.SignatureVerification.PublicKeys))
for i, pk := range r.SignatureVerification.PublicKeys {
pubKeys[i] = []byte(pk)
}
pubKeysMap[r.Name()] = pubKeys
}

return pubKeysMap
}
7 changes: 6 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ spec:
enum:
- cosign
type: string
publicKeys:
description: PublicKeys is a slice of public keys used to
verify the signatures of artifacts in the OciRegistryRule.
items:
type: string
type: array
secretName:
description: |-
SecretName is the name of the Kubernetes Secret that exists in the same namespace as the OciValidator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ spec:
enum:
- cosign
type: string
publicKeys:
description: PublicKeys is a slice of public keys used to
verify the signatures of artifacts in the OciRegistryRule.
items:
type: string
type: array
secretName:
description: |-
SecretName is the name of the Kubernetes Secret that exists in the same namespace as the OciValidator
Expand Down
22 changes: 21 additions & 1 deletion config/samples/ocivalidator-public-registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,34 @@ spec:
- ref: "ahmadibraspectrocloud/kubebuilder-cron"

# public oci registry with signature verification enabled
- name: "public oci registry with signature verification enabled"
- name: "public oci registry with signature verification enabled via a pubkey secret"
host: "registry.hub.docker.com"
validationType: "fast"
artifacts:
- ref: "ahmadibraspectrocloud/kb-guestbook:signed"
signatureVerification:
secretName: "cosign-public-keys"

# public oci registry with signature verification enabled
- name: "public oci registry with signature verification enabled via inline pubkey"
host: "registry.hub.docker.com"
validationType: "fast"
artifacts:
- ref: "ahmadibraspectrocloud/kb-guestbook:signed"
signatureVerification:
secretName: ""
publicKeys:
- |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKPuCo9AmJCpqGWhefjbhkFcr1GA3
iNa765seE3jYC3MGUe5h52393Dhy7B5bXGsg6EfPpNYamlAEWjxCpHF3Lg==
-----END PUBLIC KEY-----
- |-
-----BEGIN PUBLIC KEY-----
AnotherPublicKey
-----END PUBLIC KEY-----
# public ecr registry artifact referenced by default "latest" tag
- name: "public ecr registry"
host: "public.ecr.aws"
Expand Down
24 changes: 22 additions & 2 deletions internal/controller/ocivalidator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,21 @@ func (r *OciValidatorReconciler) secretKeyAuth(req ctrl.Request, rule v1alpha1.O
return username, password, nil
}

// signaturePubKeys retrieves the public keys that are used for signature verification.
// If a secretName is provided in the SignatureVerification field, then the secret is fetched and the pub keys are retrieved from the secret.
// Otherwise, the public keys are retrieved from the inline PublicKeys field if provided.
func (r *OciValidatorReconciler) signaturePubKeys(req ctrl.Request, rule v1alpha1.OciRegistryRule) ([][]byte, error) {
if rule.SignatureVerification.SecretName == "" {
return nil, nil
if rule.SignatureVerification.SecretName != "" {
return r.signaturePubKeysSecret(req, rule)
}

if rule.SignatureVerification.PublicKeys != nil && len(rule.SignatureVerification.PublicKeys) > 0 {
return r.signaturePubKeysInline(rule.SignatureVerification.PublicKeys), nil
}

return nil, nil
}
func (r *OciValidatorReconciler) signaturePubKeysSecret(req ctrl.Request, rule v1alpha1.OciRegistryRule) ([][]byte, error) {
pubKeysSecret := &corev1.Secret{}
nn := ktypes.NamespacedName{Name: rule.SignatureVerification.SecretName, Namespace: req.Namespace}

Expand All @@ -207,3 +217,13 @@ func (r *OciValidatorReconciler) signaturePubKeys(req ctrl.Request, rule v1alpha

return pubKeys, nil
}

func (r *OciValidatorReconciler) signaturePubKeysInline(pKeys []string) [][]byte {
pubKeys := make([][]byte, len(pKeys))

for i, key := range pKeys {
pubKeys[i] = []byte(key)
}

return pubKeys
}
2 changes: 1 addition & 1 deletion pkg/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (s *RuleService) validateReference(ctx context.Context, ref name.Reference,
}

// verify image signature (optional)
if sv.SecretName != "" {
if sv.SecretName != "" || len(sv.PublicKeys) > 0 {
verifyDetails, verifyErrs := s.ociClient.VerifySignature(ctx, ref)
if len(verifyDetails) > 0 {
details = append(details, verifyDetails...)
Expand Down

0 comments on commit a9c17b5

Please sign in to comment.