Skip to content

Commit

Permalink
Merge pull request #670 from orozery/jwt-alg
Browse files Browse the repository at this point in the history
controlplane/authz: Use a faster JWT algorithm
  • Loading branch information
orozery authored Jul 11, 2024
2 parents 6ede2d2 + 0aae398 commit 4653671
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 52 deletions.
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

0 comments on commit 4653671

Please sign in to comment.