mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 17:53:28 +00:00
Improve AWS OIDC Integration extensibility (#25327)
This PR is mostly tidying up and abstracting the current AWS OIDC integration code. This will allow for next PRs to be a little smaller.
This commit is contained in:
parent
ae8f2fa929
commit
d84d79a027
|
@ -19,6 +19,7 @@ package awsoidc
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/rds"
|
||||
|
@ -26,8 +27,8 @@ import (
|
|||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
// RDSClientRequest contains the required fields to generate an Authenticated [rds.Client].
|
||||
type RDSClientRequest struct {
|
||||
// AWSClientRequest contains the required fields to set up an AWS service client.
|
||||
type AWSClientRequest struct {
|
||||
// Token is the token used to issue the API Call.
|
||||
Token string
|
||||
|
||||
|
@ -39,7 +40,7 @@ type RDSClientRequest struct {
|
|||
}
|
||||
|
||||
// CheckAndSetDefaults checks if the required fields are present.
|
||||
func (req *RDSClientRequest) CheckAndSetDefaults() error {
|
||||
func (req *AWSClientRequest) CheckAndSetDefaults() error {
|
||||
if req.Token == "" {
|
||||
return trace.BadParameter("token is required")
|
||||
}
|
||||
|
@ -55,8 +56,8 @@ func (req *RDSClientRequest) CheckAndSetDefaults() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// NewRDSClient creates an [rds.Client] using the provided Token, RoleARN, Region and, optionally, a custom HTTP Client.
|
||||
func NewRDSClient(ctx context.Context, req RDSClientRequest) (*rds.Client, error) {
|
||||
// newAWSConfig creates a new [aws.Config] using the [AWSClientRequest] fields.
|
||||
func newAWSConfig(ctx context.Context, req *AWSClientRequest) (*aws.Config, error) {
|
||||
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(req.Region))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -68,7 +69,17 @@ func NewRDSClient(ctx context.Context, req RDSClientRequest) (*rds.Client, error
|
|||
IdentityToken(req.Token),
|
||||
)
|
||||
|
||||
return rds.NewFromConfig(cfg), nil
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// newRDSClient creates an [rds.Client] using the provided Token, RoleARN and Region.
|
||||
func newRDSClient(ctx context.Context, req *AWSClientRequest) (*rds.Client, error) {
|
||||
cfg, err := newAWSConfig(ctx, req)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return rds.NewFromConfig(*cfg), nil
|
||||
}
|
||||
|
||||
// IdentityToken is an implementation of [stscreds.IdentityTokenRetriever] for returning a static token.
|
||||
|
|
|
@ -92,6 +92,11 @@ type ListDatabasesClient interface {
|
|||
DescribeDBClusters(ctx context.Context, params *rds.DescribeDBClustersInput, optFns ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error)
|
||||
}
|
||||
|
||||
// NewListDatabasesClient creates a new ListDatabasesClient using a AWSClientRequest.
|
||||
func NewListDatabasesClient(ctx context.Context, req *AWSClientRequest) (ListDatabasesClient, error) {
|
||||
return newRDSClient(ctx, req)
|
||||
}
|
||||
|
||||
// ListDatabases calls the following AWS API:
|
||||
// https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBClusters.html
|
||||
// https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBInstances.html
|
||||
|
|
|
@ -723,6 +723,7 @@ func (h *Handler) bindDefaultEndpoints() {
|
|||
h.PUT("/webapi/sites/:site/integrations/:name", h.WithClusterAuth(h.integrationsUpdate))
|
||||
h.DELETE("/webapi/sites/:site/integrations/:name", h.WithClusterAuth(h.integrationsDelete))
|
||||
|
||||
// AWS OIDC Integration Actions
|
||||
h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/databases", h.WithClusterAuth(h.awsOIDCListDatabases))
|
||||
|
||||
// AWS OIDC Integration specific endpoints:
|
||||
|
|
|
@ -27,15 +27,46 @@ import (
|
|||
"github.com/gravitational/teleport/lib/web/ui"
|
||||
)
|
||||
|
||||
// IntegrationAWSOIDCTokenGenerator describes the required methods to generate tokens for calling AWS OIDC Integration actions.
|
||||
type IntegrationAWSOIDCTokenGenerator interface {
|
||||
// GenerateAWSOIDCToken generates a token to be used to execute an AWS OIDC Integration action.
|
||||
GenerateAWSOIDCToken(ctx context.Context, req types.GenerateAWSOIDCTokenRequest) (string, error)
|
||||
}
|
||||
|
||||
// awsOIDCListDatabases returns a list of databases using the ListDatabases action of the AWS OIDC Integration.
|
||||
func (h *Handler) awsOIDCListDatabases(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnel.RemoteSite) (interface{}, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
var req ui.AWSOIDCListDatabasesRequest
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
awsClientReq, err := h.awsOIDCClientRequest(r.Context(), req.Region, p, sctx, site)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
listDBsClient, err := awsoidc.NewListDatabasesClient(ctx, awsClientReq)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
resp, err := awsoidc.ListDatabases(ctx,
|
||||
listDBsClient,
|
||||
awsoidc.ListDatabasesRequest{
|
||||
Region: req.Region,
|
||||
NextToken: req.NextToken,
|
||||
Engines: req.Engines,
|
||||
RDSType: req.RDSType,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return ui.AWSOIDCListDatabasesResponse{
|
||||
NextToken: resp.NextToken,
|
||||
Databases: ui.MakeDatabases(resp.Databases, nil, nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// awsOIDClientRequest receives a request to execute an action for the AWS OIDC integrations.
|
||||
func (h *Handler) awsOIDCClientRequest(ctx context.Context, region string, p httprouter.Params, sctx *SessionContext, site reversetunnel.RemoteSite) (*awsoidc.AWSClientRequest, error) {
|
||||
integrationName := p.ByName("name")
|
||||
if integrationName == "" {
|
||||
return nil, trace.BadParameter("an integration name is required")
|
||||
|
@ -55,11 +86,6 @@ func (h *Handler) awsOIDCListDatabases(w http.ResponseWriter, r *http.Request, p
|
|||
return nil, trace.BadParameter("integration subkind (%s) mismatch", integration.GetSubKind())
|
||||
}
|
||||
|
||||
var req ui.AWSOIDCListDatabasesRequest
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
issuer, err := h.issuerFromPublicAddr()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -77,30 +103,9 @@ func (h *Handler) awsOIDCListDatabases(w http.ResponseWriter, r *http.Request, p
|
|||
return nil, trace.BadParameter("missing spec fields for %q (%q) integration", integration.GetName(), integration.GetSubKind())
|
||||
}
|
||||
|
||||
rdsClient, err := awsoidc.NewRDSClient(ctx, awsoidc.RDSClientRequest{
|
||||
return &awsoidc.AWSClientRequest{
|
||||
Token: token,
|
||||
RoleARN: awsoidcSpec.RoleARN,
|
||||
Region: req.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
resp, err := awsoidc.ListDatabases(ctx,
|
||||
rdsClient,
|
||||
awsoidc.ListDatabasesRequest{
|
||||
Region: req.Region,
|
||||
NextToken: req.NextToken,
|
||||
Engines: req.Engines,
|
||||
RDSType: req.RDSType,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return ui.AWSOIDCListDatabasesResponse{
|
||||
NextToken: resp.NextToken,
|
||||
Databases: ui.MakeDatabases(resp.Databases, nil, nil),
|
||||
Region: region,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ type AWSOIDCListDatabasesRequest struct {
|
|||
NextToken string `json:"nextToken"`
|
||||
}
|
||||
|
||||
// AWSOIDCListDatabasesResponse contains a list of databases and a next token is more pages are available.
|
||||
// AWSOIDCListDatabasesResponse contains a list of databases and a next token if more pages are available.
|
||||
type AWSOIDCListDatabasesResponse struct {
|
||||
// Databases contains the page of Databases
|
||||
Databases []Database `json:"databases"`
|
||||
|
|
Loading…
Reference in a new issue