Add app and db access flags to license (#5627)

This commit is contained in:
Roman Tkachenko 2021-02-22 08:35:08 -08:00 committed by GitHub
parent 64c9011710
commit 21b90a64cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 233 additions and 38 deletions

View file

@ -27,39 +27,45 @@ import (
// License defines teleport License Information
type License interface {
Resource
// GetReportsUsage returns true if teleport cluster reports usage
// to control plane
GetReportsUsage() Bool
// SetReportsUsage sets usage report
SetReportsUsage(Bool)
// GetCloud returns true if teleport cluster is hosted by Gravitational
GetCloud() Bool
// SetCloud sets cloud flag
SetCloud(Bool)
// SetReportsUsage sets usage report
SetReportsUsage(Bool)
// GetAWSProductID returns product id that limits usage to AWS instance
// with a similar product ID
GetAWSProductID() string
// SetAWSProductID sets AWS product ID
SetAWSProductID(string)
// GetAWSAccountID limits usage to AWS instance within account ID
GetAWSAccountID() string
// SetAWSAccountID sets AWS account ID that will be limiting
// usage to AWS instance
SetAWSAccountID(accountID string)
// GetSupportsKubernetes returns kubernetes support flag
GetSupportsKubernetes() Bool
// SetSupportsKubernetes sets kubernetes support flag
SetSupportsKubernetes(Bool)
// GetSupportsApplicationAccess returns application access support flag
GetSupportsApplicationAccess() Bool
// SetSupportsApplicationAccess sets application access support flag
SetSupportsApplicationAccess(Bool)
// GetSupportsDatabaseAccess returns database access support flag
GetSupportsDatabaseAccess() Bool
// SetSupportsDatabaseAccess sets database access support flag
SetSupportsDatabaseAccess(Bool)
// SetLabels sets metadata labels
SetLabels(labels map[string]string)
@ -237,25 +243,57 @@ func (c *LicenseV3) SetSupportsKubernetes(supportsK8s Bool) {
c.Spec.SupportsKubernetes = supportsK8s
}
// GetSupportsApplicationAccess returns application access support flag
func (c *LicenseV3) GetSupportsApplicationAccess() Bool {
// For backward compatibility return true if app access flag isn't set,
// or it will stop working for all users who are already using it and
// were issued licenses without this flag.
if c.Spec.SupportsApplicationAccess == nil {
return Bool(true)
}
return *c.Spec.SupportsApplicationAccess
}
// SetSupportsApplicationAccess sets application access support flag
func (c *LicenseV3) SetSupportsApplicationAccess(value Bool) {
c.Spec.SupportsApplicationAccess = &value
}
// GetSupportsDatabaseAccess returns database access support flag
func (c *LicenseV3) GetSupportsDatabaseAccess() Bool {
return c.Spec.SupportsDatabaseAccess
}
// SetSupportsDatabaseAccess sets database access support flag
func (c *LicenseV3) SetSupportsDatabaseAccess(value Bool) {
c.Spec.SupportsDatabaseAccess = value
}
// String represents a human readable version of license enabled features
func (c *LicenseV3) String() string {
var features []string
if !c.Expiry().IsZero() {
features = append(features, fmt.Sprintf("expires at %v", c.Expiry()))
}
if c.Spec.ReportsUsage.Value() {
if c.GetReportsUsage() {
features = append(features, "reports usage")
}
if c.Spec.SupportsKubernetes.Value() {
if c.GetSupportsKubernetes() {
features = append(features, "supports kubernetes")
}
if c.Spec.Cloud.Value() {
if c.GetSupportsApplicationAccess() {
features = append(features, "supports application access")
}
if c.GetSupportsDatabaseAccess() {
features = append(features, "supports database access")
}
if c.GetCloud() {
features = append(features, "is hosted by Gravitational")
}
if c.Spec.AWSProductID != "" {
if c.GetAWSProductID() != "" {
features = append(features, fmt.Sprintf("is limited to AWS product ID %q", c.Spec.AWSProductID))
}
if c.Spec.AWSAccountID != "" {
if c.GetAWSAccountID() != "" {
features = append(features, fmt.Sprintf("is limited to AWS account ID %q", c.Spec.AWSAccountID))
}
if len(features) == 0 {
@ -274,6 +312,11 @@ type LicenseSpecV3 struct {
AWSAccountID string `json:"aws_account,omitempty"`
// SupportsKubernetes turns kubernetes support on or off
SupportsKubernetes Bool `json:"k8s"`
// SupportsApplicationAccess turns application access on or off
// Note it's a pointer for backward compatibility
SupportsApplicationAccess *Bool `json:"app,omitempty"`
// SupportsDatabaseAccess turns database access on or off
SupportsDatabaseAccess Bool `json:"db,omitempty"`
// ReportsUsage turns usage reporting on or off
ReportsUsage Bool `json:"usage,omitempty"`
// Cloud is turned on when teleport is hosted by Gravitational

View file

@ -818,6 +818,12 @@ func NewBool(b bool) Bool {
return Bool(b)
}
// NewBoolP returns Bool pointer
func NewBoolP(b bool) *Bool {
val := NewBool(b)
return &val
}
// Bool is a wrapper around boolean values
type Bool bool

2
e

@ -1 +1 @@
Subproject commit 6284dd6f3ecc08e0977b58dbb24cfaa77d59408e
Subproject commit 8959cce70aac94e6f9b7ea37a162f9ccff11461c

View file

@ -23,6 +23,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/tlsca"
@ -77,6 +78,11 @@ func (s *Server) GenerateDatabaseCert(ctx context.Context, req *proto.DatabaseCe
// SignDatabaseCSR generates a client certificate used by proxy when talking
// to a remote database service.
func (s *Server) SignDatabaseCSR(ctx context.Context, req *proto.DatabaseCSRRequest) (*proto.DatabaseCSRResponse, error) {
if !modules.GetModules().Features().DB {
return nil, trace.AccessDenied(
"this Teleport cluster doesn't support database access, please contact the cluster administrator")
}
log.Debugf("Signing database CSR for cluster %v.", req.ClusterName)
clusterName, err := s.GetClusterName()

View file

@ -24,6 +24,7 @@ import (
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/lib/jwt"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
@ -37,6 +38,11 @@ import (
// The certificate is used for all access requests, which is where access
// control is enforced.
func (s *Server) CreateAppSession(ctx context.Context, req services.CreateAppSessionRequest, user services.User, checker services.AccessChecker) (services.WebSession, error) {
if !modules.GetModules().Features().App {
return nil, trace.AccessDenied(
"this Teleport cluster doesn't support application access, please contact the cluster administrator")
}
// Check that a matching parent web session exists in the backend.
parentSession, err := s.GetWebSession(ctx, types.GetWebSessionRequest{
User: req.Username,

View file

@ -46,6 +46,12 @@ const LicenseSpecV3Template = `{
"k8s": {
"type": ["string", "boolean"]
},
"app": {
"type": ["string", "boolean"]
},
"db": {
"type": ["string", "boolean"]
},
"cloud": {
"type": ["string", "boolean"]
}

View file

@ -19,6 +19,7 @@ package services
import (
"testing"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/teleport/lib/utils"
@ -45,38 +46,43 @@ func (l *LicenseSuite) TestUnmarshal(c *check.C) {
testCases := []testCase{
{
description: "simple case",
input: `{"kind": "license", "version": "v3", "metadata": {"name": "Teleport Commercial"}, "spec": {"account_id": "accountID", "usage": true, "k8s": true, "aws_account": "123", "aws_pid": "4"}}`,
input: `{"kind": "license", "version": "v3", "metadata": {"name": "Teleport Commercial"}, "spec": {"account_id": "accountID", "usage": true, "k8s": true, "app": true, "db": true, "aws_account": "123", "aws_pid": "4"}}`,
expected: MustNew("Teleport Commercial", LicenseSpecV3{
ReportsUsage: NewBool(true),
SupportsKubernetes: NewBool(true),
Cloud: NewBool(false),
AWSAccountID: "123",
AWSProductID: "4",
AccountID: "accountID",
ReportsUsage: NewBool(true),
SupportsKubernetes: NewBool(true),
SupportsApplicationAccess: types.NewBoolP(true),
SupportsDatabaseAccess: NewBool(true),
Cloud: NewBool(false),
AWSAccountID: "123",
AWSProductID: "4",
AccountID: "accountID",
}),
},
{
description: "simple case with string booleans",
input: `{"kind": "license", "version": "v3", "metadata": {"name": "license"}, "spec": {"account_id": "accountID", "usage": "yes", "k8s": "yes", "aws_account": "123", "aws_pid": "4"}}`,
input: `{"kind": "license", "version": "v3", "metadata": {"name": "license"}, "spec": {"account_id": "accountID", "usage": "yes", "k8s": "yes", "app": "yes", "db": "yes", "aws_account": "123", "aws_pid": "4"}}`,
expected: MustNew("license", LicenseSpecV3{
ReportsUsage: NewBool(true),
SupportsKubernetes: NewBool(true),
Cloud: NewBool(false),
AWSAccountID: "123",
AWSProductID: "4",
AccountID: "accountID",
ReportsUsage: NewBool(true),
SupportsKubernetes: NewBool(true),
SupportsApplicationAccess: types.NewBoolP(true),
SupportsDatabaseAccess: NewBool(true),
Cloud: NewBool(false),
AWSAccountID: "123",
AWSProductID: "4",
AccountID: "accountID",
}),
},
{
description: "with cloud flag",
input: `{"kind": "license", "version": "v3", "metadata": {"name": "license"}, "spec": {"cloud": "yes", "account_id": "accountID", "usage": "yes", "k8s": "yes", "aws_account": "123", "aws_pid": "4"}}`,
expected: MustNew("license", LicenseSpecV3{
ReportsUsage: NewBool(true),
SupportsKubernetes: NewBool(true),
Cloud: NewBool(true),
AWSAccountID: "123",
AWSProductID: "4",
AccountID: "accountID",
ReportsUsage: NewBool(true),
SupportsKubernetes: NewBool(true),
SupportsDatabaseAccess: NewBool(false),
Cloud: NewBool(true),
AWSAccountID: "123",
AWSProductID: "4",
AccountID: "accountID",
}),
},
{

View file

@ -26,9 +26,9 @@ import (
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/multiplexer"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv/db/common"
"github.com/gravitational/teleport/lib/srv/db/mysql"
"github.com/gravitational/teleport/lib/srv/db/postgres"
@ -137,8 +137,8 @@ func TestPostgresAccess(t *testing.T) {
_, role, err := auth.CreateUserAndRole(testCtx.tlsServer.Auth(), test.user, []string{test.role})
require.NoError(t, err)
role.SetDatabaseNames(services.Allow, test.allowDbNames)
role.SetDatabaseUsers(services.Allow, test.allowDbUsers)
role.SetDatabaseNames(types.Allow, test.allowDbNames)
role.SetDatabaseUsers(types.Allow, test.allowDbUsers)
err = testCtx.tlsServer.Auth().UpsertRole(ctx, role)
require.NoError(t, err)
@ -246,7 +246,7 @@ func TestMySQLAccess(t *testing.T) {
_, role, err := auth.CreateUserAndRole(testCtx.tlsServer.Auth(), test.user, []string{test.role})
require.NoError(t, err)
role.SetDatabaseUsers(services.Allow, test.allowDbUsers)
role.SetDatabaseUsers(types.Allow, test.allowDbUsers)
err = testCtx.tlsServer.Auth().UpsertRole(ctx, role)
require.NoError(t, err)
@ -283,6 +283,72 @@ func TestMySQLAccess(t *testing.T) {
}
}
type testModules struct {
modules.Modules
}
func (m *testModules) Features() modules.Features {
return modules.Features{
DB: false, // Explicily turn off database access.
}
}
// TestDatabaseAccessDisabled makes sure database access can be disabled via
// modules.
func TestDatabaseAccessDisabled(t *testing.T) {
defaultModules := modules.GetModules()
defer modules.SetModules(defaultModules)
modules.SetModules(&testModules{})
ctx := context.Background()
testCtx := setupTestContext(ctx, t)
t.Cleanup(func() { testCtx.Close() })
// Start multiplexer.
go testCtx.mux.Serve()
// Start fake Postgres server.
go testCtx.postgresServer.Serve()
// Start database proxy server.
go testCtx.proxyServer.Serve(testCtx.mux.DB())
// Start database service server.
go func() {
for conn := range testCtx.proxyConn {
testCtx.server.HandleConnection(conn)
}
}()
userName := "alice"
roleName := "admin"
dbUser := "postgres"
dbName := "postgres"
// Create user/role with the requested permissions.
_, role, err := auth.CreateUserAndRole(testCtx.tlsServer.Auth(), userName, []string{roleName})
require.NoError(t, err)
role.SetDatabaseNames(types.Allow, []string{types.Wildcard})
role.SetDatabaseUsers(types.Allow, []string{types.Wildcard})
err = testCtx.tlsServer.Auth().UpsertRole(ctx, role)
require.NoError(t, err)
// Try to connect to the database as this user.
_, err = postgres.MakeTestClient(ctx, common.TestClientConfig{
AuthClient: testCtx.authClient,
AuthServer: testCtx.authServer,
Address: testCtx.mux.DB().Addr().String(),
Cluster: testCtx.clusterName,
Username: userName,
RouteToDatabase: tlsca.RouteToDatabase{
ServiceName: "postgres-test",
Protocol: defaults.ProtocolPostgres,
Username: dbUser,
Database: dbName,
},
})
require.Error(t, err)
require.Contains(t, err.Error(), "this Teleport cluster doesn't support database access")
}
type testContext struct {
clusterName string
tlsServer *auth.TestTLSServer

View file

@ -20,7 +20,7 @@ limitations under the License.
Package uacc concerns itself with updating the user account database and log on nodes
that a client connects to with an interactive session.
This is a stub version that doesn't do anything and exists purely for compatability purposes with systems we don't support.
This is a stub version that doesn't do anything and exists purely for compatibility purposes with systems we don't support.
We do not support macOS yet because they introduced ASL for user accounting with Mac OS X 10.6 (Snow Leopard)
and integrating with that takes additional effort.

View file

@ -57,6 +57,7 @@ import (
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/teleport/lib/httplib/csrf"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/pam"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/secret"
@ -1777,6 +1778,61 @@ func (s *WebSuite) TestGetClusterDetails(c *C) {
c.Assert(cluster.AuthVersion, Equals, "")
}
type testModules struct {
modules.Modules
}
func (m *testModules) Features() modules.Features {
return modules.Features{
App: false, // Explicily turn off application access.
}
}
// TestApplicationAccessDisabled makes sure application access can be disabled
// via modules.
func TestApplicationAccessDisabled(t *testing.T) {
defaultModules := modules.GetModules()
defer modules.SetModules(defaultModules)
modules.SetModules(&testModules{})
env := newWebPack(t, 1)
defer env.close(t)
proxy := env.proxies[0]
pack := proxy.authPack(t, "foo@example.com")
// Register an application.
server := &types.ServerV2{
Kind: types.KindAppServer,
Version: types.V2,
Metadata: types.Metadata{
Namespace: defaults.Namespace,
Name: uuid.New(),
},
Spec: types.ServerSpecV2{
Version: teleport.Version,
Apps: []*types.App{
{
Name: "panel",
PublicAddr: "panel.example.com",
URI: "http://127.0.0.1:8080",
},
},
},
}
_, err := env.server.Auth().UpsertAppServer(context.Background(), server)
require.NoError(t, err)
endpoint := pack.clt.Endpoint("webapi", "sessions", "app")
_, err = pack.clt.PostJSON(context.Background(), endpoint, &CreateAppSessionRequest{
FQDN: "panel.example.com",
PublicAddr: "panel.example.com",
ClusterName: "localhost",
})
require.Error(t, err)
require.Contains(t, err.Error(), "this Teleport cluster doesn't support application access")
}
// TestCreateAppSession verifies that an existing session to the Web UI can
// be exchanged for a application specific session.
func (s *WebSuite) TestCreateAppSession(c *C) {