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

Move all functionality for assume command into credentials/console #237

Merged
merged 1 commit into from
Oct 30, 2023
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
188 changes: 0 additions & 188 deletions cmd/ocm-backplane/cloud/assume.go

This file was deleted.

2 changes: 0 additions & 2 deletions cmd/ocm-backplane/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ var CloudCmd = &cobra.Command{
func init() {
CloudCmd.AddCommand(CredentialsCmd)
CloudCmd.AddCommand(ConsoleCmd)
CloudCmd.AddCommand(TokenCmd)
CloudCmd.AddCommand(AssumeCmd)
}

func help(cmd *cobra.Command, _ []string) {
Expand Down
129 changes: 129 additions & 0 deletions cmd/ocm-backplane/cloud/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package cloud

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/sts"
v1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
"github.com/openshift/backplane-cli/pkg/awsutil"
"github.com/openshift/backplane-cli/pkg/utils"
"io"
"net/http"
)

const OldFlowSupportRole = "role/RH-Technical-Support-Access"

var StsClientWithProxy = awsutil.StsClientWithProxy
var AssumeRoleWithJWT = awsutil.AssumeRoleWithJWT
var NewStaticCredentialsProvider = credentials.NewStaticCredentialsProvider
var AssumeRoleSequence = awsutil.AssumeRoleSequence

type assumeChainResponse struct {
AssumptionSequence []namedRoleArn `json:"assumptionSequence"`
}

type namedRoleArn struct {
Name string `json:"name"`
Arn string `json:"arn"`
}

func getIsolatedCredentials(clusterID string) (aws.Credentials, error) {
if clusterID == "" {
return aws.Credentials{}, errors.New("must provide non-empty cluster ID")
}

ocmToken, err := utils.DefaultOCMInterface.GetOCMAccessToken()
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to retrieve OCM token: %w", err)
}

email, err := utils.GetStringFieldFromJWT(*ocmToken, "email")
if err != nil {
return aws.Credentials{}, fmt.Errorf("unable to extract email from given token: %w", err)
}

bpConfig, err := GetBackplaneConfiguration()
if err != nil {
return aws.Credentials{}, fmt.Errorf("error retrieving backplane configuration: %w", err)
}

if bpConfig.AssumeInitialArn == "" {
return aws.Credentials{}, errors.New("backplane config is missing required `assume-initial-arn` property")
}

initialClient, err := StsClientWithProxy(bpConfig.ProxyURL)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to create sts client: %w", err)
}

seedCredentials, err := AssumeRoleWithJWT(*ocmToken, bpConfig.AssumeInitialArn, initialClient)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to assume role using JWT: %w", err)
}

backplaneClient, err := utils.DefaultClientUtils.MakeRawBackplaneAPIClientWithAccessToken(bpConfig.URL, *ocmToken)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to create backplane client with access token: %w", err)
}

response, err := backplaneClient.GetAssumeRoleSequence(context.TODO(), clusterID)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to fetch arn sequence: %w", err)
}
if response.StatusCode != http.StatusOK {
return aws.Credentials{}, fmt.Errorf("failed to fetch arn sequence: %v", response.Status)
}

bytes, err := io.ReadAll(response.Body)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to read backplane API response body: %w", err)
}

roleChainResponse := &assumeChainResponse{}
err = json.Unmarshal(bytes, roleChainResponse)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to unmarshal response: %w", err)
}

roleAssumeSequence := make([]string, 0, len(roleChainResponse.AssumptionSequence))
for _, namedRoleArn := range roleChainResponse.AssumptionSequence {
roleAssumeSequence = append(roleAssumeSequence, namedRoleArn.Arn)
}

seedClient := sts.NewFromConfig(aws.Config{
Region: "us-east-1",
Credentials: NewStaticCredentialsProvider(seedCredentials.AccessKeyID, seedCredentials.SecretAccessKey, seedCredentials.SessionToken),
})

targetCredentials, err := AssumeRoleSequence(email, seedClient, roleAssumeSequence, bpConfig.ProxyURL, awsutil.DefaultSTSClientProviderFunc)
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to assume role sequence: %w", err)
}
return targetCredentials, nil
}

func isIsolatedBackplaneAccess(cluster *v1.Cluster) (bool, error) {
if clusterAws, ok := cluster.GetAWS(); ok {
if clusterAwsSts, ok := clusterAws.GetSTS(); ok {
if clusterAwsSts.Enabled() {
stsSupportJumpRole, err := utils.DefaultOCMInterface.GetStsSupportJumpRoleARN(cluster.ID())
if err != nil {
return false, fmt.Errorf("failed to get sts support jump role ARN for cluster %v: %w", cluster.ID(), err)
}
supportRoleArn, err := arn.Parse(stsSupportJumpRole)
if err != nil {
return false, fmt.Errorf("failed to parse ARN for jump role %v: %w", stsSupportJumpRole, err)
}
if supportRoleArn.Resource != OldFlowSupportRole {
return true, nil
}
}
}
}
return false, nil
}
Loading