Skip to content

Commit

Permalink
Merge pull request #31 from MinaFoundation/credentials-from-aws-role
Browse files Browse the repository at this point in the history
PM-869 get creds from assumed role
  • Loading branch information
piotr-iohk authored Dec 21, 2023
2 parents 03fa150 + 45ec5af commit e7ad680
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 19 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,11 @@ If the `CONFIG_FILE` environment variable is not set, the program will fall back
4. **AWS Keyspaces Configuration**:
- `AWS_KEYSPACE` - Your AWS Keyspace name.
- `AWS_REGION` - The AWS region (same as used for S3).
- `AWS_ACCESS_KEY_ID` - Your AWS Access Key ID (same as used for S3).
- `AWS_SECRET_ACCESS_KEY` - Your AWS Secret Access Key (same as used for S3).
- `AWS_WEB_IDENTITY_TOKEN_FILE` - AWS web identity token file.
- `AWS_ROLE_SESSION_NAME` - AWS role session name.
- `AWS_ROLE_ARN` - AWS role arn.
- `AWS_ACCESS_KEY_ID` - Your AWS Access Key ID. No need to set if `AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_ROLE_SESSION_NAME` and `AWS_ROLE_ARN` are set.
- `AWS_SECRET_ACCESS_KEY` - Your AWS Secret Access Key. No need to set if `AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_ROLE_SESSION_NAME` and `AWS_ROLE_ARN` are set.
- `AWS_SSL_CERTIFICATE_PATH` - The path to your SSL certificate for AWS Keyspaces.

> **Note:** Docker image already includes cert and has `AWS_SSL_CERTIFICATE_PATH` set up, however it can be overriden by providing this env variable to docker.
Expand Down
44 changes: 30 additions & 14 deletions src/delegation_backend/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ func LoadEnv(log logging.EventLogger) AppConfig {

// AWS configurations
if bucketNameSuffix := os.Getenv("AWS_BUCKET_NAME_SUFFIX"); bucketNameSuffix != "" {
accessKeyId := getEnvChecked("AWS_ACCESS_KEY_ID", log)
secretAccessKey := getEnvChecked("AWS_SECRET_ACCESS_KEY", log)
// accessKeyId, secretAccessKey are not mandatory for production set up
accessKeyId := os.Getenv("AWS_ACCESS_KEY_ID")
secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
awsRegion := getEnvChecked("AWS_REGION", log)
awsAccountId := getEnvChecked("AWS_ACCOUNT_ID", log)
bucketNameSuffix := getEnvChecked("AWS_BUCKET_NAME_SUFFIX", log)
Expand All @@ -72,18 +73,30 @@ func LoadEnv(log logging.EventLogger) AppConfig {

// AWSKeyspace configurations
if awsKeyspace := os.Getenv("AWS_KEYSPACE"); awsKeyspace != "" {
accessKeyId := getEnvChecked("AWS_ACCESS_KEY_ID", log)
secretAccessKey := getEnvChecked("AWS_SECRET_ACCESS_KEY", log)

awsRegion := getEnvChecked("AWS_REGION", log)
awsKeyspace := getEnvChecked("AWS_KEYSPACE", log)
sslCertificatePath := getEnvChecked("AWS_SSL_CERTIFICATE_PATH", log)

// if webIdentityTokenFile, roleSessionName and roleArn are set,
// we are using AWS STS to assume a role and get temporary credentials
// if they are not set, we are using AWS IAM user credentials
webIdentityTokenFile := os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE")
roleSessionName := os.Getenv("AWS_ROLE_SESSION_NAME")
roleArn := os.Getenv("AWS_ROLE_ARN")
// accessKeyId, secretAccessKey are not mandatory for production set up
accessKeyId := os.Getenv("AWS_ACCESS_KEY_ID")
secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")

config.AwsKeyspaces = &AwsKeyspacesConfig{
Keyspace: awsKeyspace,
Region: awsRegion,
AccessKeyId: accessKeyId,
SecretAccessKey: secretAccessKey,
SSLCertificatePath: sslCertificatePath,
Keyspace: awsKeyspace,
Region: awsRegion,
AccessKeyId: accessKeyId,
SecretAccessKey: secretAccessKey,
WebIdentityTokenFile: webIdentityTokenFile,
RoleSessionName: roleSessionName,
RoleArn: roleArn,
SSLCertificatePath: sslCertificatePath,
}
}

Expand Down Expand Up @@ -136,11 +149,14 @@ type AwsConfig struct {
}

type AwsKeyspacesConfig struct {
Keyspace string `json:"keyspace"`
Region string `json:"region"`
AccessKeyId string `json:"access_key_id"`
SecretAccessKey string `json:"secret_access_key"`
SSLCertificatePath string `json:"ssl_certificate_path"`
Keyspace string `json:"keyspace"`
Region string `json:"region"`
AccessKeyId string `json:"access_key_id,omitempty"`
SecretAccessKey string `json:"secret_access_key,omitempty"`
WebIdentityTokenFile string `json:"web_identity_token_file,omitempty"`
RoleSessionName string `json:"role_session_name,omitempty"`
RoleArn string `json:"role_arn,omitempty"`
SSLCertificatePath string `json:"ssl_certificate_path"`
}

type LocalFileSystemConfig struct {
Expand Down
41 changes: 38 additions & 3 deletions src/delegation_backend/aws_keyspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin/sigv4"
"github.com/gocql/gocql"
"github.com/golang-migrate/migrate/v4"
Expand All @@ -20,9 +24,40 @@ import (
// InitializeKeyspaceSession creates a new gocql session for Amazon Keyspaces using the provided configuration.
func InitializeKeyspaceSession(config *AwsKeyspacesConfig) (*gocql.Session, error) {
auth := sigv4.NewAwsAuthenticator()
auth.AccessKeyId = config.AccessKeyId
auth.SecretAccessKey = config.SecretAccessKey
auth.Region = config.Region

if config.RoleSessionName != "" && config.RoleArn != "" && config.WebIdentityTokenFile != "" {
// If role-related env variables are set, use temporary credentials
tokenBytes, err := os.ReadFile(config.WebIdentityTokenFile)
if err != nil {
return nil, fmt.Errorf("error reading web identity token file: %w", err)
}
webIdentityToken := string(tokenBytes)

awsSession, err := session.NewSession(&aws.Config{Region: aws.String(config.Region)})
if err != nil {
return nil, fmt.Errorf("error creating AWS session: %w", err)
}

stsSvc := sts.New(awsSession)
creds, err := stsSvc.AssumeRoleWithWebIdentity(&sts.AssumeRoleWithWebIdentityInput{
RoleArn: &config.RoleArn,
RoleSessionName: &config.RoleSessionName,
WebIdentityToken: &webIdentityToken,
})
if err != nil {
return nil, fmt.Errorf("unable to assume role: %w", err)
}

auth.AccessKeyId = *creds.Credentials.AccessKeyId
auth.SecretAccessKey = *creds.Credentials.SecretAccessKey
auth.SessionToken = *creds.Credentials.SessionToken
auth.Region = config.Region
} else {
// Otherwise, use credentials from the config
auth.AccessKeyId = config.AccessKeyId
auth.SecretAccessKey = config.SecretAccessKey
auth.Region = config.Region
}

// Create a SigV4 gocql cluster config
endpoint := "cassandra." + config.Region + ".amazonaws.com"
Expand Down

0 comments on commit e7ad680

Please sign in to comment.