DeployService: auto upsert IAM Join Token (#28537)

* DeployService: auto upsert IAM Join Token

When using the DeployService, the deployed services (database service
only for now) will join the Teleport Cluster using the IAM Join Method.

In order to do so, we require an IAM Token that allows the AWS Account
ID and ARN of the assumed-role.
Instead of asking the user to create it, we do it for them.

This PR creates or updates the IAM Join Token.

* AccountID is optional when calling DeployService

* dry code when upserting the token
This commit is contained in:
Marco André Dinis
2023-07-06 19:31:44 +00:00
committed by GitHub
parent 90e93e3dfe
commit 7ccb7ef891
9 changed files with 1232 additions and 23 deletions
+7
View File
@@ -98,6 +98,8 @@ type ProvisionToken interface {
SetRoles(SystemRoles)
// GetAllowRules returns the list of allow rules
GetAllowRules() []*TokenRule
// SetAllowRules sets the allow rules
SetAllowRules([]*TokenRule)
// GetAWSIIDTTL returns the TTL of EC2 IIDs
GetAWSIIDTTL() Duration
// GetJoinMethod returns joining method that must be used with this token.
@@ -327,6 +329,11 @@ func (p *ProvisionTokenV2) GetAllowRules() []*TokenRule {
return p.Spec.Allow
}
// SetAllowRules sets the allow rules.
func (p *ProvisionTokenV2) SetAllowRules(rules []*TokenRule) {
p.Spec.Allow = rules
}
// GetAWSIIDTTL returns the TTL of EC2 IIDs
func (p *ProvisionTokenV2) GetAWSIIDTTL() Duration {
return p.Spec.AWSIIDTTL
+10
View File
@@ -111,6 +111,16 @@ func newECSClient(ctx context.Context, req *AWSClientRequest) (*ecs.Client, erro
return ecs.NewFromConfig(*cfg), nil
}
// newSTSClient creates an [sts.Client] using the provided Token, RoleARN and Region.
func newSTSClient(ctx context.Context, req *AWSClientRequest) (*sts.Client, error) {
cfg, err := newAWSConfig(ctx, req)
if err != nil {
return nil, trace.Wrap(err)
}
return sts.NewFromConfig(*cfg), nil
}
// IdentityToken is an implementation of [stscreds.IdentityTokenRetriever] for returning a static token.
type IdentityToken string
+71 -2
View File
@@ -22,8 +22,10 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/gravitational/trace"
"golang.org/x/exp/slices"
@@ -91,6 +93,10 @@ type DeployServiceRequest struct {
// Region is the AWS Region
Region string
// AccountID is the AWS Account ID.
// Optional. sts.GetCallerIdentity is used if the value is not provided.
AccountID string
// SubnetIDs are the subnets associated with the service.
SubnetIDs []string
@@ -269,11 +275,54 @@ type DeployServiceClient interface {
// RegisterTaskDefinition registers a new task definition from the supplied family and containerDefinitions.
// https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ecs@v1.27.1#Client.RegisterTaskDefinition
RegisterTaskDefinition(ctx context.Context, params *ecs.RegisterTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.RegisterTaskDefinitionOutput, error)
// TokenService are the required methods to manage the IAM Join Token.
// When the deployed service connects to the cluster, it will use the IAM Join method.
// Before deploying the service, it must ensure that the token exists and has the appropriate token rul.
TokenService
// GetCallerIdentity returns details about the IAM user or role whose credentials are used to call the operation.
GetCallerIdentity(ctx context.Context, params *sts.GetCallerIdentityInput, optFns ...func(*sts.Options)) (*sts.GetCallerIdentityOutput, error)
}
type defaultDeployServiceClient struct {
*ecs.Client
stsClient *sts.Client
tokenServiceClient TokenService
}
// GetToken returns a provision token by name.
func (d *defaultDeployServiceClient) GetToken(ctx context.Context, name string) (types.ProvisionToken, error) {
return d.tokenServiceClient.GetToken(ctx, name)
}
// UpsertToken creates or updates a provision token.
func (d *defaultDeployServiceClient) UpsertToken(ctx context.Context, token types.ProvisionToken) error {
return d.tokenServiceClient.UpsertToken(ctx, token)
}
// GetCallerIdentity returns details about the IAM user or role whose credentials are used to call the operation.
func (d defaultDeployServiceClient) GetCallerIdentity(ctx context.Context, params *sts.GetCallerIdentityInput, optFns ...func(*sts.Options)) (*sts.GetCallerIdentityOutput, error) {
return d.stsClient.GetCallerIdentity(ctx, params, optFns...)
}
// NewDeployServiceClient creates a new DeployServiceClient using a AWSClientRequest.
func NewDeployServiceClient(ctx context.Context, clientReq *AWSClientRequest) (DeployServiceClient, error) {
return newECSClient(ctx, clientReq)
func NewDeployServiceClient(ctx context.Context, clientReq *AWSClientRequest, tokenServiceClient TokenService) (DeployServiceClient, error) {
ecsClient, err := newECSClient(ctx, clientReq)
if err != nil {
return nil, trace.Wrap(err)
}
stsClient, err := newSTSClient(ctx, clientReq)
if err != nil {
return nil, trace.Wrap(err)
}
return &defaultDeployServiceClient{
Client: ecsClient,
stsClient: stsClient,
tokenServiceClient: tokenServiceClient,
}, nil
}
// DeployService calls Amazon ECS APIs to deploy a Teleport Service.
@@ -312,6 +361,26 @@ func DeployService(ctx context.Context, clt DeployServiceClient, req DeployServi
return nil, trace.Wrap(err)
}
if req.AccountID == "" {
callerIdentity, err := clt.GetCallerIdentity(ctx, nil)
if err != nil {
return nil, trace.Wrap(err)
}
req.AccountID = aws.ToString(callerIdentity.Account)
}
upsertTokenReq := upsertIAMJoinTokenRequest{
tokenName: *req.TeleportIAMTokenName,
accountID: req.AccountID,
region: req.Region,
iamRole: req.TaskRoleARN,
deploymentMode: req.DeploymentMode,
}
if err := upsertIAMJoinToken(ctx, upsertTokenReq, clt); err != nil {
return nil, trace.Wrap(err)
}
teleportConfigString, err := generateTeleportConfigString(req)
if err != nil {
return nil, trace.Wrap(err)
@@ -0,0 +1,188 @@
/*
Copyright 2023 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 awsoidc
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/gravitational/trace"
"golang.org/x/exp/slices"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils"
awsutils "github.com/gravitational/teleport/api/utils/aws"
)
const (
// awsSTSShortName is the service name of the AWS STS
// Used to build the ARN matcher in the IAM Join Token.
awsSTSShortName = "sts"
)
// TokenService defines the required methods to upsert the Provision Token used by the Deploy Service.
type TokenService interface {
// GetToken returns a provision token by name.
GetToken(ctx context.Context, name string) (types.ProvisionToken, error)
// UpsertToken creates or updates a provision token.
UpsertToken(ctx context.Context, token types.ProvisionToken) error
}
// upsertIAMJoinTokenRequest is the request to create or update a Provision Token that has a rule which allows AWS Identities to join the cluster.
// Allowed AWS Identities are the ones described
// It gives access for those identities to join as system roles.
type upsertIAMJoinTokenRequest struct {
// tokenName is the Token's name to create or update.
tokenName string
// accountID is the allowed AWS Account ID.
accountID string
// region is the allowed AWS Region.
region string
// iamRole is the allowed IAM Role.
iamRole string
// deploymentMode is the service configuration that is going to be deployed
deploymentMode string
// awsARN is the IAM Role's ARN.
// This value is calculated using the AWS account, region and IAM role.
awsARN string
}
// CheckAndSetDefaults verifies the required fields are present.
func (u *upsertIAMJoinTokenRequest) CheckAndSetDefaults() error {
if u.tokenName == "" {
return trace.BadParameter("token name is required")
}
if u.accountID == "" {
return trace.BadParameter("accound id is required")
}
if u.region == "" {
return trace.BadParameter("region is required")
}
if u.iamRole == "" {
return trace.BadParameter("iam role is required")
}
if !slices.Contains(DeploymentModes, u.deploymentMode) {
return trace.BadParameter("invalid deployment mode, please use one of the following: %v", DeploymentModes)
}
partition := awsutils.GetPartitionFromRegion(u.region)
// Example ARN:
// arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143717490080920
u.awsARN = arn.ARN{
Partition: partition,
Service: awsSTSShortName,
AccountID: u.accountID,
Resource: fmt.Sprintf("assumed-role/%s/*", u.iamRole),
}.String()
return nil
}
// upsertIAMJoinToken creates or updates a Provision Token with a rule that allows Teleport Services with given AWS Identities to join the cluster.
// The allowed Identities are the ones provided in the request (Account, Region and AWSRole).
// It gives access for those identities to join as system roles.
func upsertIAMJoinToken(ctx context.Context, req upsertIAMJoinTokenRequest, clt TokenService) error {
if err := req.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
rule := &types.TokenRule{
AWSAccount: req.accountID,
AWSARN: req.awsARN,
}
token, err := clt.GetToken(ctx, req.tokenName)
switch {
case err != nil && !trace.IsNotFound(err):
return trace.Wrap(err)
case trace.IsNotFound(err):
token = &types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: req.tokenName,
},
Spec: types.ProvisionTokenSpecV2{
JoinMethod: types.JoinMethodIAM,
},
}
fallthrough
default:
existingToken, ok := token.(*types.ProvisionTokenV2)
if !ok {
return trace.BadParameter("expected token to be of type ProvisionTokenV2, got %T", token)
}
token, err = updateTokenIAMJoin(ctx, req, rule, existingToken)
if trace.IsAlreadyExists(err) {
// Early return when the required rule already exists.
return nil
}
if err != nil {
return trace.Wrap(err)
}
}
if err := clt.UpsertToken(ctx, token); err != nil {
return trace.Wrap(err)
}
return nil
}
func updateTokenIAMJoin(ctx context.Context, req upsertIAMJoinTokenRequest, rule *types.TokenRule, existingToken *types.ProvisionTokenV2) (types.ProvisionToken, error) {
if existingToken.GetJoinMethod() != types.JoinMethodIAM {
return nil, trace.BadParameter("Token %q already exists but has the wrong join method %q. "+
"Please remove it before continuing.",
req.tokenName, existingToken.GetJoinMethod(),
)
}
allRoles := existingToken.GetRoles()
switch req.deploymentMode {
case DatabaseServiceDeploymentMode:
allRoles = append(allRoles, types.RoleDatabase)
default:
return nil, trace.BadParameter("invalid deployment mode %q, supported modes: %v", req.deploymentMode, DeploymentModes)
}
uniqueRoles := utils.Deduplicate(allRoles)
existingToken.SetRoles(uniqueRoles)
for _, rule := range existingToken.GetAllowRules() {
if rule.AWSAccount == req.accountID && rule.AWSARN == req.awsARN {
return nil, trace.AlreadyExists("rule already exists")
}
}
existingToken.SetAllowRules(append(existingToken.GetAllowRules(), rule))
return existingToken, nil
}
@@ -0,0 +1,147 @@
/*
Copyright 2023 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 awsoidc
import (
"context"
"testing"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/api/types"
)
type mockGetUpsertToken struct {
token types.ProvisionToken
}
// GetToken returns a provision token by name.
func (m *mockGetUpsertToken) GetToken(ctx context.Context, name string) (types.ProvisionToken, error) {
if m.token != nil && name == m.token.GetName() {
return m.token, nil
}
return nil, trace.NotFound("token not found")
}
// UpsertToken creates or updates a provision token.
func (m *mockGetUpsertToken) UpsertToken(ctx context.Context, token types.ProvisionToken) error {
m.token = token
return nil
}
func TestUpsertIAMJoinToken(t *testing.T) {
ctx := context.Background()
t.Run("when token doesnt exist, it is created", func(t *testing.T) {
m := &mockGetUpsertToken{}
err := upsertIAMJoinToken(ctx, upsertIAMJoinTokenRequest{
tokenName: "t",
accountID: "123456789012",
region: "us-east-1",
iamRole: "myrole",
deploymentMode: DatabaseServiceDeploymentMode,
}, m)
require.NoError(t, err)
iamToken, err := m.GetToken(ctx, "t")
require.NoError(t, err)
require.Equal(t, "t", iamToken.GetName())
require.Contains(t, iamToken.GetRoles(), types.RoleDatabase)
require.Len(t, iamToken.GetAllowRules(), 1)
require.Equal(t, iamToken.GetAllowRules()[0], &types.TokenRule{
AWSAccount: "123456789012",
AWSARN: "arn:aws:sts::123456789012:assumed-role/myrole/*",
})
})
t.Run("when token exist but is missing the required allow rule and system role, it is updated", func(t *testing.T) {
m := &mockGetUpsertToken{
token: &types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: "t",
},
Spec: types.ProvisionTokenSpecV2{
JoinMethod: types.JoinMethodIAM,
Roles: []types.SystemRole{},
Allow: []*types.TokenRule{},
},
},
}
err := upsertIAMJoinToken(ctx, upsertIAMJoinTokenRequest{
tokenName: "t",
accountID: "123456789012",
region: "us-east-1",
iamRole: "myrole",
deploymentMode: DatabaseServiceDeploymentMode,
}, m)
require.NoError(t, err)
iamToken, err := m.GetToken(ctx, "t")
require.NoError(t, err)
require.Equal(t, "t", iamToken.GetName())
require.Len(t, iamToken.GetAllowRules(), 1)
require.Contains(t, iamToken.GetRoles(), types.RoleDatabase)
require.Equal(t, iamToken.GetAllowRules()[0], &types.TokenRule{
AWSAccount: "123456789012",
AWSARN: "arn:aws:sts::123456789012:assumed-role/myrole/*",
})
})
t.Run("when token exist but has an invalid join method, it returns an error", func(t *testing.T) {
m := &mockGetUpsertToken{
token: &types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: "t",
},
Spec: types.ProvisionTokenSpecV2{
JoinMethod: types.JoinMethodEC2,
Roles: []types.SystemRole{},
Allow: []*types.TokenRule{},
},
},
}
err := upsertIAMJoinToken(ctx, upsertIAMJoinTokenRequest{
tokenName: "t",
accountID: "123456789012",
region: "us-east-1",
iamRole: "myrole",
deploymentMode: DatabaseServiceDeploymentMode,
}, m)
require.ErrorContains(t, err, `Token "t" already exists but has the wrong join method "ec2". Please remove it before continuing.`)
})
t.Run("when deployment method is invalid, it returns an error", func(t *testing.T) {
m := &mockGetUpsertToken{}
err := upsertIAMJoinToken(ctx, upsertIAMJoinTokenRequest{
tokenName: "t",
accountID: "123456789012",
region: "us-east-1",
iamRole: "myrole",
deploymentMode: "invalid-deploy-method",
}, m)
require.ErrorContains(t, err, "invalid deployment mode, please use one of the following: [database-service]")
})
}
@@ -42,8 +42,10 @@ func TestDeployDBService(t *testing.T) {
awsOIDCToken := "x.y.z"
awsRegion := "us-east-1"
awsOIDCRoleARN := "arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider"
awsAccountID := "278576220453"
awsOIDCRoleARN := "arn:aws:iam::" + awsAccountID + ":role/MarcoTestRoleOIDCProvider"
integrationName := "teleportdev"
taskRole := "MarcoEC2Role"
removeKeysRegex, err := regexp.Compile(`(?s)(<AccessKeyId>).*(</AccessKeyId>).*(<SecretAccessKey>).*(</SecretAccessKey>).*(<SessionToken>).*(</SessionToken>)`)
require.NoError(t, err)
@@ -75,7 +77,8 @@ func TestDeployDBService(t *testing.T) {
deployServiceReqFunc := func(clusterName string) DeployServiceRequest {
return DeployServiceRequest{
Region: awsRegion,
Region: awsRegion,
AccountID: awsAccountID,
SubnetIDs: []string{
"subnet-0b7ab67161173748b",
"subnet-0dda93c8621eb2e99",
@@ -84,9 +87,9 @@ func TestDeployDBService(t *testing.T) {
"subnet-0ef025345dd791986",
"subnet-099632749366c2c56",
},
TaskRoleARN: "MarcoEC2Role",
TaskRoleARN: taskRole,
TeleportClusterName: clusterName,
IntegrationName: "teleportdev",
IntegrationName: integrationName,
DeploymentMode: DatabaseServiceDeploymentMode,
ProxyServerHostPort: "marcodinis.teleportdemo.net:443",
DatabaseResourceMatcherLabels: types.Labels{
@@ -106,15 +109,36 @@ func TestDeployDBService(t *testing.T) {
return r
}
rule := &types.TokenRule{
AWSAccount: "123456789012",
AWSRegions: []string{awsRegion},
AWSRole: taskRole,
}
iamJoinToken := &types.ProvisionTokenV2{
Metadata: types.Metadata{
Name: defaultTeleportIAMTokenName,
},
Spec: types.ProvisionTokenSpecV2{
JoinMethod: types.JoinMethodIAM,
Roles: types.SystemRoles{types.RoleDatabase},
Allow: []*types.TokenRule{rule},
},
}
tokenStore := mockGetUpsertToken{
token: iamJoinToken,
}
t.Run("nothing exists in aws account", func(t *testing.T) {
r := mustRecordUsing(t, "fixtures/emptyaccount")
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
resp, err := DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
resp, err := DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.NoError(t, err)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:cluster/cluster1002-teleport", resp.ClusterARN)
@@ -128,10 +152,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
resp, err := DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
resp, err := DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.NoError(t, err)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:cluster/cluster1002-teleport", resp.ClusterARN)
@@ -145,10 +169,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
_, err = DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
_, err = DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.ErrorContains(t, err, "ECS Service is draining, please retry in a couple of minutes")
})
@@ -157,10 +181,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
resp, err := DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
resp, err := DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.NoError(t, err)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:cluster/cluster1002-teleport", resp.ClusterARN)
@@ -174,10 +198,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
resp, err := DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
resp, err := DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.NoError(t, err)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:cluster/cluster1002-teleport", resp.ClusterARN)
@@ -191,10 +215,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
_, err = DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
_, err = DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.ErrorContains(t, err, `ECS Cluster "cluster1002-teleport" already exists but is not managed by Teleport. Add the following tags to allow Teleport to manage this cluster:`)
})
@@ -203,10 +227,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
_, err = DeployService(ctx, ecsClient, deployServiceReqFunc("cluster1002"))
_, err = DeployService(ctx, clt, deployServiceReqFunc("cluster1002"))
require.ErrorContains(t, err, `ECS Service "cluster1002-teleport-database-service" already exists but is not managed by Teleport. Add the following tags to allow Teleport to manage this service:`)
})
@@ -215,10 +239,10 @@ func TestDeployDBService(t *testing.T) {
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
ecsClient, err := newECSClient(ctx, awsClientRecorder)
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
resp, err := DeployService(ctx, ecsClient, deployServiceReqFunc("tenant-a.teleport.sh"))
resp, err := DeployService(ctx, clt, deployServiceReqFunc("tenant-a.teleport.sh"))
require.NoError(t, err)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:cluster/tenant-a_teleport_sh-teleport", resp.ClusterARN)
@@ -226,4 +250,23 @@ func TestDeployDBService(t *testing.T) {
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:task-definition/tenant-a_teleport_sh-teleport-database-service:1", resp.TaskDefinitionARN)
require.Equal(t, "https://us-east-1.console.aws.amazon.com/ecs/v2/clusters/tenant-a_teleport_sh-teleport/services/tenant-a_teleport_sh-teleport-database-service", resp.ServiceDashboardURL)
})
t.Run("deploying without providing an AccountID", func(t *testing.T) {
r := mustRecordUsing(t, "fixtures/without_account_id")
defer r.Stop()
awsClientRecorder := awsClientReqFunc(r.GetDefaultClient())
clt, err := NewDeployServiceClient(ctx, awsClientRecorder, &tokenStore)
require.NoError(t, err)
deployReq := deployServiceReqFunc("marco-test.teleport.sh")
deployReq.AccountID = ""
resp, err := DeployService(ctx, clt, deployReq)
require.NoError(t, err)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:cluster/marco-test_teleport_sh-teleport", resp.ClusterARN)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:service/marco-test_teleport_sh-teleport/marco-test_teleport_sh-teleport-database-service", resp.ServiceARN)
require.Equal(t, "arn:aws:ecs:us-east-1:278576220453:task-definition/marco-test_teleport_sh-teleport-database-service:1", resp.TaskDefinitionARN)
require.Equal(t, "https://us-east-1.console.aws.amazon.com/ecs/v2/clusters/marco-test_teleport_sh-teleport/services/marco-test_teleport_sh-teleport-database-service", resp.ServiceDashboardURL)
})
}
@@ -0,0 +1,735 @@
---
version: 2
interactions:
- id: 0
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 841
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- AssumeRoleWithWebIdentity
RoleArn:
- arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider
RoleSessionName:
- "1688143003997812852"
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- 4490a0f5-e4e2-4fc1-abaf-8fe12b15db90
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 1930
uncompressed: false
body: |
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithWebIdentityResult>
<Audience>discover.teleport</Audience>
<AssumedRoleUser>
<AssumedRoleId>AROAUBXDPZES62GD44VBR:1688143003997812852</AssumedRoleId>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143003997812852</Arn>
</AssumedRoleUser>
<Provider>arn:aws:iam::278576220453:oidc-provider/marcodinis.teleportdemo.net</Provider>
<Credentials>
<AccessKeyId>x</AccessKeyId><SecretAccessKey>x</SecretAccessKey><SessionToken>x</SessionToken>
<Expiration>2023-06-30T17:36:45Z</Expiration>
</Credentials>
<SubjectFromWebIdentityToken>system:proxy</SubjectFromWebIdentityToken>
</AssumeRoleWithWebIdentityResult>
<ResponseMetadata>
<RequestId>3bdfbb2b-fa78-4408-9bd3-1beed5c0ef5c</RequestId>
</ResponseMetadata>
</AssumeRoleWithWebIdentityResponse>
headers:
Content-Length:
- "1930"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:44 GMT
X-Amzn-Requestid:
- 3bdfbb2b-fa78-4408-9bd3-1beed5c0ef5c
status: 200 OK
code: 200
duration: 1.156135491s
- id: 1
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 43
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- GetCallerIdentity
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- b6da4111-9009-421d-8512-0cba88a4b753
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
X-Amz-Date:
- 20230630T163645Z
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 470
uncompressed: false
body: |
<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<GetCallerIdentityResult>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143003997812852</Arn>
<UserId>AROAUBXDPZES62GD44VBR:1688143003997812852</UserId>
<Account>278576220453</Account>
</GetCallerIdentityResult>
<ResponseMetadata>
<RequestId>dd49d394-8611-4132-abb5-c3a83bc470ee</RequestId>
</ResponseMetadata>
</GetCallerIdentityResponse>
headers:
Content-Length:
- "470"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:44 GMT
X-Amzn-Requestid:
- dd49d394-8611-4132-abb5-c3a83bc470ee
status: 200 OK
code: 200
duration: 148.178727ms
- id: 2
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 841
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- AssumeRoleWithWebIdentity
RoleArn:
- arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider
RoleSessionName:
- "1688143005305146353"
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- 589ceea1-b392-4a4c-9742-d70dc57933b2
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 1930
uncompressed: false
body: |
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithWebIdentityResult>
<Audience>discover.teleport</Audience>
<AssumedRoleUser>
<AssumedRoleId>AROAUBXDPZES62GD44VBR:1688143005305146353</AssumedRoleId>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143005305146353</Arn>
</AssumedRoleUser>
<Provider>arn:aws:iam::278576220453:oidc-provider/marcodinis.teleportdemo.net</Provider>
<Credentials>
<AccessKeyId>x</AccessKeyId><SecretAccessKey>x</SecretAccessKey><SessionToken>x</SessionToken>
<Expiration>2023-06-30T17:36:45Z</Expiration>
</Credentials>
<SubjectFromWebIdentityToken>system:proxy</SubjectFromWebIdentityToken>
</AssumeRoleWithWebIdentityResult>
<ResponseMetadata>
<RequestId>41e3b73d-0155-4815-b2c4-dc9b659fa094</RequestId>
</ResponseMetadata>
</AssumeRoleWithWebIdentityResponse>
headers:
Content-Length:
- "1930"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:44 GMT
X-Amzn-Requestid:
- 41e3b73d-0155-4815-b2c4-dc9b659fa094
status: 200 OK
code: 200
duration: 135.889596ms
- id: 3
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 1758
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: '{"containerDefinitions":[{"command":["start","--config-string","dmVyc2lvbjogdjMKdGVsZXBvcnQ6CiAgZGF0YV9kaXI6IC92YXIvbGliL3RlbGVwb3J0CiAgam9pbl9wYXJhbXM6CiAgICB0b2tlbl9uYW1lOiBkaXNjb3Zlci1hd3Mtb2lkYy1pYW0tdG9rZW4KICAgIG1ldGhvZDogaWFtCiAgcHJveHlfc2VydmVyOiBtYXJjb2RpbmlzLnRlbGVwb3J0ZGVtby5uZXQ6NDQzCiAgbG9nOgogICAgb3V0cHV0OiBzdGRlcnIKICAgIHNldmVyaXR5OiBJTkZPCiAgICBmb3JtYXQ6CiAgICAgIG91dHB1dDogdGV4dAogIGNhX3BpbjogIiIKICBkaWFnX2FkZHI6ICIiCmF1dGhfc2VydmljZToKICBlbmFibGVkOiAibm8iCiAgbGlzdGVuX2FkZHI6IDAuMC4wLjA6MzAyNQogIHByb3h5X2xpc3RlbmVyX21vZGU6IG11bHRpcGxleApzc2hfc2VydmljZToKICBlbmFibGVkOiAibm8iCiAgY29tbWFuZHM6CiAgLSBuYW1lOiBob3N0bmFtZQogICAgY29tbWFuZDogW2hvc3RuYW1lXQogICAgcGVyaW9kOiAxbTBzCnByb3h5X3NlcnZpY2U6CiAgZW5hYmxlZDogIm5vIgogIGh0dHBzX2tleXBhaXJzOiBbXQogIGh0dHBzX2tleXBhaXJzX3JlbG9hZF9pbnRlcnZhbDogMHMKICBhY21lOiB7fQpkYl9zZXJ2aWNlOgogIGVuYWJsZWQ6ICJ5ZXMiCiAgZGF0YWJhc2VzOiBbXQogIHJlc291cmNlczoKICAtIGxhYmVsczoKICAgICAgJyonOiAnKicK"],"entryPoint":["teleport"],"image":"public.ecr.aws/gravitational/teleport-distroless:13.1.5","logConfiguration":{"logDriver":"awslogs","options":{"awslogs-region":"us-east-1","awslogs-stream-prefix":"marco-test_teleport_sh-teleport-database-service/marco-test_teleport_sh-teleport-database-service","awslogs-create-group":"true","awslogs-group":"ecs-marco-test_teleport_sh-teleport"}},"name":"teleport-service"}],"cpu":"2048","executionRoleArn":"MarcoEC2Role","family":"marco-test_teleport_sh-teleport-database-service","memory":"4096","networkMode":"awsvpc","requiresCompatibilities":["FARGATE"],"tags":[{"key":"teleport.dev/origin","value":"integration_awsoidc"},{"key":"teleport.dev/cluster","value":"marco-test.teleport.sh"},{"key":"teleport.dev/integration","value":"teleportdev"}],"taskRoleArn":"MarcoEC2Role"}'
form: {}
headers:
Amz-Sdk-Invocation-Id:
- c44070a0-dfef-4ac9-a17d-7630a2d31a7a
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-amz-json-1.1
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/ecs/1.27.1
X-Amz-Date:
- 20230630T163645Z
X-Amz-Target:
- AmazonEC2ContainerServiceV20141113.RegisterTaskDefinition
url: https://ecs.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 2709
uncompressed: false
body: '{"tags":[{"key":"teleport.dev/origin","value":"integration_awsoidc"},{"key":"teleport.dev/cluster","value":"marco-test.teleport.sh"},{"key":"teleport.dev/integration","value":"teleportdev"}],"taskDefinition":{"compatibilities":["EC2","FARGATE"],"containerDefinitions":[{"command":["start","--config-string","dmVyc2lvbjogdjMKdGVsZXBvcnQ6CiAgZGF0YV9kaXI6IC92YXIvbGliL3RlbGVwb3J0CiAgam9pbl9wYXJhbXM6CiAgICB0b2tlbl9uYW1lOiBkaXNjb3Zlci1hd3Mtb2lkYy1pYW0tdG9rZW4KICAgIG1ldGhvZDogaWFtCiAgcHJveHlfc2VydmVyOiBtYXJjb2RpbmlzLnRlbGVwb3J0ZGVtby5uZXQ6NDQzCiAgbG9nOgogICAgb3V0cHV0OiBzdGRlcnIKICAgIHNldmVyaXR5OiBJTkZPCiAgICBmb3JtYXQ6CiAgICAgIG91dHB1dDogdGV4dAogIGNhX3BpbjogIiIKICBkaWFnX2FkZHI6ICIiCmF1dGhfc2VydmljZToKICBlbmFibGVkOiAibm8iCiAgbGlzdGVuX2FkZHI6IDAuMC4wLjA6MzAyNQogIHByb3h5X2xpc3RlbmVyX21vZGU6IG11bHRpcGxleApzc2hfc2VydmljZToKICBlbmFibGVkOiAibm8iCiAgY29tbWFuZHM6CiAgLSBuYW1lOiBob3N0bmFtZQogICAgY29tbWFuZDogW2hvc3RuYW1lXQogICAgcGVyaW9kOiAxbTBzCnByb3h5X3NlcnZpY2U6CiAgZW5hYmxlZDogIm5vIgogIGh0dHBzX2tleXBhaXJzOiBbXQogIGh0dHBzX2tleXBhaXJzX3JlbG9hZF9pbnRlcnZhbDogMHMKICBhY21lOiB7fQpkYl9zZXJ2aWNlOgogIGVuYWJsZWQ6ICJ5ZXMiCiAgZGF0YWJhc2VzOiBbXQogIHJlc291cmNlczoKICAtIGxhYmVsczoKICAgICAgJyonOiAnKicK"],"cpu":0,"entryPoint":["teleport"],"environment":[],"essential":true,"image":"public.ecr.aws/gravitational/teleport-distroless:13.1.5","logConfiguration":{"logDriver":"awslogs","options":{"awslogs-create-group":"true","awslogs-group":"ecs-marco-test_teleport_sh-teleport","awslogs-region":"us-east-1","awslogs-stream-prefix":"marco-test_teleport_sh-teleport-database-service/marco-test_teleport_sh-teleport-database-service"}},"mountPoints":[],"name":"teleport-service","portMappings":[],"volumesFrom":[]}],"cpu":"2048","executionRoleArn":"arn:aws:iam::278576220453:role/MarcoEC2Role","family":"marco-test_teleport_sh-teleport-database-service","memory":"4096","networkMode":"awsvpc","placementConstraints":[],"registeredAt":1.68814300616E9,"registeredBy":"arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143005305146353","requiresAttributes":[{"name":"com.amazonaws.ecs.capability.logging-driver.awslogs"},{"name":"ecs.capability.execution-role-awslogs"},{"name":"com.amazonaws.ecs.capability.docker-remote-api.1.19"},{"name":"com.amazonaws.ecs.capability.task-iam-role"},{"name":"com.amazonaws.ecs.capability.docker-remote-api.1.18"},{"name":"ecs.capability.task-eni"},{"name":"com.amazonaws.ecs.capability.docker-remote-api.1.29"}],"requiresCompatibilities":["FARGATE"],"revision":1,"status":"ACTIVE","taskDefinitionArn":"arn:aws:ecs:us-east-1:278576220453:task-definition/marco-test_teleport_sh-teleport-database-service:1","taskRoleArn":"arn:aws:iam::278576220453:role/MarcoEC2Role","volumes":[]}}'
headers:
Content-Length:
- "2709"
Content-Type:
- application/x-amz-json-1.1
Date:
- Fri, 30 Jun 2023 16:36:46 GMT
X-Amzn-Requestid:
- 4ccc07d8-eec0-4699-90bd-54a26dca3c85
status: 200 OK
code: 200
duration: 842.168015ms
- id: 4
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 841
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- AssumeRoleWithWebIdentity
RoleArn:
- arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider
RoleSessionName:
- "1688143006284945637"
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- eb3fb8bc-4d94-4ef1-8a66-97b43cfee904
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 1930
uncompressed: false
body: |
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithWebIdentityResult>
<Audience>discover.teleport</Audience>
<AssumedRoleUser>
<AssumedRoleId>AROAUBXDPZES62GD44VBR:1688143006284945637</AssumedRoleId>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143006284945637</Arn>
</AssumedRoleUser>
<Provider>arn:aws:iam::278576220453:oidc-provider/marcodinis.teleportdemo.net</Provider>
<Credentials>
<AccessKeyId>x</AccessKeyId><SecretAccessKey>x</SecretAccessKey><SessionToken>x</SessionToken>
<Expiration>2023-06-30T17:36:46Z</Expiration>
</Credentials>
<SubjectFromWebIdentityToken>system:proxy</SubjectFromWebIdentityToken>
</AssumeRoleWithWebIdentityResult>
<ResponseMetadata>
<RequestId>3c987e7b-846f-418b-8d68-dc2a3832d79d</RequestId>
</ResponseMetadata>
</AssumeRoleWithWebIdentityResponse>
headers:
Content-Length:
- "1930"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:45 GMT
X-Amzn-Requestid:
- 3c987e7b-846f-418b-8d68-dc2a3832d79d
status: 200 OK
code: 200
duration: 150.042341ms
- id: 5
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 67
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: '{"clusters":["marco-test_teleport_sh-teleport"],"include":["TAGS"]}'
form: {}
headers:
Amz-Sdk-Invocation-Id:
- ef0bc944-68cd-4118-b1c0-abd607cde639
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-amz-json-1.1
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/ecs/1.27.1
X-Amz-Date:
- 20230630T163646Z
X-Amz-Target:
- AmazonEC2ContainerServiceV20141113.DescribeClusters
url: https://ecs.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 132
uncompressed: false
body: '{"clusters":[],"failures":[{"arn":"arn:aws:ecs:us-east-1:278576220453:cluster/marco-test_teleport_sh-teleport","reason":"MISSING"}]}'
headers:
Content-Length:
- "132"
Content-Type:
- application/x-amz-json-1.1
Date:
- Fri, 30 Jun 2023 16:36:46 GMT
X-Amzn-Requestid:
- dfc8708c-a3ea-44b6-a669-1b7a20e7ac07
status: 200 OK
code: 200
duration: 156.835699ms
- id: 6
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 841
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- AssumeRoleWithWebIdentity
RoleArn:
- arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider
RoleSessionName:
- "1688143006593327122"
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- b4d0bb2f-2789-45ac-8a2d-ad140f35edfc
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 1930
uncompressed: false
body: |
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithWebIdentityResult>
<Audience>discover.teleport</Audience>
<AssumedRoleUser>
<AssumedRoleId>AROAUBXDPZES62GD44VBR:1688143006593327122</AssumedRoleId>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143006593327122</Arn>
</AssumedRoleUser>
<Provider>arn:aws:iam::278576220453:oidc-provider/marcodinis.teleportdemo.net</Provider>
<Credentials>
<AccessKeyId>x</AccessKeyId><SecretAccessKey>x</SecretAccessKey><SessionToken>x</SessionToken>
<Expiration>2023-06-30T17:36:46Z</Expiration>
</Credentials>
<SubjectFromWebIdentityToken>system:proxy</SubjectFromWebIdentityToken>
</AssumeRoleWithWebIdentityResult>
<ResponseMetadata>
<RequestId>671fcccd-faf5-4266-be46-2ae232425426</RequestId>
</ResponseMetadata>
</AssumeRoleWithWebIdentityResponse>
headers:
Content-Length:
- "1930"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:45 GMT
X-Amzn-Requestid:
- 671fcccd-faf5-4266-be46-2ae232425426
status: 200 OK
code: 200
duration: 149.356738ms
- id: 7
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 271
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: '{"capacityProviders":["FARGATE"],"clusterName":"marco-test_teleport_sh-teleport","tags":[{"key":"teleport.dev/origin","value":"integration_awsoidc"},{"key":"teleport.dev/cluster","value":"marco-test.teleport.sh"},{"key":"teleport.dev/integration","value":"teleportdev"}]}'
form: {}
headers:
Amz-Sdk-Invocation-Id:
- 1e0c7868-cb53-4696-826a-2a6b256f8e7f
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-amz-json-1.1
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/ecs/1.27.1
X-Amz-Date:
- 20230630T163646Z
X-Amz-Target:
- AmazonEC2ContainerServiceV20141113.CreateCluster
url: https://ecs.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 628
uncompressed: false
body: '{"cluster":{"activeServicesCount":0,"capacityProviders":["FARGATE"],"clusterArn":"arn:aws:ecs:us-east-1:278576220453:cluster/marco-test_teleport_sh-teleport","clusterName":"marco-test_teleport_sh-teleport","defaultCapacityProviderStrategy":[],"pendingTasksCount":0,"registeredContainerInstancesCount":0,"runningTasksCount":0,"settings":[{"name":"containerInsights","value":"disabled"}],"statistics":[],"status":"ACTIVE","tags":[{"key":"teleport.dev/cluster","value":"marco-test.teleport.sh"},{"key":"teleport.dev/origin","value":"integration_awsoidc"},{"key":"teleport.dev/integration","value":"teleportdev"}]},"clusterCount":0}'
headers:
Content-Length:
- "628"
Content-Type:
- application/x-amz-json-1.1
Date:
- Fri, 30 Jun 2023 16:36:47 GMT
X-Amzn-Requestid:
- 90e567e8-4be4-43c0-9c5f-68ae95c099c9
status: 200 OK
code: 200
duration: 351.106592ms
- id: 8
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 841
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- AssumeRoleWithWebIdentity
RoleArn:
- arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider
RoleSessionName:
- "1688143007101947892"
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- 02f62841-1540-49a6-b51e-d373c49d8253
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 1930
uncompressed: false
body: |
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithWebIdentityResult>
<Audience>discover.teleport</Audience>
<AssumedRoleUser>
<AssumedRoleId>AROAUBXDPZES62GD44VBR:1688143007101947892</AssumedRoleId>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143007101947892</Arn>
</AssumedRoleUser>
<Provider>arn:aws:iam::278576220453:oidc-provider/marcodinis.teleportdemo.net</Provider>
<Credentials>
<AccessKeyId>x</AccessKeyId><SecretAccessKey>x</SecretAccessKey><SessionToken>x</SessionToken>
<Expiration>2023-06-30T17:36:47Z</Expiration>
</Credentials>
<SubjectFromWebIdentityToken>system:proxy</SubjectFromWebIdentityToken>
</AssumeRoleWithWebIdentityResult>
<ResponseMetadata>
<RequestId>a4525bba-f394-44ff-9a6c-f53bb561030a</RequestId>
</ResponseMetadata>
</AssumeRoleWithWebIdentityResponse>
headers:
Content-Length:
- "1930"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:46 GMT
X-Amzn-Requestid:
- a4525bba-f394-44ff-9a6c-f53bb561030a
status: 200 OK
code: 200
duration: 132.303974ms
- id: 9
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 128
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: '{"cluster":"marco-test_teleport_sh-teleport","include":["TAGS"],"services":["marco-test_teleport_sh-teleport-database-service"]}'
form: {}
headers:
Amz-Sdk-Invocation-Id:
- e74d5b55-027a-44f5-8cc8-f5030cf15f9c
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-amz-json-1.1
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/ecs/1.27.1
X-Amz-Date:
- 20230630T163647Z
X-Amz-Target:
- AmazonEC2ContainerServiceV20141113.DescribeServices
url: https://ecs.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 149
uncompressed: false
body: '{"failures":[{"arn":"arn:aws:ecs:us-east-1:278576220453:service/marco-test_teleport_sh-teleport-database-service","reason":"MISSING"}],"services":[]}'
headers:
Content-Length:
- "149"
Content-Type:
- application/x-amz-json-1.1
Date:
- Fri, 30 Jun 2023 16:36:47 GMT
X-Amzn-Requestid:
- 199a7aa3-5382-4540-819c-878b75ed4ea7
status: 200 OK
code: 200
duration: 181.862256ms
- id: 10
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 841
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: ""
form:
Action:
- AssumeRoleWithWebIdentity
RoleArn:
- arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider
RoleSessionName:
- "1688143007417578644"
Version:
- "2011-06-15"
headers:
Amz-Sdk-Invocation-Id:
- 1e040ace-82a4-4df9-b80a-b5296ab6ed86
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/sts/1.19.2
url: https://sts.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 1930
uncompressed: false
body: |
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithWebIdentityResult>
<Audience>discover.teleport</Audience>
<AssumedRoleUser>
<AssumedRoleId>AROAUBXDPZES62GD44VBR:1688143007417578644</AssumedRoleId>
<Arn>arn:aws:sts::278576220453:assumed-role/MarcoTestRoleOIDCProvider/1688143007417578644</Arn>
</AssumedRoleUser>
<Provider>arn:aws:iam::278576220453:oidc-provider/marcodinis.teleportdemo.net</Provider>
<Credentials>
<AccessKeyId>x</AccessKeyId><SecretAccessKey>x</SecretAccessKey><SessionToken>x</SessionToken>
<Expiration>2023-06-30T17:36:47Z</Expiration>
</Credentials>
<SubjectFromWebIdentityToken>system:proxy</SubjectFromWebIdentityToken>
</AssumeRoleWithWebIdentityResult>
<ResponseMetadata>
<RequestId>2cec22c7-83d1-4c7a-bb21-cf81963a07a7</RequestId>
</ResponseMetadata>
</AssumeRoleWithWebIdentityResponse>
headers:
Content-Length:
- "1930"
Content-Type:
- text/xml
Date:
- Fri, 30 Jun 2023 16:36:46 GMT
X-Amzn-Requestid:
- 2cec22c7-83d1-4c7a-bb21-cf81963a07a7
status: 200 OK
code: 200
duration: 157.831407ms
- id: 11
request:
proto: ""
proto_major: 0
proto_minor: 0
content_length: 737
transfer_encoding: []
trailer: {}
host: ""
remote_addr: ""
request_uri: ""
body: '{"cluster":"marco-test_teleport_sh-teleport","desiredCount":1,"launchType":"FARGATE","networkConfiguration":{"awsvpcConfiguration":{"assignPublicIp":"ENABLED","subnets":["subnet-0b7ab67161173748b","subnet-0dda93c8621eb2e99","subnet-034f17b3f7344e375","subnet-04a07d4721a3c96e0","subnet-0ef025345dd791986","subnet-099632749366c2c56"]}},"propagateTags":"SERVICE","serviceName":"marco-test_teleport_sh-teleport-database-service","tags":[{"key":"teleport.dev/origin","value":"integration_awsoidc"},{"key":"teleport.dev/cluster","value":"marco-test.teleport.sh"},{"key":"teleport.dev/integration","value":"teleportdev"}],"taskDefinition":"arn:aws:ecs:us-east-1:278576220453:task-definition/marco-test_teleport_sh-teleport-database-service:1"}'
form: {}
headers:
Amz-Sdk-Invocation-Id:
- 22fb41be-8988-43f5-b171-1fd18c095adf
Amz-Sdk-Request:
- attempt=1; max=3
Content-Type:
- application/x-amz-json-1.1
User-Agent:
- aws-sdk-go-v2/1.18.1 os/linux lang/go/1.20.5 md/GOOS/linux md/GOARCH/amd64 api/ecs/1.27.1
X-Amz-Date:
- 20230630T163647Z
X-Amz-Target:
- AmazonEC2ContainerServiceV20141113.CreateService
url: https://ecs.us-east-1.amazonaws.com/
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 2455
uncompressed: false
body: '{"service":{"clusterArn":"arn:aws:ecs:us-east-1:278576220453:cluster/marco-test_teleport_sh-teleport","createdAt":1.688143008251E9,"createdBy":"arn:aws:iam::278576220453:role/MarcoTestRoleOIDCProvider","deploymentConfiguration":{"deploymentCircuitBreaker":{"enable":false,"rollback":false},"maximumPercent":200,"minimumHealthyPercent":100},"deploymentController":{"type":"ECS"},"deployments":[{"createdAt":1.688143008251E9,"desiredCount":1,"failedLaunchTaskCount":0,"failedTasks":0,"id":"ecs-svc/2489331754842210340","launchType":"FARGATE","networkConfiguration":{"awsvpcConfiguration":{"assignPublicIp":"ENABLED","securityGroups":[],"subnets":["subnet-0b7ab67161173748b","subnet-0dda93c8621eb2e99","subnet-034f17b3f7344e375","subnet-04a07d4721a3c96e0","subnet-0ef025345dd791986","subnet-099632749366c2c56"]}},"pendingCount":0,"platformFamily":"Linux","platformVersion":"1.4.0","replacedTaskCount":0,"rolloutState":"IN_PROGRESS","rolloutStateReason":"ECS deployment ecs-svc/2489331754842210340 in progress.","runningCount":0,"status":"PRIMARY","taskDefinition":"arn:aws:ecs:us-east-1:278576220453:task-definition/marco-test_teleport_sh-teleport-database-service:1","updatedAt":1.688143008251E9}],"desiredCount":1,"enableECSManagedTags":false,"enableExecuteCommand":false,"events":[],"launchType":"FARGATE","loadBalancers":[],"networkConfiguration":{"awsvpcConfiguration":{"assignPublicIp":"ENABLED","securityGroups":[],"subnets":["subnet-0b7ab67161173748b","subnet-0dda93c8621eb2e99","subnet-034f17b3f7344e375","subnet-04a07d4721a3c96e0","subnet-0ef025345dd791986","subnet-099632749366c2c56"]}},"pendingCount":0,"placementConstraints":[],"placementStrategy":[],"platformFamily":"Linux","platformVersion":"LATEST","propagateTags":"SERVICE","roleArn":"arn:aws:iam::278576220453:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS","runningCount":0,"schedulingStrategy":"REPLICA","serviceArn":"arn:aws:ecs:us-east-1:278576220453:service/marco-test_teleport_sh-teleport/marco-test_teleport_sh-teleport-database-service","serviceName":"marco-test_teleport_sh-teleport-database-service","serviceRegistries":[],"status":"ACTIVE","tags":[{"key":"teleport.dev/cluster","value":"marco-test.teleport.sh"},{"key":"teleport.dev/origin","value":"integration_awsoidc"},{"key":"teleport.dev/integration","value":"teleportdev"}],"taskDefinition":"arn:aws:ecs:us-east-1:278576220453:task-definition/marco-test_teleport_sh-teleport-database-service:1","version":0}}'
headers:
Content-Length:
- "2455"
Content-Type:
- application/x-amz-json-1.1
Date:
- Fri, 30 Jun 2023 16:36:48 GMT
X-Amzn-Requestid:
- d8ace682-cc96-429f-b3f1-308de7f0cad3
status: 200 OK
code: 200
duration: 768.986252ms
+7 -1
View File
@@ -130,7 +130,12 @@ func (h *Handler) awsOIDCDeployService(w http.ResponseWriter, r *http.Request, p
return nil, trace.Wrap(err)
}
deployDBServiceClient, err := awsoidc.NewDeployServiceClient(ctx, awsClientReq)
clt, err := sctx.GetUserClient(ctx, site)
if err != nil {
return nil, trace.Wrap(err)
}
deployDBServiceClient, err := awsoidc.NewDeployServiceClient(ctx, awsClientReq, clt)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -142,6 +147,7 @@ func (h *Handler) awsOIDCDeployService(w http.ResponseWriter, r *http.Request, p
deployServiceResp, err := awsoidc.DeployService(ctx, deployDBServiceClient, awsoidc.DeployServiceRequest{
Region: req.Region,
AccountID: req.AccountID,
SubnetIDs: req.SubnetIDs,
ClusterName: req.ClusterName,
ServiceName: req.ServiceName,
+4
View File
@@ -132,6 +132,10 @@ type AWSOIDCDeployServiceRequest struct {
// Region is the AWS Region for the Service.
Region string `json:"region"`
// AccountID is the AWS Account ID.
// Optional. sts.GetCallerIdentity is used if the value is not provided.
AccountID string `json:"accountId"`
// SubnetIDs associated with the Service.
// If deploying a Database Service, you should use the SubnetIDs returned by the List Database API call.
SubnetIDs []string `json:"subnetIds"`