Skip to content

Commit

Permalink
Merge pull request #3064 from redpanda-data/allow-trial-licenses
Browse files Browse the repository at this point in the history
Allow trial licenses to be considered valid
  • Loading branch information
Jeffail authored Dec 6, 2024
2 parents 65763d9 + cac2c15 commit ad959a9
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 27 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ Changelog

All notable changes to this project will be documented in this file.

## 4.44.0 - TBD

### Fixed

- Trial Redpanda Enterprise licenses are now considered valid. (@Jeffail)

## 4.43.0 - 2024-12-05

### Changed
Expand Down
4 changes: 3 additions & 1 deletion internal/license/redpanda_license.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ type RedpandaLicense struct {

// AllowsEnterpriseFeatures returns true if license type allows enterprise features.
func (r *RedpandaLicense) AllowsEnterpriseFeatures() bool {
return r.Type == 1
// Right now any enterprise or trial license that was valid when we started
// is considered valid here.
return r.Type == 1 || r.Type == 0
}

func typeDisplayName(t int) string {
Expand Down
39 changes: 16 additions & 23 deletions internal/license/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ var licensePublicKeyPem []byte

const defaultLicenseFilepath = "/etc/redpanda/redpanda.license"

var openSourceLicense = RedpandaLicense{
Type: -1,
Expiry: time.Now().Add(time.Hour * 24 * 365 * 10).Unix(),
}

// Service is the license service.
type Service struct {
logger *service.Logger
Expand Down Expand Up @@ -101,30 +106,18 @@ func InjectTestService(res *service.Resources) {
func (s *Service) readAndValidateLicense() (RedpandaLicense, error) {
licenseBytes, err := s.readLicense()
if err != nil {
return RedpandaLicense{}, err
return openSourceLicense, err
}

var license RedpandaLicense
license := openSourceLicense
if len(licenseBytes) > 0 {
if license, err = s.validateLicense(licenseBytes); err != nil {
return RedpandaLicense{}, fmt.Errorf("failed to validate license: %w", err)
}
if license.Type == 0 {
// If the license is a trial then we reject it because connect does
// not support trials.
return RedpandaLicense{}, errors.New("trial license detected, Redpanda Connect does not support enterprise license trials")
}
} else {
// An open source license is the final fall back.
year := time.Hour * 24 * 365
license = RedpandaLicense{
Expiry: time.Now().Add(10 * year).Unix(),
Type: -1,
return openSourceLicense, fmt.Errorf("failed to validate license: %w", err)
}
}

if err := license.CheckExpiry(); err != nil {
return RedpandaLicense{}, err
return openSourceLicense, err
}

s.logger.With(
Expand Down Expand Up @@ -175,11 +168,11 @@ func (s *Service) validateLicense(license []byte) (RedpandaLicense, error) {
block, _ := pem.Decode(publicKeyBytes)
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return RedpandaLicense{}, fmt.Errorf("failed to parse public key: %w", err)
return openSourceLicense, fmt.Errorf("failed to parse public key: %w", err)
}
publicKeyRSA, ok := publicKey.(*rsa.PublicKey)
if !ok {
return RedpandaLicense{}, errors.New("failed to parse public key, expected dateFormat is not RSA")
return openSourceLicense, errors.New("failed to parse public key, expected dateFormat is not RSA")
}

// Trim Whitespace and Linebreaks for input license
Expand All @@ -188,32 +181,32 @@ func (s *Service) validateLicense(license []byte) (RedpandaLicense, error) {
// 2. Split license contents by delimiter
splitParts := bytes.Split(license, []byte("."))
if len(splitParts) != 2 {
return RedpandaLicense{}, errors.New("failed to split license contents by delimiter")
return openSourceLicense, errors.New("failed to split license contents by delimiter")
}

licenseDataEncoded := splitParts[0]
signatureEncoded := splitParts[1]

licenseData, err := base64.StdEncoding.DecodeString(string(licenseDataEncoded))
if err != nil {
return RedpandaLicense{}, fmt.Errorf("failed to decode license data: %w", err)
return openSourceLicense, fmt.Errorf("failed to decode license data: %w", err)
}

signature, err := base64.StdEncoding.DecodeString(string(signatureEncoded))
if err != nil {
return RedpandaLicense{}, fmt.Errorf("failed to decode license signature: %w", err)
return openSourceLicense, fmt.Errorf("failed to decode license signature: %w", err)
}
hash := sha256.Sum256(licenseDataEncoded)

// 3. Verify license contents with static public key
if err := rsa.VerifyPKCS1v15(publicKeyRSA, crypto.SHA256, hash[:], signature); err != nil {
return RedpandaLicense{}, fmt.Errorf("failed to verify license signature: %w", err)
return openSourceLicense, fmt.Errorf("failed to verify license signature: %w", err)
}

// 4. If license contents seem to be legit, we will continue unpacking the license
var rpLicense RedpandaLicense
if err := json.Unmarshal(licenseData, &rpLicense); err != nil {
return RedpandaLicense{}, fmt.Errorf("failed to unmarshal license data: %w", err)
return openSourceLicense, fmt.Errorf("failed to unmarshal license data: %w", err)
}

return rpLicense, nil
Expand Down
14 changes: 12 additions & 2 deletions internal/license/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ func TestLicenseEnterpriseValidation(t *testing.T) {
},
IsEnterprise: false,
},
{
Name: "open source license",
License: RedpandaLicense{
Version: 3,
Organization: "meow",
Type: -1,
Expiry: time.Now().Add(time.Hour).Unix(),
},
IsEnterprise: false,
},
{
Name: "free trial license",
License: RedpandaLicense{
Expand All @@ -79,7 +89,7 @@ func TestLicenseEnterpriseValidation(t *testing.T) {
Type: 0,
Expiry: time.Now().Add(time.Hour).Unix(),
},
IsEnterprise: false,
IsEnterprise: true,
},
{
Name: "enterprise license",
Expand Down Expand Up @@ -199,7 +209,7 @@ func TestLicenseEnterpriseExplicit(t *testing.T) {
_, license = createLicense(t, RedpandaLicense{
Version: 3,
Organization: "meow",
Type: 0,
Type: -1,
Expiry: time.Now().Add(time.Hour).Unix(),
})
require.NoError(t, os.WriteFile(tmpAlsoBadLicensePath, []byte(license), 0o777))
Expand Down
2 changes: 1 addition & 1 deletion internal/license/shared_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func CheckRunningEnterprise(res *service.Resources) error {
return err
}
if !l.AllowsEnterpriseFeatures() {
return errors.New("this feature requires a valid Redpanda Enterprise Edition license from https://redpanda.com/try-enterprise?origin=rpcn. For more information check out: https://docs.redpanda.com/current/get-started/licenses/")
return errors.New("this feature requires a valid Redpanda Enterprise Edition license from https://redpanda.com/try-enterprise?origin=rpcn. For more information check out: https://docs.redpanda.com/redpanda-connect/get-started/licensing/")
}
return nil
}
Expand Down

0 comments on commit ad959a9

Please sign in to comment.