Security DevSecOps With GuardDuty Lead image: Lead Image © xcfoto, 123RF.com
Lead Image © xcfoto, 123RF.com
 

Security in the AWS cloud with GuardDuty

En Garde!

Amazon GuardDuty continuously monitors your AWS accounts and workloads for potential threats. By Chris Binnie

GuardDuty is a sophisticated threat intelligence tool offered by AWS as a specialist service that takes care of ever-evolving infrastructure attacks [1]. In this article, I show you how to enable GuardDuty on AWS through Infrastructure as Code with HashiCorp's venerable Terraform [2] and look at the type of alerts it stands guard and looks out for. Minus a third-party application ingesting the resulting alerts into a security operations center (e.g., Splunk), I'll demonstrate an end-to-end DevSecOps solution.

Cloud technologies have brought a number of significant advances in the ways we create and maintain Internet infrastructure. Coupled with DevOps practices, which help expedite application software releases, the tools in use today are a million of your Earth miles from the tools of yesteryear.

As part of the cloud's innovation, Amazon Web Services (AWS) brought to the fore utility computing, or Pay as You Go. In addition to the speedy, dynamic creation (and destruction) of resources, flexible billing, and the Infrastructure-as-a-Service (IaaS) model from AWS, do not forget the continual blessing of new features and services by the bucket load (pun intended). Some of these cloud services fall by the wayside and are quietly deprecated, and some create a significant new revenue stream for AWS.

In the same way that data centers need security monitoring, these new-fangled cloud services still present the time-honoured issue of tracking who is attacking your online resources, with a very real need to gain an insight into the attackers' targets.

Dive, Dive, Dive

AWS describes the key features of GuardDuty as "… a threat detection service that continuously monitors for malicious or unauthorized behavior to help you protect your AWS accounts and workloads. It monitors for activity such as unusual API calls or potentially unauthorized deployments that indicate a possible account compromise".

Security professionals have long known that reconnaissance plays a vital part in an attacker's work. To my DevSecOps mind, GuardDuty offers a valuable insight into such recon activities, along with the ability to help detect compromised Elastic Compute Cloud (EC2) server instances.

Security as Code

Machine learning (and less user input) is the future of computing. AWS removes the pain of handling endless rulesets related to your security and puts its foot down firmly when it comes to GuardDuty. AWS simply denies you the ability to have any control over your rulesets. Thankfully, you don't have to worry about any admin overhead.

Because no additional logging storage conduits or service configuration is required – simply some Identity Access Management (IAM) service roles that GuardDuty happily creates for you – you also don't have to worry as much about data leak protection (DLP) if your storage/access permissions are incorrect on AWS Simple Storage Service (S3) or CloudTrail, which could result in your security metrics suffering a humiliating public leak. In other words, behind the scenes, AWS configures all the plumbing automatically for you to ensure and bolster your GuardDuty configuration. It's a genuinely simple, yet highly sophisticated service offering.

Uncle Buck

Another pleasant surprise is that GuardDuty won't break your bank: Pricing follows the familiar AWS utility computing model, as seen in Figure 1 for the US East (North Virginia) AWS region. As the figure demonstrates, much of the cost depends on how much traffic is moving between your virtual private cloud (VPC) logs and how many "events" are reported by the intelligence feeds that GuardDuty uses, which means a busy server estate pays more than a quiet one.

Costs in the US East (North Virginia) AWS region as an example of GuardDuty pricing [3].
Figure 1: Costs in the US East (North Virginia) AWS region as an example of GuardDuty pricing [3].

A number of pieces of plumbing come into play with GuardDuty. Much of its innards are set up automatically when you switch GuardDuty on. For example, behind the scenes, AWS takes away some of your pain by giving the GuardDuty service just enough rights to query other AWS services (e.g., EC2 instances) to glean what's required to run GuardDuty properly. The IAM role allocated to this task, called AWSServiceRoleForAmazonGuardDuty (Listing 1), is automagically assigned to your account when a GuardDuty instance (or detector) is enabled [4].

Listing 1: Policy to Enable GuardDuty

01 {
02   "Version": "2012-10-17",
03   "Statement": [
04     {
05       "Effect": "Allow",
06       "Action": [
07         "guardduty:*"
08       ],
09       "Resource": "*"
10     },
11     {
12       "Effect": "Allow",
13       "Action": [
14         "iam:CreateServiceLinkedRole"
15       ],
16       "Resource": "arn:aws:iam::XXXXXXXXXXX:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty",
17       "Condition": {
18         "StringLike": {
19           "iam:AWSServiceName": "guardduty.amazonaws.com"
20         }
21       }
22     },
23     {
24       "Effect": "Allow",
25       "Action": [
26         "iam:PutRolePolicy",
27         "iam:DeleteRolePolicy"
28       ],
29       "Resource": "arn:aws:iam::XXXXXXXXXXXX:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty"
30     }
31   ]
32 }

Terraforming AWS

When you enable the handful of behind-the-scenes resources that get your GuardDuty instance running, you want to be certain you can destroy them with ease too. To my mind, the mighty Terraform is the best tool for this job.

As with all things DevOps, it's likely that somebody, somewhere has scratched their heads at a similar scenario or quandary you are facing and has kindly documented it somewhere online. This applies perfectly to GuardDuty. Although you can find some very useful examples on GitHub, I'll show you my functional code first and then point you toward a bells and whistles option. I've had both working beautifully in the past.

AWS CLI

Often during setup and troubleshooting with Terraform, the AWS command-line interface (CLI) is useful. To follow the code in this article, you not only need to download the latest version of Terraform (and put it in your user's path), you need to create an AWS credentials file to use the CLI [5].

I installed my AWS CLI client with the Python installer command mentioned on the AWS site:

$ pip install awscli --upgrade --user

Be warned that you're not always guaranteed to get the current version from your package manager, so using pip ultimately is a timesaver. If you think you're lacking the latest features of the CLI client, try this command:

$ aws --version

You can then check against what Google reports as the latest version. Note the section on the AWS site about adding the AWS CLI to your user's path correctly. I also tend to create a Bash alias:

alias aws='/root/.local/bin/aws'

Once you're cooking with gas, you can get your hands dirtier with GuardDuty commands.

The simple example commands shown below should need little introduction and whet your appetite sufficiently. The first command is to generate a list of "detectors," that is, instances of GuardDuty present on your account:

$ aws guardduty list-detectors --region eu-west-1 --query 'DetectorIds'

After you've run Terraform or manually hit Enable in the AWS Console or enabled through the CLI, you will probably have just one. Having run this command, which effectively shows you the money in terms of listing GuardDuty instances, you can then delete one of those listed:

$ aws guardduty delete-detectors --region eu-west-1 --detector-id 86b2f93d992891XXXXXXX54278602ed

Clearly the aws guardduty command subset offers a heap more options than just list and delete, but this should get you started. Try --help if you're keen to learn more.

Without Further Ado

To get things moving in the right direction, explore the relatively simple, concise code and how you might use the code from scratch.

To get Terraform up and running (once you've downloaded Terraform and included it in your user's path), you need an AWS account for testing; additionally, you need to use the export command to tell Terraform about your AWS access key and secret key:

$ export AWS_ACCESS_KEY_ID="<YOUR-KEY>"
$ export AWS_SECRET_ACCESS_KEY="<YOUR-SECRET-KEY>"

Next, clone the GitHub code (assuming you have Git installed) or create files with the code I'm about to show you, with this command:

$ git clone https://<github-account>/<repo-name>

Inside the directory where your Terraform code lives, check that Terraform will run correctly:

$ terraform init

Once you've answered any complaints from Terraform, hit the "plan" button with:

$ terraform plan

Although you can opt to save your planning data to a plan file, I'm keeping it super-simple in this example. The command

$ terraform apply

applies your plan.

Tomato, Tomahto

So that the terminology is clear, you're initially going to set up a single, standalone GuardDuty master account. This main account ingests member accounts, so you can centrally manage all the alerts (known as findings) from one place. More about master and member GuardDuty accounts can be found online [6].

You can also configure and pull in lots of less privileged users from member accounts who can view findings but can't affect configuration. The bells and whistles example later looks into this case further. If you're curious from a Terraform perspective, you can find more information online [7].

The code in Listing 2 is my variables.tf file. For your use, you should alter the email address and AWS account number. (Look under Billing in the AWS Console for the account number.)

Listing 2: variables.tf

01 variable "aws_region" {
02   default = "eu-west-1"
03 }
04
05 variable "aws_account" {
06   default = "XXXXXXXXXXXXX"
07 }
08
09 variable "aws_member_account_email_id" {
10   default = "pigeon @ devsecops.cc"
11 }

Listing 3 shows the output.tf file, which I've included so you can make sure you've set GuardDuty up on the correct AWS account.

Listing 3: output.tf

01 output "master_guardduty_account_id" {
02   value = "${aws_guardduty_detector.guardduty_master.account_id}"
03 }

Finally, Listing 4 offers the innards required to enable GuardDuty with Terraform from the main.tf file. This super-simple file saves your Terraform state, enables GuardDuty, and does some minimal configuration.

Listing 4: main.tf

01 provider "aws" {
02   region = "${var.aws_region}"
03 }
04
05 terraform {
06   backend "s3" {
07     region  = "eu-west-1"
08     bucket  = "chrisbinnie-terraformstate"
09     key     = "guardduty.tfstate"
10     encrypt = true
11   }
12 }
13
14 resource "aws_guardduty_detector" "guardduty_master" {
15   enable = true
16 }
17
18 resource "aws_guardduty_member" "member" {
19   account_id  = "${var.aws_account}"
20   detector_id = "${aws_guardduty_detector.guardduty_master.id}"
21   email          = "${var.aws_member_account_email_id}"
22 }

The top of Listing 4 pulls in the AWS region configured in variables.tf. You might want to add that to output.tf, too, because it's easy to set GuardDuty live in the wrong region. The second stanza down points to the S3 bucket (see the "Hole In My Bucket, Delilah" box for the limitations of Terraform and its upsides), where Terraform saves its state file. The final two sections are simply switching GuardDuty on and adding an email address in case member accounts want to be contacted in the future.

Note that you can't just run this code without creating the S3 bucket first. My bucket is chrisbinnie-terraformstate, and I'll have to create it manually with private permissions before running Terraform. Figure 2 shows that there's no point in giving the entire Internet details of your precious resources, so keep the permissions set to private in the AWS Console if you're using that method to create your bucket.

Keep your S3 bucket permissions set to private or bad things will happen.
Figure 2: Keep your S3 bucket permissions set to private or bad things will happen.

If you added some members, use the following variable syntax:

output "member_guardduty_account_id" {
  value = "${aws_guardduty_detector.guardduty_member.account_id}"
}

This is the output.tf version.

Next, Please

Once I have a back-end S3 bucket created and secured in the correct region, I can then enter the Terraform code directory and run the command:

$ terraform init

The resulting and very welcome message, among other output, is Terraform has been successfully initialized!

Now, you can run the terraform plan command, as discussed earlier. Figure  3 offers the result of your planning. Now that it's working without errors, you can run terraform apply, and as a result, GuardDuty should be configured automatically for you (Figure  4).

The successful output from the terraform plan command.
Figure 3: The successful output from the terraform plan command.
The abbreviated success message after applying the plan.
Figure 4: The abbreviated success message after applying the plan.

Don't be fooled, however; you haven't created just two resources: AWS is weaving its magic behind the scenes. In Terraform terms, it's very clean to visualize, though.

Regions

If you see the AWS Console screen shown in Figure 5, you haven't enabled GuardDuty in that region, which of course you might have intended to do. The nice thing about Infrastructure as Code is that it's relatively easy to reiterate your actions across many regions. Consider that Figure 5 is showing GuardDuty switched off; that is, no detector is running.

What? No GuardDuty? If you see the Get started button, it's switched off in that region.
Figure 5: What? No GuardDuty? If you see the Get started button, it's switched off in that region.

If you mistakenly use the wrong region, fear not. Check Disable GuardDuty (Figure 6) under the Settings option within GuardDuty in the AWS Console, and you'll see the screen in Figure 5 showing it's not present once again after a page reload.

If you make a mistake, just go into the AWS Console and check Disable GuardDuty to prevent ongoing charges.
Figure 6: If you make a mistake, just go into the AWS Console and check Disable GuardDuty to prevent ongoing charges.

Mein Gott!

You are finally up and running, and you can expect AWS to start monitoring your load balancers and public-facing EC2 IP addresses straightaway. There's a little ambiguity in the documentation, but I work on the basis that GuardDuty will only ever monitor external IP address traffic. By that, I mean the intelligence tooling isn't focused on private IP addresses (geek alert: I mean RFC 1918 [10] address space). Although some AWS documentation examples mention 10.0.0.0/16 ranges, if my memory serves.

Note that, importantly, GuardDuty is only concerned with new traffic and will never track historical communications. It will, however, let you retain findings for a while, and ideally you would output the findings to CloudWatch and then beyond (e.g., Splunk or Sumo Logic) via an AWS lambda function, which I have done in the past.

To get the most out of GuardDuty, you really need a reasonable level of public traffic hitting your resources. To make sure your account is working, a nice feature generates sample findings that let you get a feel for what GuardDuty can do. The samples comprise one of each type of finding that GuardDuty can detect.

To get better visibility into how powerful GuardDuty really is, I generated sample findings from the AWS Console (Figure 7). You'll find this capability under the Settings box above the Suspend GuardDuty section shown in Figure 6.

Generating one of each type of alert that would usually trigger GuardDuty threat intelligence alerts.
Figure 7: Generating one of each type of alert that would usually trigger GuardDuty threat intelligence alerts.

Once you have clicked Generate sample findings, you can return to the main screen. Lo and behold, you will see a flurry of activity (Figure  8). Notice the EC2 instance IDs are i-99999999, or similar, to highlight that it's fake traffic.

Sample findings give you a really good idea about how clever the venerable GuardDuty is.
Figure 8: Sample findings give you a really good idea about how clever the venerable GuardDuty is.

If you look closer, you'll find some useful attack information. SSH, IAM, DNS, Trojan, and Tor issues are flagged, just for starters. I'd recommend spending some time getting used to the formatting. You can egest this useful attack information off the API or CloudWatch, as mentioned, further upstream.

Now that GuardDuty is up and running, after a while, it should offer you some very helpful assistance through its threat intelligence. Remember, though, that GuardDuty is only looking at new traffic; it can be a little confusing at first.

If you want to continue your testing by carefully – and with the permission of AWS and the owner – probing EC2 instances in your account, you might need to whitelist or blacklist your own IP address. Check your public IP address with this natty little service:

$ curl ifconfig.io
1.2.3.4

Ding Dong

The bells and whistles GuardDuty GitHub example [11] demonstrates what your Terraform ultimately should aim to include. This example from Leap Beyond Analytics includes a number of interesting additions to the simple example.

For example, GuardDuty can ignore a list of ingested IP addresses, such as your home network or a corporate LAN, as whitelists. You can also ensure that known malicious IP addresses are flagged so that GuardDuty pays attention to those to a greater degree.

The whitelist/blacklist configuration requires an S3 bucket created in Terraform and permissions that allow the upload of and reading of such whitelist/blacklist files. Additionally, it's possible to grant extra permissions to individual users so that they can keep an eye on GuardDuty findings, which also involves some slightly more sophisticated jiggery pokery.

After making sure the resources are correctly destroyed by the Terraform code, I would recommend looking back at the example from Leap Beyond Analytics to consider what you might want to add or adapt to embellish your current functionality. AWS also has a CloudFormation template that might offer some insight [12]. In other words, you can definitely glean useful Terraforming information from a number of online sources (see also this AWS GitHub repo [13]). As ever, in all the examples shown, you should read the license before using them. Other Terraform documentation can be found online to help you out [14]-[16].

The End Is Nigh

Because the Internet is no longer the safe place that it once was, and nipping down to the shops without locking your front door isn't an option, tools such as GuardDuty that automatically update their threat intelligence are invaluable for both live and forensic threat analysis.

By completely removing added overhead and hassle, GuardDuty's auto-updating rulesets are to be cherished from an administrative perspective. I hope you agree on the great scope that exists for an affordable, sophisticated service in the cloud like GuardDuty. Stay vigilant.