Skip to content

Commit

Permalink
Validation around licenseKey field for extended kubernetes version su…
Browse files Browse the repository at this point in the history
…pport
  • Loading branch information
panktishah26 committed Feb 4, 2025
1 parent 1b680d9 commit 987ef11
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 87 deletions.
38 changes: 11 additions & 27 deletions controllers/cluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ func TestClusterReconcilerReconcileSelfManagedCluster(t *testing.T) {
Name: "my-management-cluster",
},
Spec: anywherev1.ClusterSpec{
EksaVersion: &version,
KubernetesVersion: anywherev1.Kube132,
EksaVersion: &version,
ClusterNetwork: anywherev1.ClusterNetwork{
CNIConfig: &anywherev1.CNIConfig{
Cilium: &anywherev1.CiliumConfig{},
Expand Down Expand Up @@ -1082,6 +1083,7 @@ func TestClusterReconcilerReconcileSelfManagedClusterRegAuthFailNoSecret(t *test
Name: "my-management-cluster",
},
Spec: anywherev1.ClusterSpec{
KubernetesVersion: anywherev1.Kube132,
ClusterNetwork: anywherev1.ClusterNetwork{
CNIConfig: &anywherev1.CNIConfig{
Cilium: &anywherev1.CiliumConfig{},
Expand Down Expand Up @@ -1252,7 +1254,7 @@ func TestClusterReconcilerSkipDontInstallPackagesOnSelfManaged(t *testing.T) {
Namespace: "default",
},
Spec: anywherev1.ClusterSpec{
KubernetesVersion: "v1.25",
KubernetesVersion: anywherev1.Kube132,
ClusterNetwork: anywherev1.ClusterNetwork{
CNIConfig: &anywherev1.CNIConfig{
Cilium: &anywherev1.CiliumConfig{},
Expand Down Expand Up @@ -1402,7 +1404,7 @@ func TestClusterReconcilerPackagesInstall(s *testing.T) {
Namespace: "default",
},
Spec: anywherev1.ClusterSpec{
KubernetesVersion: "v1.25",
KubernetesVersion: anywherev1.Kube132,
ClusterNetwork: anywherev1.ClusterNetwork{
CNIConfig: &anywherev1.CNIConfig{
Cilium: &anywherev1.CiliumConfig{},
Expand Down Expand Up @@ -1667,17 +1669,17 @@ func createBundle() *releasev1.Bundles {
Name: "bundles-1",
Namespace: "default",
Annotations: map[string]string{
constants.SignatureAnnotation: "MEUCIQDbVAB+yy+pdCOFet/vWMoHQA2FYiiQtq1zltBRRhRo2QIgGQopCHraD/HpvpSh4Q7rVdesXeVriJv2ucEnoidoZlg=",
constants.SignatureAnnotation: "MEQCIG8DZfnqQtx1fF5x2assfSUEvuJ9BqaCN8jaoBHxKU8SAiBwR2B/T2BC3nzmnT2uEvwyemOy+A7V/K+PkGuKGX0E1Q==",
},
},
Spec: releasev1.BundlesSpec{
VersionsBundles: []releasev1.VersionsBundle{
{
KubeVersion: "1.30",
KubeVersion: "1.32",
EksD: releasev1.EksDRelease{
Name: "test",
EksDReleaseUrl: "testdata/release.yaml",
KubeVersion: "1.30",
KubeVersion: "1.32",
},
CertManager: releasev1.CertManagerBundle{},
ClusterAPI: releasev1.CoreClusterAPI{},
Expand All @@ -1693,6 +1695,7 @@ func createBundle() *releasev1.Bundles {
ExternalEtcdBootstrap: releasev1.EtcdadmBootstrapBundle{},
ExternalEtcdController: releasev1.EtcdadmControllerBundle{},
Tinkerbell: releasev1.TinkerbellBundle{},
EndOfStandardSupport: "2030-06-30",
},
},
},
Expand Down Expand Up @@ -1745,7 +1748,7 @@ func vsphereCluster() *anywherev1.Cluster {
Kind: "VSphereDatacenterConfig",
Name: "datacenter",
},
KubernetesVersion: "1.20",
KubernetesVersion: anywherev1.Kube132,
ControlPlaneConfiguration: anywherev1.ControlPlaneConfiguration{
Count: 1,
Endpoint: &anywherev1.Endpoint{
Expand Down Expand Up @@ -1864,26 +1867,7 @@ func baseTestVsphereCluster() (*cluster.Config, *releasev1.Bundles) {
config.AWSIAMConfigs[awsIAM.Name] = awsIAM
config.OIDCConfigs[oidc.Name] = oidc

bundles := &releasev1.Bundles{
ObjectMeta: metav1.ObjectMeta{
Name: "my-bundles-ref",
Namespace: config.Cluster.Namespace,
Annotations: map[string]string{
constants.SignatureAnnotation: "MEYCIQDA40Bizd/0mdCwRCIKq10gjLdJMT0s0y57RPW/zOyWZwIhALPOFS+NZZ7QCwI7wiC1TiArMHMq4TbzIJcx85H/zjU4",
},
},
Spec: releasev1.BundlesSpec{
VersionsBundles: []releasev1.VersionsBundle{
{
KubeVersion: "v1.30",
PackageController: releasev1.PackageBundle{
HelmChart: releasev1.Image{},
},
},
},
},
}

bundles := createBundle()
config.Cluster.Spec.BundlesRef = &anywherev1.BundlesRef{
Name: bundles.Name,
Namespace: bundles.Namespace,
Expand Down
35 changes: 8 additions & 27 deletions controllers/cluster_controller_test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/aws/eks-anywhere/pkg/controller"
"github.com/aws/eks-anywhere/pkg/controller/clientutil"
"github.com/aws/eks-anywhere/pkg/controller/clusters"
"github.com/aws/eks-anywhere/release/api/v1alpha1"
)

func TestClusterReconcilerEnsureOwnerReferences(t *testing.T) {
Expand All @@ -54,7 +53,7 @@ func TestClusterReconcilerEnsureOwnerReferences(t *testing.T) {
Namespace: "default",
},
Spec: anywherev1.ClusterSpec{
KubernetesVersion: "v1.25",
KubernetesVersion: anywherev1.Kube132,
EksaVersion: &version,
},
Status: anywherev1.ClusterStatus{
Expand Down Expand Up @@ -244,8 +243,8 @@ func TestClusterReconcilerSetBundlesRef(t *testing.T) {
},
Spec: anywherev1.ClusterSpec{
BundlesRef: &anywherev1.BundlesRef{
Name: "my-bundles-ref",
Namespace: "my-namespace",
Name: "bundles-1",
Namespace: "default",
},
},
Status: anywherev1.ClusterStatus{
Expand All @@ -258,10 +257,10 @@ func TestClusterReconcilerSetBundlesRef(t *testing.T) {
Name: "my-cluster",
},
Spec: anywherev1.ClusterSpec{
KubernetesVersion: "v1.25",
KubernetesVersion: anywherev1.Kube132,
BundlesRef: &anywherev1.BundlesRef{
Name: "my-bundles-ref",
Namespace: "my-namespace",
Name: "bundles-1",
Namespace: "default",
},
},
Status: anywherev1.ClusterStatus{
Expand All @@ -275,25 +274,7 @@ func TestClusterReconcilerSetBundlesRef(t *testing.T) {
Namespace: constants.EksaSystemNamespace,
},
}
bundles := &v1alpha1.Bundles{
ObjectMeta: metav1.ObjectMeta{
Name: "my-bundles-ref",
Namespace: cluster.Spec.BundlesRef.Namespace,
Annotations: map[string]string{
constants.SignatureAnnotation: "MEYCIQDA40Bizd/0mdCwRCIKq10gjLdJMT0s0y57RPW/zOyWZwIhALPOFS+NZZ7QCwI7wiC1TiArMHMq4TbzIJcx85H/zjU4",
},
},
Spec: v1alpha1.BundlesSpec{
VersionsBundles: []v1alpha1.VersionsBundle{
{
KubeVersion: "v1.30",
PackageController: v1alpha1.PackageBundle{
HelmChart: v1alpha1.Image{},
},
},
},
},
}
bundles := createBundle()

objs := []runtime.Object{cluster, managementCluster, secret, bundles}
cb := fake.NewClientBuilder()
Expand Down Expand Up @@ -347,7 +328,7 @@ func TestClusterReconcilerSetDefaultEksaVersion(t *testing.T) {
Namespace: "default",
},
Spec: anywherev1.ClusterSpec{
KubernetesVersion: "v1.25",
KubernetesVersion: anywherev1.Kube132,
},
Status: anywherev1.ClusterStatus{
ReconciledGeneration: 1,
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ require (
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gobuffalo/flect v1.0.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,10 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down
2 changes: 2 additions & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ const (
Excludes = "LnNwZWMudmVyc2lvbnNCdW5kbGVzW10uYm9vdHN0cmFwCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmJvdHRsZXJvY2tldEhvc3RDb250YWluZXJzCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmNlcnRNYW5hZ2VyCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmNpbGl1bQouc3BlYy52ZXJzaW9uc0J1bmRsZXNbXS5jbG91ZFN0YWNrCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmNsdXN0ZXJBUEkKLnNwZWMudmVyc2lvbnNCdW5kbGVzW10uY29udHJvbFBsYW5lCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmRvY2tlcgouc3BlYy52ZXJzaW9uc0J1bmRsZXNbXS5la3NhCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmV0Y2RhZG1Cb290c3RyYXAKLnNwZWMudmVyc2lvbnNCdW5kbGVzW10uZXRjZGFkbUNvbnRyb2xsZXIKLnNwZWMudmVyc2lvbnNCdW5kbGVzW10uZmx1eAouc3BlYy52ZXJzaW9uc0J1bmRsZXNbXS5oYXByb3h5Ci5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLmtpbmRuZXRkCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLm51dGFuaXgKLnNwZWMudmVyc2lvbnNCdW5kbGVzW10ucGFja2FnZUNvbnRyb2xsZXIKLnNwZWMudmVyc2lvbnNCdW5kbGVzW10uc25vdwouc3BlYy52ZXJzaW9uc0J1bmRsZXNbXS50aW5rZXJiZWxsCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLnVwZ3JhZGVyCi5zcGVjLnZlcnNpb25zQnVuZGxlc1tdLnZTcGhlcmU="
// KMSPublicKey to verify bundle signature.
KMSPublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFZU/Z6VVMU9HioT7rGkPdJg3frC2xyQZhWFIrz5HeZEfeQ2nAdnJMLrs2Qr3V9xVrJrHA54wnIHDoPGbEhojqg=="
// LincesePublicKey is to verify the licenseKey field.
LincesePublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE96Xb67YUq+at8gFOioYlf1kxOIPio7i3Y8sFrG3a3sn/MzqQmTO9K82psqOuN+E4NdE8VajOtbyfcLo+Ojax/w=="
)

type Operation int
Expand Down
57 changes: 45 additions & 12 deletions pkg/signature/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"strings"
"text/template"

"github.com/golang-jwt/jwt/v5"
"github.com/itchyny/gojq"
"sigs.k8s.io/yaml"

Expand All @@ -50,19 +51,9 @@ func ValidateSignature(bundle *anywherev1alpha1.Bundles, pubKey string) (valid b
return false, fmt.Errorf("signature in metadata isn't base64 encoded: %w", err)
}

pubdecoded, err := base64.StdEncoding.DecodeString(pubKey)
pubkey, err := parsePublicKey(pubKey)
if err != nil {
return false, fmt.Errorf("decoding the public key as string: %w", err)
}

pubparsed, err := x509.ParsePKIXPublicKey(pubdecoded)
if err != nil {
return false, fmt.Errorf("parsing the public key (not PKIX): %w", err)
}

pubkey, ok := pubparsed.(*ecdsa.PublicKey)
if !ok {
return false, fmt.Errorf("parsing the public key (not ECDSA): %T", pubparsed)
return false, err
}

return ecdsa.VerifyASN1(pubkey, digest[:], sig), nil
Expand Down Expand Up @@ -162,3 +153,45 @@ func filterExcludes(jsonBytes []byte) ([]byte, error) {
}
return filtered, nil
}

func parsePublicKey(key string) (*ecdsa.PublicKey, error) {
pubdecoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return nil, fmt.Errorf("decoding the public key as string: %w", err)
}

pubparsed, err := x509.ParsePKIXPublicKey(pubdecoded)
if err != nil {
return nil, fmt.Errorf("parsing the public key (not PKIX): %w", err)
}

pubkey, ok := pubparsed.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("parsing the public key (not ECDSA): %T", pubparsed)
}

Check warning on line 171 in pkg/signature/manifest.go

View check run for this annotation

Codecov / codecov/patch

pkg/signature/manifest.go#L170-L171

Added lines #L170 - L171 were not covered by tests
return pubkey, nil
}

// ParseLicense parses licenseKey jwt token using the public key and returns token fields.
func ParseLicense(licenseToken string, key string) (*jwt.Token, error) {
tokenKey, err := parsePublicKey(key)
if err != nil {
return nil, err
}

Check warning on line 180 in pkg/signature/manifest.go

View check run for this annotation

Codecov / codecov/patch

pkg/signature/manifest.go#L179-L180

Added lines #L179 - L180 were not covered by tests

token, err := jwt.Parse(licenseToken, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
}
return tokenKey, nil

Check warning on line 186 in pkg/signature/manifest.go

View check run for this annotation

Codecov / codecov/patch

pkg/signature/manifest.go#L186

Added line #L186 was not covered by tests
})
if err != nil {
return nil, fmt.Errorf("parsing license token: %w", err)
}

if !token.Valid {
return nil, errors.New("licenseKey is not valid")
}

Check warning on line 194 in pkg/signature/manifest.go

View check run for this annotation

Codecov / codecov/patch

pkg/signature/manifest.go#L192-L194

Added lines #L192 - L194 were not covered by tests

return token, nil

Check warning on line 196 in pkg/signature/manifest.go

View check run for this annotation

Codecov / codecov/patch

pkg/signature/manifest.go#L196

Added line #L196 was not covered by tests
}
32 changes: 31 additions & 1 deletion pkg/signature/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ func TestValidateSignature(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(_ *testing.T) {
valid, err := ValidateSignature(tc.bundle, tc.publicKey)
fmt.Println(err)
if err != nil && !strings.Contains(err.Error(), tc.wantErr.Error()) {
t.Errorf("%v got = %v, \nwant %v", tc.name, err, tc.wantErr)
}
Expand Down Expand Up @@ -336,3 +335,34 @@ func TestFilterExcludes(t *testing.T) {
})
}
}

func TestParseLicense(t *testing.T) {
tests := []struct {
name string
licenseKey string
key string
wantErr error
}{
{
name: "invalid signing method",
licenseKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbmRWYWxpZGl0eSI6IjIwMjUtMDItMDNUMTI6MDA6MDBaIn0.XdQhB8DBE2m5Ufw6R4DCadp8D6ZLNkdfbCwOWMgSxB4",
key: constants.LincesePublicKey,
wantErr: fmt.Errorf("unexpected signing method"),
},
{
name: "malformed token",
licenseKey: "invalid.token.string",
key: constants.LincesePublicKey,
wantErr: fmt.Errorf("parsing license token"),
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := ParseLicense(tc.licenseKey, tc.key)
if err != nil && !strings.Contains(err.Error(), tc.wantErr.Error()) {
t.Errorf("%v got = %v, \nwant %v", tc.name, err, tc.wantErr)
}
})
}
}
Loading

0 comments on commit 987ef11

Please sign in to comment.