03/06/2023 - AWS, GO
As we know, if an application needs to access an account from within another account, it needs to assume role and gain temporary credentials (expires in 15 minutes but gets auto refreshed). This is called cross-account access. In short, app is running in application X and want to use resources from account Y.
For the sake of an example, I will also add an example where the application uses resources (IAM) from within the same account. For the cross-account access, we will focus on S3 resources.
Install packages below.
go get github.com/aws/aws-sdk-go-v2
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/iam
go get github.com/aws/aws-sdk-go-v2/service/s3
go get github.com/aws/aws-sdk-go-v2/service/sts
package aws
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/sts"
)
// NewDefaultConfig current credentials for the current account and returns AWS
// config. Credentials do not expire.
func NewDefaultConfig(ctx context.Context) (aws.Config, error) {
return config.LoadDefaultConfig(ctx)
}
// NewCrossAccountConfigWithRole retrieves temporary credentials from the STS
// service for assumed role and returns AWS config for cross-account access. Credentials
// last 15 minutes but get automatically refreshed.
func NewCrossAccountConfigWithRole(ctx context.Context, roleARN string) (aws.Config, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return aws.Config{}, err
}
stsClient := sts.NewFromConfig(cfg)
stsCreds := stscreds.NewAssumeRoleProvider(stsClient, roleARN)
cfg.Credentials = aws.NewCredentialsCache(stsCreds)
return cfg, nil
}
package aws
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
)
func NewIAMClient(cfg aws.Config) *iam.Client {
return iam.NewFromConfig(cfg)
}
package aws
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func NewS3Client(cfg aws.Config) *s3.Client {
return s3.NewFromConfig(cfg)
}
package api
import (
"context"
"log"
"net/http"
"github.com/aws/aws-sdk-go-v2/service/iam"
)
// CurrentAccount holds AWS current account dependencies.
type CurrentAccount struct {
IAMClient *iam.Client
}
// IAM fetches IAM groups from the current account.
func (c CurrentAccount) IAM(w http.ResponseWriter, r *http.Request) {
out, err := c.IAMClient.ListGroups(context.Background(), nil)
if err != nil {
log.Fatal(err)
}
for _, group := range out.Groups {
log.Println(*group.GroupName)
}
}
package api
import (
"context"
"log"
"net/http"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
// CrossAccount holds AWS cross-account dependencies.
type CrossAccount struct {
S3Client *s3.Client
}
// S3 fetches S3 bucktes from a cross account.
func (c CrossAccount) S3(w http.ResponseWriter, r *http.Request) {
out, err := c.S3Client.ListBuckets(context.Background(), nil)
if err != nil {
log.Fatal(err)
}
for _, bucket := range out.Buckets {
log.Println(*bucket.Name)
}
}
package main
import (
"aws/api"
"aws/aws"
"context"
"log"
"net/http"
)
func main() {
ctx := context.Background()
currentAccountConfig, err := aws.NewDefaultConfig(ctx)
if err != nil {
log.Fatal(err)
}
crossAccountConfig, err := aws.NewCrossAccountConfigWithRole(ctx, "arn:aws:iam::1234567890:role/services")
if err != nil {
log.Fatal(err)
}
currectAccountAPI := api.CurrentAccount{
IAMClient: aws.NewIAMClient(currentAccountConfig),
}
crossAccountAPI := api.CrossAccount{
S3Client: aws.NewS3Client(crossAccountConfig),
}
httpRouter := http.NewServeMux()
httpRouter.HandleFunc("/iam", currectAccountAPI.IAM)
httpRouter.HandleFunc("/s3", crossAccountAPI.S3)
log.Fatalln(http.ListenAndServe(":1234", httpRouter))
}
You could use normal environment variables instead if you prefer.
[default]
region = eu-west-1
output = json
[default]
aws_access_key_id = QWERTY09876541
aws_secret_access_key = QWERTY1234567890
$ go run -race main.go
$ curl http://localhost:1234/iam
2023/06/03 14:53:39 admins
2023/06/03 14:53:39 devops
2023/06/03 14:53:39 developers
$ curl http://localhost:1234/s3
2023/06/03 14:53:45 inanzzz-development-cvs
2023/06/03 14:53:45 inanzzz-development-images