Skip to content

Commit

Permalink
Support role bootstrapping in OSS (#11175)
Browse files Browse the repository at this point in the history
* Add role bootstrapping

* Test coverage

* Better variable names

* Remove spurious log, add better error messages
  • Loading branch information
espadolini committed Mar 18, 2022
1 parent 57ba25a commit 7c86f67
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 46 deletions.
26 changes: 17 additions & 9 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -2661,6 +2661,21 @@ func (a *ServerWithRoles) UpsertRole(ctx context.Context, role types.Role) error
}

// Some options are only available with enterprise subscription
if err := checkRoleFeatureSupport(role); err != nil {
return trace.Wrap(err)
}

// access predicate syntax is not checked as part of normal role validation in order
// to allow the available namespaces to be extended without breaking compatibility with
// older nodes/proxies (which do not need to ever evaluate said predicates).
if err := services.ValidateAccessPredicates(role); err != nil {
return trace.Wrap(err)
}

return a.authServer.UpsertRole(ctx, role)
}

func checkRoleFeatureSupport(role types.Role) error {
features := modules.GetModules().Features()
options := role.GetOptions()
allowReq, allowRev := role.GetAccessRequestConditions(types.Allow), role.GetAccessReviewConditions(types.Allow)
Expand All @@ -2679,16 +2694,9 @@ func (a *ServerWithRoles) UpsertRole(ctx context.Context, role types.Role) error
case features.AdvancedAccessWorkflows == false && !allowRev.IsZero():
return trace.AccessDenied(
"role field allow.review_requests is only available in enterprise subscriptions")
default:
return nil
}

// access predicate syntax is not checked as part of normal role validation in order
// to allow the available namespaces to be extended without breaking compatibility with
// older nodes/proxies (which do not need to ever evaluate said predicates).
if err := services.ValidateAccessPredicates(role); err != nil {
return trace.Wrap(err)
}

return a.authServer.UpsertRole(ctx, role)
}

// GetRole returns role by name
Expand Down
5 changes: 5 additions & 0 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,11 @@ func checkResourceConsistency(keyStore keystore.KeyStore, clusterName string, re
if r.GetName() == clusterName {
return trace.BadParameter("trusted cluster has same name as local cluster (%q)", clusterName)
}
case types.Role:
// Some options are only available with enterprise subscription
if err := checkRoleFeatureSupport(r); err != nil {
return trace.Wrap(err)
}
default:
// No validation checks for this resource type
}
Expand Down
83 changes: 51 additions & 32 deletions lib/services/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,80 +412,99 @@ func getResourceUnmarshaler(kind string) (ResourceUnmarshaler, bool) {
}

func init() {
RegisterResourceMarshaler(types.KindUser, func(r types.Resource, opts ...MarshalOption) ([]byte, error) {
rsc, ok := r.(types.User)
RegisterResourceMarshaler(types.KindUser, func(resource types.Resource, opts ...MarshalOption) ([]byte, error) {
user, ok := resource.(types.User)
if !ok {
return nil, trace.BadParameter("expected User, got %T", r)
return nil, trace.BadParameter("expected User, got %T", resource)
}
raw, err := MarshalUser(rsc, opts...)
bytes, err := MarshalUser(user, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return raw, nil
return bytes, nil
})
RegisterResourceUnmarshaler(types.KindUser, func(b []byte, opts ...MarshalOption) (types.Resource, error) {
rsc, err := UnmarshalUser(b, opts...)
RegisterResourceUnmarshaler(types.KindUser, func(bytes []byte, opts ...MarshalOption) (types.Resource, error) {
user, err := UnmarshalUser(bytes, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return rsc, nil
return user, nil
})

RegisterResourceMarshaler(types.KindCertAuthority, func(r types.Resource, opts ...MarshalOption) ([]byte, error) {
rsc, ok := r.(types.CertAuthority)
RegisterResourceMarshaler(types.KindCertAuthority, func(resource types.Resource, opts ...MarshalOption) ([]byte, error) {
certAuthority, ok := resource.(types.CertAuthority)
if !ok {
return nil, trace.BadParameter("expected CertAuthority, got %T", r)
return nil, trace.BadParameter("expected CertAuthority, got %T", resource)
}
raw, err := MarshalCertAuthority(rsc, opts...)
bytes, err := MarshalCertAuthority(certAuthority, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return raw, nil
return bytes, nil
})
RegisterResourceUnmarshaler(types.KindCertAuthority, func(b []byte, opts ...MarshalOption) (types.Resource, error) {
rsc, err := UnmarshalCertAuthority(b, opts...)
RegisterResourceUnmarshaler(types.KindCertAuthority, func(bytes []byte, opts ...MarshalOption) (types.Resource, error) {
certAuthority, err := UnmarshalCertAuthority(bytes, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return rsc, nil
return certAuthority, nil
})

RegisterResourceMarshaler(types.KindTrustedCluster, func(r types.Resource, opts ...MarshalOption) ([]byte, error) {
rsc, ok := r.(types.TrustedCluster)
RegisterResourceMarshaler(types.KindTrustedCluster, func(resource types.Resource, opts ...MarshalOption) ([]byte, error) {
trustedCluster, ok := resource.(types.TrustedCluster)
if !ok {
return nil, trace.BadParameter("expected TrustedCluster, got %T", r)
return nil, trace.BadParameter("expected TrustedCluster, got %T", resource)
}
raw, err := MarshalTrustedCluster(rsc, opts...)
bytes, err := MarshalTrustedCluster(trustedCluster, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return raw, nil
return bytes, nil
})
RegisterResourceUnmarshaler(types.KindTrustedCluster, func(b []byte, opts ...MarshalOption) (types.Resource, error) {
rsc, err := UnmarshalTrustedCluster(b, opts...)
RegisterResourceUnmarshaler(types.KindTrustedCluster, func(bytes []byte, opts ...MarshalOption) (types.Resource, error) {
trustedCluster, err := UnmarshalTrustedCluster(bytes, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return rsc, nil
return trustedCluster, nil
})

RegisterResourceMarshaler(types.KindGithubConnector, func(r types.Resource, opts ...MarshalOption) ([]byte, error) {
rsc, ok := r.(types.GithubConnector)
RegisterResourceMarshaler(types.KindGithubConnector, func(resource types.Resource, opts ...MarshalOption) ([]byte, error) {
githubConnector, ok := resource.(types.GithubConnector)
if !ok {
return nil, trace.BadParameter("expected GithubConnector, got %T", r)
return nil, trace.BadParameter("expected GithubConnector, got %T", resource)
}
raw, err := MarshalGithubConnector(rsc, opts...)
bytes, err := MarshalGithubConnector(githubConnector, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return raw, nil
return bytes, nil
})
RegisterResourceUnmarshaler(types.KindGithubConnector, func(b []byte, opts ...MarshalOption) (types.Resource, error) {
rsc, err := UnmarshalGithubConnector(b) // XXX: Does not support marshal options.
RegisterResourceUnmarshaler(types.KindGithubConnector, func(bytes []byte, opts ...MarshalOption) (types.Resource, error) {
githubConnector, err := UnmarshalGithubConnector(bytes) // XXX: Does not support marshal options.
if err != nil {
return nil, trace.Wrap(err)
}
return rsc, nil
return githubConnector, nil
})

RegisterResourceMarshaler(types.KindRole, func(resource types.Resource, opts ...MarshalOption) ([]byte, error) {
role, ok := resource.(types.Role)
if !ok {
return nil, trace.BadParameter("expected Role, got %T", resource)
}
bytes, err := MarshalRole(role, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return bytes, nil
})
RegisterResourceUnmarshaler(types.KindRole, func(bytes []byte, opts ...MarshalOption) (types.Resource, error) {
role, err := UnmarshalRole(bytes, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
return role, nil
})
}

Expand Down
40 changes: 35 additions & 5 deletions tool/teleport/common/teleport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ limitations under the License.
package common

import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/config"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
Expand All @@ -43,10 +43,26 @@ func TestTeleportMain(t *testing.T) {
hostname, err := os.Hostname()
require.NoError(t, err)

fixtureDir := t.TempDir()
// generate the fixture config file
configFile := filepath.Join(t.TempDir(), "teleport.yaml")
err = ioutil.WriteFile(configFile, []byte(YAMLConfig), 0660)
require.NoError(t, err)
configFile := filepath.Join(fixtureDir, "teleport.yaml")
require.NoError(t, os.WriteFile(configFile, []byte(configData), 0660))

// generate the fixture bootstrap file
bootstrapEntries := []struct{ fileName, kind, name string }{
{"role.yaml", types.KindRole, "role_name"},
{"github.yaml", types.KindGithubConnector, "github"},
{"user.yaml", types.KindRole, "user"},
}
var bootstrapData []byte
for _, entry := range bootstrapEntries {
data, err := os.ReadFile(filepath.Join("..", "..", "..", "examples", "resources", entry.fileName))
require.NoError(t, err)
bootstrapData = append(bootstrapData, data...)
bootstrapData = append(bootstrapData, "\n---\n"...)
}
bootstrapFile := filepath.Join(fixtureDir, "bootstrap.yaml")
require.NoError(t, os.WriteFile(bootstrapFile, bootstrapData, 0660))

// set defaults to test-mode (non-existing files&locations)
defaults.ConfigFilePath = "/tmp/teleport/etc/teleport.yaml"
Expand Down Expand Up @@ -111,6 +127,20 @@ func TestTeleportMain(t *testing.T) {
require.Equal(t, "10.5.5.5", conf.AdvertiseIP)
require.Equal(t, map[string]string{"a": "a1", "b": "b1"}, conf.SSH.Labels)
})

t.Run("Bootstrap", func(t *testing.T) {
_, cmd, conf := Run(Options{
Args: []string{"start", "--bootstrap", bootstrapFile},
InitOnly: true,
})
require.Equal(t, "start", cmd)
require.Equal(t, len(bootstrapEntries), len(conf.Auth.Resources))
for i, entry := range bootstrapEntries {
require.Equal(t, entry.kind, conf.Auth.Resources[i].GetKind(), entry.fileName)
require.Equal(t, entry.name, conf.Auth.Resources[i].GetName(), entry.fileName)
require.NoError(t, conf.Auth.Resources[i].CheckAndSetDefaults(), entry.fileName)
}
})
}

func TestConfigure(t *testing.T) {
Expand Down Expand Up @@ -143,7 +173,7 @@ func TestConfigure(t *testing.T) {
})
}

const YAMLConfig = `
const configData = `
teleport:
advertise_ip: 10.5.5.5
nodename: hvostongo.example.org
Expand Down

0 comments on commit 7c86f67

Please sign in to comment.