Skip to content

Commit

Permalink
feat: public OCI client with proxy from env support (#216)
Browse files Browse the repository at this point in the history
## Issue
Addresses #65

## Description
- add public OCI client (that handles basic auth and proxy from env in
HTTP transport)
- fail validation results if they specify invalid public key secrets
- add kv pairs from auth secrets to the environment (for ECR auth
keychain)
- add support for InsecureSkipTLSVerify

Required by:
- validator-labs/validator#333

---------

Signed-off-by: Tyler Gillson <tyler.gillson@gmail.com>
  • Loading branch information
TylerGillson authored Jul 19, 2024
1 parent 73525b1 commit a0ab6d6
Show file tree
Hide file tree
Showing 15 changed files with 652 additions and 507 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Dockerfile.cross
.devspace
.idea
.vscode
!.vscode/launch.json
*.swp
*.swo
*~
Expand All @@ -29,3 +30,4 @@ Dockerfile.cross
.DS_Store
.env
tmp/*
*__debug_bin*
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
run:
timeout: 5m
timeout: 10m
allow-parallel-runners: true

issues:
Expand Down
34 changes: 34 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Devspace",
"type": "go",
"request": "attach",
"mode": "remote",
"port": 2343,
"host": "127.0.0.1",
"substitutePath": [
{
"from": "${workspaceFolder}",
"to": "/workspace",
},
],
"showLog": true,
// "trace": "verbose", // use for debugging problems with delve (breakpoints not working, etc.)
},
{
"name": "Test Integration",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/internal/controller",
"env": {
// "KUBECONFIG": "",
}
}
]
}
41 changes: 22 additions & 19 deletions api/v1alpha1/ocivalidator_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,48 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// OciValidatorSpec defines the desired state of OciValidator
// OciValidatorSpec defines the desired state of OciValidator.
type OciValidatorSpec struct {
// +kubebuilder:validation:MaxItems=5
// +kubebuilder:validation:XValidation:message="OciRegistryRules must have a unique RuleName",rule="self.all(e, size(self.filter(x, x.name == e.name)) == 1)"
OciRegistryRules []OciRegistryRule `json:"ociRegistryRules,omitempty" yaml:"ociRegistryRules,omitempty"`
}

// ResultCount returns the number of validation results expected for an OciValidatorSpec
// ResultCount returns the number of validation results expected for an OciValidatorSpec.
func (s OciValidatorSpec) ResultCount() int {
return len(s.OciRegistryRules)
}

// OciRegistryRule defines the validation rule for an OCI registry
// OciRegistryRule defines the validation rule for an OCI registry.
type OciRegistryRule struct {
// Name is the name of the rule
// Name is a unique name for the OciRegistryRule.
RuleName string `json:"name" yaml:"name"`

// Host is a reference to the host URL of an OCI compliant registry
// Host is the URI of an OCI registry.
Host string `json:"host" yaml:"host"`

// Artifacts is a slice of artifacts in the host registry that should be validated.
// Artifacts is a slice of artifacts in the OCI registry that should be validated.
Artifacts []Artifact `json:"artifacts,omitempty" yaml:"artifacts,omitempty"`

// Auth provides authentication information for the registry
// Auth provides authentication information for the registry.
Auth Auth `json:"auth,omitempty" yaml:"auth,omitempty"`

// CaCert is the base64 encoded CA Certificate
// InsecureSkipTLSVerify specifies whether to skip verification of the OCI registry's TLS certificate.
InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty" yaml:"insecureSkipTLSVerify,omitempty"`

// CaCert is the CA certificate of the OCI registry.
CaCert string `json:"caCert,omitempty" yaml:"caCert,omitempty"`

// SignatureVerification provides the option to verify the signature of the image
// SignatureVerification provides signature verification options for the artifacts.
SignatureVerification SignatureVerification `json:"signatureVerification,omitempty" yaml:"signatureVerification,omitempty"`
}

// Name returns the name of the OciRegistryRule
// Name returns the name of the OciRegistryRule.
func (r OciRegistryRule) Name() string {
return r.RuleName
}

// Artifact defines the artifact to be validated
// Artifact defines an OCI artifact to be validated.
type Artifact struct {
// Ref is the path to the artifact in the host registry that should be validated.
// An individual artifact can take any of the following forms:
Expand All @@ -76,32 +79,32 @@ type Artifact struct {
LayerValidation bool `json:"layerValidation,omitempty" yaml:"layerValidation,omitempty"`
}

// Auth defines the authentication information for the registry
// Auth defines the authentication information for the registry.
type Auth struct {
// SecretName is the name of the Kubernetes Secret that exists in the same namespace as the OciValidator
// and that contains the credentials used to authenticate to the OCI Registry
// and that contains the credentials used to authenticate to the OCI Registry.
SecretName string `json:"secretName" yaml:"secretName"`
}

// SignatureVerification defines the provider and secret name to verify the signatures of artifacts in an OCI registry
// SignatureVerification defines the provider and secret name to verify the signatures of artifacts in an OCI registry.
type SignatureVerification struct {
// Provider specifies the technology used to sign the OCI Artifact
// Provider specifies the technology used to sign the OCI Artifact.
// +kubebuilder:validation:Enum=cosign
// +kubebuilder:default:=cosign
Provider string `json:"provider" yaml:"provider"`

// 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
// and that contains the trusted public keys used to sign artifacts in the OciRegistryRule.
SecretName string `json:"secretName" yaml:"secretName"`
}

// OciValidatorStatus defines the observed state of OciValidator
// OciValidatorStatus defines the observed state of OciValidator.
type OciValidatorStatus struct{}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// OciValidator is the Schema for the ocivalidators API
// OciValidator is the Schema for the ocivalidators API.
type OciValidator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand All @@ -112,7 +115,7 @@ type OciValidator struct {

//+kubebuilder:object:root=true

// OciValidatorList contains a list of OciValidator
// OciValidatorList contains a list of OciValidator.
type OciValidatorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Expand Down
35 changes: 19 additions & 16 deletions config/crd/bases/validation.spectrocloud.labs_ocivalidators.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ spec:
- name: v1alpha1
schema:
openAPIV3Schema:
description: OciValidator is the Schema for the ocivalidators API
description: OciValidator is the Schema for the ocivalidators API.
properties:
apiVersion:
description: |-
Expand All @@ -37,18 +37,18 @@ spec:
metadata:
type: object
spec:
description: OciValidatorSpec defines the desired state of OciValidator
description: OciValidatorSpec defines the desired state of OciValidator.
properties:
ociRegistryRules:
items:
description: OciRegistryRule defines the validation rule for an
OCI registry
OCI registry.
properties:
artifacts:
description: Artifacts is a slice of artifacts in the host registry
description: Artifacts is a slice of artifacts in the OCI registry
that should be validated.
items:
description: Artifact defines the artifact to be validated
description: Artifact defines an OCI artifact to be validated.
properties:
layerValidation:
description: |-
Expand All @@ -74,41 +74,44 @@ spec:
type: array
auth:
description: Auth provides authentication information for the
registry
registry.
properties:
secretName:
description: |-
SecretName is the name of the Kubernetes Secret that exists in the same namespace as the OciValidator
and that contains the credentials used to authenticate to the OCI Registry
and that contains the credentials used to authenticate to the OCI Registry.
type: string
required:
- secretName
type: object
caCert:
description: CaCert is the base64 encoded CA Certificate
description: CaCert is the CA certificate of the OCI registry.
type: string
host:
description: Host is a reference to the host URL of an OCI compliant
registry
description: Host is the URI of an OCI registry.
type: string
insecureSkipTLSVerify:
description: InsecureSkipTLSVerify specifies whether to skip
verification of the OCI registry's TLS certificate.
type: boolean
name:
description: Name is the name of the rule
description: Name is a unique name for the OciRegistryRule.
type: string
signatureVerification:
description: SignatureVerification provides the option to verify
the signature of the image
description: SignatureVerification provides signature verification
options for the artifacts.
properties:
provider:
default: cosign
description: Provider specifies the technology used to sign
the OCI Artifact
the OCI Artifact.
enum:
- cosign
type: string
secretName:
description: |-
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
and that contains the trusted public keys used to sign artifacts in the OciRegistryRule.
type: string
required:
- provider
Expand All @@ -125,7 +128,7 @@ spec:
rule: self.all(e, size(self.filter(x, x.name == e.name)) == 1)
type: object
status:
description: OciValidatorStatus defines the observed state of OciValidator
description: OciValidatorStatus defines the observed state of OciValidator.
type: object
type: object
served: true
Expand Down
8 changes: 6 additions & 2 deletions config/samples/cosign-pubkeys.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@ apiVersion: v1
kind: Secret
metadata:
name: cosign-public-keys
data:
key1.pub: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFS1B1Q285QW1KQ3BxR1doZWZqYmhrRmNyMUdBMwppTmE3NjVzZUUzallDM01HVWU1aDUyMzkzRGh5N0I1YlhHc2c2RWZQcE5ZYW1sQUVXanhDcEhGM0xnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
stringData:
key1.pub: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKPuCo9AmJCpqGWhefjbhkFcr1GA3
iNa765seE3jYC3MGUe5h52393Dhy7B5bXGsg6EfPpNYamlAEWjxCpHF3Lg==
-----END PUBLIC KEY-----
21 changes: 20 additions & 1 deletion config/samples/ocivalidator-private-registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,26 @@ spec:
- ref: "spectro-packs/spectro-packs/archive/spectro-mgmt@sha256:ddbac6e7732bf90a4e674a01bf279ce27ea8691530b8d209e6fe77477e0fa406"
auth:
secretName: oci-airgap-credentials
caCert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGRENDQWZ5Z0F3SUJBZ0lSQU1IM2F5UWUvYUkwK1Y0OGE0QnlNYVF3RFFZSktvWklodmNOQVFFTEJRQXcKRkRFU01CQUdBMVVFQXhNSmFHRnlZbTl5TFdOaE1CNFhEVEl6TURneE9UQXdNRGMwTjFvWERUSTBNRGd4T0RBdwpNRGMwTjFvd0ZERVNNQkFHQTFVRUF4TUphR0Z5WW05eUxXTmhNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DCkFROEFNSUlCQ2dLQ0FRRUFtSTlaOUp4Zmg1SlZ0REMyS1U3WS85K0srSzFGanI1T3ZOUmk1RE1iR3NpZ2t6N2wKR054ZkgwSWdJRFdPZ1Q5L3YvNTJ5N1NZcnNrYWJYRVR1TEs3ajlaTXdXck9ZZm1mckcva1VMK3FlTThPYjZZdQorSUhNV3E4Z3VOdzJ2UW9yK214eW1JRUFTc3ZsTDBzd25vSXVQWk1GbFg5NEpWNUJtR3BtVjFrNmZaSVh2b05nClVUaHFoSE4vUFVIVDNibkxYaGlTdFNCZjBIMFR1U3BLMitEVXpvOFVRdlNvaStyV0k5SXRRRENZemtrWjg0bjIKeEp6WCtHSXlvYjNsdGdXU3ZSYmRURU9VK1pmYm0xVTRMV1U4YjdhVWRZSVdwM1EzSEVZK2F1WG1SbmlRSld2aQpQVUJrNTBUQnVPNFFJSWx0VGtHS3VTM0svR2s2SU0ra2FibUY0d0lEQVFBQm8yRXdYekFPQmdOVkhROEJBZjhFCkJBTUNBcVF3SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUYKTUFNQkFmOHdIUVlEVlIwT0JCWUVGRk4vYkhTS256ZE9IZ0k4d2ttNlpPbnV0eTRxTUEwR0NTcUdTSWIzRFFFQgpDd1VBQTRJQkFRQXRxNk9vRDI2NWF4Y2x3QVg3ZzdTdEtiZFNkeVNNcC9GbEJZOEJTS0QzdUxDWUtJZmRMdnJJClhKa0Z6MUFXa3hLb1dDbyt2RFl2cEUybE42WXAvakRQZUhZd1c3WG1HQTZJZDRVZ2FtdzV2NHhVZXg5Wis0V1IKbzdqNnV1NkVYK0xOdkQzREFSOFk4aEN3S1NDV3JNWURGbWV3Wmh6N05kY1VBcEp5M3phWTZWeHMvS3dlTGxicwpwbHh2TjlIWCtocVZobC8rWkFtbFZOOVZmZkhHblpsZm5tZW5Tb3RSbjJnR3Rmc0VrV3dhR3UvOUNPbTNQZlhTCjNTY0NGZTNNSjBZbjYvcG1iQkFVVnRtRjFUOTNsT2FYZ3VIek1pWEhJdyt4NUhadnhidkRQbmZ0Z0tnQWpWWU0KRmY0ODlRb28yalVuRVNmK2JRZFczcnpjMUFaMndwbmgKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
caCert: |-
-----BEGIN CERTIFICATE-----
MIIDFDCCAfygAwIBAgIRAMH3ayQe/aI0+V48a4ByMaQwDQYJKoZIhvcNAQELBQAw
FDESMBAGA1UEAxMJaGFyYm9yLWNhMB4XDTIzMDgxOTAwMDc0N1oXDTI0MDgxODAw
MDc0N1owFDESMBAGA1UEAxMJaGFyYm9yLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAmI9Z9Jxfh5JVtDC2KU7Y/9+K+K1Fjr5OvNRi5DMbGsigkz7l
GNxfH0IgIDWOgT9/v/52y7SYrskabXETuLK7j9ZMwWrOYfmfrG/kUL+qeM8Ob6Yu
+IHMWq8guNw2vQor+mxymIEASsvlL0swnoIuPZMFlX94JV5BmGpmV1k6fZIXvoNg
UThqhHN/PUHT3bnLXhiStSBf0H0TuSpK2+DUzo8UQvSoi+rWI9ItQDCYzkkZ84n2
xJzX+GIyob3ltgWSvRbdTEOU+Zfbm1U4LWU8b7aUdYIWp3Q3HEY+auXmRniQJWvi
PUBk50TBuO4QIIltTkGKuS3K/Gk6IM+kabmF4wIDAQABo2EwXzAOBgNVHQ8BAf8E
BAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFFN/bHSKnzdOHgI8wkm6ZOnuty4qMA0GCSqGSIb3DQEB
CwUAA4IBAQAtq6OoD265axclwAX7g7StKbdSdySMp/FlBY8BSKD3uLCYKIfdLvrI
XJkFz1AWkxKoWCo+vDYvpE2lN6Yp/jDPeHYwW7XmGA6Id4Ugamw5v4xUex9Z+4WR
o7j6uu6EX+LNvD3DAR8Y8hCwKSCWrMYDFmewZhz7NdcUApJy3zaY6Vxs/KweLlbs
plxvN9HX+hqVhl/+ZAmlVN9VffHGnZlfnmenSotRn2gGtfsEkWwaGu/9COm3PfXS
3ScCFe3MJ0Yn6/pmbBAUVtmF1T93lOaXguHzMiXHIw+x5HZvxbvDPnftgKgAjVYM
Ff489Qoo2jUnESf+bQdW3rzc1AZ2wpnh
-----END CERTIFICATE-----
# private ecr registry with no artifact specified
- name: "private ecr registry"
Expand Down
18 changes: 10 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ go 1.22.0
toolchain go1.22.5

require (
github.com/aws/aws-sdk-go-v2/config v1.27.26
github.com/aws/aws-sdk-go-v2/credentials v1.17.26
github.com/aws/aws-sdk-go-v2/service/ecr v1.30.3
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240708141356-efa334f881fc
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589
github.com/go-logr/logr v1.4.2
github.com/google/go-containerregistry v0.20.1
github.com/onsi/ginkgo/v2 v2.19.0
github.com/onsi/gomega v1.33.1
github.com/sigstore/cosign/v2 v2.2.4
github.com/sigstore/sigstore v1.8.7
github.com/stretchr/testify v1.9.0
github.com/validator-labs/validator v0.0.46
github.com/validator-labs/validator v0.0.47-0.20240718162457-48dbee6c9e36
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
k8s.io/client-go v0.30.2
k8s.io/klog/v2 v2.130.1
sigs.k8s.io/cluster-api v1.7.4
sigs.k8s.io/controller-runtime v0.18.4
)
Expand Down Expand Up @@ -50,23 +50,24 @@ require (
github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.26 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.26 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.25.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240708141356-efa334f881fc // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
Expand Down Expand Up @@ -176,7 +177,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
Expand All @@ -193,11 +194,12 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.30.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/release-utils v0.7.7 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

// replace github.com/validator-labs/validator v0.0.46 => ../validator
Loading

0 comments on commit a0ab6d6

Please sign in to comment.