Generating AWS S3 POST Policy with Ruby

Introduction

I recently started a new project at work that required upload and storage of various customer documents. This included things like proof of identity, business documents, etc. We needed a secure, easy place to store these without introducing these documents to our filesystem. That’s where AWS S3 came into play. We decided to take advantage of the POST Policy to offload the heavy lifting of deploying these documents to the S3 bucket to the client. In this post, I’ll explain the

These steps can be used for Rails based projects.

Step 1: Add the AWS SDK

So, like any Ruby project, you’ll need to add the required tools to your gemfile.

  gem 'aws-sdk', '~> 2'

Then install the bundle by running this terminal command from your project root:

  bundle install

This will install the AWS SDK in your project and any dependencies that might be required.

For security reasons, Amazon suggests you set your AWS credentials and region by environment variables. This prevents secret keys from ever entering source control. Since that’s a sysops task, I’ll just set those vriables from the Ruby resource.

  ENV['AWS_ACCESS_KEY_ID'] = 'S3 Acccess Key ID'
  ENV['AWS_SECRET_ACCESS_KEY'] = 'S3 Secret Access Key'
  ENV['AWS_REGION'] = 'AWS Region'

Generate the Policy

So, we are already on the last step. Amazon makes these things so easy to use and maintain. Generating the policy is slightly more complex than installing the gem, but not by much.

For the sake of this post, I’ll just generate a policy that restricts the object key name, content length and expires in 10 minutes.

The first thing you should do is instantiate the AWS SDK and setup the object from your Ruby resource

  s3 = Aws::S3::Resource.new(region: 'MyAmazonRegion')
  obj = s3.bucket('MyBucketName')

This bind the AWS::Resource instance to our variable s3. Then we use that variable to set the bucket that we want our client to upload to. This policy will only be good for writing to the bucket you specify here. Now we need to generate the actual policy that includes the signature, expiration, file type, etc

  post_policy = obj.presigned_post({
      key: 'MyDocument.pdf',
      acl: 'public-read',
      signature_expiration: Time.now + 10.minutes,
      content_length_range: 1...35685,
      success_action_status: '201'
  }).fields

This snippet generates a presigned POST policy with:

  • A key name, so you know what the file will be named
  • A public-read ACL, so anyone with the link can view the document. You can learn more about ACL options here.
  • A signature that expires in 10 miutes. If you don’t specify the expiration, it will expire in 15 minutes by default.
  • A content length range. This means the object must be between 1 and 35685 bytes or the S3 bucket will reject the upload.
  • A success status of 201. You could also specify a success_redirect that will redirect your users to another page if you are submitting this upload from a standard form. For this project at work, the client will submit with specific calls.

Then, just return post_policy to your client and they can use it to upload the document to S3.

Conclusion

That’s it! Super simple, and a very secure way to handle uploads without impacting your server architecture much.

If you enjoyed this post or found it helpful, definitely let me know in the comments. If not, also let me know. Don’t forget to share!

Your email address will not be published. Required fields are marked *

*