Post-Image

Keeping Secure: QLDB, IAM and you

As usual with AWS provided policies, they can be a good starting point for learning a service, but we can certainly do better when it comes to putting them into production. In Part 1, I discussed the built-in options AWS provides for securing QLDB. Today I’ll show you the building blocks available for IAM and sketch out some more useful real-world policies.

This article is part of the Keeping Secure - QLDB series.

Anatomy of a QLDB Ledger

I’ll briefly explain the different resources in a QLDB ledger so we can better understand what these policies will be targeting.

QLDB Anatomy

Ledger

The Ledger is the parent resource for QLDB. It’s concerned with both the management of the ledger itself and creation of sub elements like tables and streams. It is the entry point for most of these resource policies except the Kinesis Streams.

Journal

The journal is an append-only, immutable transaction log for all entities within the Ledger - it’s the backbone of the entire QLDB service. When you write to a QLDB Table, you are writing to the journal. The transaction within the journal is then applied to the tables that are defined in the transaction. As such, it doesn’t have a specific resource that you need to create an IAM policy for. Journals can be streamed to Kinesis - this can be useful to employ QLDB as a single source of truth by exporting the transactions to another service, such as Lambda, that can populate a more performant database solution such as DynamoDB.

Table

Tables are document database stores that contain Amazon Ion documents. Ion is a superset of JSON created by Amazon that allow some interesting possibilities, the most notable for QLDB being the ability to perform SQL-like transactions such as Joins with the PartiQL language. Additionally, Tables can be tagged, which I’ll be using later in our example policies.

Catalog

The catalog is a store that contains metadata about a QLDB Table. While tables have a unique human readable name, behind the scenes they are prescribed a unique ID and this is required when doing data verification. The metadata returned for each Table is:

  • tableId - unique ID of the table
  • name - the name of the table
  • indexes - any indexes applied to the table
  • status - current status of the table, IE: ACTIVE or INACTIVE

Stream

You can use Kinesis to send transactions to a stream. It exists outside of QLDB in the Kinesis service, but you need to grant permissions within QLDB to send the transactions to the stream.

Resources

QLDB provides the following resources that can be used in IAM Policies:

Resource ARN Template
ledger arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
table arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}/table/${TableId}
catalog arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}/information_schema/user_tables
stream arn:${Partition}:qldb:${Region}:${Account}:stream/${LedgerName}/${StreamId}

Control Plane IAM Permissions

QLDB provides several permissions to secure the Control plane. They grant access to Ledger Management, Stream management and Journal management. It’s important to consider securing the management of the Ledger separately from the data in the tables if your organization has different people or teams managing the infrastructure and database operations - ideally those writing to the tables shouldn’t be able to modify the ledger itself.

Ledger Management

Ledger management is concerned with the lifecycle of a Ledger.

Action ARN Template
qldb:CreateLedger arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:DeleteLedger arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:DescribeLedger arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:ListLedgers *
qldb:ShowCatalog arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:TagResource arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:UntagResource arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:UpdateLedger arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:UpdateLedgerPermissionsMode arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}

Journal and Stream Management

Journal and Stream management actions control access to the Journal and Streams of a given Ledger. Journal data can either be exported to S3 or streamed to Kinesis.

Action ARN Template
qldb:DescribeJournalS3Export arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:ExportJournalToS3 arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:ListJournalS3ExportsForLedger arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
qldb:CancelJournalKinesisStream arn:${Partition}:qldb:${Region}:${Account}:stream/${LedgerName}/${StreamId}
qldb:DescribeJournalKinesisStream arn:${Partition}:qldb:${Region}:${Account}:stream/${LedgerName}/${StreamId}
qldb:ListJournalKinesisStreamsForLedger arn:${Partition}:qldb:${Region}:${Account}:stream/${LedgerName}/${StreamId}
qldb:StreamJournalToKinesis arn:${Partition}:qldb:${Region}:${Account}:stream/${LedgerName}/${StreamId}



Data Plane IAM Permissions

If using the STANDARD permissions mode, you can grant granular access to specific Ledgers, Tables, actions, streams, and force connectivity through an AWS PrivateLink Endpoint. If using the ALLOW_ALL mode, then any action is permissible by anyone with the SendCommand action on a ledger.

Don’t use ALLOW_ALL and instead switch to STANDARD if it’s enabled in your environment.

Actions

QLDB provides access to specific actions that can be used in IAM Policies to create granular permissions for your QLDB database. Below are some examples of the actions available. A full list can be found here.

The SendCommand permission must be enabled to allow a principal to send PartiQL commands to the ledger.

PartiQL Command Action ARN Template
- qldb:SendCommand arn:${Partition}:qldb:${Region}:${Account}:ledger/${LedgerName}
CREATE_TABLE qldb:PartiQLCreateTable arn:aws:qldb:region:account-id:ledger/ledger-name/table/*
INSERT qldb:PartiQLInsert arn:aws:qldb:region:account-id:ledger/ledger-name/table/table-id
DELETE qldb:PartiQLDelete arn:aws:qldb:region:account-id:ledger/ledger-name/table/table-id
SELECT FROM table_name qldb:PartiQLSelect arn:aws:qldb:region:account-id:ledger/ledger-name/table/table-id

VPC Endpoint Policies

One of the more intriguing control mechanisms that QLDB offers is the ability to restrict what actions can be done on a VPC-Endpoint attached to the QLDB service with the VPC Endpoint Policy. This lets you override any permissions granted to an IAM role.

Using a VPC-Endpoint then has two major advantages. First, it’s inherently more secure as any requests to QLDB will be made through the AWS backbone, remaining private. Secondly, this could be useful in the case of compromised access or other situations where you need to put the database into a read-only mode or restrict access to everyone except administrators. By changing one policy (which could leverage a SOAR technology that you have), you can control access to the entire Ledger or QLDB service for many different roles.

See below for a VPC-Endpoint policy that only allows Read Access that was shown in Part 1 of this series:

Read-Only Endpoint IAM Policy
{
  "Statement": [
    {
      "Sid": "QLDBSendCommandPermission",
      "Principal": "*",
      "Effect": "Allow",
      "Action": "qldb:SendCommand",
      "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"																				
    },
    {
      "Sid": "QLDBPartiQLReadOnlyPermissions",
      "Principal": "*",
      "Effect": "Allow",
      "Action": [
          "qldb:PartiQLSelect",
          "qldb:PartiQLHistoryFunction"
      ],
      "Resource": [
          "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/table/*",
          "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/information_schema/user_tables"
      ]
    }
  ]
}



Practical Employment

Now that we have an understanding about the types of resources and actions that can be controlled with IAM, I’ll show you how to implement them by showing three target deployments. First, we will be using every tool outlined in this article. The second will be much less restrictive, eschewing the VPC-Endpoint Policy and Tagging. Lastly, I will show a policy that will allow access through the web console.

Between these three, you can pick and choose which parts to implement in your own environment - for example, if you use a service such as CloudShell, you would omit the VPC-Endpoint piece as there’s no way to route CloudShell sessions through a VPC-Endpoint. Likewise, if you don’t have tagging on your Tables, you could omit the tagging portion of the policies. Using just the permissions policies for control is a good starting point, especially in comparison to the built-in policies provided by AWS.

Most Restrictive

Restrictive Access Infrastructure

Our ‘users’ in this example will be accessing QLDB from within a VPC. They can be any principal, be it an instance attached role, an assumable role, or a lambda execution role. If they have a route to the VPC Endpoint and the required permissions for the use-case (IE: lambda permissions if this is for a serverless function), it will suffice. Any access attempts from a place that does not have access to the VPC Endpoint will be blocked, so no updating production from your local machine at a coffee shop over public Wi-Fi.

The default VPC-Endpoint policy allows all actions. For the purposes of demonstration, I’ll be using the default, but you could control any PartiQL actions you want to allow through the Endpoint Policy as outlined in the VPC Endpoint section.

In this example architecture, we are also tagging the tables on the ledger with a data_classification tag. This will allow us to implement another layer of security.

Using tagging necessitates the qldb:ListTagsForResource action - you cannot perform any actions on a table that is tagged without this action.

Below is an example user policy that grants access to:

  • Verify records in the myExampleLedger ledger.
  • Read-Only access to any QLDB table in the myExampleLedger ledger that is tagged with the data_classification tag: public.
  • Read-Only access to the specific table (Au1EiThbt8s0z9wM26REZN).

All of these must be performed through the AWS PrivateLink Endpoint vpce-1a2b3c4d, or it will be denied the SendCommand action, which is a pre-requisite for any PartiQL actions. This could be used for a principal such as an auditor, business user or service principal that only requires read access to the database.

Restrictive Read-only IAM Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "QLDBVerifyRecords",
            "Effect": "Allow",
            "Action": [
                "qldb:GetBlock",
                "qldb:GetDigest",
                "qldb:GetRevision"
            ],
            "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
        },
        {
            "Sid": "ListTags",
            "Effect": "Allow",
            "Action": [
                "qldb:ListTagsForResource"
            ],
            "Resource": "*"
        },
        {
            "Sid": "QLDBSendCommandPermission",
            "Effect": "Allow",
            "Action": "qldb:SendCommand",
            "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
        },
        {
            "Sid": "QLDBPartiQLReadTagged",
            "Effect": "Allow",
            "Action": [
                "qldb:PartiQLSelect",
                "qldb:PartiQLHistoryFunction"
            ],
            "Resource": [
                "arn:arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/data_classification": [
                        "public"
                    ]
                }
            }
        },
		{
            "Sid": "QLDBPartiQLReadSpecificTable",
            "Effect": "Allow",
            "Action": [
                "qldb:PartiQLSelect",
                "qldb:PartiQLHistoryFunction"
            ],
            "Resource": [
                "arn:arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/table/Au1EiThbt8s0z9wM26REZN"
            ]
        },
      	{
          "Sid": "DenySendCommandFromOutsideVPCE",
          "Effect": "Deny",
          "Action": "qldb:SendCommand",
          "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger",
          "Condition": {
              "StringNotEquals": {
                  "aws:sourceVpce": "vpce-1a2b3c4d"
              }
          }
      }
    ]
}

The following policy will grant the role CRUD and data verification permissions to any tables tagged with data_classification public or private on the myExampleLedger ledger through the PrivateLink Endpoint vpce-1a2b3c4d.

Restrictive DBA IAM Policy
{
	  "Version": "2012-10-17",
	  "Statement": [
		{
			"Sid": "QLDBVerifyRecords",
			"Effect": "Allow",
			"Action": [
				"qldb:GetBlock",
				"qldb:GetDigest",
				"qldb:GetRevision"
			],
			"Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
		},
		  {
			  "Sid": "AllowSendCommand",
			  "Effect": "Allow",
			  "Action": "qldb:SendCommand",
			  "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
		  },
		  {
			  "Sid": "AllowPartiQLActions",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:PartiQLUpdate",
				  "qldb:PartiQLSelect",
				  "qldb:PartiQLInsert",
				  "qldb:PartiQLHistoryFunction",
				  "qldb:PartiQLDelete"
			  ],
			  "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/table/*",
			  "Condition": {
				  "StringEquals": {
					  "aws:ResourceTag/data_classification": [
						  "public",
						  "private"
					  ]
				  }
			  }
		  },
		  {
			  "Sid": "ListTags",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:ListTagsForResource"
			  ],
			  "Resource": "*"
		  },
		  {
			  "Sid": "DenySendCommandFromOutsideVPCE",
			  "Effect": "Deny",
			  "Action": "qldb:SendCommand",
			  "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger",
			  "Condition": {
				  "StringNotEquals": {
					  "aws:sourceVpce": "vpce-1a2b3c4d"
				  }
			  }
		  }
	  ]
}

Less Restrictive - Just IAM permissions

Less Restrictive Access Infrastructure

In this less restrictive design, our ‘users’ in this example will be accessing QLDB programmatically by assuming a role on their local machine or via CloudShell. In this case, we will remove the VPC-Endpoint and Tagging restrictions, but the permissions will otherwise largely remain the same.

CloudShell permissions not included in the policy.

Read-Only access with console Verification

Below is an example user policy that grants access to:

  • Verify records in the myExampleLedger ledger, in the terminal and browser.
  • Read-Only access to any QLDB table in the myExampleLedger ledger.
Less Restrictive Read-only IAM Policy
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "QLDBLedgerPermissions",
			"Effect": "Allow",
			"Action": [
				"qldb:SendCommand",
				"qldb:GetBlock",
				"qldb:GetDigest",
				"qldb:GetRevision",
				"qldb:DescribeLedger"
			],
			"Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
		},
		{
			"Sid": "QLDBPartiQLReadOnlyPermissions",
			"Effect": "Allow",
			"Action": [
				"qldb:PartiQLSelect",
				"qldb:PartiQLHistoryFunction"
			],
			"Resource": [
				"arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/table/*"
			]
		},
		{
			"Sid": "QLDBList",
			"Effect": "Allow",
			"Action": [
				"qldb:ListTagsForResource",
				"qldb:ListLedgers"
			],
			"Resource": "*"
		}
	]
}

DBA/Application: CRUD access

The following policy will grant the role CRUD and data verification permissions to any table on the myExampleLedger ledger.

Less Restrictive DBA IAM Policy
{
	  "Version": "2012-10-17",
	  "Statement": [
		{
			"Sid": "QLDBVerifyRecords",
			"Effect": "Allow",
			"Action": [
				"qldb:GetBlock",
				"qldb:GetDigest",
				"qldb:GetRevision"
			],
			"Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
		},
		  {
			  "Sid": "AllowSendCommand",
			  "Effect": "Allow",
			  "Action": "qldb:SendCommand",
			  "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger"
		  },
		  {
			  "Sid": "AllowPartiQLActions",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:PartiQLUpdate",
				  "qldb:PartiQLSelect",
				  "qldb:PartiQLInsert",
				  "qldb:PartiQLHistoryFunction",
				  "qldb:PartiQLDelete"
			  ],
			  "Resource": "arn:aws:qldb:us-east-1:123456789012:ledger/myExampleLedger/table/*",
		  },
		  {
			  "Sid": "ListTags",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:ListTagsForResource"
			  ],
			  "Resource": "*"
		  }
	  ]
}

Console Access

Console Access Infrastructure

In our final example, our user, who wishes to use the PartiQL query editor in the console, must be an administrator with permissions to all PartiQL actions on all ledgers. Unfortunately there doesn’t at this time appear to be a combination of permissions that allow console access without giving the keys to the kingdom. This is clearly not to be given out lightly. There is a light at the end of the tunnel however - unlike the built-in policies that AWS provides, we can strip down some more of the ledger management permissions to only allow a user to access the databases.

The key action here is ExecuteStatement which allows a principal to perform PartiQL statements in the console.

If only ExecuteStatement is all that was required :(

Console access
{
	  "Version": "2012-10-17",
	  "Statement": [
		  {
			  "Sid": "QLDBVerifyRecords",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:GetBlock",
				  "qldb:GetDigest",
				  "qldb:GetRevision"
			  ],
			  "Resource": "*"
		  },
		  {
			  "Sid": "AllowSendCommand",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:SendCommand",
				  "qldb:ExecuteStatement"
			  ],
			  "Resource": "*"
		  },
		  {
			  "Sid": "AllowPartiQLActions",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:PartiQL*"
			  ],
			  "Resource": "*"
		  },
		  {
			  "Sid": "ListTags",
			  "Effect": "Allow",
			  "Action": [
				  "qldb:ListTagsForResource",
				  "qldb:ListLedgers"
			  ],
			  "Resource": "*"
		  }
	  ]
}

Conclusions

To wrap up, so far, we have discussed:

  • What resources we can target with IAM policies.
  • The different actions available to you on both the Control plane and Data Plane.
  • How we can control the flow of traffic to QLDB by utilizing PrivateLink endpoints to not only ensure that QLDB traffic remains private, but what actions can be performed through the endpoint.
  • How to control access to specific tables by utilizing tagging on the tables and using conditional statements in our IAM policies.
  • How to put it all together to create sensible, granular permission sets for QLDB access.
  • That console access is not fully fleshed out yet, so be mindful of what you’re getting into when granting this convenient power.

That about covers everything I wanted to talk about for IAM and QLDB, thanks for reading this article, and there are a few more topics to come, so check back often or follow Keep Secure’s twitter or linkedin profiles to get notified when my next article is released.

 

About Tyler Fougere

Tyler is an AWS Certified Solutions Architect and has over a decade of enterprise systems administration experience, bringing a wealth of knowledge on Windows, Linux and MacOS infrastructure. You can find him most days and nights trying to quench an insatiable thirst for knowledge of all things cloud and technology.

Share This Article

Comments