diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index ab125d1..a8ff356 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -13,15 +13,19 @@ services:
AWS_ACCESS_KEY_ID: foo
AWS_SECRET_ACCESS_KEY: bar
TABLE_NAME: OpaDynamodbIntegrationTest
- ports:
- - 8001:8001
depends_on:
- dynamodb
links:
- dynamodb:dynamodb
command: tail -f /dev/null
+ dynamodb-admin:
+ image: aaronshaf/dynamodb-admin
+ environment:
+ DYNAMO_ENDPOINT: http://dynamodb:8000
+ depends_on:
+ - dynamodb
+ ports:
+ - 8080:8001
dynamodb:
image: amazon/dynamodb-local
command: -jar DynamoDBLocal.jar -sharedDb
- ports:
- - 8000:8000
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9780c07..86667d1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -19,5 +19,8 @@ jobs:
uses: actions/checkout@v2
- name: Test
env:
+ AWS_DEFAULT_REGION: us-east-1
+ AWS_ACCESS_KEY_ID: foo
+ AWS_SECRET_ACCESS_KEY: bar
ENDPOINT_URL: http://dynamodb:8000/
- run: make test
\ No newline at end of file
+ run: make test
diff --git a/Makefile b/Makefile
index 85e1157..cd74303 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,10 @@ run:
--authorization=basic \
--set=services.opa.credentials=null
+serve-docs:
+ # Requires nodejs and docsify
+ docsify serve ./docs
+
unit:
go test -v -short ./...
diff --git a/README.md b/README.md
index edc290a..1b764b0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# OPA DynamoDB
-Infinitely scalable policy store with instantaneous policy updates for use by small and enterprise scale teams wanting to use Open Policy Agent.
+Scalable policy store with real-time policy updates for use by small and enterprise scale teams wanting to use Open Policy Agent.
OPA DynamoDB adds custom functionality to rego policies to query data from DynamoDB.
@@ -10,11 +10,13 @@ OPA has several strategies for managing policies at scale and accepting internal
- AWS credentials can be infered by the credentials chain in Goland AWS SDK
- Retry logic and caching are implemented by the AWS SDK and this implementation
+See the [User Documentation](https://mneil.github.io/opa-dynamodb) for setup and usage.
+
## DynamoDB As A Backend
DynamoDB is an excellent backend for policy data. You can store documentesque data across dynamo rows and query them using a collections pattern. This method is efficient (single read to get entire policy) and scalable (dynamodb storage is extremely scalable).
-If you want to understand more about Single Table Design, item collections, and DynamoDB in general I recommend this book by Alex Debrie https://www.dynamodbbook.com/. I have no affiliation with Alex or his book. It's that good.
+If you want to understand more about Single Table Design, item collections, and DynamoDB in general I recommend this book by Alex Debrie https://www.dynamodbbook.com/. I have no affiliation with Alex or his book.
## Architecture
diff --git a/docs/.nojekyll b/docs/.nojekyll
new file mode 100644
index 0000000..e69de29
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..2cf4234
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,15 @@
+# OPA DynamoDB
+
+Scalable policy store with real-time policy updates for use by small and enterprise scale teams wanting to use Open Policy Agent.
+
+OPA DynamoDB adds custom functionality to rego policies to query data from DynamoDB.
+
+OPA has several strategies for managing policies at scale and accepting internal data which you can [read about here](https://www.openpolicyagent.org/docs/latest/external-data/). This repository implements [Option 5](https://www.openpolicyagent.org/docs/latest/external-data/#option-5-pull-data-during-evaluation) using DynamoDB as the external data source. This implementation also removes the current limitations described by OPA.
+
+ - Using this runtime you can test your policies against external data
+ - AWS credentials can be infered by the credentials chain in Goland AWS SDK
+ - Retry logic and caching are implemented by the AWS SDK and this implementation
+
+# Examples
+
+Read the [Getting Started](quickstart.md) for examples
diff --git a/docs/_coverpage.md b/docs/_coverpage.md
new file mode 100644
index 0000000..872cd31
--- /dev/null
+++ b/docs/_coverpage.md
@@ -0,0 +1,12 @@
+
+# OPA DynamoDB 0.1.0
+
+> DynamoDB Backend for Open Policy Agent
+
+[GitHub](https://github.com/mneil/opa-dynamodb/)
+[Get Started](#opa-dynamodb)
+
+
+
+
+)
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
new file mode 100644
index 0000000..9125ac5
--- /dev/null
+++ b/docs/_sidebar.md
@@ -0,0 +1,7 @@
+- [Getting started](quickstart.md)
+
+- Guide
+ - [Configuration](configuration.md)
+ - [Deploy](deploy.md)
+
+- [Changelog](changelog.md)
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 0000000..7740976
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,11 @@
+# Changelog
+
+## v0.1.0
+
+Initial Release
+
+Working dynamodb backend with assumptions:
+
+ - Must have both a hash key and range key on your primary partition
+ - Assumed PK and SK for the key names but can be overridden with environment variables
+ - Tests exist to prove functionality
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..d06a853
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,10 @@
+# Configuration
+
+OPA DynamoDB can be configured using environment variable. This table describes the default settings and the variable that you can set to change them.
+
+| Variable | Default | Required | Description |
+|--------------|-------------|----------|------------------------------------------------------|
+| DYNAMO_TABLE | OpaDynamoDB | No | The name of the table to get data from |
+| DYNAMO_PK | PK | No | The hash (partition) key of the primary partition |
+| DYNAMO_SK | SK | No | The sort (range) key of the primary partition |
+| ENDPOINT_URL | "" | No | DynamoDB API url. Useful for testing w/ dynamo local |
diff --git a/docs/deploy.md b/docs/deploy.md
new file mode 100644
index 0000000..4a8d593
--- /dev/null
+++ b/docs/deploy.md
@@ -0,0 +1 @@
+# Deploy
\ No newline at end of file
diff --git a/docs/examples/docker-compose.yml b/docs/examples/docker-compose.yml
new file mode 100644
index 0000000..b34d1f4
--- /dev/null
+++ b/docs/examples/docker-compose.yml
@@ -0,0 +1,40 @@
+version: '3'
+services:
+ opa:
+ image: mneil/opa-dynamodb
+ command:
+ - run
+ - --server
+ - --log-format=json-pretty
+ - --set=decision_logs.console=true
+ - --log-level=debug
+ - /quickstart/rbac.rego
+ environment:
+ ENDPOINT_URL: http://dynamodb:8000/
+ # No need to change this. DynamoLocal doesn't verify authentication
+ AWS_REGION: us-east-1
+ AWS_ACCESS_KEY_ID: foo
+ AWS_SECRET_ACCESS_KEY: bar
+ TABLE_NAME: OpaDynamoDB
+ ports:
+ - 8181:8181
+ depends_on:
+ - dynamodb
+ links:
+ - dynamodb:dynamodb
+ volumes:
+ - .:/quickstart
+ dynamodb-admin:
+ image: aaronshaf/dynamodb-admin
+ environment:
+ DYNAMO_ENDPOINT: http://dynamodb:8000
+ depends_on:
+ - dynamodb
+ ports:
+ - 8001:8001
+ dynamodb:
+ image: amazon/dynamodb-local
+ command: -jar DynamoDBLocal.jar -sharedDb
+ ports:
+ - 9000:8000
+
diff --git a/docs/examples/rbac.rego b/docs/examples/rbac.rego
new file mode 100644
index 0000000..45f4b88
--- /dev/null
+++ b/docs/examples/rbac.rego
@@ -0,0 +1,30 @@
+package rbac
+
+# user-role assignments
+# user_roles := dynamodb.policy("foo/bar", "alice")
+# user_roles := {
+# "alice": ["engineering", "webdev"],
+# "bob": ["hr"]
+# }
+
+# role-permissions assignments
+role_permissions := {
+ "engineering": [{"action": "read", "object": "server123"}],
+ "webdev": [{"action": "read", "object": "server123"},
+ {"action": "write", "object": "server123"}],
+ "hr": [{"action": "read", "object": "database456"}]
+}
+# lookup the list of roles for the user
+policy := dynamodb.policy(input.namespace, input.principal)
+# logic that implements RBAC.
+default allow = false
+allow {
+ # for each role in that list
+ r := policy.roles[_]
+ # lookup the permissions list for role r
+ permissions := role_permissions[r]
+ # for each permission
+ p := permissions[_]
+ # check if the permission granted to r matches the user's request
+ p == {"action": input.action, "object": input.object}
+}
diff --git a/docs/images/table-creation.png b/docs/images/table-creation.png
new file mode 100644
index 0000000..d92b2df
Binary files /dev/null and b/docs/images/table-creation.png differ
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..09b23be
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/quickstart.md b/docs/quickstart.md
new file mode 100644
index 0000000..22b72da
--- /dev/null
+++ b/docs/quickstart.md
@@ -0,0 +1,69 @@
+# Quick Start
+
+Prerequisites
+
+ - Docker w/ Docker Compose
+
+The fastest way to get started is with Docker and Docker Compose. You can run OPA and Dynamo DB on your own machine. Create a new folder to copy files into.
+
+Start by creating a new rego file for a policy. Create a file named `rbac.rego` with the following contents to create an RBAC policy:
+
+[filename](examples/rbac.rego ':include :type=code')
+
+Create a new file named `docker-compose.yml` and add the following contents:
+
+[filename](examples/docker-compose.yml ':include :type=code')
+
+Run the docker images with `docker-compose up -d`. This compose file will start the opa-dynamo server, a dynamodb local instance, and a [dynamodb-admin instance](https://github.com/aaronshaf/dynamodb-admin).
+
+You should see the 3 services start. Now you can open a browser to [http://localhost:8001](http://localhost:8001) and use the DynamoDB Admin interface.
+
+!> **WSL2 Users** may need to open on on the ipv6 address [http://[::1]:8001/](http://[::1]:8001/). See [this issue](https://github.com/microsoft/WSL/issues/4983) for more information
+
+?> _DynamoDB Admin_ is a GUI for exporing Dynamo data locally. It's not necessary to use this but it makes this example easier.
+
+Using the GUI, create a new table named `OpaDynamoDB`. This is the name of our table configured in the compose file. Set the Hash attribute to `PK` and the Range attribute to `SK`.
+
+
+
+Edit the table and add a policy to evaluate with our rego. Click create item and add two users with their roles:
+
+?> You have to add items individually right now in the GUI. So create one user, then repeat the process to create the next user.
+
+```json
+{
+ "PK": "foo/bar",
+ "SK": "alice",
+ "roles": ["engineering", "webdev"]
+}
+```
+```json
+{
+ "PK": "foo/bar",
+ "SK": "bob",
+ "roles": ["hr"]
+}
+```
+
+Up to this point you have:
+
+ - Defined a RBAC policy file
+ - Started a local DynamoDB Database
+ - Started OPA DynamoDB and connected to the database
+ - Filled the database with policy information
+
+Now you can query OPA to evaluate your policy file. The following query will check if user bob has the ability to read data on server123.
+
+```sh
+curl -X POST http://localhost:8181/v1/data/rbac/allow \
+ -H "Content-Type: application/json" \
+ --data '{"input":{"namespace":"foo/bar","principal":"bob","action":"read","object":"server123"}}'
+```
+
+You should receive a response similar to:
+
+```json
+{"decision_id":"648eccb3-5d8b-4001-8a7a-9e87014ea36a","result":false}
+```
+
+Try changing the principal to alice instead and the result will be true.
diff --git a/policy/policy_test.go b/policy/policy_test.go
index 560d903..b7c9713 100644
--- a/policy/policy_test.go
+++ b/policy/policy_test.go
@@ -90,51 +90,44 @@ func TestPolicyDataIntegration(t *testing.T) {
policy string
principal string
namespace string
- data []map[string]string
+ action string
+ object string
allow bool
}{
{
name: "rbac not allow bob",
policy: "rbac/authz",
- principal: "baz",
+ principal: "bob",
namespace: "foo/bar",
- data: []map[string]string{
- {
- "user": "bob",
- "action": "read",
- "object": "server123",
- },
- },
- allow: false,
+ action: "read",
+ object: "server123",
+ allow: false,
},
{
name: "rbac allow alice",
policy: "rbac/authz",
- principal: "baz",
+ principal: "alice",
namespace: "foo/bar",
- data: []map[string]string{
- {
- "user": "alice",
- "action": "read",
- "object": "server123",
- },
- },
- allow: false,
+ action: "read",
+ object: "server123",
+ allow: true,
},
}
// our actual tests is here in the loop
for _, c := range cases {
body, err := json.Marshal(struct {
- Input interface{}
+ Input interface{} `json:"input"`
}{
Input: struct {
- principal string
- namespace string
- data []map[string]string
+ Principal string `json:"principal"`
+ Namespace string `json:"namespace"`
+ Action string `json:"action"`
+ Object string `json:"object"`
}{
- principal: c.principal,
- namespace: c.namespace,
- data: c.data,
+ Principal: c.principal,
+ Namespace: c.namespace,
+ Action: c.action,
+ Object: c.object,
},
})
resp, err := http.Post(
@@ -146,13 +139,18 @@ func TestPolicyDataIntegration(t *testing.T) {
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode, c.name)
body, err = ioutil.ReadAll(resp.Body)
- var v struct {
- result struct {
- allow bool
- }
- }
+ v := Response{}
json.Unmarshal(body, &v)
- assert.Equal(t, c.allow, v.result.allow, c.name)
+ fmt.Print("THE RESULT")
+ fmt.Print(string(body))
+ assert.Equal(t, c.allow, v.Result.Allow, c.name)
}
+}
+
+type Result struct {
+ Allow bool `json:"allow"`
+}
+type Response struct {
+ Result Result `json:"result"`
}
diff --git a/store/dynamodb.go b/store/dynamodb.go
index ef4d22b..cc198e3 100644
--- a/store/dynamodb.go
+++ b/store/dynamodb.go
@@ -63,6 +63,7 @@ func (dynamo *DynamoStore) Get(namespace string, principal string) (interface{},
KeyConditionExpression: aws.String("#PK = :pk AND #SK = :sk"),
TableName: aws.String(dynamo.TableName),
}
+ log.Debugf("Query input %v", input)
result, err := dynamo.svc.Query(input)
if err != nil {
log.Error("Error querying data from dynamodb")
@@ -73,13 +74,13 @@ func (dynamo *DynamoStore) Get(namespace string, principal string) (interface{},
}
return "", err
}
+ log.Debugf("Query result items %v", result.Items)
itemLength := len(result.Items)
if itemLength == 0 {
log.Debug("No items returned from dynamodb")
return "", nil
}
- log.Debugf("%d items returned from dynamodb", itemLength)
- items := make([]map[string]interface{}, len(result.Items))
+ items := make([]map[string]interface{}, itemLength)
for index, item := range result.Items {
var tmpItem map[string]interface{}
dynamodbattribute.UnmarshalMap(item, &tmpItem)
@@ -87,6 +88,7 @@ func (dynamo *DynamoStore) Get(namespace string, principal string) (interface{},
delete(tmpItem, dynamo.SortKey)
items[index] = tmpItem
}
- log.Debugf("Policy from dynamodb %v", items)
- return items, nil
+ log.Debugf("Policy from dynamodb %v", items[0])
+ // There should only ever be one based on the query above
+ return items[0], nil
}
diff --git a/store/dynamodb_test.go b/store/dynamodb_test.go
index 9f6fe4d..f6501d8 100644
--- a/store/dynamodb_test.go
+++ b/store/dynamodb_test.go
@@ -101,10 +101,8 @@ func TestGet(t *testing.T) {
err: nil,
namespace: "foo",
principal: "bar",
- expect: []map[string]interface{}{
- {
- "foo": "bar",
- },
+ expect: map[string]interface{}{
+ "foo": "bar",
},
},
}
diff --git a/templates/fargate.yml b/templates/fargate.yml
new file mode 100644
index 0000000..6fb49ad
--- /dev/null
+++ b/templates/fargate.yml
@@ -0,0 +1,315 @@
+AWSTemplateFormatVersion: 2010-09-09
+Description: |
+ Deploy OPA DynamoDB to aws Fargate Modified from
+ https://github.com/1Strategy/fargate-cloudformation-example
+Parameters:
+ VPC:
+ Type: AWS::EC2::VPC::Id
+ SubnetA:
+ Type: AWS::EC2::Subnet::Id
+ SubnetB:
+ Type: AWS::EC2::Subnet::Id
+ Certificate:
+ Type: String
+ Default: arn:aws:acm:region:123456789012:certificate/00000000-0000-0000-0000-000000000000
+ Image:
+ Type: String
+ Default: opa-dynamodb:latest
+ ServiceName:
+ Type: String
+ Default: OPADynamoDB
+ ContainerPort:
+ Type: Number
+ Default: 80
+ LoadBalancerPort:
+ Type: Number
+ Default: 443
+ HealthCheckPath:
+ Type: String
+ Default: /healthcheck
+ HostedZoneName:
+ Type: String
+ Subdomain:
+ Type: String
+ Default: opa
+ MinContainers:
+ Type: Number
+ Default: 2
+ MaxContainers:
+ Type: Number
+ Default: 10
+ AutoScalingTargetValue:
+ Type: Number
+ Default: 50
+
+Resources:
+ Cluster:
+ Type: AWS::ECS::Cluster
+ Properties:
+ ClusterName: !Join ['', [!Ref ServiceName, Cluster]]
+ TaskDefinition:
+ Type: AWS::ECS::TaskDefinition
+ DependsOn: LogGroup
+ Properties:
+ Family: !Join ['', [!Ref ServiceName, TaskDefinition]]
+ NetworkMode: awsvpc
+ RequiresCompatibilities:
+ - FARGATE
+ Cpu: 1024
+ Memory: 1GB
+ ExecutionRoleArn: !Ref ExecutionRole
+ TaskRoleArn: !Ref TaskRole
+ ContainerDefinitions:
+ - Name: !Ref ServiceName
+ Image: !Ref Image
+ PortMappings:
+ - ContainerPort: !Ref ContainerPort
+ LogConfiguration:
+ LogDriver: awslogs
+ Options:
+ awslogs-region: !Ref AWS::Region
+ awslogs-group: !Ref LogGroup
+ awslogs-stream-prefix: ecs
+ ExecutionRole:
+ Type: AWS::IAM::Role
+ Properties:
+ RoleName: !Join ['', [!Ref ServiceName, ExecutionRole]]
+ AssumeRolePolicyDocument:
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: ecs-tasks.amazonaws.com
+ Action: sts:AssumeRole
+ ManagedPolicyArns:
+ - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
+ TaskRole:
+ Type: AWS::IAM::Role
+ Properties:
+ RoleName: !Join ['', [!Ref ServiceName, TaskRole]]
+ AssumeRolePolicyDocument:
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: ecs-tasks.amazonaws.com
+ Action: sts:AssumeRole
+ TaskPolicy:
+ Type: AWS::IAM::Policy
+ Properties:
+ PolicyName: OpaDynamoTaskPolicy
+ Roles:
+ - !Ref TaskRole
+ PolicyDocument:
+ Version: 2012-10-17
+ Statement:
+ - Effect: Allow
+ Action:
+ - dynamodb:ConditionCheckItem
+ - dynamodb:DeleteItem
+ - dynamodb:GetItem
+ - dynamodb:PutItem
+ - dynamodb:Query
+ - dynamodb:UpdateItem
+ Resource:
+ - '*'
+ AutoScalingRole:
+ Type: AWS::IAM::Role
+ Properties:
+ RoleName: !Join ['', [!Ref ServiceName, AutoScalingRole]]
+ AssumeRolePolicyDocument:
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: ecs-tasks.amazonaws.com
+ Action: sts:AssumeRole
+ ManagedPolicyArns:
+ - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole
+ ContainerSecurityGroup:
+ Type: AWS::EC2::SecurityGroup
+ Properties:
+ GroupDescription: !Join ['', [!Ref ServiceName, ContainerSecurityGroup]]
+ VpcId: !Ref VPC
+ SecurityGroupIngress:
+ - IpProtocol: tcp
+ FromPort: !Ref ContainerPort
+ ToPort: !Ref ContainerPort
+ SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
+ LoadBalancerSecurityGroup:
+ Type: AWS::EC2::SecurityGroup
+ Properties:
+ GroupDescription: !Join ['', [!Ref ServiceName, LoadBalancerSecurityGroup]]
+ VpcId: !Ref VPC
+ SecurityGroupIngress:
+ - IpProtocol: tcp
+ FromPort: !Ref LoadBalancerPort
+ ToPort: !Ref LoadBalancerPort
+ CidrIp: 0.0.0.0/0
+ Service:
+ Type: AWS::ECS::Service
+ DependsOn:
+ - ListenerHTTPS
+ Properties:
+ ServiceName: !Ref ServiceName
+ Cluster: !Ref Cluster
+ TaskDefinition: !Ref TaskDefinition
+ DeploymentConfiguration:
+ MinimumHealthyPercent: 100
+ MaximumPercent: 200
+ DesiredCount: 2
+ HealthCheckGracePeriodSeconds: 30
+ LaunchType: FARGATE
+ NetworkConfiguration:
+ AwsvpcConfiguration:
+ AssignPublicIp: ENABLED
+ Subnets:
+ - !Ref SubnetA
+ - !Ref SubnetB
+ SecurityGroups:
+ - !Ref ContainerSecurityGroup
+ LoadBalancers:
+ - ContainerName: !Ref ServiceName
+ ContainerPort: !Ref ContainerPort
+ TargetGroupArn: !Ref TargetGroup
+ TargetGroup:
+ Type: AWS::ElasticLoadBalancingV2::TargetGroup
+ Properties:
+ HealthCheckIntervalSeconds: 10
+ HealthCheckPath: !Ref HealthCheckPath
+ HealthCheckTimeoutSeconds: 5
+ UnhealthyThresholdCount: 2
+ HealthyThresholdCount: 2
+ Name: !Join ['', [!Ref ServiceName, TargetGroup]]
+ Port: !Ref ContainerPort
+ Protocol: HTTP
+ TargetGroupAttributes:
+ - Key: deregistration_delay.timeout_seconds
+ Value: 60
+ TargetType: ip
+ VpcId: !Ref VPC
+ ListenerHTTPS:
+ Type: AWS::ElasticLoadBalancingV2::Listener
+ Properties:
+ DefaultActions:
+ - TargetGroupArn: !Ref TargetGroup
+ Type: forward
+ LoadBalancerArn: !Ref LoadBalancer
+ Port: !Ref LoadBalancerPort
+ Protocol: HTTPS
+ Certificates:
+ - CertificateArn: !Ref Certificate
+ LoadBalancer:
+ Type: AWS::ElasticLoadBalancingV2::LoadBalancer
+ Properties:
+ LoadBalancerAttributes:
+ - Key: idle_timeout.timeout_seconds
+ Value: 60
+ Name: !Join ['', [!Ref ServiceName, LoadBalancer]]
+ Scheme: internet-facing
+ SecurityGroups:
+ - !Ref LoadBalancerSecurityGroup
+ Subnets:
+ - !Ref SubnetA
+ - !Ref SubnetB
+ DNSRecord:
+ Type: AWS::Route53::RecordSet
+ Properties:
+ HostedZoneName: !Join ['', [!Ref HostedZoneName, .]]
+ Name: !Join ['', [!Ref Subdomain, ., !Ref HostedZoneName, .]]
+ Type: A
+ AliasTarget:
+ DNSName: !GetAtt LoadBalancer.DNSName
+ HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
+ LogGroup:
+ Type: AWS::Logs::LogGroup
+ Properties:
+ LogGroupName: !Join ['', [/ecs/, !Ref ServiceName, TaskDefinition]]
+ AutoScalingTarget:
+ Type: AWS::ApplicationAutoScaling::ScalableTarget
+ Properties:
+ MinCapacity: !Ref MinContainers
+ MaxCapacity: !Ref MaxContainers
+ ResourceId: !Join ['/', [service, !Ref Cluster, !GetAtt Service.Name]]
+ ScalableDimension: ecs:service:DesiredCount
+ ServiceNamespace: ecs
+ RoleARN: !GetAtt AutoScalingRole.Arn
+ AutoScalingPolicy:
+ Type: AWS::ApplicationAutoScaling::ScalingPolicy
+ Properties:
+ PolicyName: !Join ['', [!Ref ServiceName, AutoScalingPolicy]]
+ PolicyType: TargetTrackingScaling
+ ScalingTargetId: !Ref AutoScalingTarget
+ TargetTrackingScalingPolicyConfiguration:
+ PredefinedMetricSpecification:
+ PredefinedMetricType: ECSServiceAverageCPUUtilization
+ ScaleInCooldown: 10
+ ScaleOutCooldown: 10
+ TargetValue: !Ref AutoScalingTargetValue
+ DynamoTable:
+ Type: AWS::DynamoDB::Table
+ Properties:
+ AttributeDefinitions:
+ - AttributeName: PK
+ AttributeType: S
+ - AttributeName: SK
+ AttributeType: S
+ BillingMode: PAY_PER_REQUEST
+ KeySchema:
+ - AttributeName: PK
+ KeyType: HASH
+ - AttributeName: SK
+ KeyType: RANGE
+ PointInTimeRecoverySpecification:
+ PointInTimeRecoveryEnabled: True
+ SSESpecification:
+ KMSMasterKeyId: String
+ SSEEnabled: True
+ SSEType: KMS
+ TableName: OpaDynamoDB
+ DynamoKey:
+ Type: AWS::KMS::Key
+ Properties:
+ Description: A KMS Key for OPA DynamoDB Table
+ Enabled: True
+ EnableKeyRotation: True
+ KeyPolicy:
+ Version: 2012-10-17
+ Statement:
+ - Sid : Allow access through Amazon DynamoDB for all principals in the account that are authorized to use Amazon DynamoDB
+ Effect : Allow
+ Principal :
+ AWS : '*'
+ Action:
+ - kms:Encrypt
+ - kms:Decrypt
+ - kms:ReEncrypt*
+ - kms:GenerateDataKey*
+ - kms:CreateGrant
+ - kms:DescribeKey
+ Resource: '*'
+ Condition:
+ StringEquals:
+ kms:CallerAccount: !Ref AWS::AccountId
+ kms:ViaService: !Sub dynamodb.${AWS::Region}.amazonaws.com
+ - Sid: Allow direct access to key metadata to the account
+ Effect: Allow
+ Principal:
+ AWS : !Sub arn:aws:iam::${AWS::AccountId}:root
+ Action:
+ - kms:Describe*
+ - kms:Get*
+ - kms:List*
+ - kms:RevokeGrant
+ Resource : '*'
+ - Sid: Allow DynamoDB Service with service principal name dynamodb.amazonaws.com to describe the key directly
+ Effect: Allow
+ Principal:
+ Service: dynamodb.amazonaws.com
+ Action:
+ - kms:Describe*
+ - kms:Get*
+ - kms:List*
+ Resource : '*'
+
+Outputs:
+ Endpoint:
+ Description: Endpoint
+ Value: !Join ['', ['https://', !Ref DNSRecord]]
\ No newline at end of file
diff --git a/testdata/attestors/rbac/authz.rego b/testdata/attestors/rbac/authz.rego
index 5ea91b4..553e8b3 100644
--- a/testdata/attestors/rbac/authz.rego
+++ b/testdata/attestors/rbac/authz.rego
@@ -19,13 +19,13 @@ role_permissions := {
default allow = false
allow {
# lookup the list of roles for the user
- roles := dynamodb.policy("foo/bar", input.user)
+ policy := dynamodb.policy(input.namespace, input.principal)
# for each role in that list
- r := roles[_]
+ r := policy.roles[_]
# lookup the permissions list for role r
permissions := role_permissions[r]
# for each permission
p := permissions[_]
# check if the permission granted to r matches the user's request
p == {"action": input.action, "object": input.object}
-}
\ No newline at end of file
+}