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

feat: implement dynamodb without creation IAM users #1200

Closed
wants to merge 1 commit into from
Closed
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
12 changes: 6 additions & 6 deletions acceptance-tests/apps/dynamodbtableapp/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@ package app

import (
"context"
"encoding/base64"
"fmt"
"log"
"net/http"
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"

appcreds "dynamodbtableapp/internal/credentials"
)

func App(creds appcreds.DynamoDBService) http.HandlerFunc {
authToken := base64.StdEncoding.EncodeToString([]byte(strings.ReplaceAll(creds.AccessKeyId, ":", "*") + ":" + creds.AccessKeySecret))
cfg, err := config.LoadDefaultConfig(
context.Background(),
config.WithCredentialsProvider(
aws.NewCredentialsCache(
credentials.NewStaticCredentialsProvider(
creds.AccessKeyId,
creds.AccessKeySecret,
"",
),
endpointcreds.New(creds.CredsEndpoint, func(options *endpointcreds.Options) {
options.AuthorizationToken = "Basic " + authToken
}),
),
),
config.WithRegion(creds.Region),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ import (
type DynamoDBService struct {
AccessKeyId string `mapstructure:"access_key_id"`
AccessKeySecret string `mapstructure:"secret_access_key"`
CredsEndpoint string `mapstructure:"creds_endpoint"`
RoleName string `mapstructure:"role_name"`
Region string `mapstructure:"region"`
TableName string `mapstructure:"dynamodb_table_name"`
}

type Creds struct {
AccessKeyId string `json:"AccessKeyId"`
SecretAccessKey string `json:"SecretAccessKey"`
}

func Read() (DynamoDBService, error) {
app, err := cfenv.Current()
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions acceptance-tests/dynamodb_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,9 @@ func config() any {
"non_key_attributes": []string{"id"},
},
},
"iam_arn": "arn:aws:iam::000000000000:user/this-user-must-exist-and-have-permission-to-assume-role",
"role_name": "this-role-must-exist-and-allow-the-above-user-to-assume-role",
"region": "us-west-1",
"creds_endpoint": "https://the-endpoint-for-the-cloud-service-broker.csb.cf-app.com/creds",
}
}
51 changes: 47 additions & 4 deletions aws-dynamodb-table.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@ provision:
type: string
details: VPC ID for instance
default: ""
- field_name: iam_arn
type: string
prohibit_update: true
required: true
default: ""
details: An existing IAM user with permission to 'assumerole' the specified `role_name`.
- field_name: role_name
type: string
prohibit_update: true
default: ""
details: |
If an existing role is passed, a new policy condition will be added to it.
If empty, a new role for each binding will be created.
- field_name: creds_endpoint
type: string
required: true
prohibit_update: true
details: The endpoint for fetching the binding creds
computed_inputs:
- name: labels
default: ${json.marshal(request.default_labels)}
Expand Down Expand Up @@ -159,6 +177,17 @@ provision:
- field_name: region
type: string
details: AWS region for the bucket
- field_name: iam_arn
type: string
details: An existing IAM user with permission to 'assumerole' the specified `role_name`.
- field_name: role_name
type: string
details: |
If an existing role is passed, a new policy condition will be added to it.
If empty, a new role for each binding will be created.
- field_name: creds_endpoint
type: string
details: The endpoint for fetching the binding creds
bind:
plan_inputs: []
user_inputs:
Expand All @@ -183,14 +212,22 @@ bind:
type: string
default: ${instance.details["dynamodb_table_name"]}
overwrite: true
- name: user_name
default: csb-${request.binding_id}
overwrite: true
type: string
- name: region
default: ${instance.details["region"]}
overwrite: true
type: string
- name: iam_arn
type: string
default: ${instance.details["iam_arn"]}
overwrite: true
- name: role_name
type: string
default: ${instance.details["role_name"]}
overwrite: true
- name: creds_endpoint
type: string
default: ${instance.details["creds_endpoint"]}
overwrite: true
template_refs:
data: terraform/dynamodb-table/bind/data.tf
main: terraform/dynamodb-table/bind/main.tf
Expand All @@ -211,9 +248,15 @@ bind:
- field_name: secret_access_key
type: string
details: AWS secret access key
- field_name: creds_endpoint
type: string
details: The endpoint for fetching the binding creds
- field_name: region
type: string
details: AWS region for the bucket
- field_name: creds_endpoint
type: string
details: The endpoint for fetching the binding creds
examples:
- name: ondemand
description: Create a dynamodb instance
Expand Down
43 changes: 41 additions & 2 deletions terraform/dynamodb-table/bind/data.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,41 @@
# See the License for the specific language governing permissions and
# limitations under the License.

data "aws_iam_policy_document" "user_policy" {
locals {
binding_role = try(aws_iam_role.new_role[0], data.aws_iam_role.provided[0])
}

resource "random_password" "source_identity" {
length = 16
special = false
}

data "aws_iam_role" "provided" {
count = length(var.role_name) == 0 ? 0 : 1

name = var.role_name
}

data "aws_iam_policy_document" "assume_role" {
statement {
sid = "assumeRole"
actions = [
"sts:AssumeRole",
"sts:TagSession",
]
principals {
type = "AWS"
identifiers = [var.iam_arn]
}
condition {
test = "StringEquals"
values = [random_password.source_identity.result]
variable = "aws:RequestTag/current-binding"
}
}
}

data "aws_iam_policy_document" "dynamo_access" {
statement {
sid = "dynamoAccess"
actions = [
Expand All @@ -21,5 +55,10 @@ data "aws_iam_policy_document" "user_policy" {
resources = [
var.dynamodb_table_arn
]
condition {
test = "StringEquals"
values = [random_password.source_identity.result]
variable = "aws:PrincipalTag/current-binding"
}
}
}
}
20 changes: 8 additions & 12 deletions terraform/dynamodb-table/bind/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

resource "aws_iam_user" "user" {
name = var.user_name
path = "/cf/"
}
resource "aws_iam_role" "new_role" {
count = length(var.role_name) == 0 ? 1 : 0

resource "aws_iam_access_key" "access_key" {
user = aws_iam_user.user.name
path = "/cf/"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

resource "aws_iam_user_policy" "user_policy" {
name = format("%s-p", var.user_name)

user = aws_iam_user.user.name
resource "aws_iam_role_policy" "add_allow_dynamodb_policy" {
role = local.binding_role.name
policy = data.aws_iam_policy_document.dynamo_access.json
}

policy = data.aws_iam_policy_document.user_policy.json
}
7 changes: 4 additions & 3 deletions terraform/dynamodb-table/bind/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
# limitations under the License.

output "access_key_id" {
value = aws_iam_access_key.access_key.id
value = local.binding_role.arn
sensitive = true
}
output "secret_access_key" {
value = aws_iam_access_key.access_key.secret
value = random_password.source_identity.result
sensitive = true
}
output "dynamodb_table_arn" { value = var.dynamodb_table_arn }
output "dynamodb_table_id" { value = var.dynamodb_table_arn }
output "region" { value = var.region }
output "region" { value = var.region }
output "creds_endpoint" { value = var.creds_endpoint }
4 changes: 3 additions & 1 deletion terraform/dynamodb-table/bind/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@

variable "dynamodb_table_arn" { type = string }
variable "dynamodb_table_id" { type = string }
variable "user_name" { type = string }
variable "iam_arn" { type = string }
variable "role_name" { type = string }
variable "creds_endpoint" { type = string }
5 changes: 4 additions & 1 deletion terraform/dynamodb-table/provision/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@
output "region" { value = var.region }
output "dynamodb_table_arn" { value = element(concat(aws_dynamodb_table.this.*.arn, tolist([])), 0) }
output "dynamodb_table_id" { value = element(concat(aws_dynamodb_table.this.*.id, tolist([])), 0) }
output "dynamodb_table_name" { value = aws_dynamodb_table.this.name }
output "dynamodb_table_name" { value = aws_dynamodb_table.this.name }
output "iam_arn" { value = var.iam_arn }
output "role_name" { value = var.role_name }
output "creds_endpoint" { value = var.creds_endpoint }
5 changes: 4 additions & 1 deletion terraform/dynamodb-table/provision/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ variable "stream_view_type" { type = string }
variable "server_side_encryption_enabled" { type = bool }

variable "write_capacity" { type = number }
variable "read_capacity" { type = number }
variable "read_capacity" { type = number }
variable "iam_arn" { type = string }
variable "role_name" { type = string }
variable "creds_endpoint" { type = string }
Loading