Skip to content

Commit

Permalink
AWS OIDC: Remove App Server that uses the integration credentials
Browse files Browse the repository at this point in the history
Users can enable the AWS App Access using the Integration credentials.
We are also creating a way for them to disable this access.
  • Loading branch information
marcoandredinis committed May 24, 2024
1 parent e65f347 commit dcef36b
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 8 deletions.
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/:name/aws-app-access", 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", "my-integration", "aws-app-access")
_, 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)
}

0 comments on commit dcef36b

Please sign in to comment.