Skip to content

Commit

Permalink
AWS OIDC: Remove App Server that uses the integration credentials (#4…
Browse files Browse the repository at this point in the history
…2012)

* AWS OIDC: Remove App Server that uses the integration credentials

Users can enable the AWS App Access using the Integration credentials.
We are also creating a way for them to disable this access.

* change url placeholders
  • Loading branch information
marcoandredinis committed Jun 11, 2024
1 parent f6b40bb commit 75d3409
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 @@ -877,7 +877,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 @@ -898,6 +898,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)
}

0 comments on commit 75d3409

Please sign in to comment.