Setting up AWS
One IAM role, read-only, scoped to one prefix of one bucket. Nothing else in your account changes. Four ways to create that role are below, and the first one takes about two minutes.
What objectgate needs in your account
If a step somewhere asks you for more than the four rows below, it is not a step objectgate needs.
| what | why | how long it lives |
|---|---|---|
| One IAM role | objectgate assumes it to list your prefix and sign download URLs. It holds three allow statements and no write action. | Until you delete it. |
| An external id on that role | objectgate issues one per connection and must present it on every assume-role call, so a leaked role ARN is not access. | Until you delete the role. |
| Your 12-digit account id | Typed into the connect form. With the external id it is enough to work out the role ARN, so nothing has to be copied back out of AWS. | Stored with the connection. |
| A KMS grant, only if you use one | Objects encrypted with a customer-managed key need kms:Decrypt on the
role and the role named in the key policy. |
Until you remove it. |
And the list of things people expect to have to change, which stay exactly as they are:
- Block Public Access
- All four settings stay on. Downloads are signed URLs, not public objects.
- The bucket policy
- Untouched. The permission sits on the role, which is also where you remove it.
- CORS
- Not needed. A download is a navigation to a redirect target, and CORS does not apply to one.
- CloudFront
- No distribution, no origin access control, no signed cookies, no key group.
- Your objects
- They stay where they are, in their storage class, under their lifecycle rules. Nothing is copied anywhere.
- An access key
- The role path creates none, so there is no long-lived secret to store or rotate.
The quickest way, one click
The connect form builds a CloudFormation link with the template and all three parameters already filled in. Opening it lands you in the console of whichever AWS account you are signed in to, on the review page of a stack that is ready to create.
-
Start the connection in objectgate first
In the dashboard, open connect a bucket, type the bucket name and prefix, and choose a role in your AWS account. The external id appears on the form at that point. It has to exist before the stack is created, because the role is named after it.
-
Open the quick-create link
Click open the CloudFormation quick-create. Sign in to AWS in that tab as a user who can create an IAM role. If you keep several accounts, check the account name in the console header before continuing.
-
Check the box and create the stack
The three parameters are already filled in. At the bottom, tick the acknowledgement that the stack creates an IAM resource with a name you chose, then create it. That box is
CAPABILITY_NAMED_IAM, and it is required because the role's name is fixed rather than generated. -
Go back to objectgate and finish
Enter your 12-digit account id and run the check. objectgate works out the role ARN, assumes it, reads one object end to end and reports each step. The stack usually finishes in under a minute; the check retries while it is still creating.
Nothing is pasted back out of AWS, and nothing is stored in objectgate until the check has read a real object.
One paste, in CloudShell
If you would rather not click through a console wizard, AWS CloudShell is already authenticated as the identity you are signed in with, so this needs no local install and no credentials on your machine. Open CloudShell from the console header and paste:
curl -sO https://objectgate.dev/objectgate-role.yaml
aws cloudformation deploy \
--stack-name objectgate-access \
--template-file objectgate-role.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
BucketName=example-bucket \
Prefix=deliveries/ \
ExternalId=PASTE_FROM_THE_CONNECT_FORM
Substitute your own bucket, your own prefix and the external id from the connect form. An
empty Prefix= grants the whole bucket. The same command run again updates the
stack in place, which is how you widen or narrow a prefix later.
To read the role back out afterwards:
aws cloudformation describe-stacks \
--stack-name objectgate-access \
--query 'Stacks[0].Outputs' --output table
The identical command works in a local shell with the AWS CLI configured. CloudShell is only the version with nothing to set up first.
As Terraform
For an account where IAM is managed as code, the role is short enough to declare directly rather than through a stack. There is no module to install.
variable "objectgate_external_id" {} # from the connect form
variable "bucket" {}
variable "prefix" { default = "deliveries/" }
data "aws_iam_policy_document" "objectgate_trust" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::AWS_ACCOUNT_ID_REPLACE_AT_BOOTSTRAP:root"]
}
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = [var.objectgate_external_id]
}
}
}
data "aws_iam_policy_document" "objectgate_read" {
statement {
actions = ["s3:GetBucketLocation"]
resources = ["arn:aws:s3:::${var.bucket}"]
}
statement {
actions = ["s3:ListBucket"]
resources = ["arn:aws:s3:::${var.bucket}"]
condition {
test = "StringLike"
variable = "s3:prefix"
values = ["${var.prefix}*"]
}
}
statement {
actions = ["s3:GetObject"]
resources = ["arn:aws:s3:::${var.bucket}/${var.prefix}*"]
}
}
resource "aws_iam_role" "objectgate" {
name = "objectgate-${var.objectgate_external_id}"
assume_role_policy = data.aws_iam_policy_document.objectgate_trust.json
max_session_duration = 3600
}
resource "aws_iam_role_policy" "objectgate" {
role = aws_iam_role.objectgate.id
policy = data.aws_iam_policy_document.objectgate_read.json
}
The role name matters. objectgate predicts the ARN from your account id and the external id, so a role called anything else will not be found and the check will report that it could not assume it.
By hand in the console
Some accounts do not allow CloudFormation. The same role can be built in the IAM console in five fields.
-
Create a role for another account
IAM, roles, create role, an AWS account, then another AWS account. The account id to enter is objectgate's, shown on the connect form beside the external id.
-
Require the external id
On the same screen, tick require external ID and paste the one from the connect form. Leave require MFA off, because a service that assumes the role cannot present an MFA code and the role would never be usable.
-
Attach the read policy
Create an inline policy from the JSON on the connect guide, substituting your bucket and prefix. Three statements: locate the bucket, list the prefix, read objects under it.
-
Name it after the external id
The role name must be
objectgate-followed by the external id, with no path prefix. Set the maximum session duration to one hour.
If your objects use a KMS key
Default S3 encryption needs nothing extra. A customer-managed key needs two grants, and
missing either produces the same denial on the first read: kms:Decrypt in the
role's own policy, and the role named as a principal in the key policy. A key policy that
does not name the role denies it whatever the role policy says. Both fragments are on the
connect guide.
Checking it worked
The connect check runs five steps in order:
- the endpoint is an https address on a public host;
- objectgate can assume the role;
- the bucket answers and reports its region;
- listing the prefix is allowed;
- one object reads back through a signed URL.
Step four can be skipped rather than passed, on a share that serves without a listing. Step five exercises the same code path a viewer's download takes. Nothing is stored if the check fails, so a half-configured role does not leave a broken connection behind. Every provider error code the check can report, and what to change for each, is on the connect guide.
Taking the access back
Delete the stack, or delete the role. Sessions already minted stop working within the hour and no new one can be obtained, so the shares behind that connection stop serving without anything having to be done in objectgate. Deleting the connection in objectgate is the same revocation from the other end, and it deletes the share cells with it.
The policy, the trust policy, the provider table for storage that is not AWS, and the full list of error codes live on the connect guide. The template itself is at objectgate.dev/objectgate-role.yaml, and it is short enough to read before you run it.