Skip to content

Commit

Permalink
Surface Cloud Entitlements (#42394)
Browse files Browse the repository at this point in the history
- Cloud entitlements are now available on Features
- Legacy logic applies if entitlements are not present
  • Loading branch information
michellescripts authored Jun 28, 2024
1 parent 80c1c72 commit 70b95e3
Show file tree
Hide file tree
Showing 43 changed files with 499 additions and 279 deletions.
33 changes: 31 additions & 2 deletions api/types/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ type License interface {

// GetSupportsFeatureHiding returns feature hiding support flag.
GetSupportsFeatureHiding() Bool
// GetSupportsFeatureHiding sets feature hiding support flag.
// SetSupportsFeatureHiding sets feature hiding support flag.
SetSupportsFeatureHiding(Bool)

// GetTrial returns the trial flag.
Expand Down Expand Up @@ -156,8 +156,24 @@ type License interface {

// GetSupportsPolicy returns Teleport Policy support flag.
GetSupportsPolicy() Bool
//SGetSupportsPolicy sets Teleport Policy support flag.
// SetSupportsPolicy sets Teleport Policy support flag.
SetSupportsPolicy(Bool)

// GetEntitlements returns the Entitlements object
GetEntitlements() map[string]EntitlementInfo
// SetEntitlements sets the Entitlements object
SetEntitlements(map[string]EntitlementInfo)
}

// EntitlementInfo is the state and limits of a particular entitlement; Example for feature X:
// { Enabled: true, Limit: 0 } => unlimited access to feature X
// { Enabled: true, Limit: >0 } => limited access to feature X
// { Enabled: false, Limit: >=0 } => no access to feature X
type EntitlementInfo struct {
// Enabled indicates the feature is 'on' if true; feature is disabled if false
Enabled Bool
// Limit indicates the allotted amount of use when limited; if 0 use is unlimited
Limit int32
}

// FeatureSource defines where the list of features enabled
Expand Down Expand Up @@ -494,6 +510,16 @@ func (c *LicenseV3) SetSupportsPolicy(value Bool) {
c.Spec.SupportsPolicy = value
}

// GetEntitlements returns Entitlements
func (c *LicenseV3) GetEntitlements() map[string]EntitlementInfo {
return c.Spec.Entitlements
}

// SetEntitlements sets Entitlements
func (c *LicenseV3) SetEntitlements(value map[string]EntitlementInfo) {
c.Spec.Entitlements = value
}

// String represents a human readable version of license enabled features
func (c *LicenseV3) String() string {
var features []string
Expand Down Expand Up @@ -601,4 +627,7 @@ type LicenseSpecV3 struct {
AnonymizationKey string `json:"anonymization_key,omitempty"`
// SupportsPolicy turns Teleport Policy features on or off.
SupportsPolicy Bool `json:"policy,omitempty"`

// entitlements define a customer’s access to a specific features
Entitlements map[string]EntitlementInfo `json:"entitlements,omitempty"`
}
48 changes: 48 additions & 0 deletions entitlements/entitlements.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Teleport
// Copyright (C) 2024 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package entitlements

type EntitlementKind string

// The EntitlementKind list should be 1:1 with the Features & FeatureStrings in salescenter/product/product.go,
// except CustomTheme which is dropped. CustomTheme entitlement only toggles the ability to "set" a theme;
// the value of that theme, if set, is stored and accessed outside of entitlements.
const (
AccessLists EntitlementKind = "AccessLists"
AccessMonitoring EntitlementKind = "AccessMonitoring"
AccessRequests EntitlementKind = "AccessRequests"
App EntitlementKind = "App"
CloudAuditLogRetention EntitlementKind = "CloudAuditLogRetention"
DB EntitlementKind = "DB"
Desktop EntitlementKind = "Desktop"
DeviceTrust EntitlementKind = "DeviceTrust"
ExternalAuditStorage EntitlementKind = "ExternalAuditStorage"
FeatureHiding EntitlementKind = "FeatureHiding"
HSM EntitlementKind = "HSM"
Identity EntitlementKind = "Identity"
JoinActiveSessions EntitlementKind = "JoinActiveSessions"
K8s EntitlementKind = "K8s"
MobileDeviceManagement EntitlementKind = "MobileDeviceManagement"
OIDC EntitlementKind = "OIDC"
OktaSCIM EntitlementKind = "OktaSCIM"
OktaUserSync EntitlementKind = "OktaUserSync"
Policy EntitlementKind = "Policy"
SAML EntitlementKind = "SAML"
SessionLocks EntitlementKind = "SessionLocks"
UpsellAlert EntitlementKind = "UpsellAlert"
UsageReporting EntitlementKind = "UsageReporting"
)
5 changes: 4 additions & 1 deletion integration/appaccess/appaccess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/httplib/reverseproxy"
Expand Down Expand Up @@ -265,7 +266,9 @@ func testClientCert(p *Pack, t *testing.T) {
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
App: true,
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.App: {Enabled: true},
},
},
})
evilUser, _ := p.CreateUser(t)
Expand Down
7 changes: 6 additions & 1 deletion integration/db/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/defaults"
Expand Down Expand Up @@ -108,7 +109,11 @@ func TestDatabaseAccessSeparateListeners(t *testing.T) {
func (p *DatabasePack) testIPPinning(t *testing.T) {
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{DB: true},
TestFeatures: modules.Features{
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.DB: {Enabled: true},
},
},
})

type testCase struct {
Expand Down
5 changes: 4 additions & 1 deletion integration/hsm/hsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/gravitational/teleport/api/breaker"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/keystore"
"github.com/gravitational/teleport/lib/auth/state"
Expand All @@ -56,7 +57,9 @@ func TestMain(m *testing.M) {
modules.SetModules(&modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
HSM: true,
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.HSM: {Enabled: true},
},
},
})

Expand Down
5 changes: 4 additions & 1 deletion integration/kube_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/integration/kube"
"github.com/gravitational/teleport/lib"
Expand Down Expand Up @@ -1345,7 +1346,9 @@ func testKubeEphemeralContainers(t *testing.T, suite *KubeSuite) {
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Kubernetes: true,
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.K8s: {Enabled: true},
},
},
})

Expand Down
7 changes: 6 additions & 1 deletion integration/proxy/proxy_tunnel_strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/auth"
Expand Down Expand Up @@ -158,7 +159,11 @@ func TestProxyTunnelStrategyProxyPeering(t *testing.T) {
// This test cannot run in parallel as set module changes the global state.
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{DB: true},
TestFeatures: modules.Features{
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.DB: {Enabled: true},
},
},
})

p := newProxyTunnelStrategy(t, "proxy-tunnel-proxy-peer",
Expand Down
7 changes: 5 additions & 2 deletions integrations/operator/controllers/resources/testlib/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (

"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/integration/helpers"
resourcesv1 "github.com/gravitational/teleport/integrations/operator/apis/resources/v1"
resourcesv2 "github.com/gravitational/teleport/integrations/operator/apis/resources/v2"
Expand Down Expand Up @@ -99,8 +100,10 @@ func defaultTeleportServiceConfig(t *testing.T) (*helpers.TeleInstance, string)
modules.SetTestModules(t, &modules.TestModules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
OIDC: true,
SAML: true,
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.OIDC: {Enabled: true},
entitlements.SAML: {Enabled: true},
},
},
})

Expand Down
9 changes: 6 additions & 3 deletions integrations/terraform/testlib/terraform_enterprise_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

eintegration "github.com/gravitational/teleport/e/integration"
eauth "github.com/gravitational/teleport/e/lib/auth"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/integrations/lib/testing/integration"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/modules"
Expand All @@ -35,10 +36,12 @@ import (
func TestTerraformEnterprise(t *testing.T) {
modules.SetTestModules(t, &modules.TestModules{
TestFeatures: modules.Features{
OIDC: true,
SAML: true,
AdvancedAccessWorkflows: true,
DeviceTrust: modules.DeviceTrustFeature{Enabled: true},
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.OIDC: {Enabled: true},
entitlements.SAML: {Enabled: true},
entitlements.DeviceTrust: {Enabled: true},
},
},
})

Expand Down
5 changes: 4 additions & 1 deletion lib/auth/access_request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/gravitational/teleport/api/types/accesslist"
"github.com/gravitational/teleport/api/types/header"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/backend/memory"
Expand Down Expand Up @@ -1543,7 +1544,9 @@ func TestUpdateAccessRequestWithAdditionalReviewers(t *testing.T) {

modules.SetTestModules(t, &modules.TestModules{
TestFeatures: modules.Features{
IdentityGovernanceSecurity: true,
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.Identity: {Enabled: true},
},
},
})

Expand Down
20 changes: 12 additions & 8 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import (
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/api/utils/retryutils"
apisshutils "github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/keystore"
"github.com/gravitational/teleport/lib/auth/native"
Expand Down Expand Up @@ -350,15 +351,15 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) {
AuthPreferenceGetter: cfg.ClusterConfiguration,
}
if cfg.KeyStoreConfig.PKCS11 != (servicecfg.PKCS11Config{}) {
if !modules.GetModules().Features().HSM {
if !modules.GetModules().Features().GetEntitlement(entitlements.HSM).Enabled {
return nil, fmt.Errorf("PKCS11 HSM support requires a license with the HSM feature enabled: %w", ErrRequiresEnterprise)
}
} else if cfg.KeyStoreConfig.GCPKMS != (servicecfg.GCPKMSConfig{}) {
if !modules.GetModules().Features().HSM {
if !modules.GetModules().Features().GetEntitlement(entitlements.HSM).Enabled {
return nil, fmt.Errorf("Google Cloud KMS support requires a license with the HSM feature enabled: %w", ErrRequiresEnterprise)
}
} else if cfg.KeyStoreConfig.AWSKMS != (servicecfg.AWSKMSConfig{}) {
if !modules.GetModules().Features().HSM {
if !modules.GetModules().Features().GetEntitlement(entitlements.HSM).Enabled {
return nil, fmt.Errorf("AWS KMS support requires a license with the HSM feature enabled: %w", ErrRequiresEnterprise)
}
} else {
Expand Down Expand Up @@ -5358,7 +5359,7 @@ func (a *Server) UpsertNode(ctx context.Context, server types.Server) (*types.Ke
func enforceLicense(t string) error {
switch t {
case types.KindKubeServer, types.KindKubernetesCluster:
if !modules.GetModules().Features().Kubernetes {
if !modules.GetModules().Features().GetEntitlement(entitlements.K8s).Enabled {
return trace.AccessDenied(
"this Teleport cluster is not licensed for Kubernetes, please contact the cluster administrator")
}
Expand Down Expand Up @@ -6882,18 +6883,21 @@ func (a *Server) getAccessRequestMonthlyUsage(ctx context.Context) (int, error)
// If so, it returns an error. This is only applicable on usage-based billing plans.
func (a *Server) verifyAccessRequestMonthlyLimit(ctx context.Context) error {
f := modules.GetModules().Features()
if f.IsLegacy() || f.IGSEnabled() {
return nil // unlimited
accessRequestsEntitlement := f.GetEntitlement(entitlements.AccessRequests)

if accessRequestsEntitlement.Limit == 0 {
return nil // unlimited access
}
monthlyLimit := f.AccessRequests.MonthlyRequestLimit

monthlyLimit := accessRequestsEntitlement.Limit

const limitReachedMessage = "cluster has reached its monthly access request limit, please contact the cluster administrator"

usage, err := a.getAccessRequestMonthlyUsage(ctx)
if err != nil {
return trace.Wrap(err)
}
if usage >= monthlyLimit {
if usage >= int(monthlyLimit) {
return trace.AccessDenied(limitReachedMessage)
}

Expand Down
Loading

0 comments on commit 70b95e3

Please sign in to comment.