ripe-shampoo-80285
06/04/2021, 2:58 AMbored-table-20691
06/04/2021, 3:13 AMfunc (tenant *Tenant) CreateIRSA() error {
ctx := tenant.pulumiCtx
oidcUrlRef := tenant.infraStack.GetStringOutput(pulumi.String("eks-oidc-url"))
oidcArnRef := tenant.infraStack.GetStringOutput(pulumi.String("eks-oidc-arn"))
serviceAccountName := fmt.Sprintf("tenant-%s-s3-sa", tenant.name)
roleName := fmt.Sprintf("tenant-%s-s3-role", tenant.name)
policyName := fmt.Sprintf("tenant-%s-s3-policy", tenant.name)
allow := "Allow"
assumeRolePolicy := pulumi.All(tenant.namespace.Metadata.Name(), oidcUrlRef, oidcArnRef).ApplyT(func(args []interface{}) (string, error) {
namespaceName := args[0].(*string)
oidcUrl := args[1].(string)
oidcArn := args[2].(string)
source, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Actions: []string{
"sts:AssumeRoleWithWebIdentity",
},
Conditions: []iam.GetPolicyDocumentStatementCondition{
{
Test: "StringEquals",
Values: []string{fmt.Sprintf("system:serviceaccount:%s:%s", *namespaceName, serviceAccountName)},
Variable: fmt.Sprintf("%s:sub", strings.TrimPrefix(oidcUrl, "https://")),
},
},
Effect: &allow,
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Identifiers: []string{oidcArn},
Type: "Federated",
},
},
},
},
}, nil)
if err != nil {
return "", err
}
return source.Json, nil
})
serviceRole, err := iam.NewRole(ctx, roleName, &iam.RoleArgs{
AssumeRolePolicy: assumeRolePolicy,
})
if err != nil {
return err
}
infraBucket := tenant.infraStack.GetStringOutput(pulumi.String("bucket"))
s3PolicyDoc := pulumi.All(infraBucket).ApplyT(func(args []interface{}) (string, error) {
bucket := args[0].(string)
bucketArn := fmt.Sprintf("arn:aws:s3:::%s", bucket)
auditLogPrefix := fmt.Sprintf("tenants/logs/audit/tenant=%s", tenant.name)
analyticsLogPrefix := fmt.Sprintf("tenants/logs/analytics/tenant=%s", tenant.name)
fsAuditLogPrefix := fmt.Sprintf("tenants/logs/fs-audit/tenant=%s", tenant.name)
operationaLogPrefix := fmt.Sprintf("tenants/logs/operational/tenant=%s", tenant.name)
source, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
// Some base bucket policies
{
Actions: []string{
"s3:HeadBucket",
},
Resources: []string{
bucketArn,
},
Effect: &allow,
},
// Allowing listing the log dirs
{
Actions: []string{
"s3:ListBucket",
},
Resources: []string{
bucketArn,
},
Conditions: []iam.GetPolicyDocumentStatementCondition{
{
Test: "StringLike",
Values: []string{
fmt.Sprintf("%s", auditLogPrefix),
fmt.Sprintf("%s/*", auditLogPrefix),
fmt.Sprintf("%s", analyticsLogPrefix),
fmt.Sprintf("%s/*", analyticsLogPrefix),
fmt.Sprintf("%s", fsAuditLogPrefix),
fmt.Sprintf("%s/*", fsAuditLogPrefix),
fmt.Sprintf("%s", operationaLogPrefix),
fmt.Sprintf("%s/*", operationaLogPrefix),
},
Variable: "s3:prefix",
},
},
Effect: &allow,
},
// Allow read/write/delete on the logging buckets
{
Actions: []string{
"s3:DeleteObject",
"s3:GetEncryptionConfiguration",
"s3:GetObject",
"s3:GetObjectTagging",
"s3:PutObject",
},
Resources: []string{
fmt.Sprintf("%s/%s", bucketArn, auditLogPrefix),
fmt.Sprintf("%s/%s/*", bucketArn, auditLogPrefix),
fmt.Sprintf("%s/%s", bucketArn, analyticsLogPrefix),
fmt.Sprintf("%s/%s/*", bucketArn, analyticsLogPrefix),
fmt.Sprintf("%s/%s", bucketArn, fsAuditLogPrefix),
fmt.Sprintf("%s/%s/*", bucketArn, fsAuditLogPrefix),
fmt.Sprintf("%s/%s", bucketArn, operationaLogPrefix),
fmt.Sprintf("%s/%s/*", bucketArn, operationaLogPrefix),
},
Effect: &allow,
},
// Only allow reading the lake bucket
{
Actions: []string{
"s3:GetObject",
"s3:GetObjectTagging",
"s3:HeadBucket",
"s3:ListBucket",
},
Resources: []string{
"arn:aws:s3:::okera-lake",
"arn:aws:s3:::okera-lake/*",
},
Effect: &allow,
},
},
}, nil)
if err != nil {
return "", err
}
return source.Json, nil
})
s3Policy, err := iam.NewPolicy(ctx, policyName, &iam.PolicyArgs{
Description: pulumi.Sprintf("S3 permissions for tenant %s", tenant.name),
Policy: s3PolicyDoc,
Tags: pulumi.StringMap{
"tenant": pulumi.String(tenant.name),
},
})
if err != nil {
return err
}
_, err = iam.NewRolePolicyAttachment(ctx, fmt.Sprintf("%s-attachment", policyName), &iam.RolePolicyAttachmentArgs{
PolicyArn: s3Policy.Arn,
Role: serviceRole,
})
if err != nil {
return err
}
serviceAccount, err := corev1.NewServiceAccount(ctx, serviceAccountName, &corev1.ServiceAccountArgs{
Metadata: &metav1.ObjectMetaArgs{
Namespace: tenant.namespace.Metadata.Name(),
Name: pulumi.String(serviceAccountName),
Annotations: pulumi.StringMap{
"<http://eks.amazonaws.com/role-arn|eks.amazonaws.com/role-arn>": serviceRole.Arn,
},
},
})
if err != nil {
return err
}
tenant.serviceAccount = serviceAccount
return nil
}
And then using it:
...
Spec: &corev1.PodSpecArgs{
ServiceAccountName: tenant.serviceAccount.Metadata.Name(),
...
A few notes:
• tenant.infraStack
is a StackReference
where I exported some values
• In this case I needed to create a specific S3 policy, which is why it is a bit more verbose, but in theory you could use one of the managed S3 policies.
Hope this helps!ripe-shampoo-80285
06/04/2021, 12:51 PMbored-table-20691
06/04/2021, 6:16 PMpulumi-eks
to create a cluster, yes?OidcProvider()
(it returns an iam.OpenIdConnectProvider
)pulumi-eks
, so it looks a bit different (I can’t use it due to some bug 😞 ):
eksCluster, err := eks.LookupCluster(ctx, &eks.LookupClusterArgs{
Name: okeraCfg.Require("eks"),
})
if err != nil {
return err
}
awsAccount := okeraCfg.Require("aws-account")
oidcUrl := eksCluster.Identities[0].Oidcs[0].Issuer
oidcArn := fmt.Sprintf("arn:aws:iam::%s:oidc-provider/%s", awsAccount, strings.TrimPrefix(oidcUrl, "https://"))
kubeconfig := kube.GenerateKubeconfig(
eksCluster.Endpoint,
eksCluster.CertificateAuthority.Data,
eksCluster.Name)
ctx.Export("kubeconfig", pulumi.ToSecret(kubeconfig))
ctx.Export("eks-oidc-url", pulumi.String(oidcUrl))
ctx.Export("eks-oidc-arn", pulumi.String(oidcArn))
ripe-shampoo-80285
06/04/2021, 6:52 PMbored-table-20691
06/04/2021, 6:58 PMripe-shampoo-80285
06/04/2021, 7:03 PMbored-table-20691
06/04/2021, 10:19 PMpulumi-eks
right now.ripe-shampoo-80285
06/05/2021, 2:35 AMbored-table-20691
06/05/2021, 4:30 AMbright-sandwich-93783
07/06/2021, 9:49 PMaws-node
service account in the kube-system
namespace when enabling the CNI plugin. Have you tried doing this?bored-table-20691
07/06/2021, 11:25 PM