Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

AWS OIDC: Remove App Server that uses the integration credentials #42012

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ func (h *Handler) bindDefaultEndpoints() {
h.POST("/webapi/sites/:site/integrations", h.WithClusterAuth(h.integrationsCreate))
h.GET("/webapi/sites/:site/integrations/:name", h.WithClusterAuth(h.integrationsGet))
h.PUT("/webapi/sites/:site/integrations/:name", h.WithClusterAuth(h.integrationsUpdate))
h.DELETE("/webapi/sites/:site/integrations/:name", h.WithClusterAuth(h.integrationsDelete))
h.DELETE("/webapi/sites/:site/integrations/:name_or_subkind", h.WithClusterAuth(h.integrationsDelete))

// AWS OIDC Integration Actions
h.GET("/webapi/scripts/integrations/configure/awsoidc-idp.sh", h.WithLimiter(h.awsOIDCConfigureIdP))
Expand All @@ -928,6 +928,10 @@ func (h *Handler) bindDefaultEndpoints() {
h.GET("/webapi/scripts/integrations/configure/access-graph-cloud-sync-iam.sh", h.WithLimiter(h.accessGraphCloudSyncOIDC))
h.GET("/webapi/scripts/integrations/configure/aws-app-access-iam.sh", h.WithLimiter(h.awsOIDCConfigureAWSAppAccessIAM))
h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/aws-app-access", h.WithClusterAuth(h.awsOIDCCreateAWSAppAccess))
// The Integration DELETE endpoint already sets the expected named param after `/integrations/`
// It must be re-used here, otherwise the router will not start.
// See https://github.com/julienschmidt/httprouter/issues/364
h.DELETE("/webapi/sites/:site/integrations/:name_or_subkind/aws-app-access/:name", h.WithClusterAuth(h.awsOIDCDeleteAWSAppAccess))
h.GET("/webapi/scripts/integrations/configure/ec2-ssm-iam.sh", h.WithLimiter(h.awsOIDCConfigureEC2SSMIAM))

// SAML IDP integration endpoints
Expand Down
2 changes: 1 addition & 1 deletion lib/web/integrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func (h *Handler) integrationsUpdate(w http.ResponseWriter, r *http.Request, p h

// integrationsDelete removes an Integration based on its name
func (h *Handler) integrationsDelete(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (interface{}, error) {
integrationName := p.ByName("name")
integrationName := p.ByName("name_or_subkind")
if integrationName == "" {
return nil, trace.BadParameter("an integration name is required")
}
Expand Down
59 changes: 58 additions & 1 deletion lib/web/integrations_awsoidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils"
Expand Down Expand Up @@ -925,7 +926,7 @@ func (h *Handler) awsOIDCDeployEC2ICE(w http.ResponseWriter, r *http.Request, p
}, nil
}

// awsOIDCDeployC2ICE creates an AppServer that uses an AWS OIDC Integration for proxying access.
// awsOIDCCreateAWSAppAccess creates an AppServer that uses an AWS OIDC Integration for proxying access.
func (h *Handler) awsOIDCCreateAWSAppAccess(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (any, error) {
ctx := r.Context()

Expand Down Expand Up @@ -984,6 +985,62 @@ func (h *Handler) awsOIDCCreateAWSAppAccess(w http.ResponseWriter, r *http.Reque
}), nil
}

// awsOIDCDeleteAWSAppAccess deletes the AWS AppServer created that uses the AWS OIDC Integration for proxying requests.
func (h *Handler) awsOIDCDeleteAWSAppAccess(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (any, error) {
ctx := r.Context()

subkind := p.ByName("name_or_subkind")
if subkind != types.IntegrationSubKindAWSOIDC {
return nil, trace.BadParameter("only aws oidc integrations are supported")
}

integrationName := p.ByName("name")
if integrationName == "" {
return nil, trace.BadParameter("an integration name is required")
}

clt, err := sctx.GetUserClient(ctx, site)
if err != nil {
return nil, trace.Wrap(err)
}

ig, err := clt.GetIntegration(ctx, integrationName)
if err != nil {
return nil, trace.Wrap(err)
}
if ig.GetSubKind() != types.IntegrationSubKindAWSOIDC {
return nil, trace.BadParameter("only aws oidc integrations are supported")
}

integrationAppServer, err := h.getAppServerByName(ctx, clt, integrationName)
if err != nil {
return nil, trace.Wrap(err)
}
if integrationAppServer.GetApp().GetIntegration() != integrationName {
return nil, trace.NotFound("app %s is not using integration %s", integrationAppServer.GetName(), integrationName)
}

if err := clt.DeleteApplicationServer(ctx, apidefaults.Namespace, integrationAppServer.GetHostID(), integrationName); err != nil {
return nil, trace.Wrap(err)
}

return nil, nil
}

func (h *Handler) getAppServerByName(ctx context.Context, userClient authclient.ClientI, appServerName string) (types.AppServer, error) {
appServers, err := userClient.GetApplicationServers(ctx, apidefaults.Namespace)
if err != nil {
return nil, trace.Wrap(err)
}

for _, s := range appServers {
if s.GetName() == appServerName {
return s, nil
}
}
return nil, trace.NotFound("app %q not found", appServerName)
}

// awsOIDCConfigureIdP returns a script that configures AWS OIDC Integration
// by creating an OIDC Identity Provider that trusts Teleport instance.
func (h *Handler) awsOIDCConfigureIdP(w http.ResponseWriter, r *http.Request, p httprouter.Params) (any, error) {
Expand Down
25 changes: 20 additions & 5 deletions lib/web/integrations_awsoidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -951,14 +951,16 @@ func TestAWSOIDCSecurityGroupsRulesConverter(t *testing.T) {
}
}

func TestAWSOIDCAppAccessAppServerCreation(t *testing.T) {
func TestAWSOIDCAppAccessAppServerCreationDeletion(t *testing.T) {
env := newWebPack(t, 1)
ctx := context.Background()

roleTokenCRD, err := types.NewRole(services.RoleNameForUser("my-user"), types.RoleSpecV6{
Allow: types.RoleConditions{
AppLabels: types.Labels{"*": []string{"*"}},
Rules: []types.Rule{
types.NewRule(types.KindIntegration, []string{types.VerbRead}),
types.NewRule(types.KindAppServer, []string{types.VerbCreate, types.VerbUpdate}),
types.NewRule(types.KindAppServer, []string{types.VerbCreate, types.VerbUpdate, types.VerbList, types.VerbDelete}),
types.NewRule(types.KindUserGroup, []string{types.VerbList, types.VerbRead}),
},
},
Expand All @@ -976,16 +978,22 @@ func TestAWSOIDCAppAccessAppServerCreation(t *testing.T) {
})
require.NoError(t, err)

_, err = env.server.Auth().CreateIntegration(context.Background(), myIntegration)
_, err = env.server.Auth().CreateIntegration(ctx, myIntegration)
require.NoError(t, err)

// Deleting the AWS App Access should return an error because it was not created yet.
deleteEndpoint := pack.clt.Endpoint("webapi", "sites", "localhost", "integrations", "aws-oidc", "aws-app-access", "my-integration")
_, err = pack.clt.Delete(ctx, deleteEndpoint)
require.Error(t, err)
require.ErrorContains(t, err, "not found")

// Create the AWS App Access for the current integration.
endpoint := pack.clt.Endpoint("webapi", "sites", "localhost", "integrations", "aws-oidc", "my-integration", "aws-app-access")
_, err = pack.clt.PostJSON(context.Background(), endpoint, nil)
_, err = pack.clt.PostJSON(ctx, endpoint, nil)
require.NoError(t, err)

// Ensure the AppServer was correctly created.
appServers, err := env.server.Auth().GetApplicationServers(context.Background(), "default")
appServers, err := env.server.Auth().GetApplicationServers(ctx, "default")
require.NoError(t, err)
require.Len(t, appServers, 1)

Expand Down Expand Up @@ -1019,4 +1027,11 @@ func TestAWSOIDCAppAccessAppServerCreation(t *testing.T) {
appServers[0],
cmpopts.IgnoreFields(types.Metadata{}, "Revision", "Namespace"),
))

// After deleting the application, it should be removed from the backend.
_, err = pack.clt.Delete(ctx, deleteEndpoint)
require.NoError(t, err)
appServers, err = env.server.Auth().GetApplicationServers(ctx, "default")
require.NoError(t, err)
require.Empty(t, appServers)
}
Loading