Check EBS volume encryption with AWS Go SDK

Aakash
4 min readMay 16, 2020

Problem:

  1. Retrieve total number of EBS volumes in specific regions in an AWS account.
  2. Retrieve number of unencrypted EBS volumes in specific regions in an AWS account
  3. Calculate % EBS volumes that are unencrypted by region

*Disclaimer: Quite a few third party tools and AWS Config check encrypted-volumes will retrieve the volumes that are not encrypted. However, if you come across an environment, where AWS Config is not enabled or third party tools are not being used and the only access you have is the read only SecurityAudit managed policy, you can use the approach below to get the necessary data.

Why should we assess this data?

This is one of the AWS Foundational Security best practices controls. Below is the rule.

[EC2.3] Attached EBS volumes should be encrypted at-rest

Category: Protect > Data protection > Encryption of data at rest

Severity: Medium

Resource: EC2 volume

AWS Config rule: encrypted-volumes

Step 1

Configure aws credentials/access-keys in your local machine with the profile option. We will use the profile name in our code to access the apis.

aws configure --profile <profilename>

Step 2

package mainfunc main(){//you can add more regions in the slice below//I just wanted data from the two regions belowreg := []string{"us-west-2", "eu-west-1"}
for _, r := range reg {
/*call func AssessEncryption with the region of interest to
get encryption status of resources*/
//Note assessencryption is just another package separate from main //Package encryption has an exported function AssessEncryption
//We are passing the region and the aws profile to the exported
//function AssessEncryption
assessencryption.AssessEncryption(r, "my-aws-profile")
}

Step 3

Let’s take a look at our package assessencryption especially the function AssessEncryption

package assessencryptionimport (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
)
//Below is a struct where we want to store details for an EBS volume //that are of interest to us.
//Volume ID for the EBS volume
//Availability Zone for the EBS volume
//Encrypted : True or False
//In Use: Whether the EBS volume is in use
type ebsVolume struct {
volumeID string
az string
encrypted bool
inusestate string
}
//AssessEncryption assesses EBS volume encryption detailsfunc AssessEncryption(region string, awsProfile string) {volumes := []ebsVolume{} //volumes is a slice of type ebsVolume //create a session with the region and the aws profile
svc := ec2.New(session.New(&aws.Config{Region:
aws.String(region), Credentials:
credentials.NewSharedCredentials("", awsProfile)}))
//initialize a variable ebsinput of type DescribeVolumesInput{} from //the ec2 package. This is part of the aws sdk for go ebsinput := &ec2.DescribeVolumesInput{}//call getvolumes that we will write with the following parameters//getVolumes will return a slice of type ebsVolume for each region
volumeresult := getVolumes(ebsinput, svc, volumes)
fmt.Printf("Total EBS volumes in Region: %v is %v\n", region, len(volumeresult))

countUnencryptedEBS := 0
for _, ev := range volumeresult {
if ev.encrypted == false {
countUnencryptedEBS++
}
}
if len(volumeresult) > 0 {
fmt.Printf("Total Unencrypted EBS volumes in Region: %v is %v\n", region, countUnencryptedEBS)
percent := float64(countUnencryptedEBS) / float64(len(volumeresult)) * 100
fmt.Printf("Percentage of Unencrypted EBS volumes in Region: %v is %.2f\n", region, percent)
}
}

Let’s take a look at our function getVolumes in package assessencryption.

func getVolumes(ebsinput *ec2.DescribeVolumesInput, svc *ec2.EC2, vol []ebsVolume) []ebsVolume { ebsoutput, err := svc.DescribeVolumes(ebsinput)
if err != nil {
log.Fatal("Error occurred")
}
//if there is at least one EBS volume in the region //we will extract the details of the volume and store it in a new //variable of type ebsVolume and append to the slice vol if len(ebsoutput.Volumes) > 0 {
for _, v := range ebsoutput.Volumes {
e := ebsVolume{}
e.volumeID = *v.VolumeId
e.az = *v.AvailabilityZone
e.encrypted = *v.Encrypted
e.inusestate = *v.State
vol = append(vol, e)
}
//AWS will send a NextToken string in case there are more results
//NextToken is nil if there are no more results
if ebsoutput.NextToken != nil {
ebsinput.SetNextToken(*ebsoutput.NextToken)
//recursively call getVolumes until NextToken is nil //this recursive call is to paginate the response
getVolumes(ebsinput, svc, vol)
}
}
return vol
}

Once you run it you will see a simple result for your AWS account and region as shown below.

Total EBS volumes in Region: us-west-2 is 1000Total Unencrypted EBS volumes in Region: us-west-2 is 100Percentage of Unencrypted EBS volumes in Region: us-west-2 is 10.00Total EBS volumes in Region: eu-west-1 is 1000Total Unencrypted EBS volumes in Region: eu-west-1 is 200Percentage of Unencrypted EBS volumes in Region: eu-west-1 is 20.00

I didn’t have much time to optimize or clean this up. Ideally I would like to create an interface for assessing encryption that can then be dynamically used to assess encryption for s3 buckets, efs, ebs etc based on the type being used to call the method. That is for another day.

Hope this helps some of you folks.

--

--