Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ spec:
region:
description: Should contain the AWS region if it cannot be inferred
type: string
role:
description: Specifies the ARN of role to assume when issuing certificates.
type: string
secretRef:
description: Needs to be specified if you want to authorize with AWS
using an access and secret key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ spec:
region:
description: Should contain the AWS region if it cannot be inferred
type: string
role:
description: Specifies the ARN of role to assume when issuing certificates.
type: string
secretRef:
description: Needs to be specified if you want to authorize with AWS
using an access and secret key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ spec:
region:
description: Should contain the AWS region if it cannot be inferred
type: string
role:
description: Specifies the ARN of role to assume when issuing certificates.
type: string
secretRef:
description: Needs to be specified if you want to authorize with AWS
using an access and secret key
Expand Down
3 changes: 3 additions & 0 deletions config/crd/bases/awspca.cert-manager.io_awspcaissuers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ spec:
region:
description: Should contain the AWS region if it cannot be inferred
type: string
role:
description: Specifies the ARN of role to assume when issuing certificates.
type: string
secretRef:
description: Needs to be specified if you want to authorize with AWS
using an access and secret key
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAClusterIssuer
metadata:
name: example
spec:
arn: <some-pca-arn>
role: <some-role-arn>
region: us-west-2
10 changes: 2 additions & 8 deletions e2e/aws_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"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/acmpca"
Expand Down Expand Up @@ -296,7 +295,7 @@ func getAccountID(ctx context.Context, cfg aws.Config) string {
return *callerID.Account
}

func getPartition(ctx context.Context, cfg aws.Config) string {
func getCallerIdentity(ctx context.Context, cfg aws.Config) *sts.GetCallerIdentityOutput {
stsClient := sts.NewFromConfig(cfg)

callerID, callerErr := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
Expand All @@ -305,12 +304,7 @@ func getPartition(ctx context.Context, cfg aws.Config) string {
panic(callerErr.Error())
}

parsedArn, parseErr := arn.Parse(*callerID.Arn)
if parseErr != nil {
return "aws"
}

return parsedArn.Partition
return callerID
}

func assumeRole(ctx context.Context, cfg aws.Config, roleName string, region string) aws.Config {
Expand Down
19 changes: 17 additions & 2 deletions e2e/awspcaissuer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/cert-manager/aws-privateca-issuer/pkg/api/v1beta1"
clientV1beta1 "github.com/cert-manager/aws-privateca-issuer/pkg/clientset/v1beta1"
Expand All @@ -31,7 +32,7 @@ type TestContext struct {
xaCfg aws.Config
caArns map[string]string

region, partition, accessKey, secretKey, endEntityResourceShareArn, subordinateCaResourceShareArn, userName, policyArn string
region, partition, accessKey, secretKey, endEntityResourceShareArn, subordinateCaResourceShareArn, userName, policyArn, roleToAssume string
}

// These are variables specific to each test
Expand Down Expand Up @@ -111,7 +112,19 @@ func InitializeTestSuite(suiteCtx *godog.TestSuiteContext) {
panic(cfgErr.Error())
}

testContext.partition = getPartition(ctx, cfg)
callerID := getCallerIdentity(ctx, cfg)

parsedArn, parseErr := arn.Parse(*callerID.Arn)
if parseErr != nil {
panic("Failed to parse caller identity ARN: " + parseErr.Error())
}

testContext.partition = parsedArn.Partition

testContext.roleToAssume = fmt.Sprintf("arn:%s:iam::%s:role/IssuerTestRole-test-us-east-1", testContext.partition, *callerID.Account)
if roleToAssumeOverride, exists := os.LookupEnv("ROLE_TO_ASSUME_OVERRIDE"); exists {
testContext.roleToAssume = roleToAssumeOverride
}

testContext.iclient, err = clientV1beta1.NewForConfig(clientConfig)

Expand Down Expand Up @@ -217,8 +230,10 @@ func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step(`^I create a namespace`, issuerContext.createNamespace)
ctx.Step(`^I create a Secret with keys ([A-Za-z_]+) and ([A-Za-z_]+) for my AWS credentials$`, issuerContext.createSecret)
ctx.Step(`^I create an AWSPCAClusterIssuer using a (RSA|ECDSA|XA) CA$`, issuerContext.createClusterIssuer)
ctx.Step(`^I create an AWSPCAClusterIssuer with role assumption$`, issuerContext.createClusterIssuerWithRole)
ctx.Step(`^I delete the AWSPCAClusterIssuer$`, issuerContext.deleteClusterIssuer)
ctx.Step(`^I create an AWSPCAIssuer using a (RSA|ECDSA|XA) CA$`, issuerContext.createNamespaceIssuer)
ctx.Step(`^I create an AWSPCAIssuer with role assumption$`, issuerContext.createNamespaceIssuerWithRole)
ctx.Step(`^I issue a (SHORT_VALIDITY|RSA|ECDSA|CA) certificate$`, issuerContext.issueCertificate)
ctx.Step(`^the certificate should be issued successfully$`, issuerContext.verifyCertificateIssued)
ctx.Step(`^the certificate request has been created$`, issuerContext.verifyCertificateRequestIsCreated)
Expand Down
11 changes: 10 additions & 1 deletion e2e/clusterissuer_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,22 @@ import (
)

func (issCtx *IssuerContext) createClusterIssuer(ctx context.Context, caType string) error {
return issCtx.createClusterIssuerWithSpec(ctx, caType, getIssuerSpec(caType))
}

func (issCtx *IssuerContext) createClusterIssuerWithRole(ctx context.Context) error {
return issCtx.createClusterIssuerWithSpec(ctx, "RSA", getIssuerSpecWithRole("RSA"))
}

func (issCtx *IssuerContext) createClusterIssuerWithSpec(ctx context.Context, caType string, spec v1beta1.AWSPCAIssuerSpec) error {
if issCtx.issuerName == "" {
issCtx.issuerName = uuid.New().String() + "--cluster-issuer--" + strings.ToLower(caType)
}

issCtx.issuerType = "AWSPCAClusterIssuer"
issSpec := v1beta1.AWSPCAClusterIssuer{
ObjectMeta: metav1.ObjectMeta{Name: issCtx.issuerName},
Spec: getIssuerSpec(caType),
Spec: spec,
}

if issCtx.secretRef != (v1beta1.AWSCredentialsSecretReference{}) {
Expand Down
6 changes: 6 additions & 0 deletions e2e/common_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ func getIssuerSpec(caType string) v1beta1.AWSPCAIssuerSpec {
}
}

func getIssuerSpecWithRole(caType string) v1beta1.AWSPCAIssuerSpec {
spec := getIssuerSpec(caType)
spec.Role = testContext.roleToAssume
return spec
}

func (issCtx *IssuerContext) createNamespace(ctx context.Context) error {
namespaceName := "pca-issuer-ns-" + uuid.New().String()
namespace := v1.Namespace{
Expand Down
15 changes: 15 additions & 0 deletions e2e/features/role_assumption.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@RoleAssumption
Feature: Issue certificates using role assumption
As a user of the aws-privateca-issuer
I need to be able to issue certificates using role assumption

Scenario: Issue a certificate with a ClusterIssuer using role assumption
Given I create an AWSPCAClusterIssuer with role assumption
When I issue a RSA certificate
Then the certificate should be issued successfully

Scenario: Issue a certificate with a namespaced Issuer using role assumption
Given I create a namespace
And I create an AWSPCAIssuer with role assumption
When I issue a RSA certificate
Then the certificate should be issued successfully
10 changes: 9 additions & 1 deletion e2e/namespaceissuer_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ import (
)

func (issCtx *IssuerContext) createNamespaceIssuer(ctx context.Context, caType string) error {
return issCtx.createNamespaceIssuerWithSpec(ctx, caType, getIssuerSpec(caType))
}

func (issCtx *IssuerContext) createNamespaceIssuerWithRole(ctx context.Context) error {
return issCtx.createNamespaceIssuerWithSpec(ctx, "RSA", getIssuerSpecWithRole("RSA"))
}

func (issCtx *IssuerContext) createNamespaceIssuerWithSpec(ctx context.Context, caType string, spec v1beta1.AWSPCAIssuerSpec) error {
issCtx.issuerName = uuid.New().String() + "--namespace-issuer--" + strings.ToLower(caType)
issCtx.issuerType = "AWSPCAIssuer"
issSpec := v1beta1.AWSPCAIssuer{
ObjectMeta: metav1.ObjectMeta{Name: issCtx.issuerName},
Spec: getIssuerSpec(caType),
Spec: spec,
}

if issCtx.secretRef != (v1beta1.AWSCredentialsSecretReference{}) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/v1beta1/awspcaissuer_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type AWSPCAIssuerSpec struct {
// Needs to be specified if you want to authorize with AWS using an access and secret key
// +optional
SecretRef AWSCredentialsSecretReference `json:"secretRef,omitempty"`
// Specifies the ARN of role to assume when issuing certificates.
// +optional
Role string `json:"role,omitempty"`
}

// AWSCredentialsSecretReference defines the secret used by the issuer
Expand Down
35 changes: 21 additions & 14 deletions pkg/aws/pca.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import (
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/acmpca"
acmpcatypes "github.com/aws/aws-sdk-go-v2/service/acmpca/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
injections "github.com/cert-manager/aws-privateca-issuer/pkg/api/injections"
api "github.com/cert-manager/aws-privateca-issuer/pkg/api/v1beta1"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
Expand Down Expand Up @@ -89,6 +91,11 @@ func GetConfig(ctx context.Context, client client.Client, spec *api.AWSPCAIssuer
}

func LoadConfig(ctx context.Context, client client.Client, spec *api.AWSPCAIssuerSpec) (aws.Config, error) {
var configOptions []func(*config.LoadOptions) error
if spec.Region != "" {
configOptions = append(configOptions, config.WithRegion(spec.Region))
}

if spec.SecretRef.Name != "" {
secretNamespaceName := types.NamespacedName{
Namespace: spec.SecretRef.Namespace,
Expand Down Expand Up @@ -118,23 +125,23 @@ func LoadConfig(ctx context.Context, client client.Client, spec *api.AWSPCAIssue
return aws.Config{}, ErrNoSecretAccessKey
}

if spec.Region != "" {
return config.LoadDefaultConfig(ctx,
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(string(accessKey), string(secretKey), "")),
config.WithRegion(spec.Region),
)
}

return config.LoadDefaultConfig(ctx,
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(string(accessKey), string(secretKey), "")),
)
} else if spec.Region != "" {
return config.LoadDefaultConfig(ctx,
config.WithRegion(spec.Region),
configOptions = append(configOptions, config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider(string(accessKey), string(secretKey), "")),
)
}

return config.LoadDefaultConfig(ctx)
cfg, err := config.LoadDefaultConfig(ctx, configOptions...)
if err != nil {
return aws.Config{}, err
}

if spec.Role != "" {
stsService := sts.NewFromConfig(cfg)
creds := stscreds.NewAssumeRoleProvider(stsService, spec.Role)
cfg.Credentials = aws.NewCredentialsCache(creds)
}

return cfg, nil
}

func ClearProvisioners() {
Expand Down