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/authz: Use a faster JWT algorithm #670

Merged
merged 1 commit into from
Jul 11, 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
2 changes: 1 addition & 1 deletion pkg/controlplane/api/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const (
AccessTokenHeader = "x-access-token"

// JWTSignatureAlgorithm defines the signing algorithm for JWT tokens.
JWTSignatureAlgorithm = jwa.RS256
JWTSignatureAlgorithm = jwa.HS256
// ExportNameJWTClaim holds the name of the requested exported service.
ExportNameJWTClaim = "export_name"
// ExportNamespaceJWTClaim holds the namespace of the requested exported service.
Expand Down
37 changes: 15 additions & 22 deletions pkg/controlplane/authz/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,8 @@ type Manager struct {
ipToPod map[string]types.NamespacedName
podList map[types.NamespacedName]podInfo

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

logger *logrus.Entry
}
Expand Down Expand Up @@ -182,25 +181,19 @@ func (m *Manager) addSecret(secret *v1.Secret) error {
return nil
}

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

jwkSignKey, err := jwk.New(privateKey)
jwkKey, err := jwk.New(key)
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)
return fmt.Errorf("unable to create JWK key: %w", err)
}

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

return nil
}
Expand Down Expand Up @@ -322,15 +315,15 @@ func (m *Manager) parseAuthorizationHeader(token string) (string, error) {
m.logger.Debug("Parsing access token.")

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

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

parsedToken, err := jwt.ParseString(
token, jwt.WithVerify(cpapi.JWTSignatureAlgorithm, jwkVerifyKey), jwt.WithValidate(true))
token, jwt.WithVerify(cpapi.JWTSignatureAlgorithm, jwkkey), jwt.WithValidate(true))
if err != nil {
return "", err
}
Expand Down Expand Up @@ -407,15 +400,15 @@ func (m *Manager) authorizeIngress(
}

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

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

// sign access token
signed, err := jwt.Sign(token, cpapi.JWTSignatureAlgorithm, jwkSignKey)
signed, err := jwt.Sign(token, cpapi.JWTSignatureAlgorithm, jwkKey)
if err != nil {
return nil, fmt.Errorf("unable to sign access token: %w", err)
}
Expand Down Expand Up @@ -458,7 +451,7 @@ func (m *Manager) SetPeerCertificates(peerTLS *tls.ParsedCertData, _ *tls.RawCer
func (m *Manager) IsReady() bool {
m.jwksLock.RLock()
defer m.jwksLock.RUnlock()
return m.jwkSignKey != nil
return m.jwkKey != nil
}

// NewManager returns a new authorization manager.
Expand Down
35 changes: 6 additions & 29 deletions pkg/controlplane/control/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@
package control

import (
"bytes"
"context"

//nolint:gosec // G505: use of weak cryptographic primitive is fine for service name
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"reflect"
Expand Down Expand Up @@ -879,22 +875,13 @@ func (m *Manager) CreateJWKSSecret(ctx context.Context) error {
}

func generateJWKSecret() ([]byte, error) {
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
keyBytes := make([]byte, 32)
_, err := rand.Read(keyBytes)
if err != nil {
return nil, fmt.Errorf("unable to generate JWK key: %w", err)
}

// PEM encode private key
keyPEM := new(bytes.Buffer)
err = pem.Encode(keyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
})
if err != nil {
return nil, fmt.Errorf("cannot encode JWK key: %w", err)
}

return []byte(base64.StdEncoding.EncodeToString(keyPEM.Bytes())), nil
return []byte(base64.StdEncoding.EncodeToString(keyBytes)), nil
}

func checkServiceLabels(service *v1.Service, importName types.NamespacedName) bool {
Expand Down Expand Up @@ -1033,28 +1020,18 @@ func endpointSliceChanged(endpointSlice1, endpointSlice2 *discv1.EndpointSlice)
return false
}

func ParseJWKSSecret(secret *v1.Secret) (*rsa.PrivateKey, error) {
func ParseJWKSSecret(secret *v1.Secret) ([]byte, error) {
keyBase64, ok := secret.Data[JWKSecretKeyName]
if !ok {
return nil, fmt.Errorf("secret missing %s key", JWKSecretKeyName)
}

keyPEM, err := base64.StdEncoding.DecodeString(string(keyBase64))
keyBytes, err := base64.StdEncoding.DecodeString(string(keyBase64))
if err != nil {
return nil, fmt.Errorf("cannot base64 decode key")
}

keyBlock, _ := pem.Decode(keyPEM)
if keyBlock == nil {
return nil, fmt.Errorf("key is not in PEM format")
}

privateKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing private key: %w", err)
}

return privateKey, nil
return keyBytes, nil
}

// NewManager returns a new control manager.
Expand Down
Loading