diff --git a/providers/aws/resources/aws.lr b/providers/aws/resources/aws.lr index 4ae62e0327..0898cb8c3f 100644 --- a/providers/aws/resources/aws.lr +++ b/providers/aws/resources/aws.lr @@ -699,8 +699,6 @@ aws.iam { virtualMfaDevices() []aws.iam.virtualmfadevice // List of server certificates stored in IAM serverCertificates() []dict - // Retrieves metadata for IAM access keys - accessKeyMetadata() []aws.iam.accessKey } // Entry in AWS IAM credential report @@ -782,11 +780,21 @@ private aws.iam.user @defaults("arn name") { // List of group ARNs that the user belongs to groups() []string // List of access keys metadata associated with the user - accessKeys() []dict + accessKeys() []aws.iam.accessKey // Login profile for the user loginProfile() aws.iam.loginProfile } +// AWS IAM accessKey definition +private aws.iam.accessKey @defaults("accessKeyId status") { + // Access Key ID + accessKeyId string + // Status of the access key (Active/Inactive) + status string + // Time when the access key was created + createDate time +} + // AWS IAM login profile for a user private aws.iam.loginProfile @defaults("createdAt") { // Time when the login profile was created @@ -2591,12 +2599,3 @@ private aws.eks.cluster @defaults("arn version status") { createdAt time } -// AWS IAM accessKey definition -private aws.iam.accessKey {"accessKeyId status" - // Access Key ID - accessKeyId string - // Status of the access key (Active/Inactive) - status string - // Time when the access key was created - createDate time -} \ No newline at end of file diff --git a/providers/aws/resources/aws.lr.go b/providers/aws/resources/aws.lr.go index 15ab8e4710..91c79c80fe 100644 --- a/providers/aws/resources/aws.lr.go +++ b/providers/aws/resources/aws.lr.go @@ -222,6 +222,10 @@ func init() { Init: initAwsIamUser, Create: createAwsIamUser, }, + "aws.iam.accessKey": { + // to override args, implement: initAwsIamAccessKey(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createAwsIamAccessKey, + }, "aws.iam.loginProfile": { // to override args, implement: initAwsIamLoginProfile(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createAwsIamLoginProfile, @@ -1553,11 +1557,20 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ return (r.(*mqlAwsIamUser).GetGroups()).ToDataRes(types.Array(types.String)) }, "aws.iam.user.accessKeys": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlAwsIamUser).GetAccessKeys()).ToDataRes(types.Array(types.Dict)) + return (r.(*mqlAwsIamUser).GetAccessKeys()).ToDataRes(types.Array(types.Resource("aws.iam.accessKey"))) }, "aws.iam.user.loginProfile": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsIamUser).GetLoginProfile()).ToDataRes(types.Resource("aws.iam.loginProfile")) }, + "aws.iam.accessKey.accessKeyId": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsIamAccessKey).GetAccessKeyId()).ToDataRes(types.String) + }, + "aws.iam.accessKey.status": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsIamAccessKey).GetStatus()).ToDataRes(types.String) + }, + "aws.iam.accessKey.createDate": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsIamAccessKey).GetCreateDate()).ToDataRes(types.Time) + }, "aws.iam.loginProfile.createdAt": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsIamLoginProfile).GetCreatedAt()).ToDataRes(types.Time) }, @@ -4986,6 +4999,22 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlAwsIamUser).LoginProfile, ok = plugin.RawToTValue[*mqlAwsIamLoginProfile](v.Value, v.Error) return }, + "aws.iam.accessKey.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamAccessKey).__id, ok = v.Value.(string) + return + }, + "aws.iam.accessKey.accessKeyId": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamAccessKey).AccessKeyId, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "aws.iam.accessKey.status": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamAccessKey).Status, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "aws.iam.accessKey.createDate": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamAccessKey).CreateDate, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) + return + }, "aws.iam.loginProfile.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlAwsIamLoginProfile).__id, ok = v.Value.(string) return @@ -12167,6 +12196,16 @@ func (c *mqlAwsIamUser) GetGroups() *plugin.TValue[[]interface{}] { func (c *mqlAwsIamUser) GetAccessKeys() *plugin.TValue[[]interface{}] { return plugin.GetOrCompute[[]interface{}](&c.AccessKeys, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("aws.iam.user", c.__id, "accessKeys") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + return c.accessKeys() }) } @@ -12187,6 +12226,60 @@ func (c *mqlAwsIamUser) GetLoginProfile() *plugin.TValue[*mqlAwsIamLoginProfile] }) } +// mqlAwsIamAccessKey for the aws.iam.accessKey resource +type mqlAwsIamAccessKey struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlAwsIamAccessKeyInternal it will be used here + AccessKeyId plugin.TValue[string] + Status plugin.TValue[string] + CreateDate plugin.TValue[*time.Time] +} + +// createAwsIamAccessKey creates a new instance of this resource +func createAwsIamAccessKey(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlAwsIamAccessKey{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + // to override __id implement: id() (string, error) + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("aws.iam.accessKey", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlAwsIamAccessKey) MqlName() string { + return "aws.iam.accessKey" +} + +func (c *mqlAwsIamAccessKey) MqlID() string { + return c.__id +} + +func (c *mqlAwsIamAccessKey) GetAccessKeyId() *plugin.TValue[string] { + return &c.AccessKeyId +} + +func (c *mqlAwsIamAccessKey) GetStatus() *plugin.TValue[string] { + return &c.Status +} + +func (c *mqlAwsIamAccessKey) GetCreateDate() *plugin.TValue[*time.Time] { + return &c.CreateDate +} + // mqlAwsIamLoginProfile for the aws.iam.loginProfile resource type mqlAwsIamLoginProfile struct { MqlRuntime *plugin.Runtime diff --git a/providers/aws/resources/aws.lr.manifest.yaml b/providers/aws/resources/aws.lr.manifest.yaml index fcf582d26b..0b7740166d 100755 --- a/providers/aws/resources/aws.lr.manifest.yaml +++ b/providers/aws/resources/aws.lr.manifest.yaml @@ -1662,6 +1662,10 @@ resources: desc: | Use the `aws.iam` resource to assess the configuration of the AWS IAM service. The resource provides a list of `aws.iam.user` resources representing GuardDuty Detectors deployed across all enabled regions. fields: + accessKeyMetadata: + min_mondoo_version: latest + accessKeys: + min_mondoo_version: latest accountPasswordPolicy: {} accountSummary: {} attachedPolicies: {} @@ -1702,6 +1706,28 @@ resources: != null\n ) \n" title: Do not setup access keys during initial user setup for all IAM users that have a console password + aws.iam.accessKey: + docs: + desc: | + The `aws.iam.accessKey` provides fields for assessing the configuration of individual IAM Access Keys. For usage, read the `aws.iam` resource documentation. This resource helps in identifying and analyzing the state and configurations of access keys, allowing for better security and management of AWS IAM credentials. + fields: + accessKeyId: {} + createDate: {} + status: {} + is_private: true + min_mondoo_version: 9.0.0 + platform: + name: + - aws + aws.iam.accessKeys: + fields: + accessKeyId: {} + createDate: {} + status: {} + min_mondoo_version: latest + platform: + name: + - aws aws.iam.group: docs: desc: | @@ -1817,6 +1843,9 @@ resources: accessKey2LastUsedDate: {} accessKey2LastUsedRegion: {} accessKey2LastUsedService: {} + accessKeyMetadata: + min_mondoo_version: latest + accessKeys: {} arn: {} cert1Active: {} cert1LastRotated: {} @@ -1835,19 +1864,6 @@ resources: platform: name: - aws - aws.iam.accessKey: - docs: - desc: | - The `aws.iam.accessKey` provides fields for assessing the configuration of individual IAM Access Keys. For usage, read the `aws.iam` resource documentation. This resource helps in identifying and analyzing the state and configurations of access keys, allowing for better security and management of AWS IAM credentials. - fields: - accessKeyId: {} - createDate: {} - status: {} - is_private: true - min_mondoo_version: 9.0.0 - platform: - name: - - aws aws.iam.virtualmfadevice: docs: desc: | diff --git a/providers/aws/resources/aws_iam.go b/providers/aws/resources/aws_iam.go index 5f2fac3760..2d0f7901cb 100644 --- a/providers/aws/resources/aws_iam.go +++ b/providers/aws/resources/aws_iam.go @@ -756,14 +756,13 @@ func (a *mqlAwsIamUser) id() (string, error) { func (a *mqlAwsIamUser) accessKeys() ([]interface{}, error) { conn := a.MqlRuntime.Connection.(*connection.AwsConnection) - svc := conn.Iam("") ctx := context.Background() - username := a.Name.Data var marker *string - res := []interface{}{} + var resources []interface{} // This will store the created resources. + for { keysResp, err := svc.ListAccessKeys(ctx, &iam.ListAccessKeysInput{ UserName: &username, @@ -772,18 +771,29 @@ func (a *mqlAwsIamUser) accessKeys() ([]interface{}, error) { if err != nil { return nil, err } - metadata, err := convert.JsonToDictSlice(keysResp.AccessKeyMetadata) - if err != nil { - return nil, err + + for _, keyMetadata := range keysResp.AccessKeyMetadata { + // Create a resource for each access key. + accessKeyResource, err := CreateResource(a.MqlRuntime, "aws.iam.accessKey", + map[string]*llx.RawData{ + "accessKeyId": llx.StringDataPtr(keyMetadata.AccessKeyId), + "status": llx.StringData(string(keyMetadata.Status)), + "createDate": llx.TimeDataPtr(keyMetadata.CreateDate), + }, + ) + if err != nil { + return nil, err + } + resources = append(resources, accessKeyResource) } - res = append(res, metadata) + if !keysResp.IsTruncated { break } marker = keysResp.Marker } - return res, nil + return resources, nil } func (a *mqlAwsIamUser) policies() ([]interface{}, error) { @@ -1360,50 +1370,3 @@ func (a *mqlAwsIamLoginProfile) init() (string, error) { // specify a precision. Using seconds is reasonable. return strconv.FormatInt(date.Unix(), 10), nil } - -func (a *mqlAwsIamUser) accessKeyMetadata() ([]interface{}, error) { - conn := a.MqlRuntime.Connection.(*connection.AwsConnection) - svc := conn.Iam("") - ctx := context.Background() - name := a.Name.Data - - res := []interface{}{} - var marker *string - for { - accessKeysResp, err := svc.ListAccessKeys(ctx, &iam.ListAccessKeysInput{ - UserName: &name, - Marker: marker, - }) - if err != nil { - return nil, err - } - - for _, metadata := range accessKeysResp.AccessKeyMetadata { - if metadata.CreateDate == nil { - continue - } - - statusStr := string(metadata.Status) - - accessKeyData := map[string]*llx.RawData{ - "AccessKeyId": llx.StringDataPtr(metadata.AccessKeyId), - "Status": llx.StringDataPtr(&statusStr), - "CreateDate": llx.TimeDataPtr(metadata.CreateDate), - } - - accessKeyResource, err := CreateResource(a.MqlRuntime, "aws.iam.accessKey", accessKeyData) - if err != nil { - return nil, err - } - - res = append(res, accessKeyResource) - } - - if !accessKeysResp.IsTruncated { - break - } - marker = accessKeysResp.Marker - } - - return res, nil -}