Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Detect deprecated AMIs #66

Merged
merged 9 commits into from
Mar 16, 2023
19 changes: 17 additions & 2 deletions api/v1alpha1/awsadapterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,23 @@ type Reservation struct {
}

type Instance struct {
HttpPutResponseHopLimit *int32 `json:"httpPutResponseHopLimit,omitempty"`
PublicDnsName *string `json:"publicDnsName,omitempty"`
HttpPutResponseHopLimit *int32 `json:"httpPutResponseHopLimit,omitempty"`
PublicDnsName *string `json:"publicDnsName,omitempty"`
AmazonMachineImage *AmazonMachineImage `json:"amazonMachineImage,omitempty"`
}

type AmazonMachineImage struct {
Id *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Location *string `json:"location,omitempty"`
Type string `json:"type,omitempty"`
Architecture string `json:"architecture,omitempty"`
Public *bool `json:"public,omitempty"`
PlatformDetails *string `json:"platformDetails,omitempty"`
Ownerid *string `json:"ownerId,omitempty"`
CreationTime *string `json:"creationTime,omitempty"`
DeprecationTime *string `json:"deprecationTime,omitempty"`
State string `json:"state,omitempty"`
}

// EKSNodeGroupResources contains info of ASG and remote access SG for node group
Expand Down
60 changes: 60 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,31 @@ spec:
type: integer
publicDnsName:
type: string
amazonMachineImage:
properties:
id:
type: string
name:
type: string
location:
type: string
type:
type: string
architecture:
type: string
public:
type: boolean
platformDetails:
type: string
ownerId:
type: string
creationTime:
type: string
deprecationTime:
type: string
state:
type: string
type: object
type: object
type: array
type: object
Expand Down
25 changes: 25 additions & 0 deletions config/crd/bases/security.nirmata.io_awsadapterconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,31 @@ spec:
instances:
items:
properties:
amazonMachineImage:
properties:
architecture:
type: string
creationTime:
type: string
deprecationTime:
type: string
id:
type: string
location:
type: string
name:
type: string
ownerId:
type: string
platformDetails:
type: string
public:
type: boolean
state:
type: string
type:
type: string
type: object
httpPutResponseHopLimit:
format: int32
type: integer
Expand Down
31 changes: 31 additions & 0 deletions config/samples/policies/check-ami-deprecation-time.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-ami-deprecation-time
annotations:
policies.kyverno.io/title: Check AMI deprecation Time
policies.kyverno.io/category: AMI Deprecation Time
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Cluster
policies.kyverno.io/description: >-
AMIs past their deprecation time
spec:
validationFailureAction: audit
background: true
rules:
- name: check-ami-deprecation-time
match:
any:
- resources:
kinds:
- AWSAdapterConfig
validate:
message: "This rule audits for AMIs that are past their deprecation time"
foreach:
- list: "request.object.status.eksCluster.compute.reservations[].instances[].amazonMachineImage"
deny:
conditions:
any:
- key: "{{ time_before('{{ element.deprecationTime }}', '{{ time_now_utc() }}') }}"
operator: Equals
value: true
61 changes: 51 additions & 10 deletions controllers/awsadapterconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,17 +368,37 @@ func (r *AWSAdapterConfigReconciler) Reconcile(ctx context.Context, req ctrl.Req
l.Error(err, "error occurred while fetching EC2 instances")
return r.updateLastPollStatusFailure(ctx, objOld, "error occurred while fetching EC2 instances", err, &l, time.Now())
} else {
for _, r := range x.Reservations {
for _, res := range x.Reservations {
tmpRes := []*securityv1alpha1.Reservation{}
for _, i := range r.Instances {
tmpIn := []*securityv1alpha1.Instance{}
tmpIn = append(tmpIn, &securityv1alpha1.Instance{
PublicDnsName: i.PublicDnsName,
HttpPutResponseHopLimit: i.MetadataOptions.HttpPutResponseHopLimit,
})
tmpRes = append(tmpRes, &securityv1alpha1.Reservation{
Instances: tmpIn,
})
for _, i := range res.Instances {
if ami, err := getAmi(ctx, ec2Client, i.ImageId); err != nil {
l.Error(err, "error occurred while fetching AMI")
return r.updateLastPollStatusFailure(ctx, objOld, "error occurred while fetching AMI", err, &l, time.Now())
} else {
tmpAmi := &securityv1alpha1.AmazonMachineImage{
Id: ami.ImageId,
Name: ami.Name,
Location: ami.ImageLocation,
Type: string(ami.ImageType),
Architecture: string(ami.Architecture),
Public: ami.Public,
PlatformDetails: ami.PlatformDetails,
Ownerid: ami.OwnerId,
CreationTime: ami.CreationDate,
DeprecationTime: ami.DeprecationTime,
State: string(ami.State),
}

tmpIn := []*securityv1alpha1.Instance{}
tmpIn = append(tmpIn, &securityv1alpha1.Instance{
PublicDnsName: i.PublicDnsName,
HttpPutResponseHopLimit: i.MetadataOptions.HttpPutResponseHopLimit,
AmazonMachineImage: tmpAmi,
})
tmpRes = append(tmpRes, &securityv1alpha1.Reservation{
Instances: tmpIn,
})
}
}
objNew.Status.EKSCluster.Compute.Reservations = tmpRes
}
Expand Down Expand Up @@ -444,6 +464,27 @@ func (r *AWSAdapterConfigReconciler) updateLastPollStatusFailure(ctx context.Con
return ctrl.Result{RequeueAfter: r.RequeueInterval}, nil
}

func getAmi(ctx context.Context, ec2Client *ec2.Client, imageId *string) (*types.Image, error) {
amis, err := ec2Client.DescribeImages(ctx, &ec2.DescribeImagesInput{
DryRun: aws.Bool(false),
ImageIds: []string{*imageId},
})
if err != nil {
return nil, err
}
if amis == nil || len(amis.Images) == 0 {
err := fmt.Errorf("no AMI with ID %s found", *imageId)
return nil, err
}

ami := &amis.Images[0]
if ami == nil {
err := fmt.Errorf("failed to retrieve details for AMI with ID %s", *imageId)
return nil, err
}
return ami, nil
}

func isStatusVacuous(status *securityv1alpha1.AWSAdapterConfigStatus) bool {
return (status.LastUpdatedTimestamp == nil &&
status.LastPollInfo == securityv1alpha1.LastPollInfo{} &&
Expand Down
1 change: 1 addition & 0 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ cat >my-policy.json <<EOF
"Sid": "Statement1",
"Effect": "Allow",
"Action": [
"ec2:DescribeImages",
"ec2:DescribeInstances",
"ec2:DescribeFlowLogs",
"ecr:DescribeRepositories",
Expand Down
4 changes: 3 additions & 1 deletion tests/best-practices/badawsacfg.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ status:
ec2SshKey: my-ssh-key
reservations:
- instances:
- httpPutResponseHopLimit: 2
- amazonMachineImage:
deprecationTime: "2023-01-05T17:40:14.000Z"
httpPutResponseHopLimit: 2
publicDnsName: ec2-55-222-222-33.us-west-1.compute.amazonaws.com
logging:
audit: false
Expand Down
14 changes: 13 additions & 1 deletion tests/best-practices/goodawsacfg.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,19 @@ status:
maxUnavailable: 1
reservations:
- instances:
- httpPutResponseHopLimit: 1
- amazonMachineImage:
architecture: x86_64
creationTime: "2023-03-05T17:40:14.000Z"
deprecationTime: "2025-03-05T17:40:14.000Z"
id: ami-0efaccd6711a132b2
location: amazon/amazon-eks-node-1.25-v11111111
name: amazon-eks-node-1.25-v11111111
ownerId: "111111111111"
platformDetails: Linux/UNIX
public: true
state: available
type: machine
httpPutResponseHopLimit: 1
createdAt: 2023-03-07 05:06:08.439 +0000 UTC
encryptionConfig:
- keyArn: arn:aws:kms:us-west-1:844333597536:key/abcd1234-5678-90ab-cdef-0123456789ab
Expand Down
10 changes: 10 additions & 0 deletions tests/best-practices/kyverno-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ results:
resource: bad-awsacfg
result: fail
kind: AWSAdapterConfig
- policy: check-ami-deprecation-time
rule: check-ami-deprecation-time
resource: good-awsacfg
result: pass
kind: AWSAdapterConfig
- policy: check-ami-deprecation-time
rule: check-ami-deprecation-time
resource: bad-awsacfg
result: fail
kind: AWSAdapterConfig
- policy: check-cluster-endpoint
rule: check-cluster-endpoint
resource: good-awsacfg
Expand Down