├── .dockerignore ├── .github └── workflows │ └── terraform.yml ├── .gitignore ├── Dockerfile ├── README.md ├── app.py ├── buildspec.yml ├── docs ├── chaos-engineering.png ├── checkov.png ├── gitleak.png ├── hadolint.png ├── kubescape.png ├── prowler.png ├── secops.png ├── slack.png ├── sonarqube.png ├── tfsec.png └── trivy.png ├── iam-role-autenticate-eks.sh ├── ingress-nginx-config └── nginx.yaml ├── k8s-deployment.yaml ├── requirements.txt ├── static ├── background.jpg └── styles.css ├── templates └── index.html └── terraform-infra ├── .terraform.lock.hcl ├── acm.tf ├── backend.tf ├── cloudfront.tf ├── data.tf ├── dev.terraform.tfvars ├── dynamodb.tf ├── ecr.tf ├── eks.tf ├── locals.tf ├── output.tf ├── provider.tf ├── s3.tf ├── variables.tf ├── vpc.tf └── waf.tf /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist 3 | .git 4 | npm-debug.log 5 | .coverage 6 | .coverage.* 7 | .env 8 | .aws 9 | -------------------------------------------------------------------------------- /.github/workflows/terraform.yml: -------------------------------------------------------------------------------- 1 | name: 'Terraform and Security Scanning' 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | terraform: 7 | description: 'Run Terraform Job (yes/no)' 8 | required: true 9 | default: 'no' 10 | infrastructure-audit: 11 | description: 'Run Infrastructure Audit Job (yes/no)' 12 | required: true 13 | default: 'no' 14 | 15 | permissions: 16 | id-token: write 17 | contents: read 18 | env: 19 | AWS_REGION: ap-south-1 20 | TF_VERSION: 1.2.6 21 | 22 | jobs: 23 | terraform: 24 | name: infrastructure-dev 25 | runs-on: ubuntu-latest 26 | if: ${{ github.event.inputs.terraform == 'yes' }} 27 | defaults: 28 | run: 29 | shell: bash 30 | 31 | steps: 32 | - name: Configure AWS Credentials 33 | uses: aws-actions/configure-aws-credentials@v1 34 | with: 35 | aws-region: ${{ env.AWS_REGION }} 36 | role-to-assume: arn:aws:iam::434605749312:role/aws-deployment-tf 37 | role-session-name: staging-session 38 | - name: Checkout 39 | uses: actions/checkout@v3 40 | 41 | - name: Setup terraform 42 | uses: hashicorp/setup-terraform@v1 43 | with: 44 | terraform_version: ${{ env.TF_VERSION }} 45 | 46 | 47 | - name: Terraform Init, Format, Validate 48 | run: | 49 | terraform init 50 | terraform fmt -check 51 | terraform validate -no-color 52 | working-directory: terraform-infra 53 | 54 | - name: Terraform Plan 55 | run: terraform plan --var-file=dev.terraform.tfvars 56 | working-directory: terraform-infra 57 | - name: Terraform Apply 58 | run: terraform apply --auto-approve --var-file=dev.terraform.tfvars 59 | working-directory: terraform-infra 60 | 61 | 62 | infrastructure-audit: 63 | name: infrastructure-audit 64 | runs-on: ubuntu-latest 65 | if: ${{ github.event.inputs.infrastructure-audit == 'yes' }} 66 | 67 | steps: 68 | - name: Checkout 69 | uses: actions/checkout@v3 70 | 71 | - name: Install tfsec 72 | run: | 73 | wget -q $(wget -qO- https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://[^\"]*linux_amd64.tar.gz") -O tfsec.tar.gz 74 | tar -xzf tfsec.tar.gz 75 | sudo install tfsec /usr/local/bin/ 76 | working-directory: terraform-infra 77 | 78 | - name: Run tfsec 79 | run: tfsec . --exclude-downloaded-modules --tfvars-file dev.terraform.tfvars 80 | working-directory: terraform-infra 81 | continue-on-error: true 82 | 83 | - name: Install Checkov 84 | run: pip install checkov 85 | 86 | - name: Run Kubernetes YAMLS files Scan 87 | run: | 88 | checkov -d . --framework kubernetes --download-external-modules true 89 | continue-on-error: true 90 | 91 | - name: Run Checkov IAC Scan 92 | run: | 93 | checkov -d . --framework terraform --download-external-modules true 94 | working-directory: terraform-infra 95 | continue-on-error: true 96 | 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Python runtime as a parent image 2 | FROM python 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Copy the current directory contents into the container at /app 8 | COPY static /app/static 9 | COPY templates /app/templates 10 | COPY app.py /app 11 | COPY requirements.txt /app 12 | 13 | 14 | # Install any needed dependencies specified in requirements.txt 15 | RUN pip install --no-cache-dir -r requirements.txt 16 | 17 | # Expose the port Flask is running on 18 | EXPOSE 5000 19 | # Define environment variable 20 | ENV FLASK_APP app.py 21 | 22 | # Run app.py when the container launches 23 | CMD ["flask", "run", "--host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AWS DevSecops CICDPipeline 2 | ![CICD](docs/secops.png) 3 | 4 | **Step 1:** 5 | After successfully creating the infrastructure, add and install the Nginx Ingress Controller and repository using the following Helm commands: 6 | ``` 7 | helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx 8 | helm repo update 9 | helm install ingress-nginx ingress-nginx/ingress-nginx --version 4.10.0 --namespace ingress-nginx --create-namespace --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-ssl-cert"="acm-cert-arn" -f nginx-config.yaml 10 | ``` 11 | You can also customize the Nginx value: [https://github.com/kubernetes/ingress-nginx] 12 | 13 | **Step 2:** Run a bash script to create and authenticate CodeBuild with AWS EKS and update the EKS cluster's aws-auth ConfigMap with the new role. 14 | 1. chmod +x `iam-role-autenticate-eks.sh` 15 | 2. `./iam-role-autenticate-eks.sh` 16 | 17 | ## Prowler 18 | ![PROWLER](docs/prowler.png) 19 | 20 | ## GITLEAK 21 | ![GITLEAK](docs/gitleak.png) 22 | 23 | ## Sonarqube 24 | ![SONARQUBE](docs/sonarqube.png) 25 | 26 | ## Hadolint 27 | ![HADOLINT](docs/hadolint.png) 28 | 29 | ## Trivy 30 | ![TRIVY](docs/trivy.png) 31 | 32 | ## Slack 33 | ![SLACK](docs/slack.png) 34 | 35 | ## Kubescape 36 | ![KUBESCAPE](docs/kubescape.png) 37 | 38 | 39 | ## TFSEC 40 | ![TFSEC](docs/tfsec.png) 41 | 42 | ## Checkov 43 | ![CHECKOV](docs/checkov.png) 44 | 45 | ## Chaos Engineering 46 | ![CE](docs/chaos-engineering.png) 47 | ## Requirements 48 | 49 | | Name | Version | 50 | |------|---------| 51 | | [terraform](#requirement\_terraform) | >= 0.15.0 | 52 | | [aws](#requirement\_aws) | >= 4.29.0 | 53 | | [random](#requirement\_random) | >= 3.6.0 | 54 | | [template](#requirement\_template) | >= 2.2.0 | 55 | 56 | ## Providers 57 | 58 | | Name | Version | 59 | |------|---------| 60 | | [aws](#provider\_aws) | 5.47.0 | 61 | 62 | ## Modules 63 | 64 | | Name | Source | Version | 65 | |------|--------|---------| 66 | | [acm\_backend](#module\_acm\_backend) | terraform-aws-modules/acm/aws | 4.0.1 | 67 | | [acm\_cf](#module\_acm\_cf) | terraform-aws-modules/acm/aws | 4.0.1 | 68 | | [ui](#module\_ui) | terraform-aws-modules/s3-bucket/aws | 3.3.0 | 69 | | [ui-cf](#module\_ui-cf) | terraform-aws-modules/cloudfront/aws | 3.4.0 | 70 | 71 | ## Resources 72 | 73 | | Name | Type | 74 | |------|------| 75 | | [aws_dynamodb_table.photos_metadata](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource | 76 | | [aws_ecr_repository.foo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource | 77 | | [aws_eip.nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | 78 | | [aws_eks_cluster.cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster) | resource | 79 | | [aws_eks_node_group.private-nodes-01](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_node_group) | resource | 80 | | [aws_eks_node_group.private-nodes-02](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_node_group) | resource | 81 | | [aws_iam_policy.node_additional_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 82 | | [aws_iam_role.demo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 83 | | [aws_iam_role.nodes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 84 | | [aws_iam_role_policy_attachment.demo-AmazonEKSClusterPolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 85 | | [aws_iam_role_policy_attachment.node-additional-permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 86 | | [aws_iam_role_policy_attachment.nodes-AmazonEC2ContainerRegistryReadOnly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 87 | | [aws_iam_role_policy_attachment.nodes-AmazonEKSWorkerNodePolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 88 | | [aws_iam_role_policy_attachment.nodes-AmazonEKS_CNI_Policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 89 | | [aws_iam_role_policy_attachment.nodes-EC2RoleForSSM](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 90 | | [aws_iam_role_policy_attachment.nodes-SSMFullAccess](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 91 | | [aws_iam_role_policy_attachment.nodes-SSMManagedInstanceCore](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 92 | | [aws_iam_role_policy_attachment.nodes-SessionManager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 93 | | [aws_internet_gateway.igw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | 94 | | [aws_kms_key.kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | 95 | | [aws_nat_gateway.nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource | 96 | | [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | 97 | | [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | 98 | | [aws_route_table_association.private-ap-south-1a](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | 99 | | [aws_route_table_association.private-ap-south-1b](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | 100 | | [aws_route_table_association.public-ap-south-1a](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | 101 | | [aws_route_table_association.public-ap-south-1b](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | 102 | | [aws_subnet.private-ap-south-1a](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | 103 | | [aws_subnet.private-ap-south-1b](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | 104 | | [aws_subnet.public-ap-south-1a](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | 105 | | [aws_subnet.public-ap-south-1b](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | 106 | | [aws_vpc.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource | 107 | | [aws_wafv2_ip_set.block_ip_set](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource | 108 | | [aws_wafv2_web_acl.main_acl](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl) | resource | 109 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 110 | | [aws_iam_policy_document.s3_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 111 | | [aws_route53_zone.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | 112 | 113 | ## Inputs 114 | 115 | | Name | Description | Type | Default | Required | 116 | |------|-------------|------|---------|:--------:| 117 | | [cluster\_config](#input\_cluster\_config) | Configuration for the cluster, detailing specifics like size, type, and other cluster-related settings. | `any` | n/a | yes | 118 | | [ecr\_names](#input\_ecr\_names) | Names of the Elastic Container Registry repositories required for the deployment. | `any` | n/a | yes | 119 | | [env](#input\_env) | The deployment environment name, e.g., 'prod', 'dev', or 'test'. | `string` | n/a | yes | 120 | | [ui\_conf](#input\_ui\_conf) | UI configuration settings, which may include theming, layout, and feature toggles. | `any` | n/a | yes | 121 | | [vpc\_config](#input\_vpc\_config) | Configuration parameters for the VPC including subnets, CIDR blocks, and other network-related settings. | `any` | n/a | yes | 122 | 123 | ## Outputs 124 | 125 | | Name | Description | 126 | |------|-------------| 127 | | [acm\_arn](#output\_acm\_arn) | n/a | 128 | | [cloudfront\_url](#output\_cloudfront\_url) | The URL of the CloudFront distribution. | 129 | | [dynamodb\_table\_name](#output\_dynamodb\_table\_name) | The name of the DynamoDB table. | 130 | | [ecr\_repository\_details](#output\_ecr\_repository\_details) | Details of the ECR repositories including URLs and ARNs | 131 | | [eks\_values\_private\_nodes\_01](#output\_eks\_values\_private\_nodes\_01) | Values related to the AWS EKS managed node group for private-nodes-01 | 132 | | [eks\_values\_private\_nodes\_02](#output\_eks\_values\_private\_nodes\_02) | Values related to the AWS EKS managed node group for private-nodes-02 | 133 | | [s3\_bucket\_name](#output\_s3\_bucket\_name) | The name of the S3 bucket. | 134 | | [vpc\_details](#output\_vpc\_details) | Details of the main VPC | 135 | 136 | You can find the video at [https://youtu.be/Uxx3Mkgc58k](https://youtu.be/Uxx3Mkgc58k) . 137 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, redirect, render_template, jsonify 2 | import boto3 3 | import uuid 4 | import os 5 | import json 6 | import logging 7 | from werkzeug.utils import secure_filename 8 | 9 | app = Flask(__name__, static_url_path='/static') 10 | logging.basicConfig(level=logging.INFO) 11 | logger = logging.getLogger(__name__) 12 | 13 | s3 = boto3.client('s3') 14 | 15 | # Environment variables 16 | S3_BUCKET = os.getenv('S3_BUCKET', 'default-bucket') 17 | CLOUDFRONT_DOMAIN = os.getenv('CLOUDFRONT_DOMAIN', 'default.cloudfront.net') 18 | 19 | # AWS DynamoDB 20 | dynamodb = boto3.resource('dynamodb', region_name='ap-south-1') 21 | table = dynamodb.Table('PhotosMetadata') 22 | 23 | @app.route('/') 24 | def index(): 25 | return render_template('index.html') 26 | 27 | @app.route('/upload', methods=['POST']) 28 | def upload_file(): 29 | file = request.files['file'] 30 | if file: 31 | original_filename = secure_filename(file.filename) # Safely secure the filename 32 | 33 | # Upload to S3 34 | s3.upload_fileobj(file, S3_BUCKET, original_filename) 35 | 36 | # Generate S3 URL 37 | s3_url = f'https://{S3_BUCKET}.s3.amazonaws.com/{original_filename}' 38 | 39 | # Generate URL from CloudFront 40 | cloudfront_url = f'https://{CLOUDFRONT_DOMAIN}/{original_filename}' 41 | 42 | # Store metadata in DynamoDB 43 | table.put_item( 44 | Item={ 45 | 'photo_id': str(uuid.uuid4()), # Generate a photo ID for internal tracking 46 | 'filename': original_filename, # Store the original filename in DynamoDB 47 | 's3_url': s3_url, 48 | 'cloudfront_url': cloudfront_url 49 | } 50 | ) 51 | logger.info(f"File uploaded successfully. CloudFront URL: {cloudfront_url}") 52 | download_instructions = f"Please download the file using this link: {cloudfront_url}" 53 | 54 | return jsonify({ 55 | 'message': 'File uploaded successfully, processing initiated.', 56 | 'cloudfront_url': cloudfront_url, 57 | 'download_instructions': download_instructions 58 | }) 59 | else: 60 | error_message = 'No file part in the request.' 61 | logger.error(error_message) 62 | return render_template('index.html', error=error_message), 400 63 | 64 | if __name__ == '__main__': 65 | app.run(debug=True, port=5000) 66 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | env: 3 | variables: 4 | AWS_DEFAULT_REGION: "ap-south-1" # Specify the region 5 | REPOSITORY_NAME: "codedevops" # ECR repository name 6 | CLUSTER_NAME: "codedevops" # Cluster name 7 | 8 | phases: 9 | install: 10 | commands: 11 | - export SONAR_SCANNER_VERSION=5.0.1.3006 12 | - export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux 13 | - curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip 14 | - unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ 15 | - export PATH=$SONAR_SCANNER_HOME/bin:$PATH 16 | - export SONAR_SCANNER_OPTS="-server" 17 | - sonar-scanner -Dsonar.organization=ravindrasinghh -Dsonar.projectKey=ravindrasinghh_codedevops -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io 18 | - echo "Installing GitLeaks..." 19 | - wget https://github.com/zricethezav/gitleaks/releases/download/v8.0.0/gitleaks_8.0.0_linux_x64.tar.gz 20 | - tar -xzf gitleaks_8.0.0_linux_x64.tar.gz 21 | - mv gitleaks /usr/local/bin/gitleaks 22 | - chmod +x /usr/local/bin/gitleaks 23 | - apt-get install wget apt-transport-https gnupg 24 | - wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | apt-key add - 25 | - echo deb https://aquasecurity.github.io/trivy-repo/deb bionic main | tee -a /etc/apt/sources.list.d/trivy.list 26 | - apt-get update 27 | - apt-get install -y trivy 28 | - echo "Installing Hadolint..." 29 | - wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/download/v2.8.0/hadolint-Linux-x86_64 30 | - chmod +x /usr/local/bin/hadolint 31 | - echo "Hadolint installed successfully!" 32 | - echo Install kubectl 33 | - curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.9/2020-11-02/bin/linux/amd64/kubectl 34 | - chmod +x ./kubectl 35 | - mv kubectl /usr/local/bin 36 | 37 | 38 | 39 | pre_build: 40 | commands: 41 | - echo Logging in to Amazon ECR....... 42 | - aws ecr get-login-password --region $AWS_DEFAULT_REGION| docker login --username AWS --password-stdin 434605749312.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com 43 | - # Replace with this to your repository URI 44 | - REPOSITORY_URI=434605749312.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY_NAME 45 | - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) 46 | - echo "Running Hadolint on Dockerfile..." 47 | - hadolint Dockerfile || true 48 | - sed -i 's@CONTAINER_IMAGE@'"$REPOSITORY_URI:$IMAGE_TAG"'@' k8s-deployment.yaml 49 | - # Update Kube config Home Directory 50 | - export KUBECONFIG=$HOME/.kube/config 51 | 52 | build: 53 | commands: 54 | - echo "Scanning repository for secrets with GitLeaks..." 55 | - gitleaks detect --source='.' --report-format=json --report-path=gitleaks-report.json 56 | - echo "Building the Docker image..." 57 | - docker build -t $REPOSITORY_URI:$IMAGE_TAG . 58 | - docker tag $REPOSITORY_URI:$IMAGE_TAG $REPOSITORY_URI:latest 59 | - echo "Scanning for vulnerabilities with Trivy..." 60 | - trivy image 434605749312.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY_NAME:latest || true 61 | 62 | post_build: 63 | commands: 64 | - echo "Pushing the Docker image..." 65 | - docker push $REPOSITORY_URI:$IMAGE_TAG 66 | - echo "Deploying the application to Kubernetes..." 67 | - CREDENTIALS=$(aws sts assume-role --role-arn arn:aws:iam::434605749312:role/EksCodeBuildKubectlRole --role-session-name codebuild-kubectl --duration-seconds 900) 68 | - export AWS_ACCESS_KEY_ID="$(echo ${CREDENTIALS} | jq -r '.Credentials.AccessKeyId')" 69 | - export AWS_SECRET_ACCESS_KEY="$(echo ${CREDENTIALS} | jq -r '.Credentials.SecretAccessKey')" 70 | - export AWS_SESSION_TOKEN="$(echo ${CREDENTIALS} | jq -r '.Credentials.SessionToken')" 71 | - export AWS_EXPIRATION=$(echo ${CREDENTIALS} | jq -r '.Credentials.Expiration') 72 | - aws eks update-kubeconfig --name $CLUSTER_NAME 73 | - kubectl apply -f k8s-deployment.yaml 74 | - echo "Done Enjoy the Day" 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /docs/chaos-engineering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/chaos-engineering.png -------------------------------------------------------------------------------- /docs/checkov.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/checkov.png -------------------------------------------------------------------------------- /docs/gitleak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/gitleak.png -------------------------------------------------------------------------------- /docs/hadolint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/hadolint.png -------------------------------------------------------------------------------- /docs/kubescape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/kubescape.png -------------------------------------------------------------------------------- /docs/prowler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/prowler.png -------------------------------------------------------------------------------- /docs/secops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/secops.png -------------------------------------------------------------------------------- /docs/slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/slack.png -------------------------------------------------------------------------------- /docs/sonarqube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/sonarqube.png -------------------------------------------------------------------------------- /docs/tfsec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/tfsec.png -------------------------------------------------------------------------------- /docs/trivy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/docs/trivy.png -------------------------------------------------------------------------------- /iam-role-autenticate-eks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Dynamically fetch the AWS Account ID 4 | ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) 5 | ROLE_NAME="EksCodeBuildKubectlRole" 6 | 7 | # Function to check if the IAM role exists 8 | check_role_existence() { 9 | aws iam get-role --role-name $ROLE_NAME > /dev/null 2>&1 10 | echo $? 11 | } 12 | 13 | # Set the trust relationship policy JSON correctly 14 | TRUST_POLICY='{ 15 | "Version": "2012-10-17", 16 | "Statement": [ 17 | { 18 | "Effect": "Allow", 19 | "Principal": { 20 | "AWS": "arn:aws:iam::'"${ACCOUNT_ID}"':root" 21 | }, 22 | "Action": "sts:AssumeRole" 23 | } 24 | ] 25 | }' 26 | 27 | # Create IAM Role if it does not exist 28 | if [ $(check_role_existence) -ne 0 ]; then 29 | echo "Creating IAM role..." 30 | aws iam create-role --role-name $ROLE_NAME --assume-role-policy-document "$TRUST_POLICY" 31 | else 32 | echo "Role already exists, skipping creation..." 33 | fi 34 | 35 | # Define inline policy for describing EKS resources 36 | INLINE_POLICY='{ 37 | "Version": "2012-10-17", 38 | "Statement": [ 39 | { 40 | "Effect": "Allow", 41 | "Action": "eks:Describe*", 42 | "Resource": "*" 43 | } 44 | ] 45 | }' 46 | 47 | # File path for inline policy 48 | POLICY_FILE="/tmp/iam-eks-describe-policy" 49 | echo "$INLINE_POLICY" > $POLICY_FILE 50 | 51 | # Attach the policy to the role 52 | aws iam put-role-policy --role-name $ROLE_NAME --policy-name eks-describe --policy-document "file://$POLICY_FILE" 53 | 54 | # Prepare the role definition for the aws-auth configmap 55 | ROLE_DEF="- rolearn: arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME} 56 | username: build 57 | groups: 58 | - system:masters" 59 | 60 | # Get the current aws-auth ConfigMap and save it 61 | TMP_FILE="/tmp/aws-auth.yml" 62 | kubectl get configmap aws-auth -n kube-system -o yaml > $TMP_FILE 63 | 64 | # Check if the role already exists in the ConfigMap 65 | if ! grep -q "$ROLE_NAME" $TMP_FILE; then 66 | echo "Modifying aws-auth ConfigMap to add new role..." 67 | # Use yq to update the YAML file safely 68 | yq e '.data.mapRoles += "'"$ROLE_DEF"'"' -i $TMP_FILE 69 | # Add last-applied-configuration annotation using current content 70 | CONFIG=$(cat $TMP_FILE | yq e -j -) 71 | yq e '.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]=$CONFIG' -i $TMP_FILE --arg CONFIG "$CONFIG" 72 | # Apply the updated ConfigMap 73 | kubectl apply -f $TMP_FILE 74 | else 75 | echo "Role already in aws-auth, no changes made..." 76 | fi 77 | 78 | # Verify the updated ConfigMap 79 | kubectl get configmap aws-auth -o yaml -n kube-system 80 | 81 | -------------------------------------------------------------------------------- /ingress-nginx-config/nginx.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | replicaCount: 2 3 | minAvailable: 2 4 | resources: 5 | limits: 6 | cpu: 200m 7 | memory: 512Mi 8 | requests: 9 | cpu: 100m 10 | memory: 256Mi 11 | updateStrategy: 12 | type: RollingUpdate 13 | rollingUpdate: 14 | maxUnavailable: 1 15 | metrics: 16 | port: 10254 17 | enabled: true 18 | serviceMonitor: 19 | enabled: false 20 | additionalLabels: 21 | release: "kube-prometheus-stack" 22 | service: 23 | annotations: 24 | prometheus.io/scrape: "true" 25 | prometheus.io/port: "10254" 26 | service: 27 | annotations: 28 | service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp 29 | service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: 'true' 30 | service.beta.kubernetes.io/aws-load-balancer-type: nlb 31 | service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https 32 | service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: 'ELBSecurityPolicy-TLS13-1-2-2021-06' 33 | service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" 34 | targetPorts: 35 | http: http 36 | https: http 37 | -------------------------------------------------------------------------------- /k8s-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: myapp-config 5 | data: 6 | S3_BUCKET: "codedevops-staging-photoapp-ui" 7 | CLOUDFRONT_DOMAIN: "share.codedevops.cloud" 8 | --- 9 | apiVersion: apps/v1 10 | kind: Deployment 11 | metadata: 12 | name: myapp 13 | spec: 14 | selector: 15 | matchLabels: 16 | app: myapp 17 | replicas: 1 18 | template: 19 | metadata: 20 | labels: 21 | app: myapp 22 | spec: 23 | containers: 24 | - image: CONTAINER_IMAGE 25 | name: myapp 26 | ports: 27 | - containerPort: 5000 28 | imagePullPolicy: Always 29 | env: 30 | - name: S3_BUCKET 31 | valueFrom: 32 | configMapKeyRef: 33 | name: myapp-config 34 | key: S3_BUCKET 35 | - name: CLOUDFRONT_DOMAIN 36 | valueFrom: 37 | configMapKeyRef: 38 | name: myapp-config 39 | key: CLOUDFRONT_DOMAIN 40 | 41 | --- 42 | apiVersion: v1 43 | kind: Service 44 | metadata: 45 | name: myapp 46 | spec: 47 | ports: 48 | - port: 80 #service port #kubeproxy will open port on worker node to which can route traffic to alb 49 | targetPort: 5000 #container port 50 | protocol: TCP 51 | type: ClusterIP 52 | selector: 53 | app: myapp 54 | --- 55 | apiVersion: networking.k8s.io/v1 56 | kind: Ingress 57 | metadata: 58 | name: myapp 59 | annotations: 60 | # Ingress class to use the NGINX Ingress Controller 61 | kubernetes.io/ingress.class: "nginx" 62 | # AWS-specific annotations for SSL and the load balancer 63 | alb.ingress.kubernetes.io/scheme: "internet-facing" 64 | alb.ingress.kubernetes.io/target-type: "ip" 65 | alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]' 66 | alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:ap-south-1:434605749312:certificate/9c87dc98-73ca-40f8-a731-280b943ea7f3" 67 | alb.ingress.kubernetes.io/ssl-redirect: '443' 68 | spec: 69 | 70 | rules: 71 | - host: photoapp.codedevops.cloud 72 | http: 73 | paths: 74 | - path: / 75 | pathType: Exact 76 | backend: 77 | service: 78 | name: myapp 79 | port: 80 | number: 80 81 | - path: /upload 82 | pathType: Prefix 83 | backend: 84 | service: 85 | name: myapp 86 | port: 87 | number: 80 88 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | boto3 3 | -------------------------------------------------------------------------------- /static/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravindrasinghh/Deploying-a-Bulletproof-Photo-Sharing-App-with-DevSecOps-Terraform-AWS-EKS-and-Chaos-Engineering/b54f3e4c60a4329cabfefd26f4b940649c8b9d87/static/background.jpg -------------------------------------------------------------------------------- /static/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-image: url('background.jpg'); 3 | background-size: cover; /* Optional: Adjust the background size */ 4 | } 5 | .custom-file-upload { 6 | display: inline-block; 7 | padding: 10px 20px; 8 | background-color: #4CAF50; 9 | color: white; 10 | font-weight: bold; 11 | cursor: pointer; 12 | border-radius: 5px; 13 | } 14 | 15 | .custom-file-upload input[type="file"] { 16 | display: none; 17 | } 18 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Photo Sharing Application 7 | 8 | 66 | 67 | 68 |
69 |

Photo Sharing Application using AWS Services

70 |
71 | 72 |
73 | 74 |
75 | {% if message %} 76 |
{{ message }}
77 | {% endif %} 78 | {% if error %} 79 |
{{ error }}
80 | {% endif %} 81 |
82 | 83 | 84 | -------------------------------------------------------------------------------- /terraform-infra/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.47.0" 6 | constraints = ">= 4.5.0, >= 4.12.0, >= 4.29.0, >= 5.12.0" 7 | hashes = [ 8 | "h1:49aEnvHJ/M8BRGAXKzU6W3zSbf7HgIrjXkXjC5DGEWY=", 9 | "zh:06037a14e47e8f82d0b3b326cd188566272b808b7970a9249a11db26d475b83d", 10 | "zh:116b7dd58ca964a1056249d2b6550f399b0a6bc9a7920b7ee134242114432c9f", 11 | "zh:1aa089c81459071c1d65ba7454f1122159e1fa1b5384e6e9ef85c8264f8a9ecb", 12 | "zh:2c1471acba40c4944aa88dda761093c0c969db6408bdc1a4fb62417788cd6bb6", 13 | "zh:3b950bea06ea4bf1ec359a97a4f1745b7efca7fc2da368843666020dd0ebc5d4", 14 | "zh:7191c5c2fce834d584153dcd5269ed3042437f224d341ad85df06b2247bd09b2", 15 | "zh:76d841b3f247f9bb3899dec3b4d871613a4ae8a83a581a827655d34b1bbee0ee", 16 | "zh:7c656ce252fafc2c915dad43a0a7da17dba975207d75841a02f3f2b92d51ec25", 17 | "zh:8ec97118cbdef64139c52b719e4e22443e67a1f37ea1597cd45b2e9b97332a35", 18 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 19 | "zh:a369deca7938236a7da59f7ad1fe18137f736764c9015ed10e88edb6e8505980", 20 | "zh:a743882fb099401eae0c86d9388a6faadbbc27b2ac9477aeef643e5de4eec3f9", 21 | "zh:d5f960f58aff06fc58e244fea6e665800384cacb8cd64a556f8e145b98650372", 22 | "zh:e31ffcfd560132ffbff2f574928ba392e663202a750750ed39a8950031b75623", 23 | "zh:ebd9061b92a772144564f35a63d5a08cb45e14a9d39294fda185f2e0de9c8e28", 24 | ] 25 | } 26 | 27 | provider "registry.terraform.io/hashicorp/helm" { 28 | version = "2.13.1" 29 | hashes = [ 30 | "h1:ucfOk3XxaOkarUDRyRH5vU5uL8mehvUxt1mHmSyWK1Q=", 31 | "zh:1bf0ae1ecfd2a5d5a57f695a33b2328ef197138f27ff372fed820c975eac9783", 32 | "zh:4676295e3a929848b98869d3040f54f17fbed3d133342b6a1f7b72d5797239e0", 33 | "zh:4bf3705e061e28d16a525aad9229fdd842cdc96f7c23d040d3148957ba3149d8", 34 | "zh:69db9550eacd61d85cf456d438f08addfefea4fcbc4f4a8119105093ea3d950a", 35 | "zh:6e11560e3ea61b141f03842771bfad143ff1c56bd0d1bc01069496107cad0ab6", 36 | "zh:733ea41e2eb4bd63cfdae6886ed47d224dabb0cd37959c6e2b213b1914a80121", 37 | "zh:74caefb2dc8e6055259d716c11194cc0709261c592d41466abf2dc0b21d88297", 38 | "zh:89682ab50b5cf1f1c41eabfc76f53a56482ac7b4bf77d9cb087d789524fd3e31", 39 | "zh:a5ff95092f2f123027b89f585612a225c9bce7e65977b4ffaf4de3ae3e7870bc", 40 | "zh:c85fce024cb5a387702ceb42a3a06e32519cd1e61bc9dd820a762da21110ab96", 41 | "zh:d828ef2db612798179322bcb3fe829a43dd47e740cabb67e3654c8561ae661ff", 42 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 43 | ] 44 | } 45 | 46 | provider "registry.terraform.io/hashicorp/random" { 47 | version = "3.6.1" 48 | constraints = ">= 3.6.0" 49 | hashes = [ 50 | "h1:8iqExjtAvirFTJkpm5YyYD+fC+DGV8NTJzKsE2c70VA=", 51 | "zh:2a0ec154e39911f19c8214acd6241e469157489fc56b6c739f45fbed5896a176", 52 | "zh:57f4e553224a5e849c99131f5e5294be3a7adcabe2d867d8a4fef8d0976e0e52", 53 | "zh:58f09948c608e601bd9d0a9e47dcb78e2b2c13b4bda4d8f097d09152ea9e91c5", 54 | "zh:5c2a297146ed6fb3fe934c800e78380f700f49ff24dbb5fb5463134948e3a65f", 55 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 56 | "zh:7ce41e26f0603e31cdac849085fc99e5cd5b3b73414c6c6d955c0ceb249b593f", 57 | "zh:8c9e8d30c4ef08ee8bcc4294dbf3c2115cd7d9049c6ba21422bd3471d92faf8a", 58 | "zh:93e91be717a7ffbd6410120eb925ebb8658cc8f563de35a8b53804d33c51c8b0", 59 | "zh:982542e921970d727ce10ed64795bf36c4dec77a5db0741d4665230d12250a0d", 60 | "zh:b9d1873f14d6033e216510ef541c891f44d249464f13cc07d3f782d09c7d18de", 61 | "zh:cfe27faa0bc9556391c8803ade135a5856c34a3fe85b9ae3bdd515013c0c87c1", 62 | "zh:e4aabf3184bbb556b89e4b195eab1514c86a2914dd01c23ad9813ec17e863a8a", 63 | ] 64 | } 65 | 66 | provider "registry.terraform.io/hashicorp/template" { 67 | version = "2.2.0" 68 | constraints = ">= 2.2.0" 69 | hashes = [ 70 | "h1:0wlehNaxBX7GJQnPfQwTNvvAf38Jm0Nv7ssKGMaG6Og=", 71 | "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386", 72 | "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53", 73 | "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603", 74 | "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16", 75 | "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776", 76 | "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451", 77 | "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae", 78 | "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde", 79 | "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d", 80 | "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2", 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /terraform-infra/acm.tf: -------------------------------------------------------------------------------- 1 | module "acm_backend" { 2 | source = "terraform-aws-modules/acm/aws" 3 | version = "4.0.1" 4 | domain_name = "codedevops.cloud" 5 | subject_alternative_names = [ 6 | "*.codedevops.cloud" 7 | ] 8 | zone_id = data.aws_route53_zone.main.id 9 | validation_method = "DNS" 10 | wait_for_validation = true 11 | tags = { 12 | Name = "${local.project}-${var.env}-backend-validation" 13 | } 14 | } 15 | 16 | data "aws_route53_zone" "main" { 17 | name = "codedevops.cloud." # Ensure the domain name ends with a dot 18 | 19 | } 20 | 21 | module "acm_cf" { 22 | source = "terraform-aws-modules/acm/aws" 23 | providers = { 24 | aws = aws.us-east-1 25 | } 26 | version = "4.0.1" 27 | domain_name = "codedevops.cloud" 28 | subject_alternative_names = [ 29 | "*.codedevops.cloud" 30 | ] 31 | zone_id = data.aws_route53_zone.main.id 32 | validation_method = "DNS" 33 | wait_for_validation = true 34 | tags = { 35 | Name = "${local.project}-${var.env}-backend-cloudfront" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /terraform-infra/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "devsecops-backend-codedevops" 4 | key = "secops-dev.tfstae" 5 | region = "ap-south-1" 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /terraform-infra/cloudfront.tf: -------------------------------------------------------------------------------- 1 | module "ui-cf" { 2 | source = "terraform-aws-modules/cloudfront/aws" 3 | version = "3.4.0" 4 | comment = "Created from terraform" 5 | aliases = ["share.codedevops.cloud", "download.codedevops.cloud"] 6 | create_distribution = true 7 | enabled = true 8 | is_ipv6_enabled = true 9 | price_class = "PriceClass_All" 10 | retain_on_delete = false 11 | wait_for_deployment = false 12 | create_origin_access_identity = true 13 | origin_access_identities = { 14 | photoapp_codedevops_ui = "ui" 15 | } 16 | origin = { 17 | ui = { 18 | domain_name = module.ui.s3_bucket_bucket_regional_domain_name 19 | s3_origin_config = { 20 | origin_access_identity = "photoapp_codedevops_ui" 21 | } 22 | } 23 | } 24 | default_root_object = "index.html" 25 | default_cache_behavior = { 26 | target_origin_id = "ui" 27 | viewer_protocol_policy = "redirect-to-https" 28 | allowed_methods = ["GET", "HEAD", "OPTIONS"] 29 | cached_methods = ["GET", "HEAD"] 30 | cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" 31 | compress = true 32 | use_forwarded_values = false 33 | min_ttl = 0 34 | default_ttl = 0 35 | max_ttl = 0 36 | } 37 | viewer_certificate = { 38 | cloudfront_default_certificate = var.ui_conf.cloudfront_default_certificate 39 | acm_certificate_arn = module.acm_cf.acm_certificate_arn 40 | ssl_support_method = var.ui_conf.ssl_support_method 41 | minimum_protocol_version = var.ui_conf.minimum_protocol_version 42 | } 43 | custom_error_response = [ 44 | { 45 | error_caching_min_ttl = "300" 46 | error_code = "403" 47 | response_page_path = "/index.html" 48 | response_code = "200" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /terraform-infra/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | 4 | data "aws_iam_policy_document" "s3_policy" { 5 | statement { 6 | actions = ["s3:GetObject"] 7 | resources = ["${module.ui.s3_bucket_arn}/*"] 8 | 9 | principals { 10 | type = "AWS" 11 | identifiers = [module.ui-cf.cloudfront_origin_access_identity_iam_arns.0] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /terraform-infra/dev.terraform.tfvars: -------------------------------------------------------------------------------- 1 | # Basic Environment Settings 2 | env = "staging" 3 | ecr_names = ["codedevops"] 4 | vpc_config = { 5 | region = "us-east-1" 6 | instance_tenancy = "default" 7 | enable_dns_hostnames = true 8 | enable_dns_support = true 9 | vpc_cidr_block = "10.20.0.0/16" #65,536 IPs 10 | pub_sub1_cidr_block = "10.20.1.0/24" #256 IPs 11 | pub_sub2_cidr_block = "10.20.2.0/24" #256 IPs 12 | private_sub1_cidr_block = "10.20.3.0/24" #256 IPs 13 | private_sub2_cidr_block = "10.20.4.0/24" #256 IPs 14 | } 15 | 16 | cluster_config = { 17 | cluster_name = "codedevops" 18 | cluster_version = "1.29" 19 | } 20 | 21 | 22 | ui_conf = { 23 | cloudfront_default_certificate = null 24 | ssl_support_method = "sni-only" 25 | minimum_protocol_version = "TLSv1.2_2021" 26 | } 27 | -------------------------------------------------------------------------------- /terraform-infra/dynamodb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "photos_metadata" { 2 | name = "PhotosMetadata" 3 | billing_mode = "PROVISIONED" 4 | read_capacity = 5 5 | write_capacity = 5 6 | hash_key = "photo_id" 7 | 8 | attribute { 9 | name = "photo_id" 10 | type = "S" # S denotes String 11 | } 12 | server_side_encryption { 13 | enabled = true 14 | kms_key_arn = aws_kms_key.kms.arn 15 | } 16 | tags = { 17 | Name = "${local.project}-PhotosMetadata" 18 | } 19 | } 20 | 21 | resource "aws_kms_key" "kms" { 22 | description = "KMS key for DynamoDB table encryption" 23 | } -------------------------------------------------------------------------------- /terraform-infra/ecr.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ecr_repository" "foo" { 2 | for_each = toset(var.ecr_names) 3 | name = each.key 4 | image_tag_mutability = "MUTABLE" 5 | 6 | # image_scanning_configuration { 7 | # scan_on_push = true 8 | 9 | # } 10 | #checkov:skip=CKV_AWS_163:ECR image scan on push is not enabled 11 | tags = { 12 | Name = "${local.project}-${var.env}-ecr" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /terraform-infra/eks.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "demo" { 2 | name = "eks-cluster-demo" 3 | 4 | assume_role_policy = < { 33 | url = repo.repository_url 34 | arn = repo.arn 35 | } 36 | } 37 | } 38 | output "vpc_details" { 39 | value = { 40 | id = aws_vpc.vpc.id 41 | cidr_block = aws_vpc.vpc.cidr_block 42 | instance_tenancy = aws_vpc.vpc.instance_tenancy 43 | enable_dns_hostnames = aws_vpc.vpc.enable_dns_hostnames 44 | enable_dns_support = aws_vpc.vpc.enable_dns_support 45 | internet_gateway = aws_internet_gateway.igw.id 46 | allocation_id = aws_eip.nat.id 47 | "private-ap-south-1a" = { 48 | id = aws_subnet.private-ap-south-1a.id 49 | cidr_block = aws_subnet.private-ap-south-1a.cidr_block 50 | availability_zone = aws_subnet.private-ap-south-1a.availability_zone 51 | } 52 | "private-ap-south-1b" = { 53 | id = aws_subnet.private-ap-south-1b.id 54 | cidr_block = aws_subnet.private-ap-south-1b.cidr_block 55 | availability_zone = aws_subnet.private-ap-south-1b.availability_zone 56 | } 57 | "public-ap-south-1a" = { 58 | id = aws_subnet.public-ap-south-1a.id 59 | cidr_block = aws_subnet.public-ap-south-1a.cidr_block 60 | availability_zone = aws_subnet.public-ap-south-1a.availability_zone 61 | } 62 | "public-ap-south-1b" = { 63 | id = aws_subnet.public-ap-south-1b.id 64 | cidr_block = aws_subnet.public-ap-south-1b.cidr_block 65 | availability_zone = aws_subnet.public-ap-south-1b.availability_zone 66 | } 67 | 68 | 69 | } 70 | description = "Details of the main VPC" 71 | } 72 | output "dynamodb_table_name" { 73 | value = aws_dynamodb_table.photos_metadata.name 74 | description = "The name of the DynamoDB table." 75 | } 76 | 77 | output "s3_bucket_name" { 78 | value = module.ui.s3_bucket_arn 79 | description = "The name of the S3 bucket." 80 | } 81 | output "cloudfront_url" { 82 | value = module.ui-cf.cloudfront_distribution_arn 83 | description = "The URL of the CloudFront distribution." 84 | } 85 | 86 | output "acm_arn" { 87 | value = { 88 | ui = module.acm_backend.acm_certificate_arn 89 | cf = module.acm_cf.acm_certificate_arn 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /terraform-infra/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "ap-south-1" 3 | allowed_account_ids = [434605749312] 4 | default_tags { 5 | tags = { 6 | environment = var.env 7 | managedby = "terraform" 8 | } 9 | } 10 | } 11 | provider "aws" { 12 | alias = "us-east-1" 13 | region = "us-east-1" 14 | allowed_account_ids = [434605749312] 15 | default_tags { 16 | tags = { 17 | environment = var.env 18 | managedby = "terraform" 19 | } 20 | } 21 | } 22 | provider "helm" { 23 | kubernetes { 24 | host = aws_eks_cluster.cluster.endpoint 25 | cluster_ca_certificate = base64decode(aws_eks_cluster.cluster.certificate_authority[0].data) 26 | exec { 27 | api_version = "client.authentication.k8s.io/v1beta1" 28 | args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.cluster.id] 29 | command = "aws" 30 | } 31 | } 32 | } 33 | terraform { 34 | required_version = ">= 0.15.0" 35 | required_providers { 36 | aws = { 37 | source = "hashicorp/aws" 38 | version = ">= 4.29.0" 39 | } 40 | random = { 41 | source = "hashicorp/random" 42 | version = ">= 3.6.0" 43 | } 44 | template = { 45 | source = "hashicorp/template" 46 | version = ">= 2.2.0" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /terraform-infra/s3.tf: -------------------------------------------------------------------------------- 1 | module "ui" { 2 | source = "terraform-aws-modules/s3-bucket/aws" 3 | version = "3.3.0" 4 | bucket = "${local.project}-${var.env}-photoapp-ui" 5 | block_public_acls = true 6 | block_public_policy = true 7 | ignore_public_acls = true 8 | restrict_public_buckets = true 9 | attach_policy = true 10 | policy = data.aws_iam_policy_document.s3_policy.json 11 | 12 | server_side_encryption_configuration = { 13 | rule = { 14 | apply_server_side_encryption_by_default = { 15 | sse_algorithm = "AES256" 16 | } 17 | } 18 | } 19 | #problem highlighted by tfsec is not that important to us and that it would be okay to ignore it. We can inform it to tfsec by adding a comment at the top of the resource block where this problem exists. 20 | #tfsec:ignore: 21 | 22 | } 23 | -------------------------------------------------------------------------------- /terraform-infra/variables.tf: -------------------------------------------------------------------------------- 1 | variable "env" { 2 | type = string 3 | description = "The deployment environment name, e.g., 'prod', 'dev', or 'test'." 4 | } 5 | 6 | variable "vpc_config" { 7 | type = any 8 | description = "Configuration parameters for the VPC including subnets, CIDR blocks, and other network-related settings." 9 | } 10 | 11 | variable "cluster_config" { 12 | type = any 13 | description = "Configuration for the cluster, detailing specifics like size, type, and other cluster-related settings." 14 | } 15 | 16 | variable "ui_conf" { 17 | type = any 18 | description = "UI configuration settings, which may include theming, layout, and feature toggles." 19 | } 20 | 21 | ## ECR 22 | variable "ecr_names" { 23 | type = any 24 | description = "Names of the Elastic Container Registry repositories required for the deployment." 25 | } 26 | -------------------------------------------------------------------------------- /terraform-infra/vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "vpc" { 2 | cidr_block = var.vpc_config.vpc_cidr_block 3 | instance_tenancy = var.vpc_config.instance_tenancy 4 | enable_dns_hostnames = var.vpc_config.enable_dns_hostnames 5 | enable_dns_support = var.vpc_config.enable_dns_support 6 | 7 | #tfsec:ignore:aws-ec2-require-vpc-flow-logs-for-all-vpcs 8 | 9 | tags = { 10 | Name = "${local.project}-${var.env}-main" 11 | } 12 | } 13 | resource "aws_internet_gateway" "igw" { 14 | vpc_id = aws_vpc.vpc.id 15 | tags = { 16 | Name = "${local.project}-${var.env}-igw" 17 | } 18 | } 19 | resource "aws_eip" "nat" { 20 | vpc = true 21 | tags = { 22 | Name = "${local.project}-${var.env}-eip" 23 | } 24 | } 25 | 26 | resource "aws_nat_gateway" "nat" { 27 | allocation_id = aws_eip.nat.id 28 | subnet_id = aws_subnet.public-ap-south-1a.id 29 | 30 | tags = { 31 | Name = "${local.project}-${var.env}-nat" 32 | } 33 | 34 | depends_on = [aws_internet_gateway.igw] 35 | } 36 | resource "aws_subnet" "private-ap-south-1a" { 37 | vpc_id = aws_vpc.vpc.id 38 | cidr_block = var.vpc_config.private_sub1_cidr_block 39 | availability_zone = "ap-south-1a" 40 | 41 | tags = { 42 | "Name" = "${local.project}-${var.env}-private-ap-south-1a" 43 | "kubernetes.io/role/internal-elb" = "1" 44 | "kubernetes.io/cluster/${var.cluster_config.cluster_name}" = "owned" 45 | } 46 | } 47 | 48 | resource "aws_subnet" "private-ap-south-1b" { 49 | vpc_id = aws_vpc.vpc.id 50 | cidr_block = var.vpc_config.private_sub2_cidr_block 51 | availability_zone = "ap-south-1b" 52 | tags = { 53 | "Name" = "${local.project}-${var.env}-private-ap-south-1b" 54 | "kubernetes.io/role/internal-elb" = "1" 55 | "kubernetes.io/cluster/${var.cluster_config.cluster_name}" = "owned" 56 | } 57 | } 58 | 59 | resource "aws_subnet" "public-ap-south-1a" { 60 | vpc_id = aws_vpc.vpc.id 61 | cidr_block = var.vpc_config.pub_sub1_cidr_block 62 | availability_zone = "ap-south-1a" 63 | map_public_ip_on_launch = true 64 | 65 | tags = { 66 | "Name" = "${local.project}-${var.env}-public-ap-south-1a" 67 | "kubernetes.io/role/elb" = "1" 68 | "kubernetes.io/cluster/${var.cluster_config.cluster_name}" = "owned" 69 | } 70 | } 71 | 72 | resource "aws_subnet" "public-ap-south-1b" { 73 | vpc_id = aws_vpc.vpc.id 74 | cidr_block = var.vpc_config.pub_sub2_cidr_block 75 | availability_zone = "ap-south-1b" 76 | map_public_ip_on_launch = true 77 | 78 | tags = { 79 | "Name" = "${local.project}-${var.env}-public-ap-south-1b" 80 | "kubernetes.io/role/elb" = "1" 81 | "kubernetes.io/cluster/${var.cluster_config.cluster_name}" = "owned" 82 | } 83 | } 84 | resource "aws_route_table" "private" { 85 | vpc_id = aws_vpc.vpc.id 86 | 87 | route { 88 | cidr_block = "0.0.0.0/0" 89 | nat_gateway_id = aws_nat_gateway.nat.id 90 | } 91 | 92 | tags = { 93 | Name = "${local.project}-${var.env}-private" 94 | } 95 | } 96 | 97 | resource "aws_route_table" "public" { 98 | vpc_id = aws_vpc.vpc.id 99 | route { 100 | cidr_block = "0.0.0.0/0" 101 | gateway_id = aws_internet_gateway.igw.id 102 | } 103 | 104 | tags = { 105 | Name = "${local.project}-${var.env}-public" 106 | } 107 | } 108 | 109 | resource "aws_route_table_association" "private-ap-south-1a" { 110 | subnet_id = aws_subnet.private-ap-south-1a.id 111 | route_table_id = aws_route_table.private.id 112 | } 113 | 114 | resource "aws_route_table_association" "private-ap-south-1b" { 115 | subnet_id = aws_subnet.private-ap-south-1b.id 116 | route_table_id = aws_route_table.private.id 117 | } 118 | 119 | resource "aws_route_table_association" "public-ap-south-1a" { 120 | subnet_id = aws_subnet.public-ap-south-1a.id 121 | route_table_id = aws_route_table.public.id 122 | } 123 | 124 | resource "aws_route_table_association" "public-ap-south-1b" { 125 | subnet_id = aws_subnet.public-ap-south-1b.id 126 | route_table_id = aws_route_table.public.id 127 | } 128 | -------------------------------------------------------------------------------- /terraform-infra/waf.tf: -------------------------------------------------------------------------------- 1 | resource "aws_wafv2_ip_set" "block_ip_set" { 2 | name = "${var.env}-block-ip-set" 3 | scope = "REGIONAL" # Use REGIONAL for ALBs 4 | ip_address_version = "IPV4" 5 | addresses = [ 6 | "106.214.95.140/32" 7 | ] 8 | 9 | description = "IP Set for blocking specific IP addresses" 10 | } 11 | resource "aws_wafv2_web_acl" "main_acl" { 12 | name = "${var.env}-web-acl" 13 | scope = "REGIONAL" 14 | description = "Web ACL with IP blocking rules for the ALB" 15 | 16 | default_action { 17 | allow {} # Default action is to allow requests 18 | } 19 | 20 | rule { 21 | name = "BlockSpecificIPs" 22 | priority = 1 23 | action { 24 | block {} 25 | } 26 | statement { 27 | ip_set_reference_statement { 28 | arn = aws_wafv2_ip_set.block_ip_set.arn 29 | } 30 | } 31 | visibility_config { 32 | cloudwatch_metrics_enabled = true 33 | metric_name = "BlockSpecificIPs" 34 | sampled_requests_enabled = true 35 | } 36 | } 37 | 38 | visibility_config { 39 | cloudwatch_metrics_enabled = true 40 | metric_name = "${var.env}-web-acl" 41 | sampled_requests_enabled = true 42 | } 43 | 44 | tags = { 45 | Name = "${var.env}-WebACL" 46 | } 47 | } 48 | --------------------------------------------------------------------------------