tf-eu-guard — Security Report

Target: examples/vulnerable-awsGenerated: 2026-08-30 16:53 UTCFindings: 52
52
Findings
5
Files
6
CRITICAL
17
HIGH
21
MEDIUM
8
LOW
45
NIS2
32
GDPR

Finding distribution

CRITICAL6
HIGH17
MEDIUM21
LOW8

Findings by severity

CRITICAL (6)

CRITICAL CKV_AWS_287 Ensure IAM policies does not allow credentials exposure
Resource
aws_iam_user_policy.app_user_policy
File
/iam.tf:11-29
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Policies that permit credential-generation actions (sts:GetFederationToken, iam:CreateAccessKey, ec2:GetPasswordData, etc.) let an attacker mint new credentials and pivot, undermining the confidentiality controls GDPR Art. 32(1)(b) requires and the access control NIS2 Art. 21(2)(i) mandates.

Remediation

Remove credential-exposing actions and scope to least privilege:
  # Before:
  "Action": ["sts:*", "iam:CreateAccessKey"]
  # After:
  "Action": ["s3:GetObject", "s3:PutObject"]
  "Resource": "arn:aws:s3:::my-app-data/*"
Documentation ↗
CRITICAL CKV_AWS_288 Ensure IAM policies does not allow data exfiltration
Resource
aws_iam_user_policy.app_user_policy
File
/iam.tf:11-29
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Broad read actions on all resources (s3:GetObject, secretsmanager:GetSecretValue, rds:DownloadDBLogFilePortion on "*") enable bulk exfiltration of data, which for personal data is a reportable breach under GDPR and a direct failure of the access control expected by NIS2 Art. 21(2)(i).

Remediation

Constrain read actions to specific resource ARNs, never "*":
  "Action": ["s3:GetObject"]
  "Resource": "arn:aws:s3:::approved-bucket/*"
Documentation ↗
CRITICAL CKV_AWS_289 Ensure IAM policies does not allow permissions management / resource exposure without constraints
Resource
aws_iam_user_policy.app_user_policy
File
/iam.tf:11-29
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Permissions-management actions (iam:PutUserPolicy, iam:AttachUserPolicy, s3:PutBucketPolicy, etc.) on unconstrained resources let a principal rewrite who can access what, collapsing access control (NIS2 Art. 21(2)(i)) and putting the confidentiality of all data at risk (GDPR Art. 32(1)(b)).

Remediation

Remove permissions-management actions from workload policies, or bound them
with a permissions boundary and explicit resource ARNs:
  "Action": ["iam:AttachUserPolicy"]
  "Resource": "arn:aws:iam::123456789012:user/app-*"
Documentation ↗
CRITICAL CKV_AWS_289 Ensure IAM policies does not allow permissions management / resource exposure without constraints
Resource
aws_iam_role_policy.app_role_policy
File
/iam.tf:53-67
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Permissions-management actions (iam:PutUserPolicy, iam:AttachUserPolicy, s3:PutBucketPolicy, etc.) on unconstrained resources let a principal rewrite who can access what, collapsing access control (NIS2 Art. 21(2)(i)) and putting the confidentiality of all data at risk (GDPR Art. 32(1)(b)).

Remediation

Remove permissions-management actions from workload policies, or bound them
with a permissions boundary and explicit resource ARNs:
  "Action": ["iam:AttachUserPolicy"]
  "Resource": "arn:aws:iam::123456789012:user/app-*"
Documentation ↗
CRITICAL CKV_AWS_17 Ensure all data stored in RDS is not publicly accessible
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

A publicly accessible database is reachable from the internet, exposing personal data to credential-stuffing and exploitation. This is a confidentiality failure under GDPR Art. 32(1)(b) and an access control failure under NIS2 Art. 21(2)(i).

Remediation

Keep the instance private and reach it only from within the VPC:
  publicly_accessible = false
  # place the instance in private subnets via a db_subnet_group and
  # restrict the security group to internal CIDRs only.
Documentation ↗
CRITICAL EUGUARD_NIS2_001 Ensure secrets are not hardcoded in Terraform (use variables/Secrets Manager)
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(e) GDPR Art. 32(1)(b)

A literal credential (database password, auth token, private key) committed to Terraform is readable by anyone with repository or state access, cannot be rotated centrally, and frequently leaks through CI logs. Storing secrets in code is an insecure-development practice under NIS2 Art. 21(2)(e) and, where the secret protects personal data, a confidentiality risk under GDPR Art. 32(1)(b).

Remediation

Never inline secrets. Source them from a secrets manager or a sensitive
variable injected at apply time:
  # Before:
  password = "ChangeMe123!"
  # After:
  password = data.aws_secretsmanager_secret_version.db.secret_string
  # or a variable marked `sensitive` and sourced from the environment/a vault.

HIGH (17)

HIGH CKV_AWS_273 Ensure access is controlled through SSO and not AWS IAM defined users
Resource
aws_iam_user.app_user
File
/iam.tf:2-8
Compliance
NIS2 Art. 21(2)(i)

Long-lived IAM users bypass centralized identity management and SSO, making it difficult to enforce MFA, audit access centrally, and revoke credentials quickly during an incident. NIS2 Art. 21(2)(i) requires effective access control policies.

Remediation

Delete standing IAM users and federate access through AWS IAM Identity Center (SSO):
  # Remove:
  # resource "aws_iam_user" "admin" { ... }
  # Use Identity Center permission sets + account assignments instead
  # (managed via aws_ssoadmin_* resources or the Identity Center console).
Documentation ↗
HIGH CKV_AWS_290 Ensure IAM policies does not allow write access without constraints
Resource
aws_iam_user_policy.app_user_policy
File
/iam.tf:11-29
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Write actions (Create/Put/Update/Delete) allowed on Resource "*" let a principal modify or destroy any resource in the account, threatening the integrity and availability of personal data (GDPR Art. 32(1)(b)) and breaking the least-privilege access control of NIS2 Art. 21(2)(i).

Remediation

Scope write actions to the specific resources the workload manages:
  "Action": ["dynamodb:PutItem", "dynamodb:UpdateItem"]
  "Resource": "arn:aws:dynamodb:eu-west-1:123456789012:table/app-*"
Documentation ↗
HIGH CKV_AWS_290 Ensure IAM policies does not allow write access without constraints
Resource
aws_iam_role_policy.app_role_policy
File
/iam.tf:53-67
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Write actions (Create/Put/Update/Delete) allowed on Resource "*" let a principal modify or destroy any resource in the account, threatening the integrity and availability of personal data (GDPR Art. 32(1)(b)) and breaking the least-privilege access control of NIS2 Art. 21(2)(i).

Remediation

Scope write actions to the specific resources the workload manages:
  "Action": ["dynamodb:PutItem", "dynamodb:UpdateItem"]
  "Resource": "arn:aws:dynamodb:eu-west-1:123456789012:table/app-*"
Documentation ↗
HIGH CKV_AWS_355 Ensure no IAM policies documents allow "*" as a statement's resource for restrictable actions
Resource
aws_iam_user_policy.app_user_policy
File
/iam.tf:11-29
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Using Resource "*" for actions that support resource-level scoping lets the policy reach every object in the account, not just those the workload owns. This over-broad reach undermines the access segregation NIS2 Art. 21(2)(i) expects and the data confidentiality GDPR Art. 32(1)(b) requires.

Remediation

Constrain each statement to specific resource ARNs:
  # Before:
  "Resource": "*"
  # After:
  "Resource": "arn:aws:s3:::my-app-data/*"
Documentation ↗
HIGH CKV_AWS_355 Ensure no IAM policies documents allow "*" as a statement's resource for restrictable actions
Resource
aws_iam_role_policy.app_role_policy
File
/iam.tf:53-67
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Using Resource "*" for actions that support resource-level scoping lets the policy reach every object in the account, not just those the workload owns. This over-broad reach undermines the access segregation NIS2 Art. 21(2)(i) expects and the data confidentiality GDPR Art. 32(1)(b) requires.

Remediation

Constrain each statement to specific resource ARNs:
  # Before:
  "Resource": "*"
  # After:
  "Resource": "arn:aws:s3:::my-app-data/*"
Documentation ↗
HIGH EUGUARD_GDPR_001 Ensure the AWS provider region is in the EU (GDPR data residency, Art. 44-49)
Resource
aws.default
File
/main.tf:11-13
Compliance
GDPR Art. 44

Deploying resources through an AWS provider pinned to a non-EU region stores personal data outside the EU/EEA - a transfer to a third country. GDPR Chapter V (Art. 44-49) permits this only under specific safeguards (an adequacy decision, Standard Contractual Clauses, or Binding Corporate Rules); an unqualified non-EU region is a data-residency violation that must be justified under one of those mechanisms or corrected.

Remediation

Pin every aws provider block to an EU region, or gate a non-EU provider
behind a documented Chapter V transfer mechanism:
  # Before:
  provider "aws" { region = "us-east-1" }
  # After:
  provider "aws" { region = "eu-west-1" }
HIGH CKV_AWS_24 Ensure no security groups allow ingress from 0.0.0.0:0 to port 22
Resource
aws_security_group.web
File
/network.tf:61-93
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

SSH (port 22) open to 0.0.0.0/0 exposes administrative shell access to the entire internet, inviting brute-force and exploitation of any host that may process personal data. This is a failure of network access control (NIS2 Art. 21(2)(i)) and of the confidentiality safeguards GDPR Art. 32(1)(b) requires.

Remediation

Restrict SSH to a bastion or corporate CIDR, never the internet:
  # Before:
  cidr_blocks = ["0.0.0.0/0"]
  # After:
  cidr_blocks = ["10.0.0.0/8"]   # or a specific admin range / use SSM Session Manager
Documentation ↗
HIGH CKV_AWS_133 Ensure that RDS instances has backup policy
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

A zero-day backup retention means no point-in-time recovery: hardware failure, corruption, or ransomware results in permanent data loss. Both NIS2 Art. 21(2)(c) and GDPR Art. 32(1)(c) require the ability to restore availability of data.

Remediation

Set a non-zero backup retention window (>= 7 days recommended):
  backup_retention_period = 7
  backup_window           = "03:00-04:00"
Documentation ↗
HIGH CKV_AWS_16 Ensure all data stored in the RDS is securely encrypted at rest
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(h) GDPR Art. 32(1)(a)

An unencrypted RDS instance leaves personal data readable on the underlying storage and in snapshots. GDPR Art. 32(1)(a) names encryption explicitly and NIS2 Art. 21(2)(h) requires cryptography controls for data at rest.

Remediation

Enable storage encryption (immutable - requires recreate/restore):
  resource "aws_db_instance" "this" {
    storage_encrypted = true
    kms_key_id        = aws_kms_key.rds.arn
    # ...
  }
Documentation ↗
HIGH CKV2_AWS_6 Ensure that S3 bucket has a Public Access block
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
GDPR Art. 32(1)(b)

A bucket with no aws_s3_bucket_public_access_block has no safety net against accidental public exposure via ACLs or bucket policy - a standing confidentiality risk for any personal data under GDPR Art. 32(1)(b).

Remediation

Attach a public access block that denies all public access (see CKV_AWS_53 for the
full resource); at minimum:
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
Documentation ↗
HIGH CKV2_AWS_6 Ensure that S3 bucket has a Public Access block
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
GDPR Art. 32(1)(b)

A bucket with no aws_s3_bucket_public_access_block has no safety net against accidental public exposure via ACLs or bucket policy - a standing confidentiality risk for any personal data under GDPR Art. 32(1)(b).

Remediation

Attach a public access block that denies all public access (see CKV_AWS_53 for the
full resource); at minimum:
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
Documentation ↗
HIGH CKV_AWS_145 Ensure that S3 buckets are encrypted with KMS by default
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
NIS2 Art. 21(2)(h) GDPR Art. 32(1)(a)

Buckets without KMS-managed encryption at rest lack the auditable key control and key-rotation guarantees expected of encrypted personal data. GDPR Art. 32(1)(a) names encryption explicitly; NIS2 Art. 21(2)(h) requires cryptography policies.

Remediation

Configure default SSE-KMS on the bucket:
  resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
    bucket = aws_s3_bucket.data.id
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "aws:kms"
        kms_master_key_id = aws_kms_key.s3.arn
      }
    }
  }
Documentation ↗
HIGH CKV_AWS_145 Ensure that S3 buckets are encrypted with KMS by default
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
NIS2 Art. 21(2)(h) GDPR Art. 32(1)(a)

Buckets without KMS-managed encryption at rest lack the auditable key control and key-rotation guarantees expected of encrypted personal data. GDPR Art. 32(1)(a) names encryption explicitly; NIS2 Art. 21(2)(h) requires cryptography policies.

Remediation

Configure default SSE-KMS on the bucket:
  resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
    bucket = aws_s3_bucket.data.id
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "aws:kms"
        kms_master_key_id = aws_kms_key.s3.arn
      }
    }
  }
Documentation ↗
HIGH CKV_AWS_53 Ensure S3 bucket has block public ACLS enabled
Resource
aws_s3_bucket_public_access_block.data
File
/s3.tf:12-19
Compliance
GDPR Art. 32(1)(b)

With block_public_acls disabled, a future ACL change can silently make objects public, defeating the confidentiality control GDPR Art. 32(1)(b) requires. The public access block is the account-level guardrail that prevents that drift.

Remediation

Enable the guardrail on the bucket's public access block:
  resource "aws_s3_bucket_public_access_block" "this" {
    bucket                  = aws_s3_bucket.data.id
    block_public_acls       = true
    ignore_public_acls      = true
    block_public_policy     = true
    restrict_public_buckets = true
  }
Documentation ↗
HIGH CKV_AWS_54 Ensure S3 bucket has block public policy enabled
Resource
aws_s3_bucket_public_access_block.data
File
/s3.tf:12-19
Compliance
GDPR Art. 32(1)(b)

With block_public_policy disabled, a bucket policy can grant anonymous access to objects, silently exposing personal data. It is the policy-side counterpart to CKV_AWS_53 and a direct confidentiality risk under GDPR Art. 32(1)(b).

Remediation

Enable the guardrail on the bucket's public access block:
  block_public_policy = true
Documentation ↗
HIGH CKV_AWS_55 Ensure S3 bucket has ignore public ACLs enabled
Resource
aws_s3_bucket_public_access_block.data
File
/s3.tf:12-19
Compliance
GDPR Art. 32(1)(b)

With ignore_public_acls disabled, public ACLs applied to the bucket or its objects are honoured rather than neutralised, allowing anonymous read/write and undermining the confidentiality control of GDPR Art. 32(1)(b).

Remediation

Enable the guardrail on the bucket's public access block:
  ignore_public_acls = true
Documentation ↗
HIGH CKV_AWS_56 Ensure S3 bucket has 'restrict_public_buckets' enabled
Resource
aws_s3_bucket_public_access_block.data
File
/s3.tf:12-19
Compliance
GDPR Art. 32(1)(b)

With restrict_public_buckets disabled, a public bucket policy grants access to all principals and anonymous users rather than only authorised AWS services and users, widening exposure of personal data contrary to GDPR Art. 32(1)(b).

Remediation

Enable the guardrail on the bucket's public access block:
  restrict_public_buckets = true
Documentation ↗

MEDIUM (21)

MEDIUM CKV_AWS_40 Ensure IAM policies are attached only to groups or roles (Reducing access management complexity may in-turn reduce opportunity for a principal to inadvertently receive or retain excessive privileges.)
Resource
aws_iam_user_policy.app_user_policy
File
/iam.tf:11-29
Compliance
NIS2 Art. 21(2)(i)

Attaching policies directly to individual IAM users makes entitlements hard to review and revoke at scale, weakening the asset- and access-management discipline NIS2 Art. 21(2)(i) expects. Group/role attachment keeps privileges auditable.

Remediation

Attach the policy to a group or role and add the user to the group:
  resource "aws_iam_group_policy_attachment" "app" {
    group      = aws_iam_group.app.name
    policy_arn = aws_iam_policy.app.arn
  }
Documentation ↗
MEDIUM CKV2_AWS_11 Ensure VPC flow logging is enabled in all VPCs
Resource
aws_vpc.main
File
/network.tf:2-11
Compliance
NIS2 Art. 21(2)(b)

Without VPC flow logs there is no network-level record of accepted/rejected connections, so lateral movement and exfiltration attempts cannot be detected or reconstructed. NIS2 Art. 21(2)(b) requires detection and analysis of incidents.

Remediation

Enable flow logs for the VPC:
  resource "aws_flow_log" "this" {
    vpc_id          = aws_vpc.main.id
    traffic_type    = "ALL"
    log_destination = aws_cloudwatch_log_group.vpc_flow.arn
    iam_role_arn    = aws_iam_role.vpc_flow.arn
  }
Documentation ↗
MEDIUM CKV2_AWS_12 Ensure the default security group of every VPC restricts all traffic
Resource
aws_vpc.main
File
/network.tf:2-11
Compliance
NIS2 Art. 21(2)(i)

An unmanaged default security group can implicitly allow traffic between any resources that fall back to it, bypassing intended segmentation. NIS2 Art. 21(2)(i) expects deliberate, least-privilege network access control.

Remediation

Manage the default SG and strip all ingress/egress:
  resource "aws_default_security_group" "default" {
    vpc_id = aws_vpc.main.id
    # no ingress or egress blocks = deny all
  }
Documentation ↗
MEDIUM CKV_AWS_130 Ensure VPC subnets do not assign public IP by default
Resource
aws_subnet.public_a
File
/network.tf:14-24
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Subnets that auto-assign public IPs place workloads directly on the internet by default, widening the attack surface for systems that may process personal data - a confidentiality risk (GDPR Art. 32(1)(b)) and weak network segmentation under NIS2 Art. 21(2)(i).

Remediation

Disable automatic public IP assignment; use NAT for egress:
  map_public_ip_on_launch = false
Documentation ↗
MEDIUM CKV_AWS_130 Ensure VPC subnets do not assign public IP by default
Resource
aws_subnet.public_b
File
/network.tf:26-36
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Subnets that auto-assign public IPs place workloads directly on the internet by default, widening the attack surface for systems that may process personal data - a confidentiality risk (GDPR Art. 32(1)(b)) and weak network segmentation under NIS2 Art. 21(2)(i).

Remediation

Disable automatic public IP assignment; use NAT for egress:
  map_public_ip_on_launch = false
Documentation ↗
MEDIUM CKV_AWS_260 Ensure no security groups allow ingress from 0.0.0.0:0 to port 80
Resource
aws_security_group.web
File
/network.tf:61-93
Compliance
NIS2 Art. 21(2)(i)

Plain-HTTP (port 80) open to the world serves traffic unencrypted and widens the attack surface. Where it fronts systems processing personal data it weakens the network access control expected by NIS2 Art. 21(2)(i); traffic should terminate TLS at a load balancer instead.

Remediation

Front the service with HTTPS and restrict or redirect port 80:
  # Terminate TLS (443) at an ALB and redirect 80 -> 443, or scope the CIDR:
  cidr_blocks = ["10.0.0.0/8"]
Documentation ↗
MEDIUM CKV_AWS_382 Ensure no security groups allow egress from 0.0.0.0:0 to port -1
Resource
aws_security_group.web
File
/network.tf:61-93
Compliance
NIS2 Art. 21(2)(i)

Unrestricted egress (all ports/protocols to 0.0.0.0/0) gives a compromised host a clear path to exfiltrate data or reach command-and-control infrastructure. Egress filtering is part of the network segmentation and access control under NIS2 Art. 21(2)(i).

Remediation

Scope egress to the destinations and ports the workload actually needs:
  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]
  }
Documentation ↗
MEDIUM CKV_AWS_382 Ensure no security groups allow egress from 0.0.0.0:0 to port -1
Resource
aws_security_group.rds
File
/network.tf:96-119
Compliance
NIS2 Art. 21(2)(i)

Unrestricted egress (all ports/protocols to 0.0.0.0/0) gives a compromised host a clear path to exfiltrate data or reach command-and-control infrastructure. Egress filtering is part of the network segmentation and access control under NIS2 Art. 21(2)(i).

Remediation

Scope egress to the destinations and ports the workload actually needs:
  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]
  }
Documentation ↗
MEDIUM CKV2_AWS_30 Ensure Postgres RDS as aws_db_instance has Query Logging enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(b) GDPR Art. 32(1)(b)

Without Postgres log capture (log_connections, log_disconnections, log_statement) there is no record of who touched personal data in the database, defeating the incident detection and analysis NIS2 Art. 21(2)(b) requires and the accountable processing security of GDPR Art. 32(1)(b).

Remediation

Enable the logging parameters in the instance's parameter group:
  resource "aws_db_parameter_group" "main" {
    parameter { name = "log_connections"  value = "1" }
    parameter { name = "log_disconnections" value = "1" }
  }
Documentation ↗
MEDIUM CKV_AWS_118 Ensure that enhanced monitoring is enabled for Amazon RDS instances
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(b)

Without enhanced monitoring, OS-level metrics (CPU, memory, process and I/O activity) are unavailable, so resource-exhaustion attacks or anomalous database host behaviour cannot be detected or investigated - contrary to the incident detection and handling required by NIS2 Art. 21(2)(b).

Remediation

Enable enhanced monitoring with a monitoring role and a non-zero interval:
  monitoring_interval = 60
  monitoring_role_arn = aws_iam_role.rds_monitoring.arn
Documentation ↗
MEDIUM CKV_AWS_129 Ensure that respective logs of Amazon Relational Database Service (Amazon RDS) are enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(b)

Without database engine logs exported to CloudWatch, suspicious queries and authentication failures leave no trail, so a data-access incident cannot be detected or investigated - contrary to NIS2 Art. 21(2)(b).

Remediation

Export the relevant engine logs:
  enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
  # (for MySQL: ["audit", "error", "general", "slowquery"])
Documentation ↗
MEDIUM CKV_AWS_157 Ensure that RDS instances have Multi-AZ enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

A single-AZ database has no standby: an availability-zone failure takes the data offline until manual recovery. Multi-AZ provides automatic failover, supporting the availability obligation of GDPR Art. 32(1)(c) and business continuity under NIS2 Art. 21(2)(c).

Remediation

Enable Multi-AZ deployment:
  multi_az = true
Documentation ↗
MEDIUM CKV_AWS_161 Ensure RDS database has IAM authentication enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(i) GDPR Art. 32(1)(b)

Relying only on static database passwords means no centralized revocation or rotation and passwords that are easily shared or leaked. IAM database authentication ties DB access to IAM identities, strengthening the access control of NIS2 Art. 21(2)(i) and the confidentiality control of GDPR Art. 32(1)(b).

Remediation

Enable IAM database authentication:
  iam_database_authentication_enabled = true
Documentation ↗
MEDIUM CKV_AWS_226 Ensure DB instance gets all minor upgrades automatically
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(e)

Postponing minor engine patches leaves known database vulnerabilities in place — often for months. Automatic minor upgrades implement the ongoing maintenance and vulnerability handling NIS2 Art. 21(2)(e) requires of systems in production.

Remediation

Enable automatic minor version upgrades:
  resource "aws_db_instance" "main" {
    auto_minor_version_upgrade = true
  }
Documentation ↗
MEDIUM CKV_AWS_293 Ensure that AWS database instances have deletion protection enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

Without deletion protection, a mistaken or malicious destroy removes the database and its data with no confirmation barrier, threatening the availability that both NIS2 Art. 21(2)(c) and GDPR Art. 32(1)(c) require.

Remediation

Enable deletion protection on the instance:
  deletion_protection = true
Documentation ↗
MEDIUM CKV_AWS_144 Ensure that S3 bucket has cross-region replication enabled
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

A bucket replicated only in one region is lost with that region. Cross-region replication is the mechanism that keeps personal data available and restorable after a regional outage, as NIS2 Art. 21(2)(c) and GDPR Art. 32(1)(c) require.

Remediation

Replicate the bucket to a second region:
  resource "aws_s3_bucket_replication_configuration" "data" {
    role   = aws_iam_role.replication.arn
    bucket = aws_s3_bucket.data.id
    rule { destination { bucket = aws_s3_bucket.replica.arn } }
  }
Documentation ↗
MEDIUM CKV_AWS_144 Ensure that S3 bucket has cross-region replication enabled
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

A bucket replicated only in one region is lost with that region. Cross-region replication is the mechanism that keeps personal data available and restorable after a regional outage, as NIS2 Art. 21(2)(c) and GDPR Art. 32(1)(c) require.

Remediation

Replicate the bucket to a second region:
  resource "aws_s3_bucket_replication_configuration" "data" {
    role   = aws_iam_role.replication.arn
    bucket = aws_s3_bucket.data.id
    rule { destination { bucket = aws_s3_bucket.replica.arn } }
  }
Documentation ↗
MEDIUM CKV_AWS_18 Ensure the S3 bucket has access logging enabled
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
NIS2 Art. 21(2)(b)

Without S3 server access logging there is no record of who read or wrote objects, so a data-access incident cannot be detected, scoped, or investigated. NIS2 Art. 21(2)(b) requires the ability to handle and analyse incidents.

Remediation

Send access logs to a dedicated logging bucket:
  resource "aws_s3_bucket_logging" "this" {
    bucket        = aws_s3_bucket.data.id
    target_bucket = aws_s3_bucket.logs.id
    target_prefix = "s3-access/"
  }
Documentation ↗
MEDIUM CKV_AWS_18 Ensure the S3 bucket has access logging enabled
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
NIS2 Art. 21(2)(b)

Without S3 server access logging there is no record of who read or wrote objects, so a data-access incident cannot be detected, scoped, or investigated. NIS2 Art. 21(2)(b) requires the ability to handle and analyse incidents.

Remediation

Send access logs to a dedicated logging bucket:
  resource "aws_s3_bucket_logging" "this" {
    bucket        = aws_s3_bucket.data.id
    target_bucket = aws_s3_bucket.logs.id
    target_prefix = "s3-access/"
  }
Documentation ↗
MEDIUM CKV_AWS_21 Ensure all data stored in the S3 bucket have versioning enabled
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

Without versioning, an accidental or malicious overwrite/delete is unrecoverable. Versioning provides point-in-time restore, supporting the recovery obligation in GDPR Art. 32(1)(c) and business continuity under NIS2 Art. 21(2)(c).

Remediation

Enable bucket versioning:
  resource "aws_s3_bucket_versioning" "this" {
    bucket = aws_s3_bucket.data.id
    versioning_configuration { status = "Enabled" }
  }
Documentation ↗
MEDIUM CKV_AWS_21 Ensure all data stored in the S3 bucket have versioning enabled
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
NIS2 Art. 21(2)(c) GDPR Art. 32(1)(c)

Without versioning, an accidental or malicious overwrite/delete is unrecoverable. Versioning provides point-in-time restore, supporting the recovery obligation in GDPR Art. 32(1)(c) and business continuity under NIS2 Art. 21(2)(c).

Remediation

Enable bucket versioning:
  resource "aws_s3_bucket_versioning" "this" {
    bucket = aws_s3_bucket.data.id
    versioning_configuration { status = "Enabled" }
  }
Documentation ↗

LOW (8)

LOW CKV2_AWS_5 Ensure that Security Groups are attached to another resource
Resource
aws_security_group.web
File
/network.tf:61-93
Compliance
NIS2 Art. 21(2)(i)

Orphaned security groups accumulate stale rules that later get attached "because they were there", importing unintended access into live environments — an unmanaged asset under the access control and asset management of NIS2 Art. 21(2)(i).

Remediation

Reference every security group from the resource it guards (and delete the rest):
  resource "aws_instance" "app" {
    vpc_security_group_ids = [aws_security_group.app.id]
  }
Documentation ↗
LOW CKV_AWS_23 Ensure every security group and rule has a description
Resource
aws_security_group.rds
File
/network.tf:96-119
Compliance
NIS2 Art. 21(2)(i)

Undocumented rules cannot be reviewed: nobody can tell which rule is a deliberate control and which is leftover access. Descriptions are what make rule review — and therefore accountable access control under NIS2 Art. 21(2)(i) — possible.

Remediation

Describe the purpose of every group and rule:
  resource "aws_security_group" "web" {
    description = "Web tier: HTTP from office CIDR only"
    ingress {
      description = "HTTP from office"
      cidr_blocks = ["203.0.113.0/24"]
    }
  }
Documentation ↗
LOW CKV2_AWS_60 Ensure RDS instance with copy tags to snapshots is enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(c)

Snapshots without the instance's tags cannot be found by restore runbooks or retention policies, so recovery depends on tribal knowledge. Tag propagation is part of accountable backup management under NIS2 Art. 21(2)(c).

Remediation

Copy tags onto snapshots:
  resource "aws_db_instance" "main" {
    copy_tags_to_snapshot = true
  }
Documentation ↗
LOW CKV_AWS_353 Ensure that RDS instances have performance insights enabled
Resource
aws_db_instance.main
File
/rds.tf:2-30
Compliance
NIS2 Art. 21(2)(b)

Performance Insights records query-level activity over time — the evidence needed to spot anomalous access patterns and analyse database incidents under NIS2 Art. 21(2)(b).

Remediation

Enable Performance Insights on the instance:
  resource "aws_db_instance" "main" {
    performance_insights_enabled = true
  }
Documentation ↗
LOW CKV2_AWS_61 Ensure that an S3 bucket has a lifecycle configuration
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
NIS2 Art. 21(2)(c)

Without lifecycle rules, log and backup objects accumulate indefinitely or are manually purged ad hoc, so retention for data protection and continuity purposes is unmanaged. Lifecycle configuration is the disciplined data lifecycle NIS2 Art. 21(2)(c) backup management expects.

Remediation

Define lifecycle rules (transition + expiry) for the bucket:
  resource "aws_s3_bucket_lifecycle_configuration" "logs" {
    bucket = aws_s3_bucket.logs.id
    rule {
      id     = "expire-logs"
      status = "Enabled"
      expiration { days = 365 }
    }
  }
Documentation ↗
LOW CKV2_AWS_61 Ensure that an S3 bucket has a lifecycle configuration
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
NIS2 Art. 21(2)(c)

Without lifecycle rules, log and backup objects accumulate indefinitely or are manually purged ad hoc, so retention for data protection and continuity purposes is unmanaged. Lifecycle configuration is the disciplined data lifecycle NIS2 Art. 21(2)(c) backup management expects.

Remediation

Define lifecycle rules (transition + expiry) for the bucket:
  resource "aws_s3_bucket_lifecycle_configuration" "logs" {
    bucket = aws_s3_bucket.logs.id
    rule {
      id     = "expire-logs"
      status = "Enabled"
      expiration { days = 365 }
    }
  }
Documentation ↗
LOW CKV2_AWS_62 Ensure S3 buckets should have event notifications enabled
Resource
aws_s3_bucket.data
File
/s3.tf:2-9
Compliance
NIS2 Art. 21(2)(b)

Without event notifications, writes to a bucket holding personal data raise no signal — unexpected mass downloads or deletions go unnoticed instead of feeding the detection capability NIS2 Art. 21(2)(b) requires.

Remediation

Subscribe at least one notification target to bucket events:
  resource "aws_s3_bucket_notification" "data" {
    bucket = aws_s3_bucket.data.id
    lambda_function { lambda_function_arn = aws_lambda_function.auditor.arn
                      events = ["s3:ObjectCreated:*"] }
  }
Documentation ↗
LOW CKV2_AWS_62 Ensure S3 buckets should have event notifications enabled
Resource
aws_s3_bucket.logs
File
/s3.tf:22-28
Compliance
NIS2 Art. 21(2)(b)

Without event notifications, writes to a bucket holding personal data raise no signal — unexpected mass downloads or deletions go unnoticed instead of feeding the detection capability NIS2 Art. 21(2)(b) requires.

Remediation

Subscribe at least one notification target to bucket events:
  resource "aws_s3_bucket_notification" "data" {
    bucket = aws_s3_bucket.data.id
    lambda_function { lambda_function_arn = aws_lambda_function.auditor.arn
                      events = ["s3:ObjectCreated:*"] }
  }
Documentation ↗