diff --git a/avd_docs/aws/ecr/AVD-AWS-0030/docs.md b/avd_docs/aws/ecr/AVD-AWS-0030/docs.md index cacc98e8..ab63da31 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0030/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0030/docs.md @@ -1,8 +1,9 @@ Repository image scans should be enabled to ensure vulnerable software can be discovered and remediated as soon as possible. + ### Impact -The ability to scan images is not being used and vulnerabilities will not be highlighted + {{ remediationActions }} diff --git a/avd_docs/aws/ecr/AVD-AWS-0031/docs.md b/avd_docs/aws/ecr/AVD-AWS-0031/docs.md index f7d30790..e6251d5d 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0031/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0031/docs.md @@ -1,10 +1,10 @@ ECR images should be set to IMMUTABLE to prevent code injection through image mutation. - This can be done by setting image_tab_mutability to IMMUTABLE + ### Impact -Image tags could be overwritten with compromised images + {{ remediationActions }} diff --git a/avd_docs/aws/ecr/AVD-AWS-0032/docs.md b/avd_docs/aws/ecr/AVD-AWS-0032/docs.md index b2078b0a..483f4b36 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0032/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0032/docs.md @@ -1,8 +1,9 @@ Allowing public access to the ECR repository risks leaking sensitive of abusable information + ### Impact -Risk of potential data leakage of sensitive artifacts + {{ remediationActions }} diff --git a/avd_docs/aws/ecr/AVD-AWS-0033/docs.md b/avd_docs/aws/ecr/AVD-AWS-0033/docs.md index a6e1af2c..25ca75f1 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0033/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0033/docs.md @@ -1,8 +1,9 @@ Images in the ECR repository are encrypted by default using AWS managed encryption keys. To increase control of the encryption and control the management of factors like key rotation, use a Customer Managed Key. + ### Impact -Using AWS managed keys does not allow for fine grained control + {{ remediationActions }} diff --git a/avd_docs/aws/efs/AVD-AWS-0037/docs.md b/avd_docs/aws/efs/AVD-AWS-0037/docs.md index c4667a47..5242c766 100644 --- a/avd_docs/aws/efs/AVD-AWS-0037/docs.md +++ b/avd_docs/aws/efs/AVD-AWS-0037/docs.md @@ -1,8 +1,9 @@ If your organization is subject to corporate or regulatory policies that require encryption of data and metadata at rest, we recommend creating a file system that is encrypted at rest, and mounting your file system using encryption of data in transit. + ### Impact -Data can be read from the EFS if compromised + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0038/docs.md b/avd_docs/aws/eks/AVD-AWS-0038/docs.md index 79bc0e01..ae15d0ed 100644 --- a/avd_docs/aws/eks/AVD-AWS-0038/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0038/docs.md @@ -1,8 +1,9 @@ By default cluster control plane logging is not turned on. Logging is available for audit, api, authenticator, controllerManager and scheduler. All logging should be turned on for cluster control plane. + ### Impact -Logging provides valuable information about access and usage + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0039/docs.md b/avd_docs/aws/eks/AVD-AWS-0039/docs.md index de61dab4..42437b50 100644 --- a/avd_docs/aws/eks/AVD-AWS-0039/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0039/docs.md @@ -1,8 +1,9 @@ EKS cluster resources should have the encryption_config block set with protection of the secrets resource. + ### Impact -EKS secrets could be read if compromised + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0040/docs.md b/avd_docs/aws/eks/AVD-AWS-0040/docs.md index b4464df0..96743956 100644 --- a/avd_docs/aws/eks/AVD-AWS-0040/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0040/docs.md @@ -1,8 +1,9 @@ EKS clusters are available publicly by default, this should be explicitly disabled in the vpc_config of the EKS cluster resource. + ### Impact -EKS can be access from the internet + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0041/docs.md b/avd_docs/aws/eks/AVD-AWS-0041/docs.md index 19a023a3..9e1ba77a 100644 --- a/avd_docs/aws/eks/AVD-AWS-0041/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0041/docs.md @@ -1,8 +1,9 @@ EKS Clusters have public access cidrs set to 0.0.0.0/0 by default which is wide open to the internet. This should be explicitly set to a more specific private CIDR range + ### Impact -EKS can be accessed from the internet + {{ remediationActions }} diff --git a/checks/cloud/aws/ecr/enable_image_scans.go b/checks/cloud/aws/ecr/enable_image_scans.go index 2cfb3e11..1eae943c 100755 --- a/checks/cloud/aws/ecr/enable_image_scans.go +++ b/checks/cloud/aws/ecr/enable_image_scans.go @@ -33,7 +33,8 @@ var CheckEnableImageScans = rules.Register( Links: cloudFormationEnableImageScansLinks, RemediationMarkdown: cloudFormationEnableImageScansRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/enable_image_scans.rego b/checks/cloud/aws/ecr/enable_image_scans.rego new file mode 100644 index 00000000..c36535be --- /dev/null +++ b/checks/cloud/aws/ecr/enable_image_scans.rego @@ -0,0 +1,41 @@ +# METADATA +# title: ECR repository has image scans disabled. +# description: | +# Repository image scans should be enabled to ensure vulnerable software can be discovered and remediated as soon as possible. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html +# custom: +# id: AVD-AWS-0030 +# avd_id: AVD-AWS-0030 +# provider: aws +# service: ecr +# severity: HIGH +# short_code: enable-image-scans +# recommended_action: Enable ECR image scanning +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository#image_scanning_configuration +# good_examples: checks/cloud/aws/ecr/enable_image_scans.tf.go +# bad_examples: checks/cloud/aws/ecr/enable_image_scans.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/enable_image_scans.cf.go +# bad_examples: checks/cloud/aws/ecr/enable_image_scans.cf.go +package builtin.aws.ecr.aws0030 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + repo.imagescanning.scanonpush.value == false + + res := result.new("Image scanning is not enabled", repo.imagescanning.scanonpush) +} diff --git a/checks/cloud/aws/ecr/enable_image_scans_test.go b/checks/cloud/aws/ecr/enable_image_scans_test.go deleted file mode 100644 index 0cf5df66..00000000 --- a/checks/cloud/aws/ecr/enable_image_scans_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package ecr - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableImageScans(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR repository with image scans disabled", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageScanning: ecr.ImageScanning{ - Metadata: trivyTypes.NewTestMetadata(), - ScanOnPush: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "ECR repository with image scans enabled", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageScanning: ecr.ImageScanning{ - Metadata: trivyTypes.NewTestMetadata(), - ScanOnPush: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckEnableImageScans.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableImageScans.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/enable_image_scans_test.rego b/checks/cloud/aws/ecr/enable_image_scans_test.rego new file mode 100644 index 00000000..ae0c46de --- /dev/null +++ b/checks/cloud/aws/ecr/enable_image_scans_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.ecr.aws0030_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0030 as check +import data.lib.test + +test_allow_image_scanning_enabled if { + inp := {"aws": {"ecr": {"repositories": [{"imagescanning": {"scanonpush": {"value": true}}}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_image_scanning_disabled if { + inp := {"aws": {"ecr": {"repositories": [{"imagescanning": {"scanonpush": {"value": false}}}]}}} + + test.assert_equal_message("Image scanning is not enabled", check.deny) with input as inp +} diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository.go b/checks/cloud/aws/ecr/enforce_immutable_repository.go index 6ebcfadc..dd9f2f41 100755 --- a/checks/cloud/aws/ecr/enforce_immutable_repository.go +++ b/checks/cloud/aws/ecr/enforce_immutable_repository.go @@ -35,7 +35,8 @@ This can be done by setting image_tab_mutability to IMMUTABLE Links: cloudFormationEnforceImmutableRepositoryLinks, RemediationMarkdown: cloudFormationEnforceImmutableRepositoryRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository.rego b/checks/cloud/aws/ecr/enforce_immutable_repository.rego new file mode 100644 index 00000000..1a89d859 --- /dev/null +++ b/checks/cloud/aws/ecr/enforce_immutable_repository.rego @@ -0,0 +1,42 @@ +# METADATA +# title: ECR images tags shouldn't be mutable. +# description: | +# ECR images should be set to IMMUTABLE to prevent code injection through image mutation. +# This can be done by setting image_tab_mutability to IMMUTABLE +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://sysdig.com/blog/toctou-tag-mutability/ +# custom: +# id: AVD-AWS-0031 +# avd_id: AVD-AWS-0031 +# provider: aws +# service: ecr +# severity: HIGH +# short_code: enforce-immutable-repository +# recommended_action: Only use immutable images in ECR +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository +# good_examples: checks/cloud/aws/ecr/enforce_immutable_repository.tf.go +# bad_examples: checks/cloud/aws/ecr/enforce_immutable_repository.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/enforce_immutable_repository.cf.go +# bad_examples: checks/cloud/aws/ecr/enforce_immutable_repository.cf.go +package builtin.aws.ecr.aws0031 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + repo.imagetagsimmutable.value == false + + res := result.new("Repository tags are mutable.", repo.imagetagsimmutable) +} diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository_test.go b/checks/cloud/aws/ecr/enforce_immutable_repository_test.go deleted file mode 100644 index 8ced0b65..00000000 --- a/checks/cloud/aws/ecr/enforce_immutable_repository_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package ecr - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnforceImmutableRepository(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR mutable image tags", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageTagsImmutable: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "ECR immutable image tags", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageTagsImmutable: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckEnforceImmutableRepository.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnforceImmutableRepository.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository_test.rego b/checks/cloud/aws/ecr/enforce_immutable_repository_test.rego new file mode 100644 index 00000000..7068fa3d --- /dev/null +++ b/checks/cloud/aws/ecr/enforce_immutable_repository_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.ecr.aws0031_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0031 as check +import data.lib.test + +test_allow_immutable_repository if { + inp := {"aws": {"ecr": {"repositories": [{"imagetagsimmutable": {"value": true}}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_immutable_repository if { + inp := {"aws": {"ecr": {"repositories": [{"imagetagsimmutable": {"value": false}}]}}} + + test.assert_equal_message("Repository tags are mutable.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/ecr/no_public_access.go b/checks/cloud/aws/ecr/no_public_access.go index dbefbaa0..06139428 100755 --- a/checks/cloud/aws/ecr/no_public_access.go +++ b/checks/cloud/aws/ecr/no_public_access.go @@ -39,7 +39,8 @@ var CheckNoPublicAccess = rules.Register( Links: cloudFormationNoPublicAccessLinks, RemediationMarkdown: cloudFormationNoPublicAccessRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/no_public_access.rego b/checks/cloud/aws/ecr/no_public_access.rego new file mode 100644 index 00000000..4dca26b3 --- /dev/null +++ b/checks/cloud/aws/ecr/no_public_access.rego @@ -0,0 +1,57 @@ +# METADATA +# title: ECR repository policy must block public access +# description: | +# Allowing public access to the ECR repository risks leaking sensitive of abusable information +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/AmazonECR/latest/public/public-repository-policies.html +# custom: +# id: AVD-AWS-0032 +# avd_id: AVD-AWS-0032 +# provider: aws +# service: ecr +# severity: HIGH +# short_code: no-public-access +# recommended_action: Do not allow public access in the policy +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository_policy#policy +# good_examples: checks/cloud/aws/ecr/no_public_access.tf.go +# bad_examples: checks/cloud/aws/ecr/no_public_access.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/no_public_access.cf.go +# bad_examples: checks/cloud/aws/ecr/no_public_access.cf.go +package builtin.aws.ecr.aws0032 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + some policy in repo.policies + value := json.unmarshal(policy.document.value) + some statement in value.Statement + has_ecr_action(statement) + has_public_access(statement) + res := result.new("Policy provides public access to the ECR repository.", policy.document) +} + +has_ecr_action(statement) if { + some action in statement.Action + startswith(action, "ecr:") +} + +has_public_access(statement) if { + statement.Principal.All +} + +has_public_access(statement) if { + "*" in statement.Principal.AWS +} diff --git a/checks/cloud/aws/ecr/no_public_access_test.go b/checks/cloud/aws/ecr/no_public_access_test.go deleted file mode 100644 index f114083e..00000000 --- a/checks/cloud/aws/ecr/no_public_access_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package ecr - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/liamg/iamgo" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicAccess(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR repository policy with wildcard principal", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: types.NewTestMetadata(), - Policies: func() []iam.Policy { - - sb := iamgo.NewStatementBuilder() - sb.WithSid("new policy") - sb.WithEffect("Allow") - sb.WithAllPrincipals(true) - sb.WithActions([]string{ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload", - "ecr:DescribeRepositories", - "ecr:GetRepositoryPolicy", - "ecr:ListImages", - "ecr:DeleteRepository", - "ecr:BatchDeleteImage", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepositoryPolicy", - }) - - builder := iamgo.NewPolicyBuilder() - builder.WithVersion("2021-10-07") - builder.WithStatement(sb.Build()) - - return []iam.Policy{ - { - Document: iam.Document{ - Metadata: types.NewTestMetadata(), - Parsed: builder.Build(), - }, - }, - } - }(), - }, - }, - }, - expected: true, - }, - { - name: "ECR repository policy with specific principal", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: types.NewTestMetadata(), - Policies: func() []iam.Policy { - - sb := iamgo.NewStatementBuilder() - sb.WithSid("new policy") - sb.WithEffect("Allow") - sb.WithAWSPrincipals([]string{"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"}) - sb.WithActions([]string{ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload", - "ecr:DescribeRepositories", - "ecr:GetRepositoryPolicy", - "ecr:ListImages", - "ecr:DeleteRepository", - "ecr:BatchDeleteImage", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepositoryPolicy", - }) - - builder := iamgo.NewPolicyBuilder() - builder.WithVersion("2021-10-07") - builder.WithStatement(sb.Build()) - - return []iam.Policy{ - { - Document: iam.Document{ - Metadata: types.NewTestMetadata(), - Parsed: builder.Build(), - }, - }, - } - }(), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckNoPublicAccess.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicAccess.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/no_public_access_test.rego b/checks/cloud/aws/ecr/no_public_access_test.rego new file mode 100644 index 00000000..475943e6 --- /dev/null +++ b/checks/cloud/aws/ecr/no_public_access_test.rego @@ -0,0 +1,35 @@ +package builtin.aws.ecr.aws0032_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0032 as check +import data.lib.test + +test_allow_without_public_access if { + inp := {"aws": {"ecr": {"repositories": [{"policies": [{"document": {"value": json.marshal({"Statement": [{ + "Action": ["ecr:*"], + "Principal": {"AWS": "arn:aws:iam::123456789012:root"}, + "Effect": "Allow", + }]})}}]}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_with_public_access_all if { + inp := {"aws": {"ecr": {"repositories": [{"policies": [{"document": {"value": json.marshal({"Statement": [{ + "Action": ["ecr:*"], + "Principal": {"All": true}, + }]})}}]}]}}} + + test.assert_equal_message("Policy provides public access to the ECR repository", check.deny) with input as inp +} + +test_deny_with_public_acces_any if { + inp := {"aws": {"ecr": {"repositories": [{"policies": [{"document": {"value": json.marshal({"Statement": [{ + "Action": ["ecr:*"], + "Principal": {"AWS": ["*"]}, + "Effect": "Allow", + }]})}}]}]}}} + + test.assert_equal_message("Policy provides public access to the ECR repository", check.deny) with input as inp +} diff --git a/checks/cloud/aws/ecr/repository_customer_key.go b/checks/cloud/aws/ecr/repository_customer_key.go index 59d33c95..56bf29de 100755 --- a/checks/cloud/aws/ecr/repository_customer_key.go +++ b/checks/cloud/aws/ecr/repository_customer_key.go @@ -34,7 +34,8 @@ var CheckRepositoryCustomerKey = rules.Register( Links: cloudFormationRepositoryCustomerKeyLinks, RemediationMarkdown: cloudFormationRepositoryCustomerKeyRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/repository_customer_key.rego b/checks/cloud/aws/ecr/repository_customer_key.rego new file mode 100644 index 00000000..ece32597 --- /dev/null +++ b/checks/cloud/aws/ecr/repository_customer_key.rego @@ -0,0 +1,49 @@ +# METADATA +# title: ECR Repository should use customer managed keys to allow more control +# description: | +# Images in the ECR repository are encrypted by default using AWS managed encryption keys. To increase control of the encryption and control the management of factors like key rotation, use a Customer Managed Key. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/AmazonECR/latest/userguide/encryption-at-rest.html +# custom: +# id: AVD-AWS-0033 +# avd_id: AVD-AWS-0033 +# provider: aws +# service: ecr +# severity: LOW +# short_code: repository-customer-key +# recommended_action: Use customer managed keys +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository#encryption_configuration +# good_examples: checks/cloud/aws/ecr/repository_customer_key.tf.go +# bad_examples: checks/cloud/aws/ecr/repository_customer_key.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/repository_customer_key.cf.go +# bad_examples: checks/cloud/aws/ecr/repository_customer_key.cf.go +package builtin.aws.ecr.aws0033 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + not is_encyption_type_kms(repo.encryption.type) + res := result.new("Repository is not encrypted using KMS.", repo.encryption.type) +} + +deny contains res if { + some repo in input.aws.ecr.repositories + is_encyption_type_kms(repo.encryption.type) + repo.encryption.kmskeyid.value == "" + res := result.new("Repository encryption does not use a customer managed KMS key.", repo.encryption.kmskeyid) +} + +is_encyption_type_kms(typ) if typ.value == "KMS" diff --git a/checks/cloud/aws/ecr/repository_customer_key_test.go b/checks/cloud/aws/ecr/repository_customer_key_test.go deleted file mode 100644 index 62168c80..00000000 --- a/checks/cloud/aws/ecr/repository_customer_key_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package ecr - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckRepositoryCustomerKey(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR repository not using KMS encryption", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: ecr.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Type: trivyTypes.String(ecr.EncryptionTypeAES256, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "ECR repository using KMS encryption but missing key", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: ecr.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Type: trivyTypes.String(ecr.EncryptionTypeKMS, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "ECR repository encrypted with KMS key", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: ecr.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Type: trivyTypes.String(ecr.EncryptionTypeKMS, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("some-kms-key", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckRepositoryCustomerKey.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckRepositoryCustomerKey.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/repository_customer_key_test.rego b/checks/cloud/aws/ecr/repository_customer_key_test.rego new file mode 100644 index 00000000..8c91a517 --- /dev/null +++ b/checks/cloud/aws/ecr/repository_customer_key_test.rego @@ -0,0 +1,30 @@ +package builtin.aws.ecr.aws0033_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0033 as check +import data.lib.test + +test_allow_repo_with_kms if { + inp := {"aws": {"ecr": {"repositories": [{"encryption": { + "type": {"value": "KMS"}, + "kmskeyid": {"value": "key"}, + }}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_repo_without_kms_encryption if { + inp := {"aws": {"ecr": {"repositories": [{"encryption": {"type": {"value": "AES256"}}}]}}} + + test.assert_equal_message("Repository is not encrypted using KMS.", check.deny) with input as inp +} + +test_deny_repo_with_kms_encryption_without_key if { + inp := {"aws": {"ecr": {"repositories": [{"encryption": { + "type": {"value": "KMS"}, + "kmskeyid": {"value": ""}, + }}]}}} + + test.assert_equal_message("Repository encryption does not use a customer managed KMS key.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption.go b/checks/cloud/aws/efs/enable_at_rest_encryption.go index d654348a..2a07bdd7 100755 --- a/checks/cloud/aws/efs/enable_at_rest_encryption.go +++ b/checks/cloud/aws/efs/enable_at_rest_encryption.go @@ -33,7 +33,8 @@ var CheckEnableAtRestEncryption = rules.Register( Links: cloudFormationEnableAtRestEncryptionLinks, RemediationMarkdown: cloudFormationEnableAtRestEncryptionRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, fs := range s.AWS.EFS.FileSystems { diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption.rego b/checks/cloud/aws/efs/enable_at_rest_encryption.rego new file mode 100644 index 00000000..fd5b0365 --- /dev/null +++ b/checks/cloud/aws/efs/enable_at_rest_encryption.rego @@ -0,0 +1,40 @@ +# METADATA +# title: EFS Encryption has not been enabled +# description: | +# If your organization is subject to corporate or regulatory policies that require encryption of data and metadata at rest, we recommend creating a file system that is encrypted at rest, and mounting your file system using encryption of data in transit. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/efs/latest/ug/encryption.html +# custom: +# id: AVD-AWS-0037 +# avd_id: AVD-AWS-0037 +# provider: aws +# service: efs +# severity: HIGH +# short_code: enable-at-rest-encryption +# recommended_action: Enable encryption for EFS +# input: +# selector: +# - type: cloud +# subtypes: +# - service: efs +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_file_system +# good_examples: checks/cloud/aws/efs/enable_at_rest_encryption.tf.go +# bad_examples: checks/cloud/aws/efs/enable_at_rest_encryption.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/efs/enable_at_rest_encryption.cf.go +# bad_examples: checks/cloud/aws/efs/enable_at_rest_encryption.cf.go +package builtin.aws.efs.aws0037 + +import rego.v1 + +deny contains res if { + some fs in input.aws.efs.filesystems + fs.encrypted.value == false + res := result.new("File system is not encrypted.", fs.encrypted) +} diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption_test.go b/checks/cloud/aws/efs/enable_at_rest_encryption_test.go deleted file mode 100644 index e2f74dae..00000000 --- a/checks/cloud/aws/efs/enable_at_rest_encryption_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package efs - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableAtRestEncryption(t *testing.T) { - tests := []struct { - name string - input efs.EFS - expected bool - }{ - { - name: "positive result", - input: efs.EFS{ - FileSystems: []efs.FileSystem{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encrypted: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }}, - }, - expected: true, - }, - { - name: "negative result", - input: efs.EFS{ - FileSystems: []efs.FileSystem{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encrypted: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }}, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EFS = test.input - results := CheckEnableAtRestEncryption.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableAtRestEncryption.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption_test.rego b/checks/cloud/aws/efs/enable_at_rest_encryption_test.rego new file mode 100644 index 00000000..95e6627a --- /dev/null +++ b/checks/cloud/aws/efs/enable_at_rest_encryption_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.efs.aws0037_test + +import rego.v1 + +import data.builtin.aws.efs.aws0037 as check +import data.lib.test + +test_allow_fs_encrypted if { + inp := {"aws": {"efs": {"filesystems": [{"encrypted": {"value": true}}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_fs_unencrypted if { + inp := {"aws": {"efs": {"filesystems": [{"encrypted": {"value": false}}]}}} + + test.assert_equal_message("File system is not encrypted.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/enable_control_plane_logging.go b/checks/cloud/aws/eks/enable_control_plane_logging.go index 407f92bf..9589a8c1 100755 --- a/checks/cloud/aws/eks/enable_control_plane_logging.go +++ b/checks/cloud/aws/eks/enable_control_plane_logging.go @@ -27,7 +27,8 @@ var CheckEnableControlPlaneLogging = rules.Register( Links: terraformEnableControlPlaneLoggingLinks, RemediationMarkdown: terraformEnableControlPlaneLoggingRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/enable_control_plane_logging.rego b/checks/cloud/aws/eks/enable_control_plane_logging.rego new file mode 100644 index 00000000..926bac94 --- /dev/null +++ b/checks/cloud/aws/eks/enable_control_plane_logging.rego @@ -0,0 +1,61 @@ +# METADATA +# title: EKS Clusters should have cluster control plane logging turned on +# description: | +# By default cluster control plane logging is not turned on. Logging is available for audit, api, authenticator, controllerManager and scheduler. All logging should be turned on for cluster control plane. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html +# custom: +# id: AVD-AWS-0038 +# avd_id: AVD-AWS-0038 +# provider: aws +# service: eks +# severity: MEDIUM +# short_code: enable-control-plane-logging +# recommended_action: Enable logging for the EKS control plane +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#enabled_cluster_log_types +# good_examples: checks/cloud/aws/eks/enable_control_plane_logging.tf.go +# bad_examples: checks/cloud/aws/eks/enable_control_plane_logging.tf.go +package builtin.aws.eks.aws0038 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.api.value == false + res := result.new("Control plane API logging is not enabled.", cluster.logging.api) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.audit.value == false + res := result.new("Control plane audit logging is not enabled.", cluster.logging.audit) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.authenticator.value == false + res := result.new("Control plane authenticator logging is not enabled.", cluster.logging.authenticator) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.controllermanager.value == false + res := result.new("Control plane controller manager logging is not enabled.", cluster.logging.controllermanager) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.scheduler.value == false + res := result.new("Control plane scheduler logging is not enabled.", cluster.logging.scheduler) +} diff --git a/checks/cloud/aws/eks/enable_control_plane_logging_test.go b/checks/cloud/aws/eks/enable_control_plane_logging_test.go deleted file mode 100644 index 324735fc..00000000 --- a/checks/cloud/aws/eks/enable_control_plane_logging_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableControlPlaneLogging(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS cluster with all cluster logging disabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Logging: eks.Logging{ - API: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Audit: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Authenticator: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - ControllerManager: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Scheduler: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS cluster with only some cluster logging enabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Logging: eks.Logging{ - API: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Audit: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Authenticator: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - ControllerManager: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Scheduler: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS cluster with all cluster logging enabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Logging: eks.Logging{ - API: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Audit: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Authenticator: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - ControllerManager: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Scheduler: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckEnableControlPlaneLogging.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableControlPlaneLogging.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/enable_control_plane_logging_test.rego b/checks/cloud/aws/eks/enable_control_plane_logging_test.rego new file mode 100644 index 00000000..fb3a7996 --- /dev/null +++ b/checks/cloud/aws/eks/enable_control_plane_logging_test.rego @@ -0,0 +1,39 @@ +package builtin.aws.eks.aws0038_test + +import rego.v1 + +import data.builtin.aws.eks.aws0038 as check +import data.lib.test + +test_allow_all_logging_enabled if { + inp := {"aws": {"eks": {"clusters": [{"logging": { + "audit": {"value": true}, + "authenticator": {"value": true}, + "controllermanager": {"value": true}, + "scheduler": {"value": true}, + }}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_all_logging_disabled if { + inp := {"aws": {"eks": {"clusters": [{"logging": { + "audit": {"value": false}, + "authenticator": {"value": false}, + "controllermanager": {"value": false}, + "scheduler": {"value": false}, + }}]}}} + + test.assert_count(check.deny, 4) with input as inp +} + +test_deny_one_logging_disabled if { + inp := {"aws": {"eks": {"clusters": [{"logging": { + "audit": {"value": false}, + "authenticator": {"value": true}, + "controllermanager": {"value": true}, + "scheduler": {"value": true}, + }}]}}} + + test.assert_equal_message("Control plane audit logging is not enabled.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/encrypt_secrets.go b/checks/cloud/aws/eks/encrypt_secrets.go index 809090bd..d70739ed 100755 --- a/checks/cloud/aws/eks/encrypt_secrets.go +++ b/checks/cloud/aws/eks/encrypt_secrets.go @@ -33,7 +33,8 @@ var CheckEncryptSecrets = rules.Register( Links: cloudFormationEncryptSecretsLinks, RemediationMarkdown: cloudFormationEncryptSecretsRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/encrypt_secrets.rego b/checks/cloud/aws/eks/encrypt_secrets.rego new file mode 100644 index 00000000..11a2a56f --- /dev/null +++ b/checks/cloud/aws/eks/encrypt_secrets.rego @@ -0,0 +1,47 @@ +# METADATA +# title: EKS should have the encryption of secrets enabled +# description: | +# EKS cluster resources should have the encryption_config block set with protection of the secrets resource. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-eks-adds-envelope-encryption-for-secrets-with-aws-kms/ +# custom: +# id: AVD-AWS-0039 +# avd_id: AVD-AWS-0039 +# provider: aws +# service: eks +# severity: HIGH +# short_code: encrypt-secrets +# recommended_action: Enable encryption of EKS secrets +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#encryption_config +# good_examples: checks/cloud/aws/eks/encrypt_secrets.tf.go +# bad_examples: checks/cloud/aws/eks/encrypt_secrets.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/eks/encrypt_secrets.cf.go +# bad_examples: checks/cloud/aws/eks/encrypt_secrets.cf.go +package builtin.aws.eks.aws0039 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.encryption.secrets.value == false + res := result.new("Cluster does not have secret encryption enabled.", cluster.encryption.secrets) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.encryption.secrets.value == true + cluster.encryption.kmskeyid.value == "" + res := result.new("Cluster encryption requires a KMS key ID, which is missing", cluster.encryption.kmskeyid) +} diff --git a/checks/cloud/aws/eks/encrypt_secrets_test.go b/checks/cloud/aws/eks/encrypt_secrets_test.go deleted file mode 100644 index 38534f85..00000000 --- a/checks/cloud/aws/eks/encrypt_secrets_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEncryptSecrets(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS Cluster with no secrets in the resources attribute", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: eks.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Secrets: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with secrets in the resources attribute but no KMS key", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: eks.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Secrets: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with secrets in the resources attribute and a KMS key", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: eks.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Secrets: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("some-arn", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckEncryptSecrets.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEncryptSecrets.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/encrypt_secrets_test.rego b/checks/cloud/aws/eks/encrypt_secrets_test.rego new file mode 100644 index 00000000..53b8bc74 --- /dev/null +++ b/checks/cloud/aws/eks/encrypt_secrets_test.rego @@ -0,0 +1,33 @@ +package builtin.aws.eks.aws0039_test + +import rego.v1 + +import data.builtin.aws.eks.aws0039 as check +import data.lib.test + +test_deny_without_secrets_and_kms if { + inp := {"aws": {"eks": {"clusters": [{"encryption": { + "kmskeyid": {"value": ""}, + "secrets": {"value": false}, + }}]}}} + + test.assert_equal_message("Cluster does not have secret encryption enabled.", check.deny) with input as inp +} + +test_deny_with_secrets_but_no_kms if { + inp := {"aws": {"eks": {"clusters": [{"encryption": { + "kmskeyid": {"value": ""}, + "secrets": {"value": true}, + }}]}}} + + test.assert_equal_message("Cluster encryption requires a KMS key ID, which is missing", check.deny) with input as inp +} + +test_allow_with_secrets_and_kms if { + inp := {"aws": {"eks": {"clusters": [{"encryption": { + "kmskeyid": {"value": "test"}, + "secrets": {"value": true}, + }}]}}} + + test.assert_empty(check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access.go b/checks/cloud/aws/eks/no_public_cluster_access.go index 6c406503..8c79c344 100755 --- a/checks/cloud/aws/eks/no_public_cluster_access.go +++ b/checks/cloud/aws/eks/no_public_cluster_access.go @@ -27,7 +27,8 @@ var CheckNoPublicClusterAccess = rules.Register( Links: terraformNoPublicClusterAccessLinks, RemediationMarkdown: terraformNoPublicClusterAccessRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/no_public_cluster_access.rego b/checks/cloud/aws/eks/no_public_cluster_access.rego new file mode 100644 index 00000000..3d8c284c --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access.rego @@ -0,0 +1,37 @@ +# METADATA +# title: EKS Clusters should have the public access disabled +# description: | +# EKS clusters are available publicly by default, this should be explicitly disabled in the vpc_config of the EKS cluster resource. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html +# custom: +# id: AVD-AWS-0040 +# avd_id: AVD-AWS-0040 +# provider: aws +# service: eks +# severity: CRITICAL +# short_code: no-public-cluster-access +# recommended_action: Don't enable public access to EKS Clusters +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#endpoint_public_access +# good_examples: checks/cloud/aws/eks/no_public_cluster_access.tf.go +# bad_examples: checks/cloud/aws/eks/no_public_cluster_access.tf.go +package builtin.aws.eks.aws0040 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.publicaccessenabled.value == true + res := result.new("Public cluster access is enabled.", cluster.publicaccessenabled) +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_test.go b/checks/cloud/aws/eks/no_public_cluster_access_test.go deleted file mode 100644 index 5d5c8f9d..00000000 --- a/checks/cloud/aws/eks/no_public_cluster_access_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicClusterAccess(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS Cluster with public access enabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with public access disabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckNoPublicClusterAccess.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicClusterAccess.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_test.rego b/checks/cloud/aws/eks/no_public_cluster_access_test.rego new file mode 100644 index 00000000..5b910e62 --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.eks.aws0040_test + +import rego.v1 + +import data.builtin.aws.eks.aws0040 as check +import data.lib.test + +test_deny_public_access_enabled if { + inp := {"aws": {"eks": {"clusters": [{"publicaccessenabled": {"value": true}}]}}} + + test.assert_equal_message("public access should be enabled", check.deny) with input as inp +} + +test_allow_public_access_disabled if { + inp := {"aws": {"eks": {"clusters": [{"publicaccessenabled": {"value": false}}]}}} + + test.assert_empty(check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go index 8f478094..8e1f6a20 100755 --- a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go +++ b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go @@ -35,7 +35,8 @@ var CheckNoPublicClusterAccessToCidr = rules.Register( Links: terraformNoPublicClusterAccessToCidrLinks, RemediationMarkdown: terraformNoPublicClusterAccessToCidrRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego new file mode 100644 index 00000000..2dd138b3 --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego @@ -0,0 +1,38 @@ +# METADATA +# title: EKS cluster should not have open CIDR range for public access +# description: | +# EKS Clusters have public access cidrs set to 0.0.0.0/0 by default which is wide open to the internet. This should be explicitly set to a more specific private CIDR range +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/eks/latest/userguide/create-public-private-vpc.html +# custom: +# id: AVD-AWS-0041 +# avd_id: AVD-AWS-0041 +# provider: aws +# service: eks +# severity: CRITICAL +# short_code: no-public-cluster-access-to-cidr +# recommended_action: Don't enable public access to EKS Clusters +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#vpc_config +# good_examples: checks/cloud/aws/eks/no_public_cluster_access_to_cidr.tf.go +# bad_examples: checks/cloud/aws/eks/no_public_cluster_access_to_cidr.tf.go +package builtin.aws.eks.aws0041 + +import rego.v1 + +# TODO: add support for "github.com/aquasecurity/trivy-checks/internal/cidr" +# deny contains res if { +# some cluster in input.aws.eks.clusters +# cluster.publicaccesenabled.value == true +# some cidr in cluster.publicaccescidrs +# } diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go deleted file mode 100644 index 390a0952..00000000 --- a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicClusterAccessToCidr(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS Cluster with public access CIDRs actively set to open", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - PublicAccessCIDRs: []trivyTypes.StringValue{ - trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with public access enabled but private CIDRs", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - PublicAccessCIDRs: []trivyTypes.StringValue{ - trivyTypes.String("10.2.0.0/8", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - { - name: "EKS Cluster with public access disabled and private CIDRs", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - PublicAccessCIDRs: []trivyTypes.StringValue{ - trivyTypes.String("10.2.0.0/8", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckNoPublicClusterAccessToCidr.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicClusterAccessToCidr.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego new file mode 100644 index 00000000..8d1d5388 --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego @@ -0,0 +1,6 @@ +package builtin.aws.eks.aws0041_test + +import rego.v1 + +import data.builtin.aws.eks.aws0041 as check +import data.lib.test