On the Meaning of Life(cycles), Versions, and Ransomware

Today we'll learn about S3 lifecycle policies, immutable data, and use one of those to keep our bill down.

Prerequisites

  • You enabled your organization CloudTrail

The Lesson

So much for free!

Back when we enabled CloudTrail I mentioned that your first trail is free. That statement was completely true, and completely false. You see, the trail is free but the S3 storage isn’t. If you look in your account you’ll probably see charges somewhere around… $00.15. Yes, 15 cents, but I promised to keep these labs as close to free as possible, so it’s time to take a little action.

Just wait until we learn about NAT gateways!

Today we will set an S3 lifecycle policy to delete any objects in our CloudTrail bucket older than 90 days. This won’t eliminate your minuscule AWS bill, but should keep it under a $.25 USD a month.

Lifecycle policies are very useful, and often underused by security pros. They allow you to define rules for how AWS manages your data. The most common uses are to migrate data to different (cheaper) storage and clean out old data (as we are doing today), but they can do quite a bit more. We can use them to save off immutable logs which can’t be deleted for a given time period, a common compliance requirement. They can also help archive older logs to manage costs.

Writing about these policies got me thinking about ransomware. It is a common tactic for someone with nefarious intentions who gains access to AWS credentials with the right permissions. They’ll copy your data out of S3, delete it, and then upload an image or text file: the ransom note.

There are a bunch of ways, both good and bad, to protect against ransomware. Obviously the best option is to never let an attacker into your account. I mean, I probably didn’t have to say that, but we security nerds like “defense in depth”, and S3 has options which range from a pain in the ass (MFA delete) to relatively easy. I want to briefly mention two today.

  • We can enable S3 versions, which keep versions of files as they are changed and deleted. The API call to delete a version is different than the one to delete a current object, so we can use IAM policies or SCPs to block version deletion.

    • The problem is that gets a bit expensive. So we can use lifecycle policies to delete old versions, similar to how we will use it today to delete objects older than 90 days.

  • Or if we really don’t want to lose data, we can move it to AWS Glacier, very cheap storage which offers lock policies that prevent deletion for a set period of time. The downside of Glacier is that, depending on the storage class, it can take a day to get data back.

We won’t be doing it today, but one decent strategy to protect data would be:

  • Use an SCP to deny deleting objects and versions.

  • Enable versions on the bucket.

  • Create a lifecycle policy to auto-delete older data, or save it to Glacier.

  • If saving to Glacier, set a lock policy so no one can delete it — even root — for a specified time period.

That’s a super simple overview and there’s a bit more nuance, but the strategy works. Personally I just use versions and the SCP, adding a lifecycle policy if I’m dealing with something like logs where I want to get rid of older data. I actually have a lifecycle policy like this set up in a personal account where I back up photos and save them to Glacier. I recently moved to a different backup service (BackBlaze) and am in the process of getting rid of all the non-Glacier copies, keeping that archive for additional resilience.

Lesson Key Points

  • Lifecycle policies enable us to define rules to move data to different (cheaper) storage, based on criteria such as object age.

  • S3 supports versions. When enabled, a new version is created whenever an object is modified or deleted.

  • Ransomware relies on an attacker getting credentials, copying your data, then deleting it.

  • We can combine versions and lifecycle policies to defend against ransomware.

    • We turn on versions, then use other controls (SCPs/IAM) to block deleting versions.

    • We use lifecycle policies to migrate versions to cheaper storage or delete them so we don’t have big bills for keeping 87,492 versions of every object.

The Lab

The lab is quick and easy today. We will create a lifecycle policy for our CloudTrail bucket in our LogArchive account which deletes objects after 90 days. For our labs that should be plenty of logs, and keep your fees down to less than a cup of coffee (I mean a cup of coffee in 1972 — not today’s prices, which exceed the known value of the universe).

Remember, this is a contrivance for lab accounts. In a real production environment you would probably want to keep logs longer, and might have regulatory retention requirements. Show up at your new job and set up this policy, and you might be unemployed again quickly — no matter how many times you scream “this dude on the Internet told me to!” as security drags you out the door.

Video Walkthrough

Step-by-Step

First, go to your IAM Identity Center portal and sign in. Then select your LogArchive account and Administrator access.

Then go to S3 and click your CloudTrail bucket, which should be the only one there.

Then click Management and Create lifecycle rule.

These next 2 screenshots show what you need to set to build the policy. First give it the name Delete old CloudTrail, then Apply to all objects in the bucket, then check the box to confirm that’s what you want, and finally Expire current versions of objects.

Keep scrolling down, set it for 90 days, then Create rule.

And…

That’s it. Yep, really. I originally thought we would need to do all this JSON policy stuff, but it really is that easy. If you were to do this via the command line or code, the JSON would look something like this:

{
  "Rules": [
    {
      "ID": "DeleteOldObjects",
      "Filter": {
        "Prefix": ""  // Specify a prefix if you only want to apply the rule to objects with a specific prefix
      },
      "Status": "Enabled",
      "Expiration": {
        "Days": 90
      },
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 90
      }
    }
  ]
}

That would delete both the objects (CurrentVersion) and the versions (NoncurrentVersionExpiration). We aren’t using versions, but I want you to see both.

That’s a wrap, see ya next week!

-Rich

Join the conversation

or to participate.