mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-21 05:55:42 +08:00
Fix AWS IID TTL type in scoped tokens (#64026)
* changing the int64 field aws_iid_ttl into a string field named iid_ttl to support duration strings and reduce repetitive naming (e.g. aws.iid_ttl instead of aws.aws_iid_ttl) * export ParseDuration so we can take advantage of the month and year units * replaces aws_iid_ttl with iid_ttl when returning GetAWSIIDTTL() from a scoped token * ignoring scopes protos from breaking change linter while the feature is still experimental
This commit is contained in:
@@ -750,8 +750,9 @@ type AWS struct {
|
||||
// allow rule in order to use this token.
|
||||
Allow []*AWS_Rule `protobuf:"bytes,1,rep,name=allow,proto3" json:"allow,omitempty"`
|
||||
// The TTL to use for AWS EC2 Instance Identity Documents used
|
||||
// to join the cluster with this token.
|
||||
AwsIidTtl int64 `protobuf:"varint,2,opt,name=aws_iid_ttl,json=awsIidTtl,proto3" json:"aws_iid_ttl,omitempty"`
|
||||
// to join the cluster with this token. This should be a duration
|
||||
// string such as "8h" or "6mo".
|
||||
IidTtl string `protobuf:"bytes,2,opt,name=iid_ttl,json=iidTtl,proto3" json:"iid_ttl,omitempty"`
|
||||
// Integration name which provides credentials for validating join attempts.
|
||||
// Currently only in use for validating the AWS Organization ID in the IAM Join method.
|
||||
Integration string `protobuf:"bytes,3,opt,name=integration,proto3" json:"integration,omitempty"`
|
||||
@@ -796,11 +797,11 @@ func (x *AWS) GetAllow() []*AWS_Rule {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AWS) GetAwsIidTtl() int64 {
|
||||
func (x *AWS) GetIidTtl() string {
|
||||
if x != nil {
|
||||
return x.AwsIidTtl
|
||||
return x.IidTtl
|
||||
}
|
||||
return 0
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AWS) GetIntegration() string {
|
||||
@@ -1478,10 +1479,10 @@ const file_teleport_scopes_joining_v1_token_proto_rawDesc = "" +
|
||||
"\x05scope\x18\x05 \x01(\tR\x05scope\x12F\n" +
|
||||
"\x04spec\x18\x06 \x01(\v22.teleport.scopes.joining.v1.StaticScopedTokensSpecR\x04spec\"Y\n" +
|
||||
"\x16StaticScopedTokensSpec\x12?\n" +
|
||||
"\x06tokens\x18\x01 \x03(\v2'.teleport.scopes.joining.v1.ScopedTokenR\x06tokens\"\xb2\x02\n" +
|
||||
"\x06tokens\x18\x01 \x03(\v2'.teleport.scopes.joining.v1.ScopedTokenR\x06tokens\"\xab\x02\n" +
|
||||
"\x03AWS\x12:\n" +
|
||||
"\x05allow\x18\x01 \x03(\v2$.teleport.scopes.joining.v1.AWS.RuleR\x05allow\x12\x1e\n" +
|
||||
"\vaws_iid_ttl\x18\x02 \x01(\x03R\tawsIidTtl\x12 \n" +
|
||||
"\x05allow\x18\x01 \x03(\v2$.teleport.scopes.joining.v1.AWS.RuleR\x05allow\x12\x17\n" +
|
||||
"\aiid_ttl\x18\x02 \x01(\tR\x06iidTtl\x12 \n" +
|
||||
"\vintegration\x18\x03 \x01(\tR\vintegration\x1a\xac\x01\n" +
|
||||
"\x04Rule\x12\x1f\n" +
|
||||
"\vaws_account\x18\x01 \x01(\tR\n" +
|
||||
|
||||
@@ -196,8 +196,9 @@ message AWS {
|
||||
repeated Rule allow = 1;
|
||||
|
||||
// The TTL to use for AWS EC2 Instance Identity Documents used
|
||||
// to join the cluster with this token.
|
||||
int64 aws_iid_ttl = 2;
|
||||
// to join the cluster with this token. This should be a duration
|
||||
// string such as "8h" or "6mo".
|
||||
string iid_ttl = 2;
|
||||
|
||||
// Integration name which provides credentials for validating join attempts.
|
||||
// Currently only in use for validating the AWS Organization ID in the IAM Join method.
|
||||
|
||||
@@ -57,7 +57,7 @@ func (d *Duration) UnmarshalJSON(data []byte) error {
|
||||
*d = Duration(0)
|
||||
return nil
|
||||
}
|
||||
out, err := parseDuration(stringVar)
|
||||
out, err := ParseDuration(stringVar)
|
||||
if err != nil {
|
||||
return trace.BadParameter("%s", err)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*d = Duration(0)
|
||||
return nil
|
||||
}
|
||||
out, err := parseDuration(stringVar)
|
||||
out, err := ParseDuration(stringVar)
|
||||
if err != nil {
|
||||
return trace.BadParameter("%s", err)
|
||||
}
|
||||
@@ -165,12 +165,12 @@ var unitMap = map[string]int64{
|
||||
"y": int64(time.Hour * 24 * 365),
|
||||
}
|
||||
|
||||
// parseDuration parses a duration string.
|
||||
// ParseDuration parses a duration string.
|
||||
// A duration string is a possibly signed sequence of
|
||||
// decimal numbers, each with optional fraction and a unit suffix,
|
||||
// such as "300ms", "-1.5h" or "2h45m".
|
||||
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
func parseDuration(s string) (Duration, error) {
|
||||
func ParseDuration(s string) (Duration, error) {
|
||||
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
|
||||
orig := s
|
||||
var d int64
|
||||
|
||||
@@ -30,7 +30,7 @@ func FuzzParseDuration(f *testing.F) {
|
||||
|
||||
f.Fuzz(func(t *testing.T, s string) {
|
||||
require.NotPanics(t, func() {
|
||||
parseDuration(s)
|
||||
ParseDuration(s)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ breaking:
|
||||
- api/proto/teleport/decision/v1alpha1
|
||||
# TODO(nklaassen): Remove ignore once the new join API is stable.
|
||||
- api/proto/teleport/join/v1
|
||||
# TODO(eriktate): Remove ignore once the new scopes API is stable.
|
||||
- api/proto/teleport/scopes
|
||||
|
||||
plugins:
|
||||
- plugin:
|
||||
|
||||
@@ -69,8 +69,8 @@ func ScopedTokenFromProvisionTokenSpec(base types.ProvisionTokenSpecV2, override
|
||||
}
|
||||
}
|
||||
scopedToken.Spec.Aws = &joiningv1.AWS{
|
||||
Allow: allow,
|
||||
AwsIidTtl: int64(base.AWSIIDTTL),
|
||||
Allow: allow,
|
||||
IidTtl: base.AWSIIDTTL.Duration().String(),
|
||||
}
|
||||
case types.JoinMethodIAM:
|
||||
allow := make([]*joiningv1.AWS_Rule, len(base.Allow))
|
||||
|
||||
@@ -53,7 +53,13 @@ func validateJoinMethod(token *joiningv1.ScopedToken) error {
|
||||
if token.GetStatus().GetSecret() == "" {
|
||||
return trace.BadParameter("secret value must be defined for a scoped token when using the token join method")
|
||||
}
|
||||
case types.JoinMethodEC2, types.JoinMethodIAM:
|
||||
case types.JoinMethodEC2:
|
||||
ttl := token.GetSpec().GetAws().GetIidTtl()
|
||||
if _, err := types.ParseDuration(ttl); ttl != "" && err != nil {
|
||||
return trace.BadParameter("invalid IID TTL value %q, must be empty or a valid duration string (e.g. 30m, 12h, 1mo)", ttl)
|
||||
}
|
||||
fallthrough
|
||||
case types.JoinMethodIAM:
|
||||
if len(token.GetSpec().GetAws().GetAllow()) == 0 {
|
||||
return trace.BadParameter("aws configuration must be defined for a scoped token when using the ec2 or iam join methods")
|
||||
}
|
||||
@@ -345,12 +351,13 @@ func (t *Token) GetAWSAllowRules() []*types.TokenRule {
|
||||
|
||||
// GetAWSIIDTTL returns the TTL of EC2 IIDs
|
||||
func (t *Token) GetAWSIIDTTL() types.Duration {
|
||||
ttl := t.scoped.GetSpec().GetAws().GetAwsIidTtl()
|
||||
if ttl == 0 {
|
||||
// default to 5 minute ttl if unspecified
|
||||
ttl, err := types.ParseDuration(t.scoped.GetSpec().GetAws().GetIidTtl())
|
||||
if err != nil {
|
||||
// if parsing fails for any reason (including an empty value) we fallback to the 5 minute default
|
||||
return types.Duration(5 * time.Minute)
|
||||
}
|
||||
return types.Duration(ttl)
|
||||
|
||||
return ttl
|
||||
}
|
||||
|
||||
// GetIntegration returns the Integration field which is used to provide
|
||||
|
||||
@@ -233,6 +233,22 @@ func TestValidateScopedToken(t *testing.T) {
|
||||
expectedStrongErr: "aws configuration must be defined for a scoped token when using the ec2 or iam join methods",
|
||||
expectedWeakErr: "aws configuration must be defined for a scoped token when using the ec2 or iam join methods",
|
||||
},
|
||||
{
|
||||
name: "ec2 token with invalid IID TTL",
|
||||
modFn: func(tok *joiningv1.ScopedToken) {
|
||||
tok.Spec.JoinMethod = string(types.JoinMethodEC2)
|
||||
tok.Spec.Aws = &joiningv1.AWS{
|
||||
Allow: []*joiningv1.AWS_Rule{
|
||||
{
|
||||
AwsAccount: "1234567890",
|
||||
},
|
||||
},
|
||||
IidTtl: "123", // no unit specified
|
||||
}
|
||||
},
|
||||
expectedStrongErr: "invalid IID TTL value",
|
||||
expectedWeakErr: "invalid IID TTL value",
|
||||
},
|
||||
{
|
||||
name: "iam token without aws configuration",
|
||||
modFn: func(tok *joiningv1.ScopedToken) {
|
||||
@@ -277,7 +293,21 @@ func TestValidateScopedToken(t *testing.T) {
|
||||
name: "valid scoped token",
|
||||
},
|
||||
{
|
||||
name: "valid ec2 scoped token",
|
||||
name: "valid ec2 scoped token with TTL",
|
||||
modFn: func(tok *joiningv1.ScopedToken) {
|
||||
tok.Spec.JoinMethod = string(types.JoinMethodEC2)
|
||||
tok.Spec.Aws = &joiningv1.AWS{
|
||||
Allow: []*joiningv1.AWS_Rule{
|
||||
{
|
||||
AwsAccount: "1234567890",
|
||||
},
|
||||
},
|
||||
IidTtl: "6mo",
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid ec2 scoped token without TTL",
|
||||
modFn: func(tok *joiningv1.ScopedToken) {
|
||||
tok.Spec.JoinMethod = string(types.JoinMethodEC2)
|
||||
tok.Spec.Aws = &joiningv1.AWS{
|
||||
|
||||
Reference in New Issue
Block a user