Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support role bootstrapping in OSS #11175

Merged
merged 5 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -2688,6 +2688,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 @@ -2706,16 +2721,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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for being thorough, nice.

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