diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 38f79ae1..7a8c268e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,7 +38,7 @@ jobs: uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: '3.x' - + - name: Setup Terraform uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 with: @@ -66,7 +66,7 @@ jobs: run: | sudo apt-get install -y shellcheck find . -name "*.sh" -print0 | xargs -0 shellcheck - + - name: Run terraform fmt run: terraform fmt -check -recursive @@ -101,7 +101,7 @@ jobs: directory: ./terraform output_format: cli,sarif output_file_path: console,results.sarif - + - name: Upload SARIF file uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 with: diff --git a/.gitignore b/.gitignore index abd60f70..01d98ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ contexts/**/.kube/ contexts/**/.talos/ contexts/**/.aws/ contexts/**/.omni/ +contexts/**/.azure/ diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 00000000..2ab1f35a --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,7 @@ +version: '3' + +tasks: + scan: + desc: Scan for security vulnerabilities + cmds: + - source .venv/bin/activate && checkov -d {{.CLI_ARGS | default "terraform/"}} 2>/dev/null \ No newline at end of file diff --git a/contexts/.gitignore b/contexts/.gitignore new file mode 100644 index 00000000..a703ae48 --- /dev/null +++ b/contexts/.gitignore @@ -0,0 +1 @@ +cloud/ \ No newline at end of file diff --git a/contexts/local/blueprint.yaml b/contexts/local/blueprint.yaml index b57b8cba..e9c4f618 100644 --- a/contexts/local/blueprint.yaml +++ b/contexts/local/blueprint.yaml @@ -14,19 +14,24 @@ sources: ref: branch: main terraform: -- path: cluster/talos -- path: gitops/flux +- source: core + path: cluster/talos +- source: core + path: gitops/flux kustomize: - name: policy-base path: policy/base + source: core components: - kyverno - name: policy-resources path: policy/resources + source: core dependsOn: - policy-base - name: csi path: csi + source: core dependsOn: - policy-resources force: true @@ -35,6 +40,7 @@ kustomize: - openebs/dynamic-localpv - name: ingress-base path: ingress/base + source: core dependsOn: - pki-resources force: true @@ -46,6 +52,7 @@ kustomize: - nginx/web - name: pki-base path: pki/base + source: core dependsOn: - policy-resources force: true @@ -54,6 +61,7 @@ kustomize: - trust-manager - name: pki-resources path: pki/resources + source: core dependsOn: - pki-base force: true @@ -62,6 +70,7 @@ kustomize: - public-issuer/selfsigned - name: dns path: dns + source: core dependsOn: - ingress-base - pki-base @@ -75,6 +84,7 @@ kustomize: - external-dns/ingress - name: gitops path: gitops/flux + source: core dependsOn: - ingress-base force: true @@ -82,6 +92,7 @@ kustomize: - webhook - name: demo path: demo/bookinfo + source: core dependsOn: - ingress-base force: true diff --git a/terraform/backend/s3/.terraform.lock.hcl b/terraform/backend/s3/.terraform.lock.hcl new file mode 100644 index 00000000..b3c31f49 --- /dev/null +++ b/terraform/backend/s3/.terraform.lock.hcl @@ -0,0 +1,46 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.90.0" + constraints = "5.90.0" + hashes = [ + "h1:4zF/N14sv3ZMo/Goa4s1B/LR+2SmK2cEZqoqO7+2F8Q=", + "h1:cJ3ab7uBP0NmD+LzxHK63ZG1o9nIppAjt6c0OafGKPw=", + "zh:0ed246595c4ffb3ea3649528ff171503db208fb20be5f750b8e359d17ee72b60", + "zh:1d5c500913b5df0fbf5e8143354aecc736cc4e66d58d4ab17deb24b721ab743a", + "zh:337f3511335e6e32431548913d1973ae077d1a4c2f77677675c92c60cd2f5e0a", + "zh:624762ff78819aee434d6c3e6c79eb93c91060be2df4f45f9014272a60b5d608", + "zh:7f4ab9bcd667e38b7d7b7aa1068535f01eef3656ecd422acccbe8238d377a15a", + "zh:84542ce0403cacee245c1a159169cc0ddb965d7d734216f9eb0bb3ff0a0bae36", + "zh:85dd27e39f2c3ab13cb5c02236b810893bd90ec6da33fabaa7ab6d116accfa10", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a0cf76959ade91958b08d186f5bcdc403395fa635f21912464da40bc7a5db4ff", + "zh:a9a48f9f7f4122b6a44b7273b4cc54020887f7346f50286d7da1278cca2ee952", + "zh:c119b826e334aac2d03ea561774dad536ccd6449e2a4f42b3af100623ae02679", + "zh:d4204ca7f1295732660c70db4ea04c3ae1f7e1ac82c0ec9d0dc549493bc45e7a", + "zh:d95f89181d12ebab1b1f964274d29795e1e6e2d112ea97caffd8a7f1326a922d", + "zh:e529c7be1037f1a9a733fc0bcbbdcc58fc44f85ed343f891e5c584b2ef56fd5c", + "zh:e541c135514a6727f20410a9a52c06cb71b4ddadaf2a41da28d599fb1c442845", + ] +} + +provider "registry.terraform.io/hashicorp/local" { + version = "2.5.2" + hashes = [ + "h1:6XyefmvbkprppmYbGmMcQW5NB4w6C363SSShzuhF4R0=", + "h1:IyFbOIO6mhikFNL/2h1iZJ6kyN3U00jgkpCLUCThAfE=", + "zh:136299545178ce281c56f36965bf91c35407c11897f7082b3b983d86cb79b511", + "zh:3b4486858aa9cb8163378722b642c57c529b6c64bfbfc9461d940a84cd66ebea", + "zh:4855ee628ead847741aa4f4fc9bed50cfdbf197f2912775dd9fe7bc43fa077c0", + "zh:4b8cd2583d1edcac4011caafe8afb7a95e8110a607a1d5fb87d921178074a69b", + "zh:52084ddaff8c8cd3f9e7bcb7ce4dc1eab00602912c96da43c29b4762dc376038", + "zh:71562d330d3f92d79b2952ffdda0dad167e952e46200c767dd30c6af8d7c0ed3", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:805f81ade06ff68fa8b908d31892eaed5c180ae031c77ad35f82cb7a74b97cf4", + "zh:8b6b3ebeaaa8e38dd04e56996abe80db9be6f4c1df75ac3cccc77642899bd464", + "zh:ad07750576b99248037b897de71113cc19b1a8d0bc235eb99173cc83d0de3b1b", + "zh:b9f1c3bfadb74068f5c205292badb0661e17ac05eb23bfe8bd809691e4583d0e", + "zh:cc4cbcd67414fefb111c1bf7ab0bc4beb8c0b553d01719ad17de9a047adff4d1", + ] +} diff --git a/terraform/backend/s3/main.tf b/terraform/backend/s3/main.tf new file mode 100644 index 00000000..6cc34ed7 --- /dev/null +++ b/terraform/backend/s3/main.tf @@ -0,0 +1,305 @@ +#--------------------------------------------------------------------------------------------------- +# Providers Configuration +# This section defines the required providers for the Terraform configuration. +# It specifies the AWS provider with a version constraint, ensuring compatibility +# with the AWS services used in this setup. +#--------------------------------------------------------------------------------------------------- + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "5.90.0" + } + } +} + +provider "aws" { + region = var.region + default_tags { + tags = merge( + var.tags, + { + ManagedBy = "Terraform" + } + ) + } +} + +data "aws_caller_identity" "current" {} + +#--------------------------------------------------------------------------------------------------- +# S3 Bucket Creation +# This section creates the S3 bucket used for storing Terraform state. +# It ensures that the bucket is unique per account and region. +#--------------------------------------------------------------------------------------------------- + +resource "aws_s3_bucket" "this" { + # checkov:skip=CKV2_AWS_62:Event notifications are not needed for terraform state bucket + # checkov:skip=CKV_AWS_144:Cross-region replication is not required for Terraform state bucket + # checkov:skip=CKV_AWS_19:Server-side encryption is configured via aws_s3_bucket_server_side_encryption_configuration + # checkov:skip=CKV_AWS_145:KMS encryption is configured via aws_s3_bucket_server_side_encryption_configuration + bucket = var.s3_bucket_name != "" ? var.s3_bucket_name : local.default_s3_bucket_name + + tags = { + Name = var.s3_bucket_name != "" ? var.s3_bucket_name : local.default_s3_bucket_name + } +} + +resource "aws_s3_bucket_lifecycle_configuration" "this" { + bucket = aws_s3_bucket.this.id + + rule { + id = "cleanup" + status = "Enabled" + + abort_incomplete_multipart_upload { + days_after_initiation = 7 + } + } +} + +#--------------------------------------------------------------------------------------------------- +# S3 Bucket Configuration +# This section defines local variables for S3 bucket naming conventions and policy statements. +# It ensures that the bucket names are unique per account and enforces security policies +# like HTTPS and encryption for data in transit and at rest. +#--------------------------------------------------------------------------------------------------- + +locals { + default_s3_bucket_name = var.s3_bucket_name != "" ? var.s3_bucket_name : "terraform-state-${var.context_id}" + kms_key_id = var.enable_kms && var.kms_key_alias == "" ? aws_kms_key.terraform_state[0].arn : "" + + bucket_policy_statements = flatten([ + { + Sid = "AllowAdminAccess", + Effect = "Allow", + Principal = { + AWS = concat( + ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"], + var.terraform_state_iam_roles + ) + }, + Action = [ + "s3:ListBucket", + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:GetBucketVersioning", + "s3:GetEncryptionConfiguration", + "s3:GetBucketLocation" + ], + Resource = [ + aws_s3_bucket.this.arn, + "${aws_s3_bucket.this.arn}/*" + ], + Condition = { + Bool = { + "aws:SecureTransport" = "true" + } + } + } + ]) +} + +#--------------------------------------------------------------------------------------------------- +# Logging Configuration +# This section sets up logging for the S3 bucket. It creates a logging bucket if enabled, +# and configures the target bucket and prefix for storing access logs, which is crucial +# for monitoring and auditing access to the S3 bucket. +#--------------------------------------------------------------------------------------------------- + +resource "aws_s3_bucket_logging" "this" { + count = var.s3_log_bucket_name != "" ? 1 : 0 + + bucket = aws_s3_bucket.this.id + + target_bucket = var.s3_log_bucket_name + target_prefix = "${aws_s3_bucket.this.id}/" +} + +#--------------------------------------------------------------------------------------------------- +# Encryption +# This section configures server-side encryption for the S3 bucket using either KMS or AES256. +# It ensures that all objects stored in the bucket are encrypted, enhancing data security. +#--------------------------------------------------------------------------------------------------- + +resource "aws_s3_bucket_server_side_encryption_configuration" "this" { + bucket = aws_s3_bucket.this.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = var.enable_kms && length(aws_kms_key.terraform_state) > 0 ? "aws:kms" : "AES256" + kms_master_key_id = var.enable_kms && length(aws_kms_key.terraform_state) > 0 ? aws_kms_key.terraform_state[0].arn : null + } + } +} + +resource "aws_s3_bucket_policy" "this" { + bucket = aws_s3_bucket.this.id + + policy = jsonencode({ + Version = "2012-10-17", + Statement = local.bucket_policy_statements + }) +} + +#--------------------------------------------------------------------------------------------------- +# Versioning +# This section enables versioning on the S3 bucket, allowing for the preservation, +# retrieval, and restoration of every version of every object stored in the bucket. +#--------------------------------------------------------------------------------------------------- + +resource "aws_s3_bucket_versioning" "this" { + bucket = aws_s3_bucket.this.id + versioning_configuration { + status = "Enabled" + } +} + +#--------------------------------------------------------------------------------------------------- +# Public Access Block +# This section configures the public access block settings for the S3 bucket, +# preventing public access to the bucket and its objects, thereby enhancing security. +#--------------------------------------------------------------------------------------------------- + +resource "aws_s3_bucket_public_access_block" "this" { + bucket = aws_s3_bucket.this.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +#--------------------------------------------------------------------------------------------------- +# DynamoDB Table for Locking +# This section creates a DynamoDB table used for state locking and consistency checking +# during Terraform operations, ensuring that only one process can modify the state at a time. +#--------------------------------------------------------------------------------------------------- + +resource "aws_dynamodb_table" "terraform_locks" { + # checkov:skip=CKV_AWS_119:Encryption is not necessary for this DynamoDB table as it is used solely for Terraform state locking, which does not involve sensitive data. + count = var.enable_dynamodb ? 1 : 0 + + name = "terraform-state-locks-${var.context_id}" + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + + attribute { + name = "LockID" + type = "S" + } + + point_in_time_recovery { + enabled = true + } +} + +#--------------------------------------------------------------------------------------------------- +# KMS Key Policy Document for Terraform State Encryption +# This section defines the IAM policy document for the KMS key used to encrypt the Terraform state. +# It grants necessary permissions to the AWS account root user for managing the KMS key, +# while ensuring that write access is constrained to specific actions. +#--------------------------------------------------------------------------------------------------- + +data "aws_iam_policy_document" "terraform_state_kms_policy" { + statement { + sid = "AllowKeyAdministration" + effect = "Allow" + + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + + actions = [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:TagResource", + "kms:UntagResource", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ] + resources = ["arn:aws:kms:${var.region}:${data.aws_caller_identity.current.account_id}:key/*"] + condition { + test = "StringEquals" + variable = "kms:CallerAccount" + values = ["${data.aws_caller_identity.current.account_id}"] + } + } + + statement { + sid = "AllowKeyUsage" + effect = "Allow" + + principals { + type = "AWS" + identifiers = [ + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + ] + } + + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ] + resources = ["arn:aws:kms:${var.region}:${data.aws_caller_identity.current.account_id}:key/*"] + condition { + test = "StringEquals" + variable = "kms:CallerAccount" + values = ["${data.aws_caller_identity.current.account_id}"] + } + } +} + +#--------------------------------------------------------------------------------------------------- +# KMS Key for Terraform State Encryption (if not provided externally) +# This section creates a KMS key for encrypting the Terraform state file in S3, +# if an external KMS key is not provided. It includes key rotation and deletion settings. +#--------------------------------------------------------------------------------------------------- + +resource "aws_kms_key" "terraform_state" { + count = var.enable_kms && var.kms_key_alias == "" ? 1 : 0 + + description = "KMS key for encrypting Terraform state file in S3" + enable_key_rotation = true + deletion_window_in_days = 7 + policy = data.aws_iam_policy_document.terraform_state_kms_policy.json +} + +resource "aws_kms_alias" "terraform_state_alias" { + count = var.enable_kms && var.kms_key_alias == "" ? 1 : 0 + + name = var.kms_key_alias != "" ? var.kms_key_alias : "alias/terraform-state-${var.context_id}" + target_key_id = aws_kms_key.terraform_state[0].key_id +} + +#--------------------------------------------------------------------------------------------------- +# Backend Configuration Output +# This section outputs the backend configuration to a local file in tfvars format +#--------------------------------------------------------------------------------------------------- + +resource "local_file" "backend_config" { + count = 1 + + content = <