Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

controlplane: Store JWK keys in k8s secret #654

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions cmd/cl-controlplane/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ func (o *Options) Run() error {
namespace: {},
},
},
&v1.Secret{}: {
Namespaces: map[string]cache.Config{
namespace: {},
},
},
},
},
LeaderElection: true,
Expand All @@ -180,11 +185,7 @@ func (o *Options) Run() error {
controlplaneServerListenAddress := fmt.Sprintf("0.0.0.0:%d", api.ListenPort)
grpcServer := grpc.NewServer("controlplane-grpc", controlplaneCertData.ServerConfig())

authzManager, err := authz.NewManager(mgr.GetClient(), namespace)
if err != nil {
return fmt.Errorf("cannot create authorization manager: %w", err)
}

authzManager := authz.NewManager(mgr.GetClient(), namespace)
peerCertsWatcher.AddConsumer(authzManager)

err = authz.CreateControllers(authzManager, mgr)
Expand All @@ -196,6 +197,9 @@ func (o *Options) Run() error {

controlManager := control.NewManager(mgr.GetClient(), namespace)
peerCertsWatcher.AddConsumer(controlManager)
if err := controlManager.CreateJWKSSecret(context.Background()); err != nil {
return fmt.Errorf("cannot create JWKS secret: %w", err)
}

err = control.CreateControllers(controlManager, mgr)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions config/operator/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ rules:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- ""
resources:
Expand Down
3 changes: 3 additions & 0 deletions pkg/bootstrap/platform/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "update"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch", "create", "delete", "update"]
Expand Down
16 changes: 15 additions & 1 deletion pkg/controlplane/authz/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,21 @@ func CreateControllers(mgr *Manager, controllerManager ctrl.Manager) error {
AddHandler: func(ctx context.Context, object any) error {
return nil
},
DeleteHandler: func(ctx context.Context, name types.NamespacedName) error {
DeleteHandler: func(_ context.Context, _ types.NamespacedName) error {
return nil
},
})
if err != nil {
return err
}

err = controller.AddToManager(controllerManager, &controller.Spec{
Name: "authz.secret",
Object: &v1.Secret{},
AddHandler: func(_ context.Context, object any) error {
return mgr.addSecret(object.(*v1.Secret))
},
DeleteHandler: func(context.Context, types.NamespacedName) error {
return nil
},
})
Expand Down
76 changes: 51 additions & 25 deletions pkg/controlplane/authz/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ package authz

import (
"context"
"crypto/rand"
"crypto/rsa"
"fmt"
"sync"
"time"
Expand All @@ -33,6 +31,7 @@ import (
"github.com/clusterlink-net/clusterlink/pkg/apis/clusterlink.net/v1alpha1"
cpapi "github.com/clusterlink-net/clusterlink/pkg/controlplane/api"
"github.com/clusterlink-net/clusterlink/pkg/controlplane/authz/connectivitypdp"
"github.com/clusterlink-net/clusterlink/pkg/controlplane/control"
"github.com/clusterlink-net/clusterlink/pkg/controlplane/peer"
"github.com/clusterlink-net/clusterlink/pkg/util/tls"
)
Expand Down Expand Up @@ -109,6 +108,7 @@ type Manager struct {
ipToPod map[string]types.NamespacedName
podList map[types.NamespacedName]podInfo

jwksLock sync.RWMutex
jwkSignKey jwk.Key
jwkVerifyKey jwk.Key

Expand Down Expand Up @@ -176,6 +176,35 @@ func (m *Manager) addPod(pod *v1.Pod) {
}
}

// addSecret adds a new secret.
func (m *Manager) addSecret(secret *v1.Secret) error {
if secret.Namespace != m.namespace || secret.Name != control.JWKSecretName {
return nil
}

privateKey, err := control.ParseJWKSSecret(secret)
if err != nil {
return fmt.Errorf("cannot parse JWKS secret: %w", err)
}

jwkSignKey, err := jwk.New(privateKey)
if err != nil {
return fmt.Errorf("unable to create JWK signing key: %w", err)
}

jwkVerifyKey, err := jwk.New(privateKey.PublicKey)
if err != nil {
return fmt.Errorf("unable to create JWK verifing key: %w", err)
}

m.jwksLock.Lock()
defer m.jwksLock.Unlock()
m.jwkSignKey = jwkSignKey
m.jwkVerifyKey = jwkVerifyKey

return nil
}

// getPodInfoByIP returns the information about the Pod with the specified IP address.
func (m *Manager) getPodInfoByIP(ip string) *podInfo {
m.podLock.RLock()
Expand Down Expand Up @@ -292,8 +321,16 @@ func (m *Manager) authorizeEgress(ctx context.Context, req *egressAuthorizationR
func (m *Manager) parseAuthorizationHeader(token string) (string, error) {
m.logger.Debug("Parsing access token.")

m.jwksLock.RLock()
jwkVerifyKey := m.jwkVerifyKey
m.jwksLock.RUnlock()

if jwkVerifyKey == nil {
return "", fmt.Errorf("jwk verify key undefined")
}

parsedToken, err := jwt.ParseString(
token, jwt.WithVerify(cpapi.JWTSignatureAlgorithm, m.jwkVerifyKey), jwt.WithValidate(true))
token, jwt.WithVerify(cpapi.JWTSignatureAlgorithm, jwkVerifyKey), jwt.WithValidate(true))
if err != nil {
return "", err
}
Expand Down Expand Up @@ -369,8 +406,16 @@ func (m *Manager) authorizeIngress(
return nil, fmt.Errorf("unable to generate access token: %w", err)
}

m.jwksLock.RLock()
jwkSignKey := m.jwkSignKey
m.jwksLock.RUnlock()

if jwkSignKey == nil {
return nil, fmt.Errorf("jwk sign key undefined")
}

// sign access token
signed, err := jwt.Sign(token, cpapi.JWTSignatureAlgorithm, m.jwkSignKey)
signed, err := jwt.Sign(token, cpapi.JWTSignatureAlgorithm, jwkSignKey)
if err != nil {
return nil, fmt.Errorf("unable to sign access token: %w", err)
}
Expand Down Expand Up @@ -411,34 +456,15 @@ func (m *Manager) SetPeerCertificates(peerTLS *tls.ParsedCertData, _ *tls.RawCer
}

// NewManager returns a new authorization manager.
func NewManager(cl client.Client, namespace string) (*Manager, error) {
// generate RSA key-pair for JWT signing
// TODO: instead of generating, read from k8s secret
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, fmt.Errorf("unable to generate RSA keys: %w", err)
}

jwkSignKey, err := jwk.New(rsaKey)
if err != nil {
return nil, fmt.Errorf("unable to create JWK signing key: %w", err)
}

jwkVerifyKey, err := jwk.New(rsaKey.PublicKey)
if err != nil {
return nil, fmt.Errorf("unable to create JWK verifing key: %w", err)
}

func NewManager(cl client.Client, namespace string) *Manager {
return &Manager{
client: cl,
namespace: namespace,
connectivityPDP: connectivitypdp.NewPDP(),
loadBalancer: NewLoadBalancer(),
peerClient: make(map[string]*peer.Client),
jwkSignKey: jwkSignKey,
jwkVerifyKey: jwkVerifyKey,
ipToPod: make(map[string]types.NamespacedName),
podList: make(map[types.NamespacedName]podInfo),
logger: logrus.WithField("component", "controlplane.authz.manager"),
}, nil
}
}
18 changes: 15 additions & 3 deletions pkg/controlplane/control/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func CreateControllers(mgr *Manager, controllerManager ctrl.Manager) error {
Object: &v1alpha1.Export{},
NeedsLeaderElection: true,
AddHandler: func(ctx context.Context, object any) error {
return mgr.AddExport(ctx, object.(*v1alpha1.Export))
return mgr.addExport(ctx, object.(*v1alpha1.Export))
},
DeleteHandler: func(ctx context.Context, name types.NamespacedName) error {
return nil
Expand All @@ -79,9 +79,21 @@ func CreateControllers(mgr *Manager, controllerManager ctrl.Manager) error {
Object: &v1alpha1.Import{},
NeedsLeaderElection: true,
AddHandler: func(ctx context.Context, object any) error {
return mgr.AddImport(ctx, object.(*v1alpha1.Import))
return mgr.addImport(ctx, object.(*v1alpha1.Import))
},
DeleteHandler: mgr.DeleteImport,
DeleteHandler: mgr.deleteImport,
})
if err != nil {
return err
}

err = controller.AddToManager(controllerManager, &controller.Spec{
Name: "control.secret",
Object: &v1.Secret{},
AddHandler: func(ctx context.Context, object any) error {
return mgr.addSecret(ctx, object.(*v1.Secret))
},
DeleteHandler: mgr.deleteSecret,
})
if err != nil {
return err
Expand Down
Loading
Loading