Revert "Introduce ProvisionTokenV3 (#16361)" (#16934)

This reverts commit 3fba50261f.
This commit is contained in:
Noah Stride 2022-10-03 15:14:01 +01:00 committed by GitHub
parent 791f97eba7
commit eb42cabbea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 2745 additions and 5815 deletions

View file

@ -1964,58 +1964,25 @@ func (c *Client) DeleteTrustedCluster(ctx context.Context, name string) error {
return trail.FromGRPC(err)
}
// DELETE IN 13.0 (strideynet)
func (c *Client) getTokenV2(ctx context.Context, name string) (types.ProvisionToken, error) {
//nolint:staticcheck // SA1019 Deprecated call will be removed in 13.0
resp, err := c.grpc.GetToken(ctx, &types.ResourceRequest{Name: name}, c.callOpts...)
if err != nil {
return nil, trail.FromGRPC(err)
}
return resp.V3(), nil
}
// GetToken returns a provision token by name.
func (c *Client) GetToken(ctx context.Context, name string) (types.ProvisionToken, error) {
if name == "" {
return nil, trace.BadParameter("cannot get token, missing name")
}
resp, err := c.grpc.GetTokenV3(ctx, &types.ResourceRequest{Name: name}, c.callOpts...)
resp, err := c.grpc.GetToken(ctx, &types.ResourceRequest{Name: name}, c.callOpts...)
if err != nil {
err = trail.FromGRPC(err)
if trace.IsNotImplemented(err) {
return c.getTokenV2(ctx, name)
}
return nil, err
return nil, trail.FromGRPC(err)
}
return resp, nil
}
// DELETE IN 13.0 (strideynet)
func (c *Client) getTokensV2(ctx context.Context) ([]types.ProvisionToken, error) {
//nolint:staticcheck // SA1019 Deprecated call will be removed in 13.0
// GetTokens returns a list of active provision tokens for nodes and users.
func (c *Client) GetTokens(ctx context.Context) ([]types.ProvisionToken, error) {
resp, err := c.grpc.GetTokens(ctx, &emptypb.Empty{}, c.callOpts...)
if err != nil {
return nil, trail.FromGRPC(err)
}
tokens := make([]types.ProvisionToken, len(resp.ProvisionTokens))
for i, token := range resp.ProvisionTokens {
tokens[i] = token.V3()
}
return tokens, nil
}
// GetTokens returns a list of active provision tokens for nodes and users.
func (c *Client) GetTokens(ctx context.Context) ([]types.ProvisionToken, error) {
resp, err := c.grpc.GetTokensV3(ctx, &emptypb.Empty{}, c.callOpts...)
if err != nil {
err = trail.FromGRPC(err)
if trace.IsNotImplemented(err) {
return c.getTokensV2(ctx)
}
return nil, err
}
tokens := make([]types.ProvisionToken, len(resp.ProvisionTokens))
for i, token := range resp.ProvisionTokens {
tokens[i] = token
@ -2025,13 +1992,21 @@ func (c *Client) GetTokens(ctx context.Context) ([]types.ProvisionToken, error)
// UpsertToken creates or updates a provision token.
func (c *Client) UpsertToken(ctx context.Context, token types.ProvisionToken) error {
_, err := c.grpc.UpsertTokenV3(ctx, token.V3(), c.callOpts...)
tokenV2, ok := token.(*types.ProvisionTokenV2)
if !ok {
return trace.BadParameter("invalid type %T", token)
}
_, err := c.grpc.UpsertToken(ctx, tokenV2, c.callOpts...)
return trail.FromGRPC(err)
}
// CreateToken creates a provision token.
func (c *Client) CreateToken(ctx context.Context, token types.ProvisionToken) error {
_, err := c.grpc.CreateTokenV3(ctx, token.V3(), c.callOpts...)
tokenV2, ok := token.(*types.ProvisionTokenV2)
if !ok {
return trace.BadParameter("invalid type %T", token)
}
_, err := c.grpc.CreateToken(ctx, tokenV2, c.callOpts...)
return trail.FromGRPC(err)
}

View file

@ -45,9 +45,9 @@ func EventToGRPC(in types.Event) (*proto.Event, error) {
out.Resource = &proto.Event_StaticTokens{
StaticTokens: r,
}
case *types.ProvisionTokenV3:
out.Resource = &proto.Event_ProvisionTokenV3{
ProvisionTokenV3: r,
case *types.ProvisionTokenV2:
out.Resource = &proto.Event_ProvisionToken{
ProvisionToken: r,
}
case *types.ClusterNameV2:
out.Resource = &proto.Event_ClusterName{
@ -207,7 +207,7 @@ func EventFromGRPC(in proto.Event) (*types.Event, error) {
} else if r := in.GetStaticTokens(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetProvisionTokenV3(); r != nil {
} else if r := in.GetProvisionToken(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetClusterName(); r != nil {

File diff suppressed because it is too large Load diff

View file

@ -115,8 +115,6 @@ message Event {
types.KubernetesClusterV3 KubernetesCluster = 33 [(gogoproto.jsontag) = "kubernetes_cluster,omitempty"];
// Installer is an installer resource
types.InstallerV1 Installer = 34 [(gogoproto.jsontag) = "installer,omitempty"];
// ProvisionTokenV3 is a V3 Provision Token resource.
types.ProvisionTokenV3 ProvisionTokenV3 = 35 [(gogoproto.jsontag) = "provision_token_v3,omitempty"];
}
}
@ -2429,30 +2427,14 @@ service AuthService {
// DeleteTrustedCluster deletes an existing Trusted Cluster in a backend by name.
rpc DeleteTrustedCluster(types.ResourceRequest) returns (google.protobuf.Empty);
// GetToken retrieves a v2 token described by the given request.
// Deprecated: use GetTokenV3.
// DELETE IN 13.0 (strideynet).
// GetToken retrieves a token described by the given request.
rpc GetToken(types.ResourceRequest) returns (types.ProvisionTokenV2);
// GetTokenV3 retrieves a token described by the given request.
rpc GetTokenV3(types.ResourceRequest) returns (types.ProvisionTokenV3);
// GetTokens retrieves all v2 tokens.
// Deprecated: use GetTokensV3.
// DELETE IN 13.0 (strideynet).
// GetToken retrieves all tokens.
rpc GetTokens(google.protobuf.Empty) returns (types.ProvisionTokenV2List);
// GetTokensV3 retrieves all tokens.
rpc GetTokensV3(google.protobuf.Empty) returns (types.ProvisionTokenV3List);
// UpsertToken upserts a v2 token in a backend.
// Deprecated: use UpsertTokenV3.
// DELETE IN 13.0 (strideynet).
// UpsertToken upserts a token in a backend.
rpc UpsertToken(types.ProvisionTokenV2) returns (google.protobuf.Empty);
// UpsertTokenV3 upserts a v3 token in a backend.
rpc UpsertTokenV3(types.ProvisionTokenV3) returns (google.protobuf.Empty);
// CreateToken creates a v2 token in a backend.
// Deprecated: Use CreateTokenV3.
// DELETE IN 13.0 (strideynet)
// CreateToken creates a token in a backend.
rpc CreateToken(types.ProvisionTokenV2) returns (google.protobuf.Empty);
// CreateTokenV3 creates a v3 token in a backend.
rpc CreateTokenV3(types.ProvisionTokenV3) returns (google.protobuf.Empty);
// GenerateToken generates a new auth token.
rpc GenerateToken(GenerateTokenRequest) returns (GenerateTokenResponse);
// DeleteToken deletes an existing token in a backend described by the given request.

View file

@ -963,103 +963,6 @@ message ProvisionTokenSpecV2 {
];
}
// ProvisionTokenV3 specifies provisioning token
message ProvisionTokenV3 {
option (gogoproto.goproto_stringer) = false;
option (gogoproto.stringer) = false;
// Kind is a resource kind
string Kind = 1 [(gogoproto.jsontag) = "kind"];
// SubKind is an optional resource sub kind, used in some resources
string SubKind = 2 [(gogoproto.jsontag) = "sub_kind,omitempty"];
// Version is version
string Version = 3 [(gogoproto.jsontag) = "version"];
// Metadata is resource metadata
Metadata Metadata = 4 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "metadata"
];
// Spec is a provisioning token V3 spec
ProvisionTokenSpecV3 Spec = 5 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "spec"
];
}
// ProvisionTokenV3List is a list of provisioning tokens.
message ProvisionTokenV3List {
// ProvisionTokens is a list of provisioning tokens.
repeated ProvisionTokenV3 ProvisionTokens = 1;
}
message ProvisionTokenSpecV3 {
// Roles is a list of roles associated with the token, that will be converted
// to metadata in the SSH and X509 certificates issued to the user of the
// token.
repeated string Roles = 1 [
(gogoproto.jsontag) = "roles",
(gogoproto.casttype) = "SystemRole"
];
// JoinMethod is the joining method required in order to use this token.
// Supported joining methods include:
// - "token"
// - "ec2"
// - "iam"
string JoinMethod = 2 [
(gogoproto.jsontag) = "join_method",
(gogoproto.casttype) = "JoinMethod"
];
// BotName is the name of the bot this token grants access to.
// This field must be set if the token includes the `Bot` role. It cannot be
// set if the token does not include this role.
string BotName = 3 [(gogoproto.jsontag) = "bot_name,omitempty"];
// SuggestedLabels is a set of labels that resources should set when using
// this token to enroll themselves in the cluster.
wrappers.LabelValues SuggestedLabels = 4 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "suggested_labels,omitempty",
(gogoproto.customtype) = "Labels"
];
// IAM allows the configuration of options specific to the "iam" join method.
ProvisionTokenSpecV3AWSIAM IAM = 5 [(gogoproto.jsontag) = "aws_iam,omitempty"];
// EC2 allows the configuration of options specific to the "ec2" join method.
ProvisionTokenSpecV3AWSEC2 EC2 = 6 [(gogoproto.jsontag) = "aws_ec2,omitempty"];
}
message ProvisionTokenSpecV3AWSEC2 {
message Rule {
// Account is the AWS account ID.
string Account = 1 [(gogoproto.jsontag) = "account,omitempty"];
// Regions is a list of AWS regions a node is allowed to join from.
repeated string Regions = 2 [(gogoproto.jsontag) = "regions,omitempty"];
// Role is the the ARN of the AWS role that the auth server will assume
// in order to call the ec2 API.
string RoleARN = 3 [(gogoproto.jsontag) = "role_arn,omitempty"];
}
// Allow is a list of TokenRules, nodes using this token must match one
// allow rule to use this token.
repeated Rule Allow = 1 [(gogoproto.jsontag) = "allow,omitempty"];
// IIDTTL is the TTL to use for AWS EC2 Instance Identity Documents used
// to join the cluster with this token.
int64 IIDTTL = 2 [
(gogoproto.jsontag) = "iid_ttl,omitempty",
(gogoproto.casttype) = "Duration"
];
}
message ProvisionTokenSpecV3AWSIAM {
message Rule {
// Account is the AWS account ID.
string Account = 1 [(gogoproto.jsontag) = "account,omitempty"];
// ARN is used for the IAM join method, the AWS identity of joining nodes
// must match this ARN. Supports wildcards "*" and "?".
string ARN = 2 [(gogoproto.jsontag) = "arn,omitempty"];
}
// Allow is a list of TokenRules, nodes using this token must match one
// allow rule to use this token.
repeated Rule Allow = 1 [(gogoproto.jsontag) = "allow,omitempty"];
}
// StaticTokensV2 implements the StaticTokens interface.
message StaticTokensV2 {
option (gogoproto.goproto_stringer) = false;
@ -1069,7 +972,7 @@ message StaticTokensV2 {
string Kind = 1 [(gogoproto.jsontag) = "kind"];
// SubKind is an optional resource sub kind, used in some resources
string SubKind = 2 [(gogoproto.jsontag) = "sub_kind,omitempty"];
// Version is the version of the resource kind
// Version is version
string Version = 3 [(gogoproto.jsontag) = "version"];
// Metadata is resource metadata
Metadata Metadata = 4 [

View file

@ -288,10 +288,6 @@ const (
// V1 is the first version of resources. Note: The first version was
// not explicitly versioned.
V1 = "v1"
// VDeleted indicates that the version information for this resource
// cannot be determined as it is being included as part of a delete Event.
VDeleted = "deleted"
)
// WebSessionSubKinds lists subkinds of web session resources

View file

@ -78,33 +78,22 @@ type ProvisionToken interface {
// GetSuggestedLabels returns the set of labels that the resource should add when adding itself to the cluster
GetSuggestedLabels() Labels
// V1 returns V1 version of the resource
V1() *ProvisionTokenV1
// String returns user friendly representation of the resource
String() string
// V3 returns the V3 representation of a ProvisionToken. This can be used
// for conversion to a concrete type in preparation for serialization into
// a rpc message.
V3() *ProvisionTokenV3
// V2 returns the V2 representation of a ProvisionToken
// This exists for the transition period where the V2 Token RPCs still exist
// and we may need to be able to convert V3 tokens to a V2 representation
// for serialization.
// DELETE IN 13.0 (strideynet)
V2() (*ProvisionTokenV2, error)
}
// NewProvisionToken returns a new provision token with the given roles.
func NewProvisionToken(token string, roles SystemRoles, expires time.Time) (ProvisionToken, error) {
return NewProvisionTokenFromSpec(token, expires, ProvisionTokenSpecV3{
JoinMethod: JoinMethodToken,
Roles: roles,
return NewProvisionTokenFromSpec(token, expires, ProvisionTokenSpecV2{
Roles: roles,
})
}
// NewProvisionTokenFromSpec returns a new provision token with the given spec.
func NewProvisionTokenFromSpec(token string, expires time.Time, spec ProvisionTokenSpecV3) (ProvisionToken, error) {
t := &ProvisionTokenV3{
func NewProvisionTokenFromSpec(token string, expires time.Time, spec ProvisionTokenSpecV2) (ProvisionToken, error) {
t := &ProvisionTokenV2{
Metadata: Metadata{
Name: token,
Expires: &expires,
@ -117,8 +106,44 @@ func NewProvisionTokenFromSpec(token string, expires time.Time, spec ProvisionTo
return t, nil
}
// V3 returns V3 version of the ProvisionTokenV2 resource.
func (p *ProvisionTokenV2) V3() *ProvisionTokenV3 {
// MustCreateProvisionToken returns a new valid provision token
// or panics, used in tests
func MustCreateProvisionToken(token string, roles SystemRoles, expires time.Time) ProvisionToken {
t, err := NewProvisionToken(token, roles, expires)
if err != nil {
panic(err)
}
return t
}
// setStaticFields sets static resource header and metadata fields.
func (p *ProvisionTokenV2) setStaticFields() {
p.Kind = KindToken
p.Version = V2
}
// CheckAndSetDefaults checks and set default values for any missing fields.
func (p *ProvisionTokenV2) CheckAndSetDefaults() error {
p.setStaticFields()
if err := p.Metadata.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
if len(p.Spec.Roles) == 0 {
return trace.BadParameter("provisioning token is missing roles")
}
if err := SystemRoles(p.Spec.Roles).Check(); err != nil {
return trace.Wrap(err)
}
if SystemRoles(p.Spec.Roles).Include(RoleBot) && p.Spec.BotName == "" {
return trace.BadParameter("token with role %q must set bot_name", RoleBot)
}
if p.Spec.BotName != "" && !SystemRoles(p.Spec.Roles).Include(RoleBot) {
return trace.BadParameter("can only set bot_name on token with role %q", RoleBot)
}
hasAllowRules := len(p.Spec.Allow) > 0
if p.Spec.JoinMethod == JoinMethodUnspecified {
// Default to the ec2 join method if any allow rules were specified,
@ -130,82 +155,209 @@ func (p *ProvisionTokenV2) V3() *ProvisionTokenV3 {
p.Spec.JoinMethod = JoinMethodToken
}
}
v3 := &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: p.Metadata,
Spec: ProvisionTokenSpecV3{
Roles: p.Spec.Roles,
JoinMethod: p.Spec.JoinMethod,
BotName: p.Spec.BotName,
SuggestedLabels: p.Spec.SuggestedLabels,
},
}
// Add provider specific config.
switch p.Spec.JoinMethod {
case JoinMethodIAM:
iam := &ProvisionTokenSpecV3AWSIAM{}
for _, rule := range p.Spec.Allow {
iam.Allow = append(iam.Allow, &ProvisionTokenSpecV3AWSIAM_Rule{
Account: rule.AWSAccount,
ARN: rule.AWSARN,
})
case JoinMethodToken:
if hasAllowRules {
return trace.BadParameter("allow rules are not compatible with the %q join method", JoinMethodToken)
}
v3.Spec.IAM = iam
case JoinMethodEC2:
ec2 := &ProvisionTokenSpecV3AWSEC2{}
for _, rule := range p.Spec.Allow {
ec2.Allow = append(ec2.Allow, &ProvisionTokenSpecV3AWSEC2_Rule{
Account: rule.AWSAccount,
RoleARN: rule.AWSRole,
Regions: rule.AWSRegions,
})
if !hasAllowRules {
return trace.BadParameter("the %q join method requires defined token allow rules", JoinMethodEC2)
}
ec2.IIDTTL = p.Spec.AWSIIDTTL
v3.Spec.EC2 = ec2
for _, allowRule := range p.Spec.Allow {
if allowRule.AWSARN != "" {
return trace.BadParameter(`the %q join method does not support the "aws_arn" parameter`, JoinMethodEC2)
}
if allowRule.AWSAccount == "" && allowRule.AWSRole == "" {
return trace.BadParameter(`allow rule for %q join method must set "aws_account" or "aws_role"`, JoinMethodEC2)
}
}
if p.Spec.AWSIIDTTL == 0 {
// default to 5 minute ttl if unspecified
p.Spec.AWSIIDTTL = Duration(5 * time.Minute)
}
case JoinMethodIAM:
if !hasAllowRules {
return trace.BadParameter("the %q join method requires defined token allow rules", JoinMethodIAM)
}
for _, allowRule := range p.Spec.Allow {
if allowRule.AWSRole != "" {
return trace.BadParameter(`the %q join method does not support the "aws_role" parameter`, JoinMethodIAM)
}
if len(allowRule.AWSRegions) != 0 {
return trace.BadParameter(`the %q join method does not support the "aws_regions" parameter`, JoinMethodIAM)
}
if allowRule.AWSAccount == "" && allowRule.AWSARN == "" {
return trace.BadParameter(`allow rule for %q join method must set "aws_account" or "aws_arn"`, JoinMethodEC2)
}
}
default:
return trace.BadParameter("unknown join method %q", p.Spec.JoinMethod)
}
return v3
return nil
}
// GetVersion returns resource version
func (p *ProvisionTokenV2) GetVersion() string {
return p.Version
}
// GetRoles returns a list of teleport roles
// that will be granted to the user of the token
// in the crendentials
func (p *ProvisionTokenV2) GetRoles() SystemRoles {
return p.Spec.Roles
}
// SetRoles sets teleport roles
func (p *ProvisionTokenV2) SetRoles(r SystemRoles) {
p.Spec.Roles = r
}
// GetAllowRules returns the list of allow rules
func (p *ProvisionTokenV2) GetAllowRules() []*TokenRule {
return p.Spec.Allow
}
// GetAWSIIDTTL returns the TTL of EC2 IIDs
func (p *ProvisionTokenV2) GetAWSIIDTTL() Duration {
return p.Spec.AWSIIDTTL
}
// GetJoinMethod returns joining method that must be used with this token.
func (p *ProvisionTokenV2) GetJoinMethod() JoinMethod {
return p.Spec.JoinMethod
}
// GetBotName returns the BotName field which must be set for joining bots.
func (p *ProvisionTokenV2) GetBotName() string {
return p.Spec.BotName
}
// GetKind returns resource kind
func (p *ProvisionTokenV2) GetKind() string {
return p.Kind
}
// GetSubKind returns resource sub kind
func (p *ProvisionTokenV2) GetSubKind() string {
return p.SubKind
}
// SetSubKind sets resource subkind
func (p *ProvisionTokenV2) SetSubKind(s string) {
p.SubKind = s
}
// GetResourceID returns resource ID
func (p *ProvisionTokenV2) GetResourceID() int64 {
return p.Metadata.ID
}
// SetResourceID sets resource ID
func (p *ProvisionTokenV2) SetResourceID(id int64) {
p.Metadata.ID = id
}
// GetMetadata returns metadata
func (p *ProvisionTokenV2) GetMetadata() Metadata {
return p.Metadata
}
// SetMetadata sets resource metatada
func (p *ProvisionTokenV2) SetMetadata(meta Metadata) {
p.Metadata = meta
}
// GetSuggestedLabels returns the labels the resource should set when using this token
func (p *ProvisionTokenV2) GetSuggestedLabels() Labels {
return p.Spec.SuggestedLabels
}
// V1 returns V1 version of the resource
func (p *ProvisionTokenV2) V1() *ProvisionTokenV1 {
return &ProvisionTokenV1{
Roles: p.Spec.Roles,
Expires: p.Metadata.Expiry(),
Token: p.Metadata.Name,
}
}
// V2 returns V2 version of the resource
func (p *ProvisionTokenV2) V2() *ProvisionTokenV2 {
return p
}
// SetExpiry sets expiry time for the object
func (p *ProvisionTokenV2) SetExpiry(expires time.Time) {
p.Metadata.SetExpiry(expires)
}
// Expiry returns object expiry setting
func (p *ProvisionTokenV2) Expiry() time.Time {
return p.Metadata.Expiry()
}
// GetName returns server name
func (p *ProvisionTokenV2) GetName() string {
return p.Metadata.Name
}
// SetName sets the name of the TrustedCluster.
func (p *ProvisionTokenV2) SetName(e string) {
p.Metadata.Name = e
}
// String returns the human readable representation of a provisioning token.
func (p ProvisionTokenV2) String() string {
expires := "never"
if !p.Metadata.Expiry().IsZero() {
expires = p.Metadata.Expiry().String()
if !p.Expiry().IsZero() {
expires = p.Expiry().String()
}
return fmt.Sprintf("ProvisionToken(Roles=%v, Expires=%v)", p.Spec.Roles, expires)
}
// ProvisionTokensToV1 converts provision tokens to V1 list
func ProvisionTokensToV1(in []ProvisionToken) []ProvisionTokenV1 {
if in == nil {
return nil
}
out := make([]ProvisionTokenV1, len(in))
for i := range in {
out[i] = *in[i].V1()
}
return out
}
// ProvisionTokensFromV1 converts V1 provision tokens to resource list
// This exists to allow the ProvisionTokenV1s embedded within the StaticTokens
// specification to be converted to something that implements the
// ProvisionToken interface, so that they can be used as part of the join
// process in the same way that a ProvisionToken can be.
func ProvisionTokensFromV1(in []ProvisionTokenV1) []ProvisionToken {
if in == nil {
return nil
}
out := make([]ProvisionToken, len(in))
for i := range in {
out[i] = in[i].V3()
out[i] = in[i].V2()
}
return out
}
// V3 returns V3 version of the ProvisionTokenV1 resource.
// This is handy for converting a ProvisionTokenV1 embedded within a
// StaticToken to a ProvisionToken interface implementing type.
func (p *ProvisionTokenV1) V3() *ProvisionTokenV3 {
t := &ProvisionTokenV3{
// V1 returns V1 version of the resource
func (p *ProvisionTokenV1) V1() *ProvisionTokenV1 {
return p
}
// V2 returns V2 version of the resource
func (p *ProvisionTokenV1) V2() *ProvisionTokenV2 {
t := &ProvisionTokenV2{
Kind: KindToken,
Version: V3,
Version: V2,
Metadata: Metadata{
Name: p.Token,
Namespace: defaults.Namespace,
},
Spec: ProvisionTokenSpecV3{
Roles: p.Roles,
JoinMethod: JoinMethodToken,
Spec: ProvisionTokenSpecV2{
Roles: p.Roles,
},
}
if !p.Expires.IsZero() {
@ -224,274 +376,3 @@ func (p ProvisionTokenV1) String() string {
return fmt.Sprintf("ProvisionToken(Roles=%v, Expires=%v)",
p.Roles, expires)
}
// ProvisionTokenV3 methods
// setStaticFields sets static resource header and metadata fields.
func (p *ProvisionTokenV3) setStaticFields() {
p.Kind = KindToken
p.Version = V3
}
func (p *ProvisionTokenV3) CheckAndSetDefaults() error {
p.setStaticFields()
if err := p.Metadata.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
if len(p.Spec.Roles) == 0 {
return trace.BadParameter("provisioning token is missing roles")
}
if err := SystemRoles(p.Spec.Roles).Check(); err != nil {
return trace.Wrap(err)
}
if p.Spec.BotName == "" && SystemRoles(p.Spec.Roles).Include(RoleBot) {
return trace.BadParameter("token with role %q must set bot_name", RoleBot)
} else if p.Spec.BotName != "" && !SystemRoles(p.Spec.Roles).Include(RoleBot) {
return trace.BadParameter("can only set bot_name on token with role %q", RoleBot)
}
switch p.Spec.JoinMethod {
case JoinMethodIAM:
providerCfg := p.Spec.IAM
if providerCfg == nil {
return trace.BadParameter(
`"aws_iam" configuration must be provided for join method %q`,
JoinMethodIAM,
)
}
if err := providerCfg.checkAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
case JoinMethodEC2:
providerCfg := p.Spec.EC2
if providerCfg == nil {
return trace.BadParameter(
`"aws_ec2" configuration must be provided for join method %q`,
JoinMethodIAM,
)
}
if err := providerCfg.checkAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
case JoinMethodToken:
case "":
return trace.BadParameter(`"join_method" must be specified`)
default:
return trace.BadParameter(`join method %q not recognized. check documentation for valid values.`, p.Spec.JoinMethod)
}
return nil
}
// GetAllowRules returns the list of allow rules.
func (p *ProvisionTokenV3) GetAllowRules() []*TokenRule {
// For now, we convert the V3 rules to V2 rules, to allow the auth server
// implementation to remain the same with the introduction of V3.
// GCP OIDC PR will swap the auth server to use the V3 rules and this
// method will be gotten ridden of.
rules := []*TokenRule{}
switch p.Spec.JoinMethod {
case JoinMethodIAM:
for _, rule := range p.Spec.IAM.Allow {
rules = append(rules, &TokenRule{
AWSAccount: rule.Account,
AWSARN: rule.ARN,
})
}
case JoinMethodEC2:
for _, rule := range p.Spec.EC2.Allow {
rules = append(rules, &TokenRule{
AWSAccount: rule.Account,
AWSRegions: rule.Regions,
AWSRole: rule.RoleARN,
})
}
}
return rules
}
// GetAWSIIDTTL returns the TTL of EC2 IIDs.
func (p *ProvisionTokenV3) GetAWSIIDTTL() Duration {
ec2 := p.Spec.EC2
if ec2 == nil {
// `GetAWSIIDTTL()` should never be called for a token that is not ec2
// join type - so this branch is unlikely to execute.
return 0
}
return ec2.IIDTTL
}
// GetRoles returns a list of teleport roles
// that will be granted to the user of the token
// in the credentials.
func (p *ProvisionTokenV3) GetRoles() SystemRoles {
return p.Spec.Roles
}
// SetRoles sets teleport roles.
func (p *ProvisionTokenV3) SetRoles(r SystemRoles) {
p.Spec.Roles = r
}
// SetExpiry sets expiry time for the object.
func (p *ProvisionTokenV3) SetExpiry(expires time.Time) {
p.Metadata.SetExpiry(expires)
}
// Expiry returns object expiry setting.
func (p *ProvisionTokenV3) Expiry() time.Time {
return p.Metadata.Expiry()
}
// GetName returns token name.
func (p *ProvisionTokenV3) GetName() string {
return p.Metadata.Name
}
// SetName sets the name of the ProvisionTokenV3.
func (p *ProvisionTokenV3) SetName(e string) {
p.Metadata.Name = e
}
// GetBotName returns the BotName field which must be set for joining bots.
func (p *ProvisionTokenV3) GetBotName() string {
return p.Spec.BotName
}
// GetKind returns resource kind.
func (p *ProvisionTokenV3) GetKind() string {
return p.Kind
}
// GetSubKind returns resource sub kind.
func (p *ProvisionTokenV3) GetSubKind() string {
return p.SubKind
}
// SetSubKind sets resource subkind.
func (p *ProvisionTokenV3) SetSubKind(s string) {
p.SubKind = s
}
// GetResourceID returns resource ID.
func (p *ProvisionTokenV3) GetResourceID() int64 {
return p.Metadata.ID
}
// SetResourceID sets resource ID.
func (p *ProvisionTokenV3) SetResourceID(id int64) {
p.Metadata.ID = id
}
// GetVersion returns resource version.
func (p *ProvisionTokenV3) GetVersion() string {
return p.Version
}
// GetMetadata returns metadata.
func (p *ProvisionTokenV3) GetMetadata() Metadata {
return p.Metadata
}
// SetMetadata sets resource metadata.
func (p *ProvisionTokenV3) SetMetadata(meta Metadata) {
p.Metadata = meta
}
// GetJoinMethod returns joining method that must be used with this token.
func (p *ProvisionTokenV3) GetJoinMethod() JoinMethod {
return p.Spec.JoinMethod
}
// GetSuggestedLabels returns the labels the resource should set when using this token
func (p *ProvisionTokenV3) GetSuggestedLabels() Labels {
return p.Spec.SuggestedLabels
}
// String returns the human readable representation of a provisioning token.
func (p ProvisionTokenV3) String() string {
expires := "never"
if !p.Expiry().IsZero() {
expires = p.Expiry().String()
}
return fmt.Sprintf("ProvisionToken(Roles=%v, Expires=%v)", p.Spec.Roles, expires)
}
// V3 returns the V3 representation of a ProvisionToken
func (p *ProvisionTokenV3) V3() *ProvisionTokenV3 {
return p
}
var ProvisionTokenNotBackwardsCompatibleErr = trace.Errorf("token cannot be converted to V2 and must be fetched using V3 API")
// V2 returns the V2 representation of a ProvisionToken
// DELETE IN 13.0 (strideynet) - This will no longer be necessary once v2 RPCs
// have been removed
func (p *ProvisionTokenV3) V2() (*ProvisionTokenV2, error) {
v2 := &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: p.Metadata,
Spec: ProvisionTokenSpecV2{
Roles: p.Spec.Roles,
BotName: p.Spec.BotName,
JoinMethod: p.Spec.JoinMethod,
SuggestedLabels: p.Spec.SuggestedLabels,
Allow: p.GetAllowRules(),
},
}
switch p.Spec.JoinMethod {
case JoinMethodEC2:
v2.Spec.AWSIIDTTL = p.Spec.EC2.IIDTTL
case JoinMethodToken, JoinMethodIAM:
// No special action to take
default:
return nil, trace.Wrap(ProvisionTokenNotBackwardsCompatibleErr)
}
return v2, nil
}
// checkAndSetDefault ensures that the AWS EC2 specific configuration is
// valid.
// It must provide allow rules, and those allow rules must have account or
// role configured.
// A default value for IIDTTL is also applied if not provided.
func (a *ProvisionTokenSpecV3AWSEC2) checkAndSetDefaults() error {
if len(a.Allow) == 0 {
return trace.BadParameter("the %q join method requires defined token allow rules", JoinMethodEC2)
}
for _, allowRule := range a.Allow {
if allowRule.Account == "" && allowRule.RoleARN == "" {
return trace.BadParameter(
`allow rule for %q join method must set "account" or "role"`,
JoinMethodEC2,
)
}
}
if a.IIDTTL == 0 {
// default to 5 minute ttl if unspecified
a.IIDTTL = Duration(5 * time.Minute)
}
return nil
}
// checkAndSetDefault ensures that the AWS IAM specific configuration is
// valid.
// It must provide allow rules, and those allow rules must have account or
// arn configured.
func (a *ProvisionTokenSpecV3AWSIAM) checkAndSetDefaults() error {
if len(a.Allow) == 0 {
return trace.BadParameter("the %q join method requires defined token allow rules", JoinMethodIAM)
}
for _, allowRule := range a.Allow {
if allowRule.Account == "" && allowRule.ARN == "" {
return trace.BadParameter(
`allow rule for %q join method must set "account" or "arn"`,
JoinMethodEC2,
)
}
}
return nil
}

View file

@ -20,617 +20,253 @@ import (
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
)
func TestNewProvisionToken(t *testing.T) {
name := "foo"
roles := SystemRoles{RoleNode}
expires := time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC)
tok, err := NewProvisionToken(name, roles, expires)
require.NoError(t, err)
require.Equal(t, &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: name,
Expires: &expires,
Namespace: defaults.Namespace,
},
Spec: ProvisionTokenSpecV3{
JoinMethod: JoinMethodToken,
Roles: roles,
},
}, tok)
}
func TestNewProvisionTokenFromSpec(t *testing.T) {
name := "foo"
expires := time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC)
spec := ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodToken,
}
tok, err := NewProvisionTokenFromSpec(name, expires, spec)
require.NoError(t, err)
require.Equal(t, &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: name,
Expires: &expires,
Namespace: defaults.Namespace,
},
Spec: spec,
}, tok)
}
func TestProvisionTokenV1_V3(t *testing.T) {
roles := SystemRoles{RoleNop}
name := "foo-tok"
expires := time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC)
v1 := ProvisionTokenV1{
Roles: roles,
Token: name,
Expires: expires,
}
v3 := v1.V3()
require.Equal(t, &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: name,
Expires: &expires,
Namespace: defaults.Namespace,
},
Spec: ProvisionTokenSpecV3{
Roles: roles,
JoinMethod: JoinMethodToken,
},
}, v3)
}
func TestProvisionTokenV2_V3(t *testing.T) {
tests := []struct {
name string
token *ProvisionTokenV2
want *ProvisionTokenV3
func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
testcases := []struct {
desc string
token *ProvisionTokenV2
expected *ProvisionTokenV2
expectedErr error
}{
{
name: "token",
desc: "empty",
token: &ProvisionTokenV2{},
expectedErr: &trace.BadParameterError{},
},
{
desc: "missing roles",
token: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV2{
Roles: SystemRoles{RoleBot},
JoinMethod: JoinMethodToken,
BotName: "bot-foo",
SuggestedLabels: Labels{
"label": []string{"foo"},
},
Name: "test",
},
},
want: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
expectedErr: &trace.BadParameterError{},
},
{
desc: "invalid role",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "foo",
Name: "test",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleBot},
JoinMethod: JoinMethodToken,
BotName: "bot-foo",
SuggestedLabels: Labels{
"label": []string{"foo"},
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode, "not a role"},
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "simple token",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
},
},
expected: &ProvisionTokenV2{
Kind: "token",
Version: "v2",
Metadata: Metadata{
Name: "test",
Namespace: "default",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "token",
},
},
},
{
name: "iam",
desc: "implicit ec2 method",
token: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "foo",
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodIAM,
Roles: []SystemRole{RoleNode},
Allow: []*TokenRule{
{
AWSAccount: "xyzzy",
AWSARN: "arn-123",
AWSAccount: "1234",
AWSRole: "1234/role",
AWSRegions: []string{"us-west-2"},
},
},
},
},
want: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
expected: &ProvisionTokenV2{
Kind: "token",
Version: "v2",
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodIAM,
IAM: &ProvisionTokenSpecV3AWSIAM{
Allow: []*ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "xyzzy",
ARN: "arn-123",
},
},
},
},
},
},
{
name: "ec2",
token: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "foo",
Name: "test",
Namespace: "default",
},
Spec: ProvisionTokenSpecV2{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodEC2,
AWSIIDTTL: NewDuration(time.Second * 37),
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{
{
AWSAccount: "a-account",
AWSRegions: []string{"a-region"},
AWSRole: "a-role",
AWSAccount: "1234",
AWSRole: "1234/role",
AWSRegions: []string{"us-west-2"},
},
},
AWSIIDTTL: Duration(5 * time.Minute),
},
},
want: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
},
{
desc: "explicit ec2 method",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "foo",
Name: "test",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodEC2,
EC2: &ProvisionTokenSpecV3AWSEC2{
IIDTTL: NewDuration(time.Second * 37),
Allow: []*ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: "a-account",
Regions: []string{"a-region"},
RoleARN: "a-role",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{{AWSAccount: "1234"}},
},
},
expected: &ProvisionTokenV2{
Kind: "token",
Version: "v2",
Metadata: Metadata{
Name: "test",
Namespace: "default",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{{AWSAccount: "1234"}},
AWSIIDTTL: Duration(5 * time.Minute),
},
},
},
{
desc: "ec2 method no allow rules",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "ec2 method with aws_arn",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{
{
AWSAccount: "1234",
AWSARN: "1234",
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v3 := tt.token.V3()
require.Equal(t, tt.want, v3)
})
}
}
func validProvisionTokenV3(modifier func(p *ProvisionTokenV3)) *ProvisionTokenV3 {
token := &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: "foo",
Namespace: defaults.Namespace,
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodToken,
},
}
if modifier != nil {
modifier(token)
}
return token
}
func TestProvisionTokenV3_CheckAndSetDefaults(t *testing.T) {
tests := []struct {
name string
token *ProvisionTokenV3
// want indicates the token that should be present after the validation
// has been called. If this is not provided, the original value of token
// is used.
want *ProvisionTokenV3
wantErr error
}{
{
name: "valid token",
token: validProvisionTokenV3(nil),
expectedErr: &trace.BadParameterError{},
},
{
name: "invalid missing roles",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.Roles = SystemRoles{}
}),
wantErr: &trace.BadParameterError{},
desc: "ec2 method empty rule",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{{}},
},
},
expectedErr: &trace.BadParameterError{},
},
{
name: "invalid non-existent role",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.Roles = SystemRoles{"supreme_leader"}
}),
wantErr: &trace.BadParameterError{},
desc: "iam method",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{{AWSAccount: "1234"}},
},
},
expected: &ProvisionTokenV2{
Kind: "token",
Version: "v2",
Metadata: Metadata{
Name: "test",
Namespace: "default",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "ec2",
Allow: []*TokenRule{{AWSAccount: "1234"}},
AWSIIDTTL: Duration(5 * time.Minute),
},
},
},
{
name: "valid bot",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.Roles = SystemRoles{RoleBot}
p.Spec.BotName = "a_bot"
}),
},
{
name: "invalid missing bot name",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.Roles = SystemRoles{RoleBot}
}),
wantErr: &trace.BadParameterError{},
},
{
name: "invalid bot name set but not bot token",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.BotName = "set_by_mistake"
}),
wantErr: &trace.BadParameterError{},
},
{
name: "missing join method",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.JoinMethod = ""
}),
wantErr: &trace.BadParameterError{},
},
{
name: "invalid join method",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.JoinMethod = "ethereal-presence"
}),
wantErr: &trace.BadParameterError{},
},
{
name: "missing iam configuration",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.JoinMethod = JoinMethodIAM
}),
wantErr: &trace.BadParameterError{},
},
{
name: "missing ec2 configuration",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.JoinMethod = JoinMethodEC2
}),
wantErr: &trace.BadParameterError{},
},
{
name: "valid iam configuration",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.JoinMethod = JoinMethodIAM
p.Spec.IAM = &ProvisionTokenSpecV3AWSIAM{
Allow: []*ProvisionTokenSpecV3AWSIAM_Rule{
desc: "iam method with aws_role",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "iam",
Allow: []*TokenRule{
{
Account: "foo",
AWSAccount: "1234",
AWSRole: "1234/role",
},
},
}
}),
},
},
expectedErr: &trace.BadParameterError{},
},
{
name: "valid ec2 configuration",
token: validProvisionTokenV3(func(p *ProvisionTokenV3) {
p.Spec.JoinMethod = JoinMethodEC2
p.Spec.EC2 = &ProvisionTokenSpecV3AWSEC2{
Allow: []*ProvisionTokenSpecV3AWSEC2_Rule{
desc: "iam method with aws_regions",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: "iam",
Allow: []*TokenRule{
{
Account: "foo",
AWSAccount: "1234",
AWSRegions: []string{"us-west-2"},
},
},
IIDTTL: NewDuration(time.Minute * 12),
}
}),
},
},
expectedErr: &trace.BadParameterError{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Ensure we run check n set on a clone - so we can check
// for no changes
token := proto.Clone(tt.token).(*ProvisionTokenV3)
err := token.CheckAndSetDefaults()
if tt.wantErr != nil {
require.ErrorAs(t, err, &tt.wantErr)
for _, tc := range testcases {
t.Run(tc.desc, func(t *testing.T) {
err := tc.token.CheckAndSetDefaults()
if tc.expectedErr != nil {
require.ErrorAs(t, err, &tc.expectedErr)
return
}
require.NoError(t, err)
want := tt.want
if want == nil {
want = tt.token
}
require.Equal(t, want, token)
})
}
}
func TestProvisionTokenV3_GetAllowRules(t *testing.T) {
tests := []struct {
name string
token ProvisionTokenV3
want []*TokenRule
}{
{
name: "ec2",
token: ProvisionTokenV3{
Spec: ProvisionTokenSpecV3{
JoinMethod: JoinMethodEC2,
EC2: &ProvisionTokenSpecV3AWSEC2{
Allow: []*ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: "foo",
Regions: []string{"eu-west-666", "us-coast-612"},
RoleARN: "a-role",
},
{
Account: "bar",
Regions: []string{"a-region"},
RoleARN: "b-role",
},
},
},
},
},
want: []*TokenRule{
{
AWSAccount: "foo",
AWSRegions: []string{"eu-west-666", "us-coast-612"},
AWSRole: "a-role",
},
{
AWSAccount: "bar",
AWSRegions: []string{"a-region"},
AWSRole: "b-role",
},
},
},
{
name: "iam",
token: ProvisionTokenV3{
Spec: ProvisionTokenSpecV3{
JoinMethod: JoinMethodIAM,
IAM: &ProvisionTokenSpecV3AWSIAM{
Allow: []*ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "foo",
ARN: "arn-amazon-foo",
},
{
Account: "bar",
ARN: "arn-amazon-bar",
},
},
},
},
},
want: []*TokenRule{
{
AWSAccount: "foo",
AWSARN: "arn-amazon-foo",
},
{
AWSAccount: "bar",
AWSARN: "arn-amazon-bar",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rules := tt.token.GetAllowRules()
require.Equal(t, tt.want, rules)
})
}
}
func TestProvisionTokenV3_GetAWSIIDTTL(t *testing.T) {
t.Run("unset", func(t *testing.T) {
p := ProvisionTokenV3{
Spec: ProvisionTokenSpecV3{},
}
require.Equal(t, Duration(0), p.GetAWSIIDTTL())
})
t.Run("set", func(t *testing.T) {
duration := NewDuration(time.Second * 6)
p := ProvisionTokenV3{
Spec: ProvisionTokenSpecV3{
EC2: &ProvisionTokenSpecV3AWSEC2{
IIDTTL: duration,
},
},
}
got := p.GetAWSIIDTTL()
require.Equal(t, duration, got)
})
}
func TestProvisionTokenV3_V2(t *testing.T) {
tests := []struct {
name string
token *ProvisionTokenV3
want *ProvisionTokenV2
wantError string
}{
{
name: "token",
token: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
BotName: "foo",
JoinMethod: JoinMethodToken,
SuggestedLabels: Labels{
"foo": []string{"bar"},
},
},
},
want: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV2{
Roles: SystemRoles{RoleNop},
BotName: "foo",
JoinMethod: JoinMethodToken,
SuggestedLabels: Labels{
"foo": []string{"bar"},
},
Allow: []*TokenRule{},
},
},
},
{
name: "ec2",
token: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodEC2,
EC2: &ProvisionTokenSpecV3AWSEC2{
IIDTTL: NewDuration(time.Minute * 300),
Allow: []*ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: "xyzzy",
Regions: []string{"eurasia-1"},
RoleARN: "lord-commander",
},
},
},
},
},
want: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV2{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodEC2,
Allow: []*TokenRule{
{
AWSAccount: "xyzzy",
AWSRegions: []string{"eurasia-1"},
AWSRole: "lord-commander",
},
},
AWSIIDTTL: NewDuration(time.Minute * 300),
},
},
},
{
name: "iam",
token: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodIAM,
IAM: &ProvisionTokenSpecV3AWSIAM{
Allow: []*ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "xyzzy",
ARN: "arn-123",
},
},
},
},
},
want: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV2{
Roles: SystemRoles{RoleNop},
JoinMethod: JoinMethodIAM,
Allow: []*TokenRule{
{
AWSAccount: "xyzzy",
AWSARN: "arn-123",
},
},
},
},
},
{
name: "inconvertible type",
token: &ProvisionTokenV3{
Kind: KindToken,
Version: V3,
Metadata: Metadata{
Name: "foo",
},
Spec: ProvisionTokenSpecV3{
Roles: SystemRoles{RoleNop},
JoinMethod: "join-method-does-not-exist",
IAM: &ProvisionTokenSpecV3AWSIAM{
Allow: []*ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "xyzzy",
ARN: "arn-123",
},
},
},
},
},
want: nil,
wantError: ProvisionTokenNotBackwardsCompatibleErr.Error(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.token.V2()
require.Equal(t, tt.want, got)
if tt.wantError != "" {
require.EqualError(t, err, tt.wantError)
} else {
require.NoError(t, err)
}
require.Equal(t, tc.token, tc.expected)
})
}
}

View file

@ -29,6 +29,8 @@ import (
type StaticTokens interface {
// Resource provides common resource properties.
Resource
// SetStaticTokens sets the list of static tokens used to provision nodes.
SetStaticTokens([]ProvisionToken)
// GetStaticTokens gets the list of static tokens used to provision nodes.
GetStaticTokens() []ProvisionToken
}
@ -104,6 +106,11 @@ func (c *StaticTokensV2) GetMetadata() Metadata {
return c.Metadata
}
// SetStaticTokens sets the list of static tokens used to provision nodes.
func (c *StaticTokensV2) SetStaticTokens(s []ProvisionToken) {
c.Spec.StaticTokens = ProvisionTokensToV1(s)
}
// GetStaticTokens gets the list of static tokens used to provision nodes.
func (c *StaticTokensV2) GetStaticTokens() []ProvisionToken {
return ProvisionTokensFromV1(c.Spec.StaticTokens)

File diff suppressed because it is too large Load diff

View file

@ -158,15 +158,12 @@ func TestEC2NodeJoin(t *testing.T) {
token, err := types.NewProvisionTokenFromSpec(
tokenName,
time.Now().Add(time.Hour),
types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodEC2,
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: iid.AccountID,
Regions: []string{iid.Region},
},
types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: iid.AccountID,
AWSRegions: []string{iid.Region},
},
},
})
@ -236,16 +233,14 @@ func TestIAMNodeJoin(t *testing.T) {
token, err := types.NewProvisionTokenFromSpec(
tokenName,
time.Now().Add(time.Hour),
types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode, types.RoleProxy},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: *id.Account,
},
types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode, types.RoleProxy},
Allow: []*types.TokenRule{
{
AWSAccount: *id.Account,
},
},
JoinMethod: types.JoinMethodIAM,
})
require.NoError(t, err)

View file

@ -1,31 +0,0 @@
/*
Copyright 2022 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package helpers
import (
"time"
"github.com/gravitational/teleport/api/types"
)
func MustCreateProvisionToken(token string, roles types.SystemRoles, expires time.Time) types.ProvisionToken {
t, err := types.NewProvisionToken(token, roles, expires)
if err != nil {
panic(err)
}
return t
}

View file

@ -2162,7 +2162,7 @@ func testMapRoles(t *testing.T, suite *integrationTestSuite) {
require.NoError(t, err)
trustedClusterToken := "trusted-cluster-token"
err = main.Process.GetAuthServer().UpsertToken(ctx,
helpers.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
services.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
require.NoError(t, err)
trustedCluster := main.AsTrustedCluster(trustedClusterToken, types.RoleMap{
{Remote: mainDevs, Local: []string{auxDevs}},
@ -2667,7 +2667,7 @@ func testTrustedTunnelNode(t *testing.T, suite *integrationTestSuite) {
require.NoError(t, err)
trustedClusterToken := "trusted-cluster-token"
err = main.Process.GetAuthServer().UpsertToken(ctx,
helpers.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
services.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
require.NoError(t, err)
trustedCluster := main.AsTrustedCluster(trustedClusterToken, types.RoleMap{
{Remote: mainDevs, Local: []string{auxDevs}},
@ -4263,7 +4263,7 @@ func testRotateTrustedClusters(t *testing.T, suite *integrationTestSuite) {
require.NoError(t, err)
trustedClusterToken := "trusted-cluster-token"
err = svc.GetAuthServer().UpsertToken(ctx,
helpers.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
services.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
require.NoError(t, err)
trustedCluster := main.AsTrustedCluster(trustedClusterToken, types.RoleMap{
{Remote: mainDevs, Local: []string{auxDevs}},

View file

@ -43,6 +43,7 @@ import (
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
@ -540,7 +541,7 @@ func testKubeTrustedClustersClientCert(t *testing.T, suite *KubeSuite) {
require.NoError(t, err)
trustedClusterToken := "trusted-clsuter-token"
err = main.Process.GetAuthServer().UpsertToken(ctx,
helpers.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
services.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
require.NoError(t, err)
trustedCluster := main.AsTrustedCluster(trustedClusterToken, types.RoleMap{
{Remote: mainRole.GetName(), Local: []string{auxRole.GetName()}},
@ -795,7 +796,7 @@ func testKubeTrustedClustersSNI(t *testing.T, suite *KubeSuite) {
require.NoError(t, err)
trustedClusterToken := "trusted-cluster-token"
err = main.Process.GetAuthServer().UpsertToken(ctx,
helpers.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
services.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
require.NoError(t, err)
trustedCluster := main.AsTrustedCluster(trustedClusterToken, types.RoleMap{
{Remote: mainRole.GetName(), Local: []string{auxRole.GetName()}},

View file

@ -382,7 +382,7 @@ func withTrustedCluster() proxySuiteOptionsFunc {
trustedClusterToken := "trustedclustertoken"
err := root.Process.GetAuthServer().UpsertToken(context.Background(),
helpers.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
types.MustCreateProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}))
require.NoError(t, err)
trustedCluster := root.AsTrustedCluster(trustedClusterToken, types.RoleMap{
{Remote: rootRole.GetName(), Local: []string{secondRole.GetName()}},

View file

@ -289,7 +289,7 @@ func (s *Server) checkOrCreateBotToken(ctx context.Context, req *proto.CreateBot
ttl = defaults.DefaultBotJoinTTL
}
tokenSpec := types.ProvisionTokenSpecV3{
tokenSpec := types.ProvisionTokenSpecV2{
Roles: types.SystemRoles{types.RoleBot},
JoinMethod: types.JoinMethodToken,
BotName: botName,

View file

@ -2922,8 +2922,7 @@ func (g *GRPCServer) DeleteTrustedCluster(ctx context.Context, req *types.Resour
return &emptypb.Empty{}, nil
}
// GetToken retrieves a v2 token by name.
// DELETE IN 13
// GetToken retrieves a token by name.
func (g *GRPCServer) GetToken(ctx context.Context, req *types.ResourceRequest) (*types.ProvisionTokenV2, error) {
auth, err := g.authenticate(ctx)
if err != nil {
@ -2933,32 +2932,14 @@ func (g *GRPCServer) GetToken(ctx context.Context, req *types.ResourceRequest) (
if err != nil {
return nil, trace.Wrap(err)
}
v2, err := t.V2()
if err != nil {
return nil, trace.Wrap(err)
provisionTokenV2, ok := t.(*types.ProvisionTokenV2)
if !ok {
return nil, trace.Errorf("encountered unexpected token type: %T", t)
}
return v2, nil
}
// GetTokenV3 retrieves a token by name.
// If the specified token is v2, it will be converted to v3.
func (g *GRPCServer) GetTokenV3(ctx context.Context, req *types.ResourceRequest) (*types.ProvisionTokenV3, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
t, err := auth.ServerWithRoles.GetToken(ctx, req.Name)
if err != nil {
return nil, trace.Wrap(err)
}
return t.V3(), nil
return provisionTokenV2, nil
}
// GetTokens retrieves all tokens.
// DELETE IN 13
func (g *GRPCServer) GetTokens(ctx context.Context, _ *emptypb.Empty) (*types.ProvisionTokenV2List, error) {
auth, err := g.authenticate(ctx)
if err != nil {
@ -2968,54 +2949,20 @@ func (g *GRPCServer) GetTokens(ctx context.Context, _ *emptypb.Empty) (*types.Pr
if err != nil {
return nil, trace.Wrap(err)
}
provisionTokensV2 := make([]*types.ProvisionTokenV2, 0, len(ts))
for _, t := range ts {
v2, err := t.V2()
if err != nil {
return nil, trace.Wrap(err)
provisionTokensV2 := make([]*types.ProvisionTokenV2, len(ts))
for i, t := range ts {
var ok bool
if provisionTokensV2[i], ok = t.(*types.ProvisionTokenV2); !ok {
return nil, trace.Errorf("encountered unexpected token type: %T", t)
}
provisionTokensV2 = append(provisionTokensV2, v2)
}
return &types.ProvisionTokenV2List{
ProvisionTokens: provisionTokensV2,
}, nil
}
// GetTokensV3 retrieves all tokens.
// V2 tokens will be converted to V3 for presentation.
func (g *GRPCServer) GetTokensV3(ctx context.Context, _ *emptypb.Empty) (*types.ProvisionTokenV3List, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
ts, err := auth.ServerWithRoles.GetTokens(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
v3Tokens := make([]*types.ProvisionTokenV3, len(ts))
for i, t := range ts {
v3Tokens[i] = t.V3()
}
return &types.ProvisionTokenV3List{
ProvisionTokens: v3Tokens,
}, nil
}
// UpsertToken upserts a token.
// DELETE IN 13
func (g *GRPCServer) UpsertToken(ctx context.Context, token *types.ProvisionTokenV2) (*emptypb.Empty, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
if err = auth.ServerWithRoles.UpsertToken(ctx, token.V3()); err != nil {
return nil, trace.Wrap(err)
}
return &emptypb.Empty{}, nil
}
// UpsertTokenV3 upserts a v3 token.
func (g *GRPCServer) UpsertTokenV3(ctx context.Context, token *types.ProvisionTokenV3) (*emptypb.Empty, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
@ -3027,20 +2974,7 @@ func (g *GRPCServer) UpsertTokenV3(ctx context.Context, token *types.ProvisionTo
}
// CreateToken creates a token.
// DELETE IN 13
func (g *GRPCServer) CreateToken(ctx context.Context, token *types.ProvisionTokenV2) (*emptypb.Empty, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
if err = auth.ServerWithRoles.CreateToken(ctx, token.V3()); err != nil {
return nil, trace.Wrap(err)
}
return &emptypb.Empty{}, nil
}
// CreateTokenV3 creates a v3 token.
func (g *GRPCServer) CreateTokenV3(ctx context.Context, token *types.ProvisionTokenV3) (*emptypb.Empty, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)

View file

@ -169,7 +169,7 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
testCases := []struct {
desc string
tokenSpec types.ProvisionTokenSpecV3
tokenSpec types.ProvisionTokenSpecV2
ec2Client ec2Client
request types.RegisterUsingTokenRequest
expectError func(error) bool
@ -177,15 +177,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
}{
{
desc: "basic passing case",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -202,19 +199,16 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "pass with multiple rules",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance2.account,
Regions: []string{instance2.region},
},
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance2.account,
AWSRegions: []string{instance2.region},
},
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -231,15 +225,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "pass with multiple regions",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{"us-east-1", instance1.region, "us-east-2"},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{"us-east-1", instance1.region, "us-east-2"},
},
},
},
@ -256,14 +247,11 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "pass with no regions",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
},
},
},
@ -280,15 +268,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "wrong account",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: "bad account",
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "bad account",
AWSRegions: []string{instance1.region},
},
},
},
@ -305,15 +290,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "wrong region",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{"bad region"},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{"bad region"},
},
},
},
@ -330,15 +312,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "bad HostID",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -355,15 +334,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "no identity document",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -379,15 +355,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "bad identity document",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -404,15 +377,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "instance already joined",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance2.account,
Regions: []string{instance2.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance2.account,
AWSRegions: []string{instance2.region},
},
},
},
@ -429,15 +399,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "instance already joined, fake ID",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance2.account,
Regions: []string{instance2.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance2.account,
AWSRegions: []string{instance2.region},
},
},
},
@ -454,15 +421,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "instance not running",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -479,15 +443,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "instance not exists",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -504,15 +465,12 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "TTL expired",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
},
@ -529,18 +487,15 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "custom TTL pass",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
IIDTTL: types.Duration(10 * time.Minute),
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
AWSIIDTTL: types.Duration(10 * time.Minute),
},
ec2Client: ec2ClientRunning{},
request: types.RegisterUsingTokenRequest{
@ -555,18 +510,15 @@ func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
},
{
desc: "custom TTL fail",
tokenSpec: types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{types.RoleNode},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
IIDTTL: types.Duration(10 * time.Minute),
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
AWSIIDTTL: types.Duration(10 * time.Minute),
},
ec2Client: ec2ClientRunning{},
request: types.RegisterUsingTokenRequest{
@ -634,8 +586,7 @@ func TestHostUniqueCheck(t *testing.T) {
token, err := types.NewProvisionTokenFromSpec(
"test_token",
time.Now().Add(time.Minute),
types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodEC2,
types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{
types.RoleNode,
types.RoleProxy,
@ -644,12 +595,10 @@ func TestHostUniqueCheck(t *testing.T) {
types.RoleApp,
types.RoleWindowsDesktop,
},
EC2: &types.ProvisionTokenSpecV3AWSEC2{
Allow: []*types.ProvisionTokenSpecV3AWSEC2_Rule{
{
Account: instance1.account,
Regions: []string{instance1.region},
},
Allow: []*types.TokenRule{
{
AWSAccount: instance1.account,
AWSRegions: []string{instance1.region},
},
},
})

View file

@ -132,7 +132,7 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc string
tokenName string
requestTokenName string
tokenSpec types.ProvisionTokenSpecV3
tokenSpec types.ProvisionTokenSpecV2
stsClient stsClient
iamRegisterOptions []iamRegisterOption
challengeResponseOptions []challengeResponseOption
@ -143,17 +143,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "basic passing case",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -168,17 +166,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wildcard arn 1",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::role/admins-*",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::role/admins-*",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -193,17 +189,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wildcard arn 2",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::role/admins-???",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::role/admins-???",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -218,17 +212,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wrong token",
tokenName: "test-token",
requestTokenName: "wrong-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -243,17 +235,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "challenge response error",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -269,17 +259,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wrong arn",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::role/admins-???",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::role/admins-???",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -294,17 +282,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wrong challenge",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -322,17 +308,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wrong account",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -347,17 +331,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "sts api error",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusForbidden,
@ -369,17 +351,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "wrong sts host",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -397,17 +377,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "regional sts endpoint",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -425,17 +403,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "unsigned challenge header",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -453,17 +429,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "fips pass",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -485,17 +459,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "non-fips client pass v11",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -517,17 +489,15 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
desc: "non-fips client fail v12",
tokenName: "test-token",
requestTokenName: "test-token",
tokenSpec: types.ProvisionTokenSpecV3{
Roles: []types.SystemRole{types.RoleNode},
JoinMethod: types.JoinMethodIAM,
IAM: &types.ProvisionTokenSpecV3AWSIAM{
Allow: []*types.ProvisionTokenSpecV3AWSIAM_Rule{
{
Account: "1234",
ARN: "arn:aws::1111",
},
tokenSpec: types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{types.RoleNode},
Allow: []*types.TokenRule{
{
AWSAccount: "1234",
AWSARN: "arn:aws::1111",
},
},
JoinMethod: types.JoinMethodIAM,
},
stsClient: &mockClient{
respStatusCode: http.StatusOK,
@ -555,7 +525,6 @@ func TestAuth_RegisterUsingIAMMethod(t *testing.T) {
time.Now().Add(time.Minute),
tc.tokenSpec)
require.NoError(t, err)
require.NoError(t, a.UpsertToken(ctx, token))
defer func() {
require.NoError(t, a.DeleteToken(ctx, token.GetName()))

View file

@ -258,10 +258,9 @@ func TestAuth_RegisterUsingToken(t *testing.T) {
func newBotToken(t *testing.T, tokenName, botName string, role types.SystemRole, expiry time.Time) types.ProvisionToken {
t.Helper()
token, err := types.NewProvisionTokenFromSpec(tokenName, expiry, types.ProvisionTokenSpecV3{
JoinMethod: types.JoinMethodToken,
Roles: []types.SystemRole{role},
BotName: botName,
token, err := types.NewProvisionTokenFromSpec(tokenName, expiry, types.ProvisionTokenSpecV2{
Roles: []types.SystemRole{role},
BotName: botName,
})
require.NoError(t, err, "could not create bot token")
return token

View file

@ -3350,7 +3350,7 @@ func TestEventsClusterConfig(t *testing.T) {
require.NoError(t, err)
suite.ExpectDeleteResource(t, w, 3*time.Second, &types.ResourceHeader{
Kind: types.KindToken,
Version: types.VDeleted,
Version: types.V2,
Metadata: types.Metadata{
Namespace: apidefaults.Namespace,
Name: token.GetName(),

View file

@ -2674,7 +2674,7 @@ func TestCacheWatchKindExistsInEvents(t *testing.T) {
types.KindClusterAuthPreference: types.DefaultAuthPreference(),
types.KindSessionRecordingConfig: types.DefaultSessionRecordingConfig(),
types.KindStaticTokens: &types.StaticTokensV2{},
types.KindToken: &types.ProvisionTokenV3{},
types.KindToken: &types.ProvisionTokenV2{},
types.KindUser: &types.UserV2{},
types.KindRole: &types.RoleV5{Version: types.V4},
types.KindNamespace: &types.Namespace{},

View file

@ -336,7 +336,7 @@ type provisionTokenParser struct {
func (p *provisionTokenParser) parse(event backend.Event) (types.Resource, error) {
switch event.Type {
case types.OpDelete:
return resourceHeader(event, types.KindToken, types.VDeleted, 0)
return resourceHeader(event, types.KindToken, types.V2, 0)
case types.OpPut:
token, err := services.UnmarshalProvisionToken(event.Item.Value,
services.WithResourceID(event.Item.ID),

View file

@ -18,6 +18,7 @@ package services
import (
"context"
"time"
"github.com/gravitational/trace"
@ -47,6 +48,16 @@ type Provisioner interface {
GetTokens(ctx context.Context) ([]types.ProvisionToken, error)
}
// MustCreateProvisionToken returns a new valid provision token
// or panics, used in tests
func MustCreateProvisionToken(token string, roles types.SystemRoles, expires time.Time) types.ProvisionToken {
t, err := types.NewProvisionToken(token, roles, expires)
if err != nil {
panic(err)
}
return t
}
// UnmarshalProvisionToken unmarshals the ProvisionToken resource from JSON.
func UnmarshalProvisionToken(data []byte, opts ...MarshalOption) (types.ProvisionToken, error) {
if len(data) == 0 {
@ -66,38 +77,21 @@ func UnmarshalProvisionToken(data []byte, opts ...MarshalOption) (types.Provisio
switch h.Version {
case "":
// ProvisionTokenV1 is converted to V3, as ProvisionTokenV1 no longer
// implements the ProvisionToken interface.
var p types.ProvisionTokenV1
err := utils.FastUnmarshal(data, &p)
if err != nil {
return nil, trace.Wrap(err)
}
v3 := p.V3()
v2 := p.V2()
if cfg.ID != 0 {
v3.SetResourceID(cfg.ID)
v2.SetResourceID(cfg.ID)
}
return v3, nil
return v2, nil
case types.V2:
// ProvisionTokenV2 is converted to V3, as ProvisionTokenV2 is no
// longer supported.
var p types.ProvisionTokenV2
if err := utils.FastUnmarshal(data, &p); err != nil {
return nil, trace.BadParameter(err.Error())
}
v3 := p.V3()
if cfg.ID != 0 {
v3.SetResourceID(cfg.ID)
}
if err := v3.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
return v3, nil
case types.V3:
var p types.ProvisionTokenV3
if err := utils.FastUnmarshal(data, &p); err != nil {
return nil, trace.BadParameter(err.Error())
}
if err := p.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
@ -121,7 +115,7 @@ func MarshalProvisionToken(provisionToken types.ProvisionToken, opts ...MarshalO
}
switch provisionToken := provisionToken.(type) {
case *types.ProvisionTokenV3:
case *types.ProvisionTokenV2:
if !cfg.PreserveResourceID {
// avoid modifying the original object
// to prevent unexpected data races
@ -129,6 +123,9 @@ func MarshalProvisionToken(provisionToken types.ProvisionToken, opts ...MarshalO
copy.SetResourceID(0)
provisionToken = &copy
}
if cfg.GetVersion() == types.V1 {
return utils.FastMarshal(provisionToken.V1())
}
return utils.FastMarshal(provisionToken)
default:
return nil, trace.BadParameter("unrecognized provision token version %T", provisionToken)

View file

@ -1,125 +0,0 @@
/*
Copyright 2022 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package services
import (
"testing"
"time"
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/utils/golden"
"github.com/stretchr/testify/require"
)
func mustCreateProvisionToken(token string, roles types.SystemRoles, expires time.Time) types.ProvisionToken {
t, err := types.NewProvisionToken(token, roles, expires)
if err != nil {
panic(err)
}
return t
}
func TestUnmarshalProvisionToken(t *testing.T) {
expiry := time.Date(2000, 0, 0, 0, 0, 0, 0, time.UTC)
tests := []struct {
name string
data []byte
opts []MarshalOption
want types.ProvisionToken
}{
{
name: "v1",
data: []byte(`{"roles":["Nop"],"token":"foo","expires":"1999-11-30T00:00:00Z"}`),
want: (&types.ProvisionTokenV1{
Token: "foo",
Roles: types.SystemRoles{types.RoleNop},
Expires: expiry,
}).V3(),
},
{
name: "v2",
data: []byte(`{"kind":"token","version":"v2","metadata":{"name":"foo","expires":"1999-11-30T00:00:00Z"},"spec":{"roles":["Nop"],"join_method":"token"}}`),
want: (&types.ProvisionTokenV2{
Kind: types.KindToken,
Version: types.V2,
Metadata: types.Metadata{
Name: "foo",
Expires: &expiry,
Namespace: defaults.Namespace,
},
Spec: types.ProvisionTokenSpecV2{
Roles: types.SystemRoles{types.RoleNop},
JoinMethod: types.JoinMethodToken,
},
}).V3(),
},
{
name: "v3",
data: []byte(`{"kind":"token","version":"v3","metadata":{"name":"foo","expires":"1999-11-30T00:00:00Z"},"spec":{"roles":["Nop"],"join_method":"token"}}`),
want: mustCreateProvisionToken(
"foo",
types.SystemRoles{types.RoleNop},
expiry,
),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := UnmarshalProvisionToken(tt.data, tt.opts...)
require.NoError(t, err)
require.Equal(t, tt.want, got)
})
}
}
func TestMarshalProvisionToken(t *testing.T) {
v3 := mustCreateProvisionToken(
"foo",
types.SystemRoles{types.RoleNop},
time.Date(2000, 0, 0, 0, 0, 0, 0, time.UTC),
)
v3.SetResourceID(1337)
tests := []struct {
name string
token types.ProvisionToken
opts []MarshalOption
}{
{
name: "v3",
token: v3,
},
{
name: "v3 - PreserveResourceID",
token: v3,
opts: []MarshalOption{PreserveResourceID()},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bytes, err := MarshalProvisionToken(tt.token, tt.opts...)
require.NoError(t, err)
if golden.ShouldSet() {
golden.Set(t, bytes)
}
require.Equal(t, golden.Get(t), bytes)
})
}
}

View file

@ -647,6 +647,29 @@ func (s *ServicesTestSuite) TokenCRUD(t *testing.T) {
_, err = s.ProvisioningS.GetToken(ctx, "token")
require.True(t, trace.IsNotFound(err))
// check tokens backwards compatibility and marshal/unmarshal
expiry := time.Now().UTC().Add(time.Hour)
v1 := &types.ProvisionTokenV1{
Token: "old",
Roles: types.SystemRoles{types.RoleNode, types.RoleProxy},
Expires: expiry,
}
v2, err := types.NewProvisionToken(v1.Token, v1.Roles, expiry)
require.NoError(t, err)
// Tokens in different version formats are backwards and forwards
// compatible
require.Empty(t, cmp.Diff(v1.V2(), v2))
require.Empty(t, cmp.Diff(v2.V1(), v1))
// Marshal V1, unmarshal V2
data, err := services.MarshalProvisionToken(v2, services.WithVersion(types.V1))
require.NoError(t, err)
out, err := services.UnmarshalProvisionToken(data)
require.NoError(t, err)
require.Empty(t, cmp.Diff(out, v2))
// Test delete all tokens
tok, err = types.NewProvisionToken("token1", types.SystemRoles{types.RoleAuth, types.RoleNode}, time.Time{})
require.NoError(t, err)
@ -1374,7 +1397,6 @@ func (s *ServicesTestSuite) Events(t *testing.T) {
kind: types.WatchKind{
Kind: types.KindToken,
},
expectDeleteVersion: true,
crud: func(context.Context) types.Resource {
expires := time.Now().UTC().Add(time.Hour)
tok, err := types.NewProvisionToken("token",
@ -1821,18 +1843,14 @@ skiploop:
}
// delete events don't have IDs yet
header.SetResourceID(0)
if tc.expectDeleteVersion {
header.Version = types.VDeleted
}
ExpectDeleteResource(t, w, 3*time.Second, header)
}
}
type eventTest struct {
name string
kind types.WatchKind
expectDeleteVersion bool
crud func(context.Context) types.Resource
name string
kind types.WatchKind
crud func(context.Context) types.Resource
}
func eventsTestKinds(tests []eventTest) []types.WatchKind {

View file

@ -1 +0,0 @@
{"kind":"token","version":"v3","metadata":{"name":"foo","expires":"1999-11-30T00:00:00Z"},"spec":{"roles":["Nop"],"join_method":"token"}}

View file

@ -1 +0,0 @@
{"kind":"token","version":"v3","metadata":{"name":"foo","expires":"1999-11-30T00:00:00Z","id":1337},"spec":{"roles":["Nop"],"join_method":"token"}}

View file

@ -70,9 +70,6 @@ type scriptSettings struct {
}
func (h *Handler) createTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
// TODO: This API needs to switch to ProvisionTokenSpecV3 down the road
// Is there a reasonable way for us to determine v2 vs v3 specs - or
// do we need to introduce a separate endpoint.
var req types.ProvisionTokenSpecV2
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
@ -137,14 +134,10 @@ func (h *Handler) createTokenHandle(w http.ResponseWriter, r *http.Request, para
types.InternalResourceIDLabel: apiutils.Strings{uuid.NewString()},
}
v2 := types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: tokenName,
Expires: &expires,
},
Spec: req,
provisionToken, err := types.NewProvisionTokenFromSpec(tokenName, expires, req)
if err != nil {
return nil, trace.Wrap(err)
}
provisionToken := v2.V3()
err = clt.CreateToken(r.Context(), provisionToken)
if err != nil {

View file

@ -303,11 +303,11 @@ func TestGetNodeJoinScript(t *testing.T) {
},
mockGetToken: func(_ context.Context, token string) (types.ProvisionToken, error) {
if token == validToken || token == validIAMToken {
return &types.ProvisionTokenV3{
return &types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: token,
},
Spec: types.ProvisionTokenSpecV3{
Spec: types.ProvisionTokenSpecV2{
SuggestedLabels: types.Labels{
types.InternalResourceIDLabel: utils.Strings{internalResourceID},
},
@ -399,7 +399,7 @@ func TestGetAppJoinScript(t *testing.T) {
m := &mockedNodeAPIGetter{
mockGetToken: func(_ context.Context, token string) (types.ProvisionToken, error) {
if token == testTokenID {
return &types.ProvisionTokenV3{
return &types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: token,
},