Skip to content

Commit

Permalink
🐛 ensure aws ebs scan cleans up after itself
Browse files Browse the repository at this point in the history
  • Loading branch information
vjeffrey authored and chris-rock committed Sep 27, 2023
1 parent e5497d3 commit c5c9bf4
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 26 deletions.
6 changes: 5 additions & 1 deletion providers/aws/connection/awsec2ebsconn/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ import (

func (c *AwsEbsConnection) DetachVolumeFromInstance(ctx context.Context, volume *awsec2ebstypes.VolumeInfo) error {
log.Info().Msg("detach volume")
var deviceName string
if c.volumeMounter != nil {
deviceName = c.volumeMounter.VolumeAttachmentLoc
}
res, err := c.scannerRegionEc2svc.DetachVolume(ctx, &ec2.DetachVolumeInput{
Device: aws.String(c.volumeMounter.VolumeAttachmentLoc), VolumeId: &volume.Id,
Device: aws.String(deviceName), VolumeId: &volume.Id,
InstanceId: &c.scannerInstance.Id,
})
if err != nil {
Expand Down
38 changes: 34 additions & 4 deletions providers/aws/connection/awsec2ebsconn/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,21 +106,21 @@ func NewAwsEbsConnection(id uint32, conf *inventory.Config, asset *inventory.Ass
// check if we got the no setup override option. this implies the target volume is already attached to the instance
// this is used in cases where we need to test a snapshot created from a public marketplace image. the volume gets attached to a brand
// new instance, and then that instance is started and we scan the attached fs
var volLocation string
var volLocation, volId string
if conf.Options[snapshot.NoSetup] == "true" || conf.Options[snapshot.IsSetup] == "true" {
log.Info().Msg("skipping setup step")
} else {
var ok bool
var err error
switch c.targetType {
case awsec2ebstypes.EBSTargetInstance:
ok, volLocation, err = c.SetupForTargetInstance(ctx, instanceinfo)
ok, volLocation, volId, err = c.SetupForTargetInstance(ctx, instanceinfo)
conf.PlatformId = awsec2.MondooInstanceID(i.AccountID, conf.Options["region"], convert.ToString(instanceinfo.InstanceId))
case awsec2ebstypes.EBSTargetVolume:
ok, volLocation, err = c.SetupForTargetVolume(ctx, *volumeid)
ok, volLocation, volId, err = c.SetupForTargetVolume(ctx, *volumeid)
conf.PlatformId = awsec2.MondooVolumeID(volumeid.Account, volumeid.Region, volumeid.Id)
case awsec2ebstypes.EBSTargetSnapshot:
ok, volLocation, err = c.SetupForTargetSnapshot(ctx, *snapshotid)
ok, volLocation, volId, err = c.SetupForTargetSnapshot(ctx, *snapshotid)
conf.PlatformId = awsec2.MondooSnapshotID(snapshotid.Account, snapshotid.Region, snapshotid.Id)
default:
return c, errors.New("invalid target type")
Expand All @@ -136,6 +136,12 @@ func NewAwsEbsConnection(id uint32, conf *inventory.Config, asset *inventory.Ass
}
// set is setup to true
asset.Connections[0].Options[snapshot.IsSetup] = "true"
// save the other information to asset connection options too
asset.Connections[0].Options["volume-id"] = volId
asset.Connections[0].Options["volume-loc"] = volLocation
if c.scanVolumeInfo.Tags["createdBy"] == "Mondoo" {
asset.Connections[0].Options["createdBy"] = "Mondoo"
}
}
asset.PlatformIds = []string{conf.PlatformId}

Expand Down Expand Up @@ -187,6 +193,8 @@ func NewAwsEbsConnection(id uint32, conf *inventory.Config, asset *inventory.Ass
}
asset.Id = conf.Type
asset.Platform.Runtime = c.Runtime()
asset.Connections[0].Options["scanner-id"] = c.scannerInstance.Id
asset.Connections[0].Options["scanner-region"] = c.scannerInstance.Region
return c, nil
}

Expand All @@ -205,7 +213,29 @@ func (c *AwsEbsConnection) Close() {
return
}
}
// we seem to be losing all the connection info we
// had when we started by the time we get here.
// we should figure out what is happening
// for now, reassemble the info needed from the asset
// connection options
ctx := context.Background()
opts := c.asset.Connections[0].Options
c.volumeMounter = &snapshot.VolumeMounter{
ScanDir: opts["mounted"],
VolumeAttachmentLoc: opts["volume-loc"],
}
c.scanVolumeInfo = &awsec2ebstypes.VolumeInfo{
Id: opts["volume-id"],
Tags: map[string]string{"createdBy": opts["createdBy"]},
}
cfg, err := config.LoadDefaultConfig(context.Background())
if err != nil {
log.Error().Err(err).Msg("cfg")
return
}
cfg.Region = opts["scanner-region"]
c.scannerRegionEc2svc = ec2.NewFromConfig(cfg)
c.scannerInstance.Id = opts["scanner-id"]
if c.volumeMounter != nil {
err := c.volumeMounter.UnmountVolumeFromInstance()
if err != nil {
Expand Down
37 changes: 18 additions & 19 deletions providers/aws/connection/awsec2ebsconn/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (c *AwsEbsConnection) Validate(ctx context.Context) (*types.Instance, *awse
return nil, nil, nil, errors.New("cannot validate; unrecognized ebs target")
}

func (c *AwsEbsConnection) SetupForTargetVolume(ctx context.Context, volume awsec2ebstypes.VolumeInfo) (bool, string, error) {
func (c *AwsEbsConnection) SetupForTargetVolume(ctx context.Context, volume awsec2ebstypes.VolumeInfo) (bool, string, string, error) {
log.Debug().Interface("volume", volume).Msg("setup for target volume")
if !volume.IsAvailable {
return c.SetupForTargetVolumeUnavailable(ctx, volume)
Expand All @@ -70,7 +70,7 @@ func (c *AwsEbsConnection) SetupForTargetVolume(ctx context.Context, volume awse
return c.AttachVolumeToInstance(ctx, volume)
}

func (c *AwsEbsConnection) SetupForTargetVolumeUnavailable(ctx context.Context, volume awsec2ebstypes.VolumeInfo) (bool, string, error) {
func (c *AwsEbsConnection) SetupForTargetVolumeUnavailable(ctx context.Context, volume awsec2ebstypes.VolumeInfo) (bool, string, string, error) {
found, snapId, err := c.FindRecentSnapshotForVolume(ctx, volume)
if err != nil {
// only log the error here, this is not a blocker
Expand All @@ -79,41 +79,41 @@ func (c *AwsEbsConnection) SetupForTargetVolumeUnavailable(ctx context.Context,
if !found {
snapId, err = c.CreateSnapshotFromVolume(ctx, volume)
if err != nil {
return false, "", err
return false, "", "", err
}
}
snapId, err = c.CopySnapshotToRegion(ctx, snapId)
if err != nil {
return false, "", err
return false, "", "", err
}
volId, err := c.CreateVolumeFromSnapshot(ctx, snapId)
if err != nil {
return false, "", err
return false, "", "", err
}
c.scanVolumeInfo = &volId
return c.AttachVolumeToInstance(ctx, volId)
}

func (c *AwsEbsConnection) SetupForTargetSnapshot(ctx context.Context, snapshot awsec2ebstypes.SnapshotId) (bool, string, error) {
func (c *AwsEbsConnection) SetupForTargetSnapshot(ctx context.Context, snapshot awsec2ebstypes.SnapshotId) (bool, string, string, error) {
log.Debug().Interface("snapshot", snapshot).Msg("setup for target snapshot")
snapId, err := c.CopySnapshotToRegion(ctx, snapshot)
if err != nil {
return false, "", err
return false, "", "", err
}
volId, err := c.CreateVolumeFromSnapshot(ctx, snapId)
if err != nil {
return false, "", err
return false, "", "", err
}
c.scanVolumeInfo = &volId
return c.AttachVolumeToInstance(ctx, volId)
}

func (c *AwsEbsConnection) SetupForTargetInstance(ctx context.Context, instanceinfo *types.Instance) (bool, string, error) {
func (c *AwsEbsConnection) SetupForTargetInstance(ctx context.Context, instanceinfo *types.Instance) (bool, string, string, error) {
log.Debug().Str("instance id", *instanceinfo.InstanceId).Msg("setup for target instance")
var err error
v, err := c.GetVolumeInfoForInstance(ctx, instanceinfo)
if err != nil {
return false, "", err
return false, "", "", err
}
found, snapId, err := c.FindRecentSnapshotForVolume(ctx, v)
if err != nil {
Expand All @@ -123,16 +123,16 @@ func (c *AwsEbsConnection) SetupForTargetInstance(ctx context.Context, instancei
if !found {
snapId, err = c.CreateSnapshotFromVolume(ctx, v)
if err != nil {
return false, "", err
return false, "", "", err
}
}
snapId, err = c.CopySnapshotToRegion(ctx, snapId)
if err != nil {
return false, "", err
return false, "", "", err
}
volId, err := c.CreateVolumeFromSnapshot(ctx, snapId)
if err != nil {
return false, "", err
return false, "", "", err
}
c.scanVolumeInfo = &volId
return c.AttachVolumeToInstance(ctx, volId)
Expand Down Expand Up @@ -391,15 +391,14 @@ func AttachVolume(ctx context.Context, ec2svc *ec2.Client, location string, volI
return location, res.State, nil
}

func (c *AwsEbsConnection) AttachVolumeToInstance(ctx context.Context, volume awsec2ebstypes.VolumeInfo) (bool, string, error) {
func (c *AwsEbsConnection) AttachVolumeToInstance(ctx context.Context, volume awsec2ebstypes.VolumeInfo) (bool, string, string, error) {
log.Info().Str("volume id", volume.Id).Msg("attach volume")
location := newVolumeAttachmentLoc()
ready := false
loc, state, err := AttachVolume(ctx, c.scannerRegionEc2svc, newVolumeAttachmentLoc(), volume.Id, c.scannerInstance.Id)
if err != nil {
return ready, "", err
return ready, "", "", err
}
location = loc // warning: there is no guarantee from AWS that the device will be placed there
location := loc // warning: there is no guarantee from AWS that the device will be placed there
log.Debug().Str("location", location).Msg("target volume")

/*
Expand All @@ -415,15 +414,15 @@ func (c *AwsEbsConnection) AttachVolumeToInstance(ctx context.Context, volume aw
time.Sleep(10 * time.Second)
resp, err := c.scannerRegionEc2svc.DescribeVolumes(ctx, &ec2.DescribeVolumesInput{VolumeIds: []string{volume.Id}})
if err != nil {
return ready, location, err
return ready, location, "", err
}
if len(resp.Volumes) == 1 {
volState = resp.Volumes[0].State
}
log.Info().Interface("state", volState).Msg("waiting for volume attachment completion")
}
}
return true, location, nil
return true, location, volume.Id, nil
}

func awsTagsToMap(tags []types.Tag) map[string]string {
Expand Down
7 changes: 5 additions & 2 deletions providers/aws/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,11 @@ func parseFlagsToOptions(m map[string]*llx.Primitive) map[string]string {
func (s *Service) Shutdown(req *plugin.ShutdownReq) (*plugin.ShutdownRes, error) {
for i := range s.runtimes {
runtime := s.runtimes[i]
if conn, ok := runtime.Connection.(awsec2ebsconn.AwsEbsConnection); ok {
conn.Close()
if conn, ok := runtime.Connection.(shared.Connection); ok {
if conn.Type() == awsec2ebsconn.EBSConnectionType {
conn := runtime.Connection.(*awsec2ebsconn.AwsEbsConnection)
conn.Close()
}
}
}
return &plugin.ShutdownRes{}, nil
Expand Down

0 comments on commit c5c9bf4

Please sign in to comment.