Skip to content

Commit

Permalink
Authorize to encrypt already encrypted volumes
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrt committed Sep 27, 2020
1 parent 7bace00 commit 18bfcc7
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 69 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 2.1.0

- Authorize encryption of already encrypted volumes (key migration)

## 2.0.3

- Adding a security check on KMS alias

## 2.0.2

- Adding option for choosing to restart or not an instance after encryption
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Encrypt EBS volumes from AWS EC2 instances

This tool let you :
- Encrypt all the EBS volumes for an instance
- If volumes already encrypted, re-encrypt these with the given key
- Duplicate all the source tags to the target
- Apply DeleteOnTermination flag if needs
- Preserve the original volume or not as an option (thank to @cobaltjacket)
Expand Down
6 changes: 1 addition & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ func Execute() {
func init() {
cobra.OnInitialize(initConfig)

// TODO: Not yet implemented
//rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ec2cryptomatic.yaml)")

}

// initConfig reads in config file and ENV variables if set.
Expand All @@ -63,8 +60,7 @@ func initConfig() {
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
home, err := homedir.Dir(); if err != nil {
fmt.Println(err)
os.Exit(1)
}
Expand Down
11 changes: 4 additions & 7 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,21 @@ var runCmd = &cobra.Command{

fmt.Print("\t\t-=[ EC2Cryptomatic ]=-\n")

awsSession, err := session.NewSession(&aws.Config{Region: aws.String(region)})
if err != nil {
awsSession, err := session.NewSession(&aws.Config{Region: aws.String(region)}); if err != nil {
log.Fatalln("Cannot create an AWS awsSession object: " + err.Error())
}

kmsService := kms.New(awsSession)
kmsInput := &kms.DescribeKeyInput{KeyId: aws.String(kmsAlias)}
_, errorKmsKey := kmsService.DescribeKey(kmsInput); if errorKmsKey != nil {
if _, errorKmsKey := kmsService.DescribeKey(kmsInput); errorKmsKey != nil {
log.Fatalln("Error with this key: " + errorKmsKey.Error())
}

ec2Instance, instanceError := ec2instance.New(awsSession, instanceID)
if instanceError != nil {
ec2Instance, instanceError := ec2instance.New(awsSession, instanceID); if instanceError != nil {
log.Fatalln(instanceError)
}

errorAlgorithm := algorithm.EncryptInstance(ec2Instance, kmsAlias, discard, startInstance)
if errorAlgorithm != nil {
if errorAlgorithm := algorithm.EncryptInstance(ec2Instance, kmsAlias, discard, startInstance); errorAlgorithm != nil {
log.Fatalln("/!\\ " + errorAlgorithm.Error())
}
},
Expand Down
2 changes: 1 addition & 1 deletion constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package constants

const (
// VERSION is the overall version of the program
VERSION string = "2.0.2"
VERSION string = "2.1.0"
// VolumeMaxAttempts how many attempts EC2 EBS waiters will used between SDK actions (snapshot ebsvolume)
VolumeMaxAttempts int = 10000
// InstanceMaxAttempts how many attempts EC2 waiters will used between SDK actions (attach/detach ebsvolume)
Expand Down
33 changes: 12 additions & 21 deletions internal/algorithm/encrypt_algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,43 @@ import (
"github.com/jbrt/ec2cryptomatic/internal/ec2instance"
)

var nonEligibleForEncryptionError = errors.New("instance must be stopped and compatible with EBS encryption")

// EncryptInstance will takes an instanceID and encrypt all the related EBS volumes
func EncryptInstance(ec2 *ec2instance.Ec2Instance, kmsKeyAlias string, sourceDiscard bool, startInstance bool) error {
func EncryptInstance(ec2 *ec2instance.Ec2Instance, kmsKeyAlias string, discardSource bool, startInstance bool) error {

if !ec2.IsStopped() || !ec2.IsSupportsEncryptedVolumes() {
return errors.New("instance must be stopped and compatible with EBS encryption")
return nonEligibleForEncryptionError
}

actionDone := false
for _, ebsVolume := range ec2.GetEBSMappedVolumes() {
log.Println("-- Beginning work on EBS volume " + *ebsVolume.Ebs.VolumeId)

sourceVolume, volumeError := ec2.GetEBSVolume(*ebsVolume.Ebs.VolumeId)
if volumeError != nil {
sourceVolume, volumeError := ec2.GetEBSVolume(*ebsVolume.Ebs.VolumeId); if volumeError != nil {
return errors.New("Problem with volume initialization: " + volumeError.Error())
}

if sourceVolume.IsEncrypted() {
log.Println("-- This volume is already encrypted nothing to do with this one")
continue
}

encryptedVolume, encryptedVolumeError := sourceVolume.EncryptVolume(kmsKeyAlias)
if encryptedVolumeError != nil {
encryptedVolume, encryptedVolumeError := sourceVolume.EncryptVolume(kmsKeyAlias); if encryptedVolumeError != nil {
log.Printf("Problem while encrypting volume: %s (%s)\n", *ebsVolume.Ebs.VolumeId, encryptedVolumeError.Error())
continue
}

swappingError := ec2.SwapBlockDevice(ebsVolume, encryptedVolume)
if swappingError != nil {
if swappingError := ec2.SwapBlockDevice(ebsVolume, encryptedVolume); swappingError != nil {
log.Println("Problem while trying to swap volumes: " + swappingError.Error())
continue
}

if sourceDiscard {
if discardSource {
_ = sourceVolume.DeleteVolume()
}
actionDone = true
}

if actionDone {
if startInstance {
log.Println("Let's starts instance " + *ec2.InstanceID)
startError := ec2.StartInstance()
if startError != nil {
return startError
}
if actionDone && startInstance {
log.Println("Let's starts instance " + *ec2.InstanceID)
if startError := ec2.StartInstance(); startError != nil {
return startError
}
}

Expand Down
29 changes: 11 additions & 18 deletions internal/ebsvolume/ebsvolume.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ func (v VolumeToEncrypt) takeSnapshot() (*ec2.Snapshot, error) {
VolumeId: v.describe.VolumeId,
}

snapshot, errSnapshot := v.client.CreateSnapshot(snapShotInput)
if errSnapshot != nil {
snapshot, errSnapshot := v.client.CreateSnapshot(snapShotInput); if errSnapshot != nil {
return nil, errSnapshot
}

Expand All @@ -62,20 +61,18 @@ func (v VolumeToEncrypt) takeSnapshot() (*ec2.Snapshot, error) {

// DeleteVolume will delete the given EBS volume
func (v VolumeToEncrypt) DeleteVolume() error {
log.Println("--- Delete volume " + *v.volumeID)
_, errDelete := v.client.DeleteVolume(&ec2.DeleteVolumeInput{VolumeId: v.volumeID})
if errDelete != nil {
log.Println("---> Delete volume " + *v.volumeID)
if _, errDelete := v.client.DeleteVolume(&ec2.DeleteVolumeInput{VolumeId: v.volumeID}); errDelete != nil {
return errDelete
}
return nil
}

// EncryptVolume will produce an encrypted version of the EBS volume
func (v VolumeToEncrypt) EncryptVolume(kmsKeyID string) (*ec2.Volume, error) {
log.Println("--- Start encryption process for volume " + *v.volumeID)
log.Println("---> Start encryption process for volume " + *v.volumeID)
encrypted := true
snapshot, errSnapshot := v.takeSnapshot()
if errSnapshot != nil {
snapshot, errSnapshot := v.takeSnapshot(); if errSnapshot != nil {
return nil, errSnapshot
}

Expand All @@ -88,19 +85,17 @@ func (v VolumeToEncrypt) EncryptVolume(kmsKeyID string) (*ec2.Volume, error) {
}

// Adding tags if needed
tagsWithoutAwsDedicatedTags := v.getTagSpecifications()
if tagsWithoutAwsDedicatedTags != nil {
if tagsWithoutAwsDedicatedTags := v.getTagSpecifications(); tagsWithoutAwsDedicatedTags != nil {
volumeInput.TagSpecifications = tagsWithoutAwsDedicatedTags
}

// If EBS volume is IO, let's get the IOPs parameter
if strings.HasPrefix(*v.describe.VolumeType, "io") {
log.Println("--- This volumes is IO one let's set IOPs to ", *v.describe.Iops)
log.Println("---> This volumes is IO one let's set IOPs to ", *v.describe.Iops)
volumeInput.Iops = aws.Int64(*v.describe.Iops)
}

volume, errVolume := v.client.CreateVolume(volumeInput)
if errVolume != nil {
volume, errVolume := v.client.CreateVolume(volumeInput); if errVolume != nil {
return nil, errVolume
}

Expand All @@ -127,12 +122,10 @@ func (v VolumeToEncrypt) IsEncrypted() bool {

// New returns a well construct EC2Instance object ec2instance
func New(ec2Client *ec2.EC2, volumeID string) (*VolumeToEncrypt, error) {

// Trying to describe the given ec2instance as security mechanism (ec2instance is exists ? credentials are ok ?)
input := &ec2.DescribeVolumesInput{VolumeIds: []*string{aws.String(volumeID)}}
describe, errDescribe := ec2Client.DescribeVolumes(input)
if errDescribe != nil {
log.Println("--- Cannot get information from volume " + volumeID)
volumeInput := &ec2.DescribeVolumesInput{VolumeIds: []*string{aws.String(volumeID)}}
describe, errDescribe := ec2Client.DescribeVolumes(volumeInput); if errDescribe != nil {
log.Println("---> Cannot get information from volume " + volumeID)
return nil, errDescribe
}

Expand Down
29 changes: 12 additions & 17 deletions internal/ec2instance/ec2instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/jbrt/ec2cryptomatic/internal/ebsvolume"
)

var unsupportedInstanceTypes = []string{"c1.", "m1.", "m2.", "t1."}

// Ec2Instance is the main type of that package. Will be returned by new.
// It contains all data relevant for an ec2instance
type Ec2Instance struct {
Expand All @@ -27,8 +29,7 @@ func (e Ec2Instance) GetEBSMappedVolumes() []*ec2.InstanceBlockDeviceMapping {

// GetEBSVolume returns a specific EBS volume with high level methods
func (e Ec2Instance) GetEBSVolume(volumeID string) (*ebsvolume.VolumeToEncrypt, error) {
ebsVolume, volumeError := ebsvolume.New(e.ec2client, volumeID)
if volumeError != nil {
ebsVolume, volumeError := ebsvolume.New(e.ec2client, volumeID); if volumeError != nil {
return nil, volumeError
}
return ebsVolume, nil
Expand All @@ -44,7 +45,6 @@ func (e Ec2Instance) IsStopped() bool {

// IsSupportsEncryptedVolumes will check if the ec2instance supports EBS encrypted volumes (not all instances types support that).
func (e Ec2Instance) IsSupportsEncryptedVolumes() bool {
unsupportedInstanceTypes := []string{"c1.", "m1.", "m2.", "t1."}
for _, instance := range unsupportedInstanceTypes {
if strings.HasPrefix(*e.describeInstance.InstanceType, instance) {
return false
Expand All @@ -57,8 +57,7 @@ func (e Ec2Instance) IsSupportsEncryptedVolumes() bool {
func (e Ec2Instance) StartInstance() error {
log.Println("-- Start ec2instance " + *e.InstanceID)
input := &ec2.StartInstancesInput{InstanceIds: []*string{aws.String(*e.InstanceID)}}
_, errStart := e.ec2client.StartInstances(input)
if errStart != nil {
if _, errStart := e.ec2client.StartInstances(input); errStart != nil {
return errStart
}
return nil
Expand All @@ -67,8 +66,7 @@ func (e Ec2Instance) StartInstance() error {
//SwapBlockDevice will swap two EBS volumes from an EC2 ec2instance
func (e Ec2Instance) SwapBlockDevice(source *ec2.InstanceBlockDeviceMapping, target *ec2.Volume) error {
detach := &ec2.DetachVolumeInput{VolumeId: aws.String(*source.Ebs.VolumeId)}
_, errDetach := e.ec2client.DetachVolume(detach)
if errDetach != nil {
if _, errDetach := e.ec2client.DetachVolume(detach); errDetach != nil {
return errDetach
}

Expand All @@ -88,8 +86,7 @@ func (e Ec2Instance) SwapBlockDevice(source *ec2.InstanceBlockDeviceMapping, tar
VolumeId: aws.String(*target.VolumeId),
}

_, errAttach := e.ec2client.AttachVolume(attach)
if errAttach != nil {
if _, errAttach := e.ec2client.AttachVolume(attach); errAttach != nil {
return errAttach
}

Expand All @@ -110,8 +107,7 @@ func (e Ec2Instance) SwapBlockDevice(source *ec2.InstanceBlockDeviceMapping, tar

requestModify, _ := e.ec2client.ModifyInstanceAttributeRequest(&attributeInput)

errorRequest := requestModify.Send()
if errorRequest != nil {
if errorRequest := requestModify.Send(); errorRequest != nil {
return errorRequest
}

Expand All @@ -122,16 +118,15 @@ func (e Ec2Instance) SwapBlockDevice(source *ec2.InstanceBlockDeviceMapping, tar

// New returns a well construct EC2Instance object ec2instance
func New(session *session.Session, instanceID string) (*Ec2Instance, error) {
log.Println("Let's encrypt ec2instance " + instanceID)
log.Println("Let's encrypt EC2 instance " + instanceID)

// Trying to describeInstance the given ec2instance as security mechanism (ec2instance is exists ? credentials are ok ?)
ec2client := ec2.New(session)
input := &ec2.DescribeInstancesInput{InstanceIds: []*string{aws.String(instanceID)}}
ec2Input := &ec2.DescribeInstancesInput{InstanceIds: []*string{aws.String(instanceID)}}

describe, errDescribe := ec2client.DescribeInstances(input)
if errDescribe != nil {
log.Println("-- Cannot find EC2 ec2instance " + instanceID)
return &Ec2Instance{}, errDescribe
describe, errDescribe := ec2client.DescribeInstances(ec2Input); if errDescribe != nil {
log.Println("-- Cannot find EC2 instance " + instanceID)
return nil, errDescribe
}

instance := &Ec2Instance{
Expand Down

0 comments on commit 18bfcc7

Please sign in to comment.