Skip to content

Commit

Permalink
Added parsing code and refactored
Browse files Browse the repository at this point in the history
Signed-off-by: Maia Iyer <[email protected]>
  • Loading branch information
maia-iyer committed Oct 22, 2024
1 parent ce34b7c commit 5ab66d8
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"context"

"k8s.io/client-go/rest"
"k8s.io/client-go/dynamic"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -20,38 +18,6 @@ var gvrFederation = schema.GroupVersionResource{
Resource: "clusterfederatedtrustdomains",
}


// CRDManager defines the interface for managing CRDs
type CRDManager interface {
// TODO add List/Create/Update/Delete functions for Federation CRD
// ListClusterFederatedTrustDomain has the same signature as spire api
ListClusterFederatedTrustDomains(ListFederationRelationshipsRequest) (ListFederationRelationshipsResponse, error)
}

type SPIRECRDManager struct {
className string
kubeClient *dynamic.DynamicClient
}

// NewSPIRECRDManager initializes new SPIRECRDManager
func NewSPIRECRDManager(className string) (*SPIRECRDManager, error) {
// assume in-cluster
config, err := rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("error with in-cluster config: %v", err)
}
// create client
kubeClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("error creating kube client: %v", err)
}

return &SPIRECRDManager{
className: className,
kubeClient: kubeClient,
}, nil
}

type ListFederationRelationshipsRequest trustdomain.ListFederationRelationshipsRequest
type ListFederationRelationshipsResponse trustdomain.ListFederationRelationshipsResponse

Expand All @@ -64,12 +30,29 @@ func (s *SPIRECRDManager) ListClusterFederatedTrustDomains(inp ListFederationRel
}

for _, trustDomain := range trustDomainList.Items {
// parse TrustDomain into ClusterFederatedTrustDomain object
var clusterFederatedTrustDomain spirev1alpha1.ClusterFederatedTrustDomain
err := runtime.DefaultUnstructuredConverter.FromUnstructured(trustDomain.Object, &clusterFederatedTrustDomain)
if err != nil {
return ListFederationRelationshipsResponse{}, fmt.Errorf("error parsing trustdomain: %v", err)
}
fmt.Printf("Federation CRD Name: %s, Trustdomain: %s, Spec: %+v\n", clusterFederatedTrustDomain.Name, clusterFederatedTrustDomain.Spec.TrustDomain, clusterFederatedTrustDomain.Spec)

// parse ClusterFederatedTrustDomain object into Federation object
federation, err := spirev1alpha1.ParseClusterFederatedTrustDomainSpec(&clusterFederatedTrustDomain.Spec)
if err != nil {
return ListFederationRelationshipsResponse{}, fmt.Errorf("error parsing crd spec: %v", err)
}

fmt.Printf("Federation object: %+v\n", federation)

// parse Federation object into spire API object
spireAPIFederation, err := federationRelationshipToAPI(*federation)
if err != nil {
return ListFederationRelationshipsResponse{}, fmt.Errorf("error parsing into spire API object: %v", err)
}

fmt.Printf("SPIRE Federation object: %+v\n", spireAPIFederation)
}

return ListFederationRelationshipsResponse{}, nil
Expand Down
140 changes: 140 additions & 0 deletions pkg/agent/spirecrd/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package spirecrd

import (
"fmt"
"errors"
"time"
"crypto"
"crypto/x509"

"github.com/spiffe/spire-controller-manager/pkg/spireapi"
apitypes "github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/go-spiffe/v2/bundle/spiffebundle"
)

// TODO this file has much code duplicated from spire-controller-manager - would ideally import functions directly

// This code taken from https://github.com/spiffe/spire-controller-manager/blob/main/pkg/spireapi/types.go
// For parsing from Federation object to spire types
func federationRelationshipToAPI(in spireapi.FederationRelationship) (*apitypes.FederationRelationship, error) {
if in.TrustDomain.IsZero() {
return nil, errors.New("trust domain is missing")
}
if err := spireapi.ValidateBundleEndpointURL(in.BundleEndpointURL); err != nil {
return nil, fmt.Errorf("invalid bundle endpoint URL: %w", err)
}

out := &apitypes.FederationRelationship{
TrustDomain: in.TrustDomain.Name(),
BundleEndpointUrl: in.BundleEndpointURL,
}

if in.TrustDomainBundle != nil {
trustDomainBundle, err := bundleToAPI(in.TrustDomainBundle)
if err != nil {
return nil, fmt.Errorf("invalid trust domain bundle: %w", err)
}
out.TrustDomainBundle = trustDomainBundle
}

switch profile := in.BundleEndpointProfile.(type) {
case spireapi.HTTPSWebProfile:
out.BundleEndpointProfile = &apitypes.FederationRelationship_HttpsWeb{
HttpsWeb: &apitypes.HTTPSWebProfile{},
}
case spireapi.HTTPSSPIFFEProfile:
out.BundleEndpointProfile = &apitypes.FederationRelationship_HttpsSpiffe{
HttpsSpiffe: &apitypes.HTTPSSPIFFEProfile{
EndpointSpiffeId: profile.EndpointSPIFFEID.String(),
},
}
default:
return nil, fmt.Errorf("unrecognized bundle endpoint profile type %T", profile)
}
return out, nil
}

// This code taken from https://github.com/spiffe/spire-controller-manager/blob/main/pkg/spireapi/types.go
// For parsing from Bundle object to spire types
func bundleToAPI(in *spiffebundle.Bundle) (*apitypes.Bundle, error) {
trustDomain := in.TrustDomain().Name()
if trustDomain == "" {
return nil, errors.New("trust domain is missing")
}
x509Authorities, err := x509AuthoritiesToAPI(in.X509Authorities())
if err != nil {
return nil, err
}
jwtAuthorities, err := jwtAuthoritiesToAPI(in.JWTAuthorities())
if err != nil {
return nil, err
}
sequenceNumber, _ := in.SequenceNumber()
refreshHint, _ := in.RefreshHint()
return &apitypes.Bundle{
TrustDomain: trustDomain,
X509Authorities: x509Authorities,
JwtAuthorities: jwtAuthorities,
SequenceNumber: sequenceNumber,
RefreshHint: int64(refreshHint / time.Second),
}, nil
}

// This code taken from https://github.com/spiffe/spire-controller-manager/blob/main/pkg/spireapi/types.go
// For parsing from x509Authorities object to spire types
func x509AuthoritiesToAPI(ins []*x509.Certificate) ([]*apitypes.X509Certificate, error) {
var outs []*apitypes.X509Certificate
if ins != nil {
outs = make([]*apitypes.X509Certificate, 0, len(ins))
for _, in := range ins {
out, err := x509AuthorityToAPI(in)
if err != nil {
return nil, err
}
outs = append(outs, out)
}
}
return outs, nil
}

// This code taken from https://github.com/spiffe/spire-controller-manager/blob/main/pkg/spireapi/types.go
// For parsing from x509Authority object to spire types
func x509AuthorityToAPI(in *x509.Certificate) (*apitypes.X509Certificate, error) {
if len(in.Raw) == 0 {
return nil, errors.New("x509 certificate is missing raw data")
}
return &apitypes.X509Certificate{Asn1: in.Raw}, nil
}

// This code taken from https://github.com/spiffe/spire-controller-manager/blob/main/pkg/spireapi/types.go
// For parsing from JWT Authorities object to spire types
func jwtAuthoritiesToAPI(ins map[string]crypto.PublicKey) ([]*apitypes.JWTKey, error) {
var outs []*apitypes.JWTKey
if ins != nil {
outs = make([]*apitypes.JWTKey, 0, len(ins))
for keyID, publicKey := range ins {
out, err := jwtAuthorityToAPI(keyID, publicKey)
if err != nil {
return nil, err
}
outs = append(outs, out)
}
}
return outs, nil
}

// This code taken from https://github.com/spiffe/spire-controller-manager/blob/main/pkg/spireapi/types.go
// For parsing from JWT Authority object to spire types
func jwtAuthorityToAPI(keyID string, publicKey crypto.PublicKey) (*apitypes.JWTKey, error) {
if keyID == "" {
return nil, errors.New("key ID is missing")
}
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal public key: %w", err)
}
return &apitypes.JWTKey{
KeyId: keyID,
PublicKey: publicKeyBytes,
}, nil
}
39 changes: 39 additions & 0 deletions pkg/agent/spirecrd/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package spirecrd

import (
"fmt"
"k8s.io/client-go/rest"
"k8s.io/client-go/dynamic"
)

// CRDManager defines the interface for managing CRDs
type CRDManager interface {
// TODO add List/Create/Update/Delete functions for Federation CRD
// ListClusterFederatedTrustDomain has the same signature as spire api
ListClusterFederatedTrustDomains(ListFederationRelationshipsRequest) (ListFederationRelationshipsResponse, error)
}

type SPIRECRDManager struct {
className string
kubeClient *dynamic.DynamicClient
}

// NewSPIRECRDManager initializes new SPIRECRDManager
func NewSPIRECRDManager(className string) (*SPIRECRDManager, error) {
// assume in-cluster
config, err := rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("error with in-cluster config: %v", err)
}
// create client
kubeClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("error creating kube client: %v", err)
}

return &SPIRECRDManager{
className: className,
kubeClient: kubeClient,
}, nil
}

0 comments on commit 5ab66d8

Please sign in to comment.