├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── DEVSECOPS_IMPLEMENTATION_README.md ├── Dockerfile ├── Jenkinsfile ├── README.md ├── images ├── login.png ├── test └── transactions.png ├── kubernetes ├── bankapp-deployment.yml ├── bankapp-hpa.yml ├── bankapp-ingress.yml ├── bankapp-namespace.yml ├── bankapp-service.yml ├── cert-issuer.yml ├── configmap.yml ├── ingress-headers-configmap.yaml ├── mysql-deployment.yml ├── mysql-service.yml ├── nginx-deployment.yml ├── nginx-service.yml ├── persistent-volume-claim.yml ├── persistent-volume.yml └── secrets.yml ├── mvnw ├── mvnw.cmd ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── bankapp │ │ │ ├── BankappApplication.java │ │ │ ├── config │ │ │ ├── SecurityConfig.java │ │ │ └── WebConfig.java │ │ │ ├── controller │ │ │ └── BankController.java │ │ │ ├── model │ │ │ ├── Account.java │ │ │ └── Transaction.java │ │ │ ├── repository │ │ │ ├── AccountRepository.java │ │ │ └── TransactionRepository.java │ │ │ └── service │ │ │ └── AccountService.java │ └── resources │ │ ├── application.properties │ │ ├── static │ │ └── mysql │ │ │ └── SQLScript.txt │ │ └── templates │ │ ├── dashboard.html │ │ ├── login.html │ │ ├── register.html │ │ └── transactions.html └── test │ └── java │ └── com │ └── example │ └── bankapp │ └── BankappApplicationTests.java ├── terraform ├── ec2.tf ├── main.tf ├── output.tf ├── terraform.tf └── variable.tf └── vars ├── code_checkout.groovy ├── docker_build.groovy ├── docker_compose.groovy ├── docker_push.groovy ├── owasp_dependency.groovy ├── sonarqube_analysis.groovy ├── sonarqube_code_quality.groovy └── trivy_scan.groovy /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | terraform/.terraform.lock.hcl 35 | terraform/.terraform.tfstate.lock.info 36 | terraform/mega-project-key1 37 | terraform/mega-project-key1.pub 38 | terraform/.terraform/providers/registry.terraform.io/hashicorp/aws/5.82.2/linux_amd64/LICENSE.txt 39 | terraform/.terraform/providers/registry.terraform.io/hashicorp/aws/5.82.2/linux_amd64/terraform-provider-aws_v5.82.2_x5 40 | terraform/terraform.tfstate 41 | terraform/terraform.tfstate.backup 42 | terraform/terraform.tfstate 43 | terraform/terraform.tfstate.backup 44 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip 20 | -------------------------------------------------------------------------------- /DEVSECOPS_IMPLEMENTATION_README.md: -------------------------------------------------------------------------------- 1 | # End-to-End DevSecOps Implementation 2 | 3 | ## **Phase 1: Initial Setup and Deployment** 4 | 5 | ### **Step 1: Launch EC2 (Ubuntu 22.04)** 6 | - Launch an AWS EC2 instance (Ubuntu) with the following specifications: 7 | - Instance type: `t2.large` 8 | - Root volume: `29GB` 9 | - Connect to the instance using SSH. 10 | 11 | --- 12 | 13 | ### **Step 2: Clone the Code** 14 | - Update all the packages and clone the application's code repository onto the EC2 instance: 15 | ```bash 16 | sudo apt-get update 17 | git clone https://github.com/pundir8372/DevOps-mega-project.git 18 | ``` 19 | 20 | --- 21 | 22 | ### **Step 3: Install Docker and Docker Compose** 23 | - Install Docker: 24 | ```bash 25 | sudo apt-get update 26 | sudo apt-get install docker.io -y 27 | sudo usermod -aG docker $USER # Replace $USER with your username 28 | newgrp docker 29 | sudo chmod 777 /var/run/docker.sock 30 | ``` 31 | 32 | - Install Docker Compose: 33 | ```bash 34 | sudo apt-get install docker-compose-v2 35 | ``` 36 | 37 | --- 38 | 39 | ## **Phase 2: Security** 40 | 41 | ### **1. Install SonarQube and Trivy** 42 | - Install **SonarQube** to analyze code for vulnerabilities: 43 | ```bash 44 | docker run -d --name sonar -p 9000:9000 sonarqube:lts-community 45 | ``` 46 | - Access SonarQube at `http://:9000` (default credentials: admin/admin). 47 | 48 | - Install **Trivy** for container vulnerability scanning: 49 | ```bash 50 | sudo apt-get install wget apt-transport-https gnupg lsb-release 51 | wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - 52 | echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list 53 | sudo apt-get update 54 | sudo apt-get install trivy 55 | ``` 56 | - Scan a Docker image with Trivy: 57 | ```bash 58 | trivy image 59 | ``` 60 | 61 | --- 62 | 63 | ### **2. Configure SonarQube** 64 | - Integrate SonarQube with your CI/CD pipeline by adding the server details and authentication token in Jenkins: 65 | 1. Generate a SonarQube token from the SonarQube dashboard. 66 | 2. Add the token to Jenkins: 67 | Go to **Jenkins Dashboard → Manage Jenkins → Credentials → Add Secret Text**. 68 | 69 | --- 70 | 71 | ## **Phase 3: CI/CD Setup** 72 | 73 | ### **1. Install Jenkins** 74 | - Install Jenkins for automation: 75 | ```bash 76 | sudo apt update 77 | sudo apt install fontconfig openjdk-17-jre -y 78 | wget -O /usr/share/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key 79 | echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null 80 | sudo apt-get update 81 | sudo apt-get install jenkins -y 82 | sudo systemctl start jenkins 83 | sudo systemctl enable jenkins 84 | ``` 85 | - Access Jenkins at `http://:8080`. 86 | 87 | --- 88 | 89 | ### **2. Install Necessary Plugins** 90 | - Go to **Manage Jenkins → Plugins → Available Plugins** and install: 91 | - OWASP Dependency-Check 92 | - SonarQube Scanner 93 | - Docker 94 | - Pipeline Stage View 95 | 96 | --- 97 | 98 | ### **3. Add DockerHub Credentials** 99 | - Add DockerHub credentials to Jenkins: 100 | 1. Go to **Manage Jenkins → Manage Credentials → System → Global credentials (unrestricted).** 101 | 2. Click **Add Credentials** and select **Secret text**. 102 | 3. Enter your DockerHub credentials (username and password). 103 | 104 | --- 105 | 106 | ### **4. Set Up Sonar Scanner and Dependency Check** 107 | - Configure tools in Jenkins: 108 | 1. **Sonar Scanner**: 109 | Go to **Manage Jenkins → Global Tool Configuration** and add Sonar Scanner. 110 | 2. **OWASP Dependency-Check**: 111 | Go to **Global Tool Configuration** and add Dependency-Check tool. 112 | 113 | --- 114 | 115 | ## **Phase 4: Automate with CI/CD Pipeline** 116 | - Create a Jenkins pipeline for automation with the following stages: 117 | - Clean workspace. 118 | - Clone code from GitHub. 119 | - Analyze code with SonarQube. 120 | - Perform OWASP Dependency-Check. 121 | - Scan for vulnerabilities with Trivy. 122 | - Build and push Docker images to DockerHub. 123 | - Deploy the application. 124 | 125 | --- 126 | 127 | ### **5. Post-Build Actions** 128 | - Configure Jenkins to send email notifications upon build success or failure using **Email Extension Plugin**. 129 | - Success notifications should include deployment details such as the Git repository, branch name, and Docker image. 130 | - Failure notifications should provide details to debug the issue. 131 | 132 | --- 133 | 134 | ## **Application Deployment** 135 | - Deploy the application using Docker Compose: 136 | ```bash 137 | docker-compose up -d 138 | ``` 139 | - Verify the deployment by accessing the application's endpoint in a browser. 140 | 141 | --- 142 | 143 | **Phase 4: Cleanup** 144 | 145 | 1. **Cleanup AWS EC2 Instances:** 146 | - Terminate AWS EC2 instances that are no longer needed. 147 | 148 | 149 | ## **Conclusion** 150 | This project involves setting up a secure, automated CI/CD pipeline for deploying a Spring Boot application using Jenkins, SonarQube, Trivy, and Docker. By following the steps outlined above, you can ensure high code quality and secure deployments in your DevSecOps workflow. 151 | 152 | 153 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #---------------------------------- 2 | # Stage 1 3 | #---------------------------------- 4 | 5 | # Import docker image with maven installed 6 | FROM maven:3.8.3-openjdk-17 as builder 7 | 8 | # Set working directory 9 | WORKDIR /app 10 | 11 | # Copy source code from local to container 12 | COPY . /app 13 | 14 | # Build application and skip test cases 15 | RUN mvn clean install -DskipTests=true 16 | 17 | #-------------------------------------- 18 | # Stage 2 19 | #-------------------------------------- 20 | 21 | # Import small size java image 22 | FROM openjdk:17-alpine as deployer 23 | 24 | # Copy build from stage 1 (builder) 25 | COPY --from=builder /app/target/*.jar /app/target/bankapp.jar 26 | 27 | # Expose application port 28 | EXPOSE 8080 29 | 30 | # Start the application 31 | ENTRYPOINT ["java", "-jar", "/app/target/bankapp.jar"] 32 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | @Library('Shared') _ // Referencing your shared library 2 | parameters { 3 | string(name: 'DOCKER_TAG', defaultValue: 'v1', description: 'Setting docker image for latest push') 4 | } 5 | 6 | pipeline { 7 | agent any 8 | environment { 9 | SONAR_HOME = tool "Sonar" 10 | DOCKER_IMAGE = "bankapp" 11 | GIT_REPO = "https://github.com/Amitabh-DevOps/DevOps-mega-project.git" 12 | GIT_BRANCH = "project" 13 | } 14 | stages { 15 | stage("Clean Workspace") { 16 | steps { 17 | cleanWs() 18 | } 19 | } 20 | stage("Code Clone") { 21 | steps { 22 | script { 23 | code_checkout("https://github.com/Amitabh-DevOps/DevOps-mega-project.git", "project") 24 | } 25 | } 26 | } 27 | stage("SonarQube Quality Analysis") { 28 | steps { 29 | sonarqube_analysis('Sonar', 'bankapp', 'bankapp') 30 | } 31 | } 32 | // stage("OWASP Dependency Check") { 33 | // steps { 34 | // owasp_dependency() 35 | // } 36 | // } 37 | stage("Sonar Quality Gate Scan") { 38 | steps { 39 | sonarqube_code_quality() 40 | } 41 | } 42 | stage("Trivy File System Scan") { 43 | steps { 44 | trivy_scan() 45 | } 46 | } 47 | stage("Docker Build") { 48 | steps { 49 | docker_build("bankapp", "${params.DOCKER_TAG}", "amitabhdevops") 50 | } 51 | } 52 | stage("Push to Docker Hub") { 53 | steps { 54 | docker_push("bankapp", "${params.DOCKER_TAG}", "amitabhdevops") 55 | } 56 | } 57 | } 58 | post { 59 | success { 60 | echo "Pipeline completed successfully!" 61 | emailext ( 62 | subject: "SUCCESS: Jenkins Pipeline for ${DOCKER_IMAGE}", 63 | body: """ 64 |
65 |

🎉 Pipeline Execution: SUCCESS 🎉

66 |

67 | Hello Team, 68 |

69 |

70 | The Jenkins pipeline for ${DOCKER_IMAGE} completed successfully! 71 |

72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
DetailsValues
Git Repository${GIT_REPO}
Branch${GIT_BRANCH}
86 |

87 | Visit Pipeline Logs for more details. 88 |

89 |

90 | Thanks,
91 | Jenkins 92 |

93 |
94 | """, 95 | to: "amitabhdevops2024@gmail.com", 96 | from: "jenkins@example.com", 97 | mimeType: 'text/html', 98 | attachmentsPattern: '**/table-report.html' // This will pick up the report from the workspace 99 | ) 100 | } 101 | failure { 102 | echo "Pipeline failed. Please check the logs." 103 | emailext ( 104 | subject: "FAILURE: Jenkins Pipeline for ${DOCKER_IMAGE}", 105 | body: """ 106 |
107 |

🚨 Pipeline Execution: FAILURE 🚨

108 |

109 | Hello Team, 110 |

111 |

112 | Unfortunately, the Jenkins pipeline for ${DOCKER_IMAGE} has failed. 113 |

114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
DetailsValues
Git Repository${GIT_REPO}
Branch${GIT_BRANCH}
128 |

129 | Visit Pipeline Logs for more details. 130 |

131 |

132 | Thanks,
133 | Jenkins 134 |

135 |
136 | """, 137 | to: "amitabhdevops2024@gmail.com", 138 | from: "jenkins@example.com", 139 | mimeType: 'text/html', 140 | attachmentsPattern: '**/table-report.html' // This will pick up the report from the workspace 141 | ) 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevSecOps Mega Project-Springboot Bankapp 2 | 3 | ## End-to-End Bank Application Deployment using DevSecOps on AWS EKS 4 | - This is a multi-tier bank an application written in Java (Springboot). 5 | 6 | 7 | ## Step 1: Create an IAM User with Administrator Permissions 8 | 9 | 1. **Login to AWS Console:** Open the [AWS Management Console](https://aws.amazon.com/console/). 10 | 11 | 2. **Navigate to IAM:** Go to the Identity and Access Management (IAM) service. 12 | 13 | 3. **Create User:** 14 | 15 | * Click on **Users** > **Add Users**. 16 | 17 | * Enter a username, e.g., `mega-project-user`. 18 | 19 | * Select **Programmatic access** to generate an access key. 20 | 21 | 4. **Attach Permissions:** Attach the policy `AdministratorAccess`. 22 | 23 | 5. **Generate Access Key:** 24 | 25 | * In the Security tab, create an access key. 26 | 27 | * Save the **Access Key ID** and **Secret Access Key** securely. 28 | 29 | 30 | --- 31 | 32 | ## Step 2: Setting Up Visual Studio Code (VSCode) 33 | 34 | ### Adding Linux Terminal in VSCode (Windows Users) 35 | 36 | If you are on Windows, refer to this [guide](https://amitabhdevops.hashnode.dev/a-step-by-step-guide-to-adding-ubuntu-wsl-in-vs-code-terminal) to integrate an Ubuntu terminal in VSCode for seamless project execution. 37 | 38 | --- 39 | 40 | ## Step 3: Fork and Clone the Project Repository 41 | 42 | 1. **Fork the Repository:** 43 | 44 | * Open the repository [DevOps Mega Project](https://github.com/Amitabh-DevOps/DevOps-mega-project) on GitHub. 45 | 46 | * Click **Fork** to create a copy in your GitHub account. 47 | 48 | 2. **Clone the Repository:** 49 | 50 | * Open the terminal in VSCode. 51 | 52 | * Clone the repository: 53 | 54 | ```bash 55 | git clone https://github.com//DevOps-mega-project.git 56 | ``` 57 | 58 | * Switch to the project branch: 59 | 60 | ```bash 61 | git checkout project 62 | ``` 63 | 64 | 65 | --- 66 | 67 | ## Step 4: Install AWS CLI and Configure It 68 | 69 | 1. **Install AWS CLI:** 70 | 71 | ```bash 72 | sudo apt update 73 | sudo apt install awscli -y 74 | ``` 75 | 76 | 2. **Configure AWS CLI:** 77 | 78 | ```bash 79 | aws configure 80 | ``` 81 | 82 | * Enter the Access Key ID and Secret Access Key. 83 | 84 | * Specify your preferred AWS region (e.g., `eu-west-1`). 85 | 86 | 87 | --- 88 | 89 | ## Step 5: Build and Push Docker Image 90 | 91 | 1. **Build the Docker Image:** 92 | 93 | ```bash 94 | docker build -t /bankapp:latest . 95 | ``` 96 | 97 | 2. **Login to DockerHub:** 98 | 99 | ```bash 100 | docker login 101 | ``` 102 | 103 | 3. **Push the Image to DockerHub:** 104 | 105 | ```bash 106 | docker push /bankapp:latest 107 | ``` 108 | 109 | 4. **Update Deployment File:** 110 | 111 | * Update the `bankapp-deployment.yml` file to use your Docker image. 112 | 113 | 114 | --- 115 | 116 | ## Step 6: Set Up Infrastructure with Terraform 117 | 118 | 1. **Generate SSH Key:** 119 | 120 | ```bash 121 | ssh-keygen 122 | ``` 123 | 124 | * Enter a name as `mega-project-key`. 125 | 126 | * Update the `variable.tf` file with the key name , if you have entered another key name. 127 | 128 | 2. **Initialize Terraform**: Run the initialization command to download provider plugins and prepare your working directory: 129 | 130 | ```bash 131 | terraform init 132 | ``` 133 | 134 | 3. **Plan Terraform Execution**: Preview the resources Terraform will create: 135 | 136 | ```bash 137 | terraform plan 138 | ``` 139 | 140 | 4. **Apply Terraform Configuration**: Deploy the infrastructure using: 141 | 142 | ```bash 143 | terraform apply --auto-approve 144 | ``` 145 | 146 | 5. **Connect to EC2 Instance**: Once the infrastructure is created, connect to your EC2 instance: 147 | 148 | ```bash 149 | ssh -i mega-project-key.pem ubuntu@ 150 | ``` 151 | 152 | 153 | --- 154 | 155 | ## Step 7: Install Essential DevOps Tools on created Instance. 156 | 157 | Follow this [guide](https://amitabhdevops.hashnode.dev/how-to-install-essential-devops-tools-on-ubuntulinux) to install: 158 | 159 | * AWS CLI 160 | 161 | * kubectl 162 | 163 | * eksctl 164 | 165 | 166 | ### Install eksctl: 167 | 168 | ```bash 169 | ARCH=amd64 170 | PLATFORM=$(uname -s)_$ARCH 171 | 172 | curl -sLO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$PLATFORM.tar.gz" 173 | tar -xzf eksctl_$PLATFORM.tar.gz -C /tmp && rm eksctl_$PLATFORM.tar.gz 174 | sudo mv /tmp/eksctl /usr/local/bin 175 | ``` 176 | 177 | --- 178 | 179 | ## Step 8: Create Kubernetes Cluster 180 | 181 | 1. **Create EKS Cluster:** 182 | 183 | ```bash 184 | eksctl create cluster --name bankapp-cluster --region eu-west-1 --without-nodegroup 185 | ``` 186 | 187 | 2. **Verify Cluster Creation:** 188 | 189 | ```bash 190 | eksctl get clusters 191 | ``` 192 | 193 | 3. **Associate IAM OIDC Provider:** 194 | 195 | ```bash 196 | eksctl utils associate-iam-oidc-provider --region=eu-west-1 --cluster=bankapp-cluster --approve 197 | ``` 198 | 199 | 4. **Create Node Group:** 200 | 201 | ```bash 202 | eksctl create nodegroup \ 203 | --cluster=bankapp-cluster \ 204 | --region=eu-west-1 \ 205 | --name=bankapp-ng \ 206 | --node-type=t2.medium \ 207 | --nodes=2 \ 208 | --nodes-min=2 \ 209 | --nodes-max=2 \ 210 | --node-volume-size=15 \ 211 | --ssh-access \ 212 | --ssh-public-key=mega-project-key 213 | ``` 214 | 215 | 216 | --- 217 | 218 | ## Step 9: Set Up ArgoCD 219 | 220 | #### Step 1: Create a Namespace for ArgoCD 221 | 222 | To ensure ArgoCD has its own isolated environment within your Kubernetes cluster, create a dedicated namespace. 223 | 224 | ```bash 225 | kubectl create ns argocd 226 | ``` 227 | 228 | --- 229 | 230 | #### Step 2: Install ArgoCD 231 | 232 | Use the official installation manifest from ArgoCD’s GitHub repository to deploy it to your cluster. 233 | 234 | ```bash 235 | kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml 236 | ``` 237 | 238 | This command installs all required ArgoCD components in the `argocd` namespace. 239 | 240 | --- 241 | 242 | #### Step 3: Install ArgoCD CLI 243 | 244 | To interact with the ArgoCD server from your local machine or a terminal, install the ArgoCD command-line interface (CLI). 245 | 246 | ```bash 247 | curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 248 | sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd 249 | rm argocd-linux-amd64 250 | ``` 251 | 252 | Once installed, verify the installation using: 253 | 254 | ```bash 255 | argocd version 256 | ``` 257 | 258 | --- 259 | 260 | #### Step 4: Check ArgoCD Services 261 | 262 | To confirm that ArgoCD services are running: 263 | 264 | ```bash 265 | kubectl get svc -n argocd 266 | ``` 267 | 268 | This lists all services in the `argocd` namespace. Take note of the `argocd-server` service, as it will be exposed in the next step. 269 | 270 | --- 271 | 272 | #### Step 5: Expose ArgoCD Server Using NodePort 273 | 274 | By default, the `argocd-server` service is of type `ClusterIP`, which makes it accessible only within the cluster. Change it to `NodePort` to expose it externally. 275 | 276 | ```bash 277 | kubectl patch svc argocd-server -n argocd -p '{"spec":{"type": "NodePort"}}' 278 | ``` 279 | 280 | Retrieve the updated service information to identify the assigned NodePort: 281 | 282 | ```bash 283 | kubectl get svc -n argocd 284 | ``` 285 | 286 | Note the port in the `PORT(S)` column (e.g., `30529`). 287 | 288 | --- 289 | 290 | #### Step 6: Configure AWS Inbound Rule for NodePort 291 | 292 | If your Kubernetes cluster is hosted on AWS, ensure that the assigned NodePort is accessible by adding an inbound rule to your security group. Allow traffic on this port from the internet to the worker node(s). 293 | 294 | --- 295 | 296 | #### Step 7: Access ArgoCD Web UI 297 | 298 | With the NodePort and the worker node’s public IP, access the ArgoCD web UI: 299 | 300 | ```bash 301 | http://: 302 | ``` 303 | 304 | ![image](https://github.com/user-attachments/assets/32222a1f-3aea-450b-a7e5-0f44b34702ed) 305 | 306 | 307 | For the initial login: 308 | 309 | * **Username:** `admin` 310 | 311 | * **Password:** Retrieve using the following command: 312 | 313 | 314 | ```bash 315 | kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d 316 | ``` 317 | 318 | Change the password after logging in by navigating to the user info section in the ArgoCD UI. 319 | 320 | --- 321 | 322 | #### Step 8: Log In to ArgoCD via CLI 323 | 324 | To log in from the CLI, use the public IP and NodePort: 325 | 326 | ```bash 327 | argocd login : --username admin 328 | ``` 329 | 330 | For example: 331 | 332 | ```bash 333 | argocd login 54.154.41.147:30529 --username admin 334 | ``` 335 | 336 | --- 337 | 338 | #### Step 9: Check ArgoCD Cluster Configuration 339 | 340 | To view the cluster configurations managed by ArgoCD: 341 | 342 | ```bash 343 | argocd cluster list 344 | ``` 345 | 346 | --- 347 | 348 | #### Step 10: Add a Cluster to ArgoCD 349 | 350 | If your cluster is not already added, first identify its context: 351 | 352 | ```bash 353 | kubectl config get-contexts 354 | ``` 355 | 356 | Then, add the desired cluster to ArgoCD. Replace the placeholders with your actual cluster context and name: 357 | 358 | ```bash 359 | argocd cluster add --name 360 | ``` 361 | 362 | For example: 363 | 364 | ```bash 365 | argocd cluster add mega-project-user@bankapp-cluster.eu-west-1.eksctl.io --name bankapp-cluster 366 | ``` 367 | 368 | #### Step 11: Add Project Repository in ArgoCD UI 369 | 370 | To integrate your Git repository with ArgoCD: 371 | 372 | 1. Navigate to **Settings** > **Repositories** in the ArgoCD UI. 373 | 374 | 2. Click on **Connect Repo** and provide the appropriate repository URL. 375 | 376 | 3. Select the connection method as HTTPS. If the repository is private: 377 | 378 | * Enter your username and password to authenticate. 379 | 380 | * Otherwise, skip the authentication step for public repositories. 381 | 382 | 4. Choose the default project (or any specific project, if configured) and complete the setup. 383 | 384 | 385 | Once connected, your repository will be ready for deploying applications via ArgoCD. 386 | 387 | --- 388 | 389 | ## Step 10: Installing Helm, Ingress Controller, and Setting Up Metrics for HPA in Kubernetes 390 | 391 | ### 1\. Install Helm 392 | 393 | **Helm** is a powerful Kubernetes package manager that simplifies the deployment and management of applications within your Kubernetes clusters. To get started, follow the steps below to install Helm on your local system: 394 | 395 | ```bash 396 | # Download the Helm installation script 397 | curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 398 | 399 | # Change script permissions to make it executable 400 | chmod 700 get_helm.sh 401 | 402 | # Run the installation script 403 | ./get_helm.sh 404 | ``` 405 | 406 | After running the script, Helm will be installed, and you can start using it to deploy applications to your Kubernetes cluster. 407 | 408 | --- 409 | 410 | ### 2\. Install Ingress Controller Using Helm 411 | 412 | An **Ingress Controller** is necessary to manage external HTTP/HTTPS access to your services in Kubernetes. In this step, we will install the NGINX Ingress Controller using Helm. 413 | 414 | To install the NGINX Ingress Controller, execute the following commands: 415 | 416 | ```bash 417 | # Add the NGINX Ingress controller Helm repository 418 | helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx 419 | 420 | # Update the Helm repository to ensure you have the latest charts 421 | helm repo update 422 | 423 | # Install the ingress-nginx controller in the ingress-nginx namespace 424 | helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace 425 | ``` 426 | 427 | This command installs the NGINX Ingress controller into your Kubernetes cluster, creating a new namespace called `ingress-nginx`. This Ingress controller will handle routing and load balancing for your services. 428 | 429 | --- 430 | 431 | ### 3\. Apply Metrics Server for HPA 432 | 433 | To enable **Horizontal Pod Autoscaling (HPA)** in your Kubernetes cluster, the **metrics-server** is required to collect resource usage data like CPU and memory from the pods. HPA scales your application based on these metrics. 434 | 435 | Run the following command to apply the **metrics-server**: 436 | 437 | ```bash 438 | # Install metrics-server to collect resource usage metrics 439 | kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml 440 | ``` 441 | 442 | Once installed, the metrics-server will start collecting data from your Kubernetes nodes and pods, enabling you to configure HPA based on these metrics. 443 | 444 | --- 445 | 446 | ### 4\. Install Cert-Manager for SSL/TLS Certificates 447 | 448 | For securing your application with **HTTPS** using your custom domain name, you need to generate SSL/TLS certificates. **Cert-Manager** is a Kubernetes tool that automates the management and issuance of these certificates. 449 | 450 | To install Cert-Manager, use the following command: 451 | 452 | ```bash 453 | # Apply Cert-Manager components to your cluster 454 | kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml 455 | ``` 456 | 457 | Once installed, Cert-Manager will be responsible for automatically issuing and renewing SSL/TLS certificates for your services. You can then configure Cert-Manager to issue a certificate for your application and configure HTTPS with your domain. 458 | 459 | --- 460 | 461 | ## Step 11: Creating an Application on ArgoCD 462 | 463 | ### 1\. General Section 464 | 465 | * **Application Name**: Choose a name for your application. 466 | 467 | * **Project Name**: Select **default**. 468 | 469 | * **Sync Policy**: Choose **Automatic**. 470 | 471 | * Enable **Prune Resources** and **Self-Heal**. 472 | 473 | * Check **Auto Create Namespace**. 474 | 475 | 476 | --- 477 | 478 | ### 2\. Source Section 479 | 480 | * **Repo URL**: Enter the URL of your Git repository. 481 | 482 | * **Revision**: Select the branch (e.g., `main`). 483 | 484 | * **Path**: Specify the directory containing your Kubernetes manifests (e.g., `k8s`). 485 | 486 | 487 | --- 488 | 489 | ### 3\. Destination Section 490 | 491 | * **Cluster**: Select your desired cluster. 492 | 493 | * **Namespace**: Use `bankapp-namespace`. 494 | 495 | 496 | --- 497 | 498 | ### 4\. Create the Application 499 | 500 | Click **Create** to finish the setup and deploy your application. 501 | 502 | ![image](https://github.com/user-attachments/assets/c264ccbc-9ba7-40e1-8925-47e48ec65b26) 503 | 504 | 505 | ![image](https://github.com/user-attachments/assets/dbabd258-d314-4648-b17a-f17bdb1ec55d) 506 | 507 | 508 | --- 509 | 510 | ## Step 12: Exposing the Application via Ingress or NodePort 511 | 512 | In this step, we will walk through two options to expose your application to the outside world: one using an **ALB** (Application Load Balancer) with a CNAME record and the other using **NodePort** if you don't have a domain. 513 | 514 | --- 515 | 516 | ### Option 1: Expose via ALB and CNAME 517 | 518 | 1. Run the following command to get the **ALB External-IP** of the `ingress-nginx-controller`: 519 | 520 | ```bash 521 | kubectl get svc -n ingress-nginx 522 | ``` 523 | 524 | 2. Copy the **External-IP** from the output and create a **CNAME record** on your domain. For example, use [`amitabh.letsdeployit.com`](http://amitabh.letsdeployit.com) as your domain, and replace it in the `bankapp-ingress.yml` file with your domain name, take a reference of below image: 525 | 526 | 1. ![image](https://github.com/user-attachments/assets/117c42ad-025c-4ad7-af88-064ddbfc86b4) 527 | 528 | 529 | 3. After updating the `bankapp-ingress.yml` file, sync the application in **ArgoCD**. 530 | 531 | 4. Once synchronized, open your browser and access the application via your domain (e.g., [`amitabh.letsdeployit.com`](http://amitabh.letsdeployit.com)). 532 | 533 | 1. ![image](https://github.com/user-attachments/assets/8c7076dc-2908-45a9-a920-86ff281e2a4b) 534 | 535 | 536 | 2. ![image](https://github.com/user-attachments/assets/ab5e3652-5296-4744-bc38-1ec2fd7a32a7) 537 | 538 | 539 | 540 | --- 541 | 542 | ### Option 2: Expose via NodePort 543 | 544 | If you don't have a domain, you can expose the service using **NodePort**. 545 | 546 | 1. Before patching, check the existing services in the `bankapp-namespace`: 547 | 548 | ```bash 549 | kubectl get svc -n bankapp-namespace 550 | ``` 551 | 552 | 2. Patch the **bankapp-service** to expose it via **NodePort**: 553 | 554 | ```bash 555 | kubectl patch svc bankapp-service -n bankapp-namespace -p '{"spec": {"type": "NodePort"}}' 556 | ``` 557 | 558 | 3. After patching, check the service again to get the **NodePort**: 559 | 560 | ```bash 561 | kubectl get svc -n bankapp-namespace 562 | ``` 563 | 564 | 4. Now, access your application in the browser using the URL format: `http://:`. 565 | 566 | ![image](https://github.com/user-attachments/assets/1414aff3-7137-4c3c-b043-f63d964eee54) 567 | 568 | 569 | ![image](https://github.com/user-attachments/assets/bc9fff7e-5aee-40f5-963f-5683e9dc1a8f) 570 | 571 | 572 | 573 | --- 574 | 575 | ## Step 13: Setting Up Jenkins for Continuous Integration. 576 | 577 | ### 1\. Install Jenkins on the Master Node 578 | 579 | Install **Jenkins** on the master node by following this blog: [How to Install Essential DevOps Tools on Ubuntu Linux](https://amitabhdevops.hashnode.dev/how-to-install-essential-devops-tools-on-ubuntulinux). 580 | 581 | After installation, open port **8080** on the master node and access Jenkins in your browser: 582 | 583 | ```bash 584 | http://:8080 585 | ``` 586 | 587 | Complete the Jenkins setup by following the on-screen instructions to configure the admin username and password. 588 | 589 | --- 590 | 591 | ### 2\. Install Docker and Configure User Permissions 592 | 593 | To integrate Jenkins with Docker, you need to install **Docker** and add both the current user and the **Jenkins** user to the Docker group: 594 | 595 | 1. Install Docker (if not already installed). 596 | 597 | 2. Add the current user to the Docker group: 598 | 599 | ```bash 600 | sudo usermod -aG docker $USER && newgrp docker 601 | ``` 602 | 603 | 3. Add the **Jenkins** user to the Docker group: 604 | 605 | ```bash 606 | sudo usermod -aG docker jenkins 607 | ``` 608 | 609 | 4. Restart Jenkins: 610 | 611 | ```bash 612 | sudo systemctl restart jenkins 613 | ``` 614 | 615 | 616 | --- 617 | 618 | ### 3\. Add DockerHub Credentials 619 | 620 | Add your **DockerHub** credentials to Jenkins. You can refer to this blog for detailed steps: [Django Notes App using Jenkins CI/CD](https://amitabhdevops.hashnode.dev/django-notes-app-using-jenkins-cicd#heading-step-12-set-up-docker-hub-credentials-in-jenkins). 621 | 622 | --- 623 | 624 | ### 4\. Add GitHub Credentials 625 | 626 | Add **GitHub** credentials to Jenkins as well to enable seamless integration with your GitHub repository. 627 | 628 | --- 629 | 630 | ### 5\. Set Up Webhook for Continuous Integration 631 | 632 | To automatically trigger Jenkins builds on changes in your GitHub repository, set up a webhook. Follow the instructions in this blog: [Set Up Webhooks for Automatic Deployment](https://amitabhdevops.hashnode.dev/django-notes-app-using-jenkins-cicd#heading-step-13-set-up-webhooks-for-automatic-deployment). 633 | 634 | --- 635 | 636 | ### 6\. Create a Jenkins Pipeline Job 637 | 638 | Create a **Jenkins Pipeline** job using the reference in this blog: [Create a Jenkins Pipeline Job](https://amitabhdevops.hashnode.dev/django-notes-app-using-jenkins-cicd#heading-step-10-create-a-jenkins-pipeline-job). 639 | 640 | While creating the job, ensure that you check the box for **This project is parameterized** to allow dynamic configuration during the build. 641 | 642 | --- 643 | 644 | ### 7\. Build the Pipeline 645 | 646 | Once everything is set up, trigger the pipeline build by selecting **Build with Parameters**. Enter the required parameters and start the build process. Monitor the build logs for any errors. If any issues arise, resolve them. 647 | 648 | * Check the **Docker Hub** for the tagged images after the build. 649 | 650 | * Ensure that the **bankapp-deployment** is using the correct image tag from **Docker Hub**. take a reference of below image 651 | 652 | ![image](https://github.com/user-attachments/assets/4600f18f-c57d-4e40-b517-4d9507f09a0b) 653 | 654 | 655 | 656 | * ![image](https://github.com/user-attachments/assets/c1db5a21-1f22-49c6-b674-5604efb8dc2f) 657 | 658 | 659 | 660 | ![image](https://github.com/user-attachments/assets/cfd5988d-471d-481d-9bf9-f0581c4b7d98) 661 | 662 | 663 | ![image](https://github.com/user-attachments/assets/b0c815ec-9960-443d-95e7-69511b462ee6) 664 | 665 | 666 | ![image](https://github.com/user-attachments/assets/4135926b-b89b-4202-813c-3ecf387e0475) 667 | 668 | 669 | ![image](https://github.com/user-attachments/assets/fe09aa64-a777-4a8e-9da4-b999a3734119) 670 | 671 | 672 | --- 673 | 674 | ## Step 14: Setting Up Observability with Prometheus and Grafana 675 | 676 | ### 1\. Add Prometheus Helm Repository 677 | 678 | Start by adding the **Prometheus** Helm repository: 679 | 680 | ```bash 681 | helm repo add prometheus-community https://prometheus-community.github.io/helm-charts 682 | ``` 683 | 684 | --- 685 | 686 | ### 2\. Create the Prometheus Namespace 687 | 688 | Create a dedicated namespace for Prometheus: 689 | 690 | ```bash 691 | kubectl create namespace prometheus 692 | ``` 693 | 694 | --- 695 | 696 | ### 3\. Install Prometheus 697 | 698 | Install the **Prometheus** and **Grafana** stack using Helm in the `prometheus` namespace: 699 | 700 | ```bash 701 | helm install stable prometheus-community/kube-prometheus-stack -n prometheus 702 | ``` 703 | 704 | --- 705 | 706 | ### 4\. Get Services in the Prometheus Namespace 707 | 708 | To view the services running in the `prometheus` namespace, use the following command: 709 | 710 | ```bash 711 | kubectl get svc -n prometheus 712 | ``` 713 | 714 | --- 715 | 716 | ### 5\. Expose Grafana via NodePort 717 | 718 | Expose **Grafana** through **NodePort** by patching the service: 719 | 720 | ```bash 721 | kubectl patch svc stable-grafana -n prometheus -p '{"spec": {"type": "NodePort"}}' 722 | ``` 723 | 724 | Run the following command again to get the **NodePort** and open it in your browser using the **Master Node's Public IP**: 725 | 726 | ```bash 727 | kubectl get svc -n prometheus 728 | ``` 729 | 730 | --- 731 | 732 | ### 6\. Access Grafana 733 | 734 | To access **Grafana**, use the **admin** username and retrieve the password by running the following command: 735 | 736 | ```bash 737 | kubectl get secret --namespace prometheus stable-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo 738 | ``` 739 | 740 | --- 741 | 742 | ### 7\. Monitoring Your Application 743 | 744 | Now that **Prometheus** and **Grafana** are set up, you can use **Grafana** to monitor your application metrics. Grafana will pull metrics from **Prometheus**, and you can create dashboards to visualize various aspects of your application’s performance. 745 | 746 | ![image](https://github.com/user-attachments/assets/76e48681-ee88-4c10-a231-60ecbeab836c) 747 | 748 | 749 | ![image](https://github.com/user-attachments/assets/0bea99b0-1316-4076-b823-c4dc5fb4a180) 750 | 751 | 752 | ![image](https://github.com/user-attachments/assets/c06f10e7-9a4f-420b-8586-c172a44b1a96) 753 | 754 | 755 | ![image](https://github.com/user-attachments/assets/e0fb4173-00aa-479d-b1ad-9d36c42c7ee4) 756 | 757 | 758 | --- 759 | 760 | ## Conclusion 761 | 762 | In conclusion, your DevSecOps Mega Project showcases a well-structured and automated pipeline using industry-standard tools. You've effectively integrated AWS, Docker, Kubernetes (EKS), Helm, and ArgoCD for deployment automation. By leveraging Terraform for infrastructure as code and implementing security best practices like IAM roles, SSL certificates, and Horizontal Pod Autoscaling, your setup ensures a secure, scalable, and efficient environment. The project demonstrates strong knowledge in cloud infrastructure, containerization, and CI/CD practices, positioning you well for real-world DevSecOps implementation. 763 | 764 | --- 765 | 766 | # Project ended here. 767 | 768 | below is for opensource opportunity for contrubutors. 769 | 770 | --- 771 | 772 | ## Open Source Opportunity: Contribute and Win Free Gift Hampers from TrainWithShubham 773 | 774 | We are thrilled to announce an exciting **open-source opportunity** for contributors to this project. As a token of appreciation, contributors will receive **Free Gift Hampers** from **TrainWithShubham**. The hampers may include a variety of items to make your contribution even more rewarding. 775 | 776 | ## How You Can Contribute 777 | 778 | We are looking for contributions in the following areas: 779 | 780 | --- 781 | 782 | ### 1\. **Solve the Application Login Issue After Domain Mapping** 783 | 784 | After mapping the domain to the application, there may be issues with user login. If you have experience in troubleshooting or resolving login issues post-domain mapping, we encourage you to contribute a solution. Possible areas for improvement include authentication, DNS configurations, or session management. 785 | 786 | ### Resolved: 787 | 788 | https://github.com/user-attachments/assets/4baf7031-5744-40c5-8035-10e4d1cc4457 789 | 790 | 791 | --- 792 | 793 | ### 2\. **Add the Security (Sec) Part of DevSecOps to the Project** 794 | 795 | Security should be a priority for any application. By incorporating **DevSecOps** practices into the project, we can significantly improve its security. Contributions may involve: 796 | 797 | * Implementing automated security scans within the CI/CD pipeline. 798 | 799 | * Integrating tools like **OWASP ZAP**, **Trivy**, or **SonarQube** for vulnerability assessments. 800 | 801 | * Securing Kubernetes, Docker, and application infrastructure. 802 | 803 | 804 | --- 805 | 806 | ### 3\. **Further Improvements** 807 | 808 | We are always looking for ways to improve the project. If you have ideas for additional features or optimizations, we would love to see them: 809 | 810 | * Enhancing performance and scalability. 811 | 812 | * Improving monitoring and logging capabilities. 813 | 814 | * Streamlining deployment and automation processes. 815 | 816 | * Improving user experience and interface design. 817 | 818 | 819 | --- 820 | 821 | ## How to Contribute 822 | 823 | To contribute, simply **fork the repository**, make your changes, and submit a **pull request (PR)**. If your contribution is accepted, you will receive a **Free Gift Hamper** from **TrainWithShubham** as a thank-you for your efforts. 824 | 825 | --- 826 | 827 | ## Join the Open-Source Community 828 | 829 | We welcome all developers, whether beginners or experienced, to join the open-source community and contribute to this project. Let’s work together to make this project even better! 830 | 831 | We look forward to your innovative contributions, and remember, great work deserves great rewards! 832 | -------------------------------------------------------------------------------- /images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amitabh-DevOps/DevOps-mega-project/cffede80d1413dfad5d170c6154f05fa5a355a0e/images/login.png -------------------------------------------------------------------------------- /images/test: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /images/transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amitabh-DevOps/DevOps-mega-project/cffede80d1413dfad5d170c6154f05fa5a355a0e/images/transactions.png -------------------------------------------------------------------------------- /kubernetes/bankapp-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: bankapp 5 | namespace: bankapp-namespace 6 | labels: 7 | app: bankapp 8 | spec: 9 | replicas: 2 10 | selector: 11 | matchLabels: 12 | app: bankapp 13 | template: 14 | metadata: 15 | labels: 16 | app: bankapp 17 | spec: 18 | containers: 19 | - name: bankapp 20 | image: snehcreate/bankappv3 21 | ports: 22 | - containerPort: 8080 23 | resources: 24 | limits: 25 | memory: "1Gi" 26 | cpu: "500m" 27 | requests: 28 | memory: "512Mi" 29 | cpu: "250m" 30 | env: 31 | - name: SPRING_DATASOURCE_URL 32 | valueFrom: 33 | configMapKeyRef: 34 | name: bankapp-config 35 | key: SPRING_DATASOURCE_URL 36 | - name: SPRING_DATASOURCE_USERNAME 37 | valueFrom: 38 | configMapKeyRef: 39 | name: bankapp-config 40 | key: SPRING_DATASOURCE_USERNAME 41 | - name: MYSQL_DATABASE 42 | valueFrom: 43 | configMapKeyRef: 44 | name: bankapp-config 45 | key: MYSQL_DATABASE 46 | - name: SPRING_DATASOURCE_PASSWORD 47 | valueFrom: 48 | secretKeyRef: 49 | name: mysql-secret 50 | key: SPRING_DATASOURCE_PASSWORD 51 | -------------------------------------------------------------------------------- /kubernetes/bankapp-hpa.yml: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v2 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: bankapp-hpa 5 | namespace: bankapp-namespace 6 | spec: 7 | scaleTargetRef: 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | name: bankapp 11 | minReplicas: 1 12 | maxReplicas: 5 13 | metrics: 14 | - type: Resource 15 | resource: 16 | name: cpu 17 | target: 18 | type: Utilization 19 | averageUtilization: 40 20 | -------------------------------------------------------------------------------- /kubernetes/bankapp-ingress.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: bankapp-ingress 5 | namespace: bankapp-namespace 6 | annotations: 7 | nginx.ingress.kubernetes.io/proxy-body-size: "50m" 8 | nginx.ingress.kubernetes.io/ssl-redirect: "true" # Force HTTPS 9 | cert-manager.io/cluster-issuer: letsencrypt-prod # Use Let's Encrypt 10 | nginx.ingress.kubernetes.io/proxy-set-headers: "ingress-headers" 11 | nginx.ingress.kubernetes.io/rewrite-target: / 12 | nginx.ingress.kubernetes.io/affinity: cookie 13 | nginx.ingress.kubernetes.io/session-cookie-name: bankapp_session 14 | nginx.ingress.kubernetes.io/session-cookie-hash: sha1 15 | spec: 16 | tls: 17 | - hosts: 18 | - megaproject.letsdeployit.com 19 | secretName: bankapp-tls-secret 20 | ingressClassName: nginx 21 | rules: 22 | - host: "megaproject.letsdeployit.com" 23 | http: 24 | paths: 25 | - path: / 26 | pathType: Prefix 27 | backend: 28 | service: 29 | name: bankapp-service 30 | port: 31 | number: 8080 32 | - path: /nginx 33 | pathType: Prefix 34 | backend: 35 | service: 36 | name: nginx-service 37 | port: 38 | number: 80 39 | -------------------------------------------------------------------------------- /kubernetes/bankapp-namespace.yml: -------------------------------------------------------------------------------- 1 | kind: Namespace 2 | apiVersion: v1 3 | metadata: 4 | name: bankapp-namespace 5 | labels: 6 | name: bankapp-namespace -------------------------------------------------------------------------------- /kubernetes/bankapp-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: bankapp-service 5 | namespace: bankapp-namespace 6 | labels: 7 | app: bankapp 8 | spec: 9 | type: LoadBalancer 10 | selector: 11 | app: bankapp 12 | ports: 13 | - protocol: TCP 14 | port: 8080 15 | targetPort: 8080 16 | #nodePort: 30080 17 | -------------------------------------------------------------------------------- /kubernetes/cert-issuer.yml: -------------------------------------------------------------------------------- 1 | kind: ClusterIssuer 2 | apiVersion: cert-manager.io/v1 3 | 4 | metadata: 5 | name: letsencrypt-prod 6 | spec: 7 | acme: 8 | # The ACME server URL 9 | server: https://acme-v02.api.letsencrypt.org/directory 10 | # Email address used for ACME registration 11 | email: shubhamnath5@gmail.com 12 | # Name of a secret used to store the ACME account private key 13 | privateKeySecretRef: 14 | name: letsencrypt-prod-key 15 | # Enable the HTTP-01 challenge provider 16 | solvers: 17 | - http01: 18 | ingress: 19 | class: nginx 20 | 21 | 22 | -------------------------------------------------------------------------------- /kubernetes/configmap.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: bankapp-config 5 | namespace: bankapp-namespace 6 | data: 7 | MYSQL_DATABASE: BankDB 8 | SPRING_DATASOURCE_URL: jdbc:mysql://mysql-svc.bankapp-namespace.svc.cluster.local:3306/BankDB?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC 9 | SPRING_DATASOURCE_USERNAME: root 10 | -------------------------------------------------------------------------------- /kubernetes/ingress-headers-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: ingress-headers 5 | namespace: bankapp-namespace 6 | data: 7 | X-Forwarded-Host: "$host" 8 | X-Forwarded-Proto: "$scheme" 9 | X-Forwarded-For: "$proxy_add_x_forwarded_for" 10 | -------------------------------------------------------------------------------- /kubernetes/mysql-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mysql 5 | namespace: bankapp-namespace 6 | labels: 7 | app: mysql 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: mysql 13 | template: 14 | metadata: 15 | labels: 16 | app: mysql 17 | spec: 18 | containers: 19 | - name: mysql 20 | image: amitabhdevops/mysql 21 | ports: 22 | - containerPort: 3306 23 | env: 24 | - name: MYSQL_ROOT_PASSWORD 25 | valueFrom: 26 | secretKeyRef: 27 | name: mysql-secret 28 | key: MYSQL_ROOT_PASSWORD 29 | - name: MYSQL_DATABASE 30 | valueFrom: 31 | configMapKeyRef: 32 | name: bankapp-config 33 | key: MYSQL_DATABASE 34 | volumeMounts: 35 | - name: mysql-pv-storage 36 | mountPath: /var/lib/mysql 37 | subPath: mysql-data # Optional: Ensure a subdirectory is used for better volume organization 38 | resources: 39 | limits: 40 | memory: "512Mi" 41 | cpu: "500m" 42 | requests: 43 | memory: "256Mi" 44 | cpu: "250m" 45 | livenessProbe: 46 | exec: 47 | command: 48 | - mysqladmin 49 | - ping 50 | - -h 51 | - localhost 52 | initialDelaySeconds: 30 53 | periodSeconds: 10 54 | readinessProbe: 55 | exec: 56 | command: 57 | - mysqladmin 58 | - ping 59 | - -h 60 | - localhost 61 | initialDelaySeconds: 30 62 | periodSeconds: 10 63 | volumes: 64 | - name: mysql-pv-storage 65 | persistentVolumeClaim: 66 | claimName: mysql-pvc 67 | -------------------------------------------------------------------------------- /kubernetes/mysql-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: mysql-svc 5 | namespace: bankapp-namespace 6 | labels: 7 | app: mysql 8 | spec: 9 | type: ClusterIP # Keeps it internal to the cluster 10 | selector: 11 | app: mysql 12 | ports: 13 | - protocol: TCP 14 | port: 3306 15 | targetPort: 3306 16 | -------------------------------------------------------------------------------- /kubernetes/nginx-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | namespace: bankapp-namespace 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: nginx 11 | template: 12 | metadata: 13 | labels: 14 | app: nginx 15 | spec: 16 | containers: 17 | - name: nginx 18 | image: nginx:latest 19 | ports: 20 | - containerPort: 80 21 | -------------------------------------------------------------------------------- /kubernetes/nginx-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: nginx-service 5 | namespace: bankapp-namespace 6 | spec: 7 | selector: 8 | app: nginx 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 80 13 | -------------------------------------------------------------------------------- /kubernetes/persistent-volume-claim.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: mysql-pvc 5 | namespace: bankapp-namespace 6 | labels: 7 | app: bankapp 8 | spec: 9 | accessModes: 10 | - ReadWriteOnce 11 | resources: 12 | requests: 13 | storage: 10Gi 14 | storageClassName: standard 15 | -------------------------------------------------------------------------------- /kubernetes/persistent-volume.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: mysql-pv 5 | namespace: bankapp-namespace 6 | labels: 7 | app: bankapp 8 | spec: 9 | capacity: 10 | storage: 10Gi 11 | volumeMode: Filesystem 12 | accessModes: 13 | - ReadWriteOnce 14 | persistentVolumeReclaimPolicy: Retain # Keeps the PV after the PVC is deleted 15 | storageClassName: standard # Make sure this matches your cluster's default storage class 16 | hostPath: 17 | # path: "/tmp/bankapp-mysql" # This will be stored on the host machine running KIND 18 | path: /mnt/data/mysql 19 | type: DirectoryOrCreate 20 | -------------------------------------------------------------------------------- /kubernetes/secrets.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: mysql-secret 5 | namespace: bankapp-namespace 6 | type: Opaque 7 | data: 8 | MYSQL_ROOT_PASSWORD: dGVzdEAxMjM= # Base64 for "test@123" 9 | SPRING_DATASOURCE_PASSWORD: dGVzdEAxMjM= # Base64 for "test@123" 10 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Optional ENV vars 25 | # ----------------- 26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source 27 | # MVNW_REPOURL - repo url base for downloading maven distribution 28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output 30 | # ---------------------------------------------------------------------------- 31 | 32 | set -euf 33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x 34 | 35 | # OS specific support. 36 | native_path() { printf %s\\n "$1"; } 37 | case "$(uname)" in 38 | CYGWIN* | MINGW*) 39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" 40 | native_path() { cygpath --path --windows "$1"; } 41 | ;; 42 | esac 43 | 44 | # set JAVACMD and JAVACCMD 45 | set_java_home() { 46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched 47 | if [ -n "${JAVA_HOME-}" ]; then 48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 49 | # IBM's JDK on AIX uses strange locations for the executables 50 | JAVACMD="$JAVA_HOME/jre/sh/java" 51 | JAVACCMD="$JAVA_HOME/jre/sh/javac" 52 | else 53 | JAVACMD="$JAVA_HOME/bin/java" 54 | JAVACCMD="$JAVA_HOME/bin/javac" 55 | 56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then 57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 59 | return 1 60 | fi 61 | fi 62 | else 63 | JAVACMD="$( 64 | 'set' +e 65 | 'unset' -f command 2>/dev/null 66 | 'command' -v java 67 | )" || : 68 | JAVACCMD="$( 69 | 'set' +e 70 | 'unset' -f command 2>/dev/null 71 | 'command' -v javac 72 | )" || : 73 | 74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then 75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | # hash string like Java String::hashCode 82 | hash_string() { 83 | str="${1:-}" h=0 84 | while [ -n "$str" ]; do 85 | char="${str%"${str#?}"}" 86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) 87 | str="${str#?}" 88 | done 89 | printf %x\\n $h 90 | } 91 | 92 | verbose() { :; } 93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } 94 | 95 | die() { 96 | printf %s\\n "$1" >&2 97 | exit 1 98 | } 99 | 100 | trim() { 101 | # MWRAPPER-139: 102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. 103 | # Needed for removing poorly interpreted newline sequences when running in more 104 | # exotic environments such as mingw bash on Windows. 105 | printf "%s" "${1}" | tr -d '[:space:]' 106 | } 107 | 108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties 109 | while IFS="=" read -r key value; do 110 | case "${key-}" in 111 | distributionUrl) distributionUrl=$(trim "${value-}") ;; 112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; 113 | esac 114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" 115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" 116 | 117 | case "${distributionUrl##*/}" in 118 | maven-mvnd-*bin.*) 119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ 120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in 121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; 122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; 123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; 124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;; 125 | *) 126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 127 | distributionPlatform=linux-amd64 128 | ;; 129 | esac 130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" 131 | ;; 132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; 133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; 134 | esac 135 | 136 | # apply MVNW_REPOURL and calculate MAVEN_HOME 137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" 139 | distributionUrlName="${distributionUrl##*/}" 140 | distributionUrlNameMain="${distributionUrlName%.*}" 141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}" 142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" 143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" 144 | 145 | exec_maven() { 146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : 147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" 148 | } 149 | 150 | if [ -d "$MAVEN_HOME" ]; then 151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME" 152 | exec_maven "$@" 153 | fi 154 | 155 | case "${distributionUrl-}" in 156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; 157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; 158 | esac 159 | 160 | # prepare tmp dir 161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then 162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } 163 | trap clean HUP INT TERM EXIT 164 | else 165 | die "cannot create temp dir" 166 | fi 167 | 168 | mkdir -p -- "${MAVEN_HOME%/*}" 169 | 170 | # Download and Install Apache Maven 171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 172 | verbose "Downloading from: $distributionUrl" 173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 174 | 175 | # select .zip or .tar.gz 176 | if ! command -v unzip >/dev/null; then 177 | distributionUrl="${distributionUrl%.zip}.tar.gz" 178 | distributionUrlName="${distributionUrl##*/}" 179 | fi 180 | 181 | # verbose opt 182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' 183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v 184 | 185 | # normalize http auth 186 | case "${MVNW_PASSWORD:+has-password}" in 187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;; 188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; 189 | esac 190 | 191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then 192 | verbose "Found wget ... using wget" 193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" 194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then 195 | verbose "Found curl ... using curl" 196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" 197 | elif set_java_home; then 198 | verbose "Falling back to use Java to download" 199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" 200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" 201 | cat >"$javaSource" <<-END 202 | public class Downloader extends java.net.Authenticator 203 | { 204 | protected java.net.PasswordAuthentication getPasswordAuthentication() 205 | { 206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); 207 | } 208 | public static void main( String[] args ) throws Exception 209 | { 210 | setDefault( new Downloader() ); 211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); 212 | } 213 | } 214 | END 215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java 216 | verbose " - Compiling Downloader.java ..." 217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" 218 | verbose " - Running Downloader.java ..." 219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" 220 | fi 221 | 222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 223 | if [ -n "${distributionSha256Sum-}" ]; then 224 | distributionSha256Result=false 225 | if [ "$MVN_CMD" = mvnd.sh ]; then 226 | echo "Checksum validation is not supported for maven-mvnd." >&2 227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 228 | exit 1 229 | elif command -v sha256sum >/dev/null; then 230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then 231 | distributionSha256Result=true 232 | fi 233 | elif command -v shasum >/dev/null; then 234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then 235 | distributionSha256Result=true 236 | fi 237 | else 238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 240 | exit 1 241 | fi 242 | if [ $distributionSha256Result = false ]; then 243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 245 | exit 1 246 | fi 247 | fi 248 | 249 | # unzip and move 250 | if command -v unzip >/dev/null; then 251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" 252 | else 253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" 254 | fi 255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" 256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" 257 | 258 | clean || : 259 | exec_maven "$@" 260 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM https://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.3.3 9 | 10 | 11 | com.example 12 | bankapp 13 | 0.0.1-SNAPSHOT 14 | bankapp 15 | Banking Web Application 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 17 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-data-jpa 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-security 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-thymeleaf 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-web 48 | 49 | 50 | org.thymeleaf.extras 51 | thymeleaf-extras-springsecurity6 52 | 53 | 54 | 55 | mysql 56 | mysql-connector-java 57 | 8.0.33 58 | runtime 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-test 63 | test 64 | 65 | 66 | org.springframework.security 67 | spring-security-test 68 | test 69 | 70 | 71 | 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-maven-plugin 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-compiler-plugin 81 | 3.8.0 82 | 83 | 1.8 84 | 1.8 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/BankappApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BankappApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BankappApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.config; 2 | 3 | import com.example.bankapp.service.AccountService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.security.web.SecurityFilterChain; 13 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 14 | 15 | @Configuration 16 | @EnableWebSecurity 17 | public class SecurityConfig { 18 | 19 | @Autowired 20 | AccountService accountService; 21 | 22 | @Bean 23 | public static PasswordEncoder passwordEncoder() { 24 | return new BCryptPasswordEncoder(); 25 | } 26 | 27 | @Bean 28 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 29 | http 30 | .csrf(csrf -> csrf.disable()) 31 | .authorizeHttpRequests(authz -> authz 32 | .requestMatchers("/register").permitAll() 33 | .anyRequest().authenticated() 34 | ) 35 | .formLogin(form -> form 36 | .loginPage("/login") 37 | .loginProcessingUrl("/login") 38 | .defaultSuccessUrl("/dashboard", true) 39 | .permitAll() 40 | ) 41 | .logout(logout -> logout 42 | .invalidateHttpSession(true) 43 | .clearAuthentication(true) 44 | .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) 45 | .logoutSuccessUrl("/login?logout") 46 | .permitAll() 47 | ) 48 | .headers(headers -> headers 49 | .frameOptions(frameOptions -> frameOptions.sameOrigin()) 50 | ); 51 | 52 | return http.build(); 53 | } 54 | 55 | @Autowired 56 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 57 | auth.userDetailsService(accountService).passwordEncoder(passwordEncoder()); 58 | 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.yourpackage.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | @Configuration 9 | public class WebConfig implements WebMvcConfigurer { 10 | @Override 11 | public void addCorsMappings(CorsRegistry registry) { 12 | registry.addMapping("/**") 13 | .allowedOrigins("https://bankapp.snehaws.com") // Your frontend domain 14 | .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") 15 | .allowCredentials(true); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/controller/BankController.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.controller; 2 | 3 | import com.example.bankapp.model.Account; 4 | import com.example.bankapp.service.AccountService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.ui.Model; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | 13 | import java.math.BigDecimal; 14 | 15 | @Controller 16 | public class BankController { 17 | 18 | @Autowired 19 | private AccountService accountService; 20 | 21 | @GetMapping("/dashboard") 22 | public String dashboard(Model model) { 23 | String username = SecurityContextHolder.getContext().getAuthentication().getName(); 24 | Account account = accountService.findAccountByUsername(username); 25 | model.addAttribute("account", account); 26 | return "dashboard"; 27 | } 28 | 29 | @GetMapping("/register") 30 | public String showRegistrationForm() { 31 | return "register"; 32 | } 33 | 34 | @PostMapping("/register") 35 | public String registerAccount(@RequestParam String username, @RequestParam String password, Model model) { 36 | try { 37 | accountService.registerAccount(username, password); 38 | return "redirect:/login"; 39 | } catch (RuntimeException e) { 40 | model.addAttribute("error", e.getMessage()); 41 | return "register"; 42 | } 43 | } 44 | 45 | @GetMapping("/login") 46 | public String login() { 47 | return "login"; 48 | } 49 | 50 | @PostMapping("/deposit") 51 | public String deposit(@RequestParam BigDecimal amount) { 52 | String username = SecurityContextHolder.getContext().getAuthentication().getName(); 53 | Account account = accountService.findAccountByUsername(username); 54 | accountService.deposit(account, amount); 55 | return "redirect:/dashboard"; 56 | } 57 | 58 | @PostMapping("/withdraw") 59 | public String withdraw(@RequestParam BigDecimal amount, Model model) { 60 | String username = SecurityContextHolder.getContext().getAuthentication().getName(); 61 | Account account = accountService.findAccountByUsername(username); 62 | 63 | try { 64 | accountService.withdraw(account, amount); 65 | } catch (RuntimeException e) { 66 | model.addAttribute("error", e.getMessage()); 67 | model.addAttribute("account", account); 68 | return "dashboard"; 69 | } 70 | 71 | return "redirect:/dashboard"; 72 | } 73 | 74 | @GetMapping("/transactions") 75 | public String transactionHistory(Model model) { 76 | String username = SecurityContextHolder.getContext().getAuthentication().getName(); 77 | Account account = accountService.findAccountByUsername(username); 78 | model.addAttribute("transactions", accountService.getTransactionHistory(account)); 79 | return "transactions"; 80 | } 81 | 82 | @PostMapping("/transfer") 83 | public String transferAmount(@RequestParam String toUsername, @RequestParam BigDecimal amount, Model model) { 84 | String username = SecurityContextHolder.getContext().getAuthentication().getName(); 85 | Account fromAccount = accountService.findAccountByUsername(username); 86 | 87 | try { 88 | accountService.transferAmount(fromAccount, toUsername, amount); 89 | } catch (RuntimeException e) { 90 | model.addAttribute("error", e.getMessage()); 91 | model.addAttribute("account", fromAccount); 92 | return "dashboard"; 93 | } 94 | 95 | return "redirect:/dashboard"; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/model/Account.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.model; 2 | 3 | import jakarta.persistence.*; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | 7 | import java.math.BigDecimal; 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | @Entity 12 | public class Account implements UserDetails { 13 | 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | private String username; 18 | private String password; 19 | private BigDecimal balance; 20 | 21 | @OneToMany(mappedBy = "account") 22 | private List transactions; 23 | 24 | @Transient 25 | private Collection authorities; 26 | 27 | public Account() { 28 | 29 | } 30 | 31 | public Account(String username, String password, BigDecimal balance, List transactions, Collection authorities) { 32 | this.username = username; 33 | this.password = password; 34 | this.balance = balance; 35 | this.transactions = transactions; 36 | this.authorities = authorities; 37 | } 38 | 39 | @Override 40 | public Collection getAuthorities() { 41 | return authorities; 42 | } 43 | 44 | public void setAuthorities(Collection authorities) { 45 | this.authorities = authorities; 46 | } 47 | 48 | public Long getId() { 49 | return id; 50 | } 51 | 52 | public void setId(Long id) { 53 | this.id = id; 54 | } 55 | 56 | public String getUsername() { 57 | return username; 58 | } 59 | 60 | public void setUsername(String username) { 61 | this.username = username; 62 | } 63 | 64 | public String getPassword() { 65 | return password; 66 | } 67 | 68 | public void setPassword(String password) { 69 | this.password = password; 70 | } 71 | 72 | public BigDecimal getBalance() { 73 | return balance; 74 | } 75 | 76 | public void setBalance(BigDecimal balance) { 77 | this.balance = balance; 78 | } 79 | 80 | public List getTransactions() { 81 | return transactions; 82 | } 83 | 84 | public void setTransactions(List transactions) { 85 | this.transactions = transactions; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/model/Transaction.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.model; 2 | 3 | import jakarta.persistence.*; 4 | import java.math.BigDecimal; 5 | import java.time.LocalDateTime; 6 | 7 | @Entity 8 | public class Transaction { 9 | 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | private Long id; 13 | private BigDecimal amount; 14 | private String type; 15 | private LocalDateTime timestamp; 16 | 17 | @ManyToOne 18 | @JoinColumn(name = "account_id") 19 | private Account account; 20 | 21 | public Transaction() { 22 | 23 | } 24 | 25 | public Transaction(BigDecimal amount, String type, LocalDateTime timestamp, Account account) { 26 | this.amount = amount; 27 | this.type = type; 28 | this.timestamp = timestamp; 29 | this.account = account; 30 | } 31 | 32 | public Long getId() { 33 | return id; 34 | } 35 | 36 | public void setId(Long id) { 37 | this.id = id; 38 | } 39 | 40 | public BigDecimal getAmount() { 41 | return amount; 42 | } 43 | 44 | public void setAmount(BigDecimal amount) { 45 | this.amount = amount; 46 | } 47 | 48 | public String getType() { 49 | return type; 50 | } 51 | 52 | public void setType(String type) { 53 | this.type = type; 54 | } 55 | 56 | public LocalDateTime getTimestamp() { 57 | return timestamp; 58 | } 59 | 60 | public void setTimestamp(LocalDateTime timestamp) { 61 | this.timestamp = timestamp; 62 | } 63 | 64 | public Account getAccount() { 65 | return account; 66 | } 67 | 68 | public void setAccount(Account account) { 69 | this.account = account; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/repository/AccountRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.repository; 2 | 3 | import com.example.bankapp.model.Account; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface AccountRepository extends JpaRepository { 9 | Optional findByUsername(String username); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/repository/TransactionRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.repository; 2 | 3 | import com.example.bankapp.model.Transaction; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface TransactionRepository extends JpaRepository { 9 | List findByAccountId(Long accountId); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/example/bankapp/service/AccountService.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp.service; 2 | 3 | import com.example.bankapp.model.Account; 4 | import com.example.bankapp.model.Transaction; 5 | import com.example.bankapp.repository.AccountRepository; 6 | import com.example.bankapp.repository.TransactionRepository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.security.core.userdetails.UserDetailsService; 12 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 13 | import org.springframework.security.crypto.password.PasswordEncoder; 14 | import org.springframework.stereotype.Service; 15 | 16 | import java.math.BigDecimal; 17 | import java.time.LocalDateTime; 18 | import java.util.Arrays; 19 | import java.util.Collection; 20 | import java.util.List; 21 | 22 | @Service 23 | public class AccountService implements UserDetailsService { 24 | 25 | @Autowired 26 | PasswordEncoder passwordEncoder; 27 | 28 | @Autowired 29 | private AccountRepository accountRepository; 30 | 31 | @Autowired 32 | private TransactionRepository transactionRepository; 33 | 34 | public Account findAccountByUsername(String username) { 35 | return accountRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("Account not found")); 36 | } 37 | 38 | public Account registerAccount(String username, String password) { 39 | if (accountRepository.findByUsername(username).isPresent()) { 40 | throw new RuntimeException("Username already exists"); 41 | } 42 | 43 | Account account = new Account(); 44 | account.setUsername(username); 45 | account.setPassword(passwordEncoder.encode(password)); // Encrypt password 46 | account.setBalance(BigDecimal.ZERO); // Initial balance set to 0 47 | return accountRepository.save(account); 48 | } 49 | 50 | 51 | public void deposit(Account account, BigDecimal amount) { 52 | account.setBalance(account.getBalance().add(amount)); 53 | accountRepository.save(account); 54 | 55 | Transaction transaction = new Transaction( 56 | amount, 57 | "Deposit", 58 | LocalDateTime.now(), 59 | account 60 | ); 61 | transactionRepository.save(transaction); 62 | } 63 | 64 | public void withdraw(Account account, BigDecimal amount) { 65 | if (account.getBalance().compareTo(amount) < 0) { 66 | throw new RuntimeException("Insufficient funds"); 67 | } 68 | account.setBalance(account.getBalance().subtract(amount)); 69 | accountRepository.save(account); 70 | 71 | Transaction transaction = new Transaction( 72 | amount, 73 | "Withdrawal", 74 | LocalDateTime.now(), 75 | account 76 | ); 77 | transactionRepository.save(transaction); 78 | } 79 | 80 | public List getTransactionHistory(Account account) { 81 | return transactionRepository.findByAccountId(account.getId()); 82 | } 83 | 84 | @Override 85 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 86 | 87 | Account account = findAccountByUsername(username); 88 | if (account == null) { 89 | throw new UsernameNotFoundException("Username or Password not found"); 90 | } 91 | return new Account( 92 | account.getUsername(), 93 | account.getPassword(), 94 | account.getBalance(), 95 | account.getTransactions(), 96 | authorities()); 97 | } 98 | 99 | public Collection authorities() { 100 | return Arrays.asList(new SimpleGrantedAuthority("USER")); 101 | } 102 | 103 | public void transferAmount(Account fromAccount, String toUsername, BigDecimal amount) { 104 | if (fromAccount.getBalance().compareTo(amount) < 0) { 105 | throw new RuntimeException("Insufficient funds"); 106 | } 107 | 108 | Account toAccount = accountRepository.findByUsername(toUsername) 109 | .orElseThrow(() -> new RuntimeException("Recipient account not found")); 110 | 111 | // Deduct from sender's account 112 | fromAccount.setBalance(fromAccount.getBalance().subtract(amount)); 113 | accountRepository.save(fromAccount); 114 | 115 | // Add to recipient's account 116 | toAccount.setBalance(toAccount.getBalance().add(amount)); 117 | accountRepository.save(toAccount); 118 | 119 | // Create transaction records for both accounts 120 | Transaction debitTransaction = new Transaction( 121 | amount, 122 | "Transfer Out to " + toAccount.getUsername(), 123 | LocalDateTime.now(), 124 | fromAccount 125 | ); 126 | transactionRepository.save(debitTransaction); 127 | 128 | Transaction creditTransaction = new Transaction( 129 | amount, 130 | "Transfer In from " + fromAccount.getUsername(), 131 | LocalDateTime.now(), 132 | toAccount 133 | ); 134 | transactionRepository.save(creditTransaction); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=bankapp 2 | # MySQL Database configuration 3 | spring.datasource.url=jdbc:mysql://localhost:3306/bankappdb?useSSL=false&serverTimezone=UTC 4 | spring.datasource.username=root 5 | spring.datasource.password=test@123 6 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 7 | 8 | # JPA & Hibernate configuration 9 | spring.jpa.hibernate.ddl-auto=update 10 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect 11 | spring.jpa.show-sql=true 12 | 13 | server.forward-headers-strategy=native 14 | server.use-forward-headers=true 15 | -------------------------------------------------------------------------------- /src/main/resources/static/mysql/SQLScript.txt: -------------------------------------------------------------------------------- 1 | CREATE DATABASE bankappdb; -------------------------------------------------------------------------------- /src/main/resources/templates/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dashboard - Goldencat Bank 5 | 6 | 116 | 117 | 118 | 126 | 127 |
128 |
129 |

130 |

131 |
132 | 133 | 134 | 139 | 140 |
141 | 142 |
143 | 146 |
147 |
148 |
149 | 150 | 151 |
152 | 153 |
154 |
155 |
156 | 157 | 158 |
159 | 162 |
163 |
164 |
165 | 166 | 167 |
168 | 169 |
170 |
171 |
172 | 173 | 174 |
175 | 178 |
179 |
180 |
181 | 182 | 183 |
184 |
185 | 186 | 187 |
188 | 189 |
190 |
191 |
192 |
193 | 194 |

195 |
196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login - Goldencat Bank 5 | 6 | 7 | 116 | 117 | 118 | 129 | 130 |
131 |

Login

132 |
133 |
134 | 135 | 136 |
137 |
138 | 139 | 140 |
141 | 142 |
143 |

Don't have an account? Register here

144 | 145 |
146 | Invalid username or password. 147 |
148 |
149 | 150 | 151 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /src/main/resources/templates/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Register - Goldencat Bank 5 | 6 | 7 | 116 | 117 | 118 | 129 | 130 |
131 |

Register a New Account

132 |
133 |
134 | 135 | 136 |
137 |
138 | 139 | 140 |
141 | 142 |
143 |

Already have an account? Login here

144 | 145 |
146 | User already present. 147 |
148 |
149 | 150 | 151 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /src/main/resources/templates/transactions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Transaction History - Goldencat Bank 5 | 6 | 88 | 89 | 90 | 98 | 99 |
100 |

Transaction History

101 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 119 | 120 | 121 | 122 |
IDTypeAmountDate
117 | 118 |
123 |
124 | 125 |

Back to Dashboard

126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/test/java/com/example/bankapp/BankappApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.bankapp; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class BankappApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /terraform/ec2.tf: -------------------------------------------------------------------------------- 1 | # Fetch the AMI for the instance based on specified filters 2 | data "aws_ami" "ubuntu" { 3 | owners = [var.aws_ami_owners] 4 | most_recent = true 5 | 6 | filter { 7 | name = "name" 8 | values = [var.aws_instance_os_type] 9 | } 10 | 11 | filter { 12 | name = "state" 13 | values = ["available"] 14 | } 15 | } 16 | 17 | # Create an SSH key pair 18 | resource "aws_key_pair" "terraform_key" { 19 | key_name = var.aws_key_pair_name 20 | public_key = file(var.aws_key_pair_public_key) 21 | } 22 | 23 | # Get the default VPC for the region 24 | resource "aws_default_vpc" "default" {} 25 | 26 | # Create a security group 27 | resource "aws_security_group" "terraform_sg" { 28 | name = var.aws_sg_name 29 | description = var.aws_sg_description 30 | vpc_id = aws_default_vpc.default.id 31 | 32 | ingress { 33 | description = "Allow access to SSH port 22" 34 | from_port = 22 35 | to_port = 22 36 | protocol = var.ssh_protocol 37 | cidr_blocks = [var.ssh_cidr] 38 | } 39 | 40 | ingress { 41 | description = "Allow access to HTTP port 80" 42 | from_port = 80 43 | to_port = 80 44 | protocol = var.http_protocol 45 | cidr_blocks = [var.http_cidr] 46 | } 47 | 48 | ingress { 49 | description = "Allow access to HTTPS port 443" 50 | from_port = 443 51 | to_port = 443 52 | protocol = var.https_protocol 53 | cidr_blocks = [var.https_cidr] 54 | } 55 | 56 | egress { 57 | description = "Allow all outgoing traffic" 58 | from_port = 0 59 | to_port = 0 60 | protocol = var.outgoing_protocol 61 | cidr_blocks = [var.outgoing_cidr] 62 | } 63 | 64 | tags = { 65 | Name = var.aws_sg_name 66 | } 67 | } 68 | 69 | # Create an EC2 instance 70 | resource "aws_instance" "mega_project_instance" { 71 | ami = data.aws_ami.ubuntu.id 72 | instance_type = var.aws_instance_type 73 | key_name = aws_key_pair.terraform_key.key_name 74 | security_groups = [aws_security_group.terraform_sg.name] 75 | 76 | root_block_device { 77 | volume_size = var.aws_instance_storage_size 78 | volume_type = var.aws_instance_volume_type 79 | } 80 | 81 | tags = { 82 | Name = var.aws_instance_name 83 | } 84 | } -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | # Configure the AWS Provider 2 | provider "aws" { 3 | region = var.aws_region 4 | } -------------------------------------------------------------------------------- /terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "instance_private_ip" { 2 | value = aws_instance.mega_project_instance.private_ip 3 | description = "The private IP address of the main server instance." 4 | } 5 | 6 | output "instance_public_ip" { 7 | value = aws_instance.mega_project_instance.public_ip 8 | description = "The public IP address of the main server instance." 9 | } -------------------------------------------------------------------------------- /terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "5.82.2" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /terraform/variable.tf: -------------------------------------------------------------------------------- 1 | # aws variable 2 | variable "aws_region" { 3 | description = "The AWS region to deploy resources in." 4 | type = string 5 | default = "eu-west-1" 6 | } 7 | 8 | # EC2 instance variables 9 | variable "aws_instance_type" { 10 | description = "Defines the type of EC2 instance to be created (e.g., t2.micro, t3.small)." 11 | type = string 12 | default = "t2.large" 13 | } 14 | 15 | variable "aws_instance_name" { 16 | description = "Specifies the name tag for the EC2 instance, providing easy identification in AWS." 17 | type = string 18 | default = "mega-project-instance" 19 | } 20 | 21 | variable "aws_instance_os_type" { 22 | description = "Defines the operating system image filter for selecting an appropriate AMI (e.g., Ubuntu 20.04)." 23 | type = string 24 | default = "ubuntu/images/hvm-ssd/*amd64*" 25 | } 26 | 27 | variable "aws_ami_owners" { 28 | description = "The owner ID of the AMI to use. Default is Canonical (for Ubuntu AMIs)." 29 | type = string 30 | default = "099720109477" 31 | } 32 | 33 | variable "aws_instance_storage_size" { 34 | description = "Specifies the size of the root block storage for the EC2 instance, in GB." 35 | type = number 36 | default = 30 37 | } 38 | 39 | variable "aws_instance_volume_type" { 40 | description = "Defines the volume type for the EC2 instance (e.g., gp2, gp3, io1)." 41 | type = string 42 | default = "gp3" 43 | } 44 | 45 | # Key pair variables 46 | variable "aws_key_pair_name" { 47 | description = "The name of the SSH key pair to use for accessing the EC2 instance." 48 | type = string 49 | default = "mega-project-key" 50 | } 51 | 52 | variable "aws_key_pair_public_key" { 53 | description = "The path to the public key file for the SSH key pair." 54 | type = string 55 | default = "mega-project-key.pub" 56 | } 57 | 58 | # Security group variables 59 | variable "aws_sg_name" { 60 | description = "Defines the name of the AWS Security Group to be created." 61 | type = string 62 | default = "mega-project-sg" 63 | } 64 | 65 | variable "aws_sg_description" { 66 | description = "Describes the purpose of the AWS Security Group being created." 67 | type = string 68 | default = "This security group allows SSH, HTTP, and HTTPS traffic." 69 | } 70 | 71 | variable "ssh_protocol" { 72 | description = "Specifies the protocol to use for SSH traffic (default: TCP)." 73 | type = string 74 | default = "tcp" 75 | } 76 | 77 | variable "http_protocol" { 78 | description = "Specifies the protocol to use for HTTP traffic (default: TCP)." 79 | type = string 80 | default = "tcp" 81 | } 82 | 83 | variable "https_protocol" { 84 | description = "Specifies the protocol to use for HTTPS traffic (default: TCP)." 85 | type = string 86 | default = "tcp" 87 | } 88 | 89 | variable "ssh_cidr" { 90 | description = "Defines the CIDR block to allow SSH access (default: open to all)." 91 | type = string 92 | default = "0.0.0.0/0" 93 | } 94 | 95 | variable "http_cidr" { 96 | description = "Defines the CIDR block to allow HTTP access (default: open to all)." 97 | type = string 98 | default = "0.0.0.0/0" 99 | } 100 | 101 | variable "https_cidr" { 102 | description = "Defines the CIDR block to allow HTTPS access (default: open to all)." 103 | type = string 104 | default = "0.0.0.0/0" 105 | } 106 | 107 | variable "outgoing_protocol" { 108 | description = "Specifies the protocol for all outgoing traffic (default: allow all)." 109 | type = string 110 | default = "-1" 111 | } 112 | 113 | variable "outgoing_cidr" { 114 | description = "Defines the CIDR block for all outgoing traffic (default: open to all)." 115 | type = string 116 | default = "0.0.0.0/0" 117 | } -------------------------------------------------------------------------------- /vars/code_checkout.groovy: -------------------------------------------------------------------------------- 1 | def call(String url, String branch) { 2 | try { 3 | git url: url, branch: branch 4 | } catch (Exception e) { 5 | error "Failed to checkout code from ${url} on branch ${branch}. Error: ${e.message}" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /vars/docker_build.groovy: -------------------------------------------------------------------------------- 1 | def call(String ProjectName,String ImageTag,String DockerHubUser){ 2 | sh "docker build -t ${DockerHubUser}/${ProjectName}:${ImageTag} ." 3 | } 4 | -------------------------------------------------------------------------------- /vars/docker_compose.groovy: -------------------------------------------------------------------------------- 1 | def call(){ 2 | sh "docker compose down && docker compose up -d --build" 3 | } -------------------------------------------------------------------------------- /vars/docker_push.groovy: -------------------------------------------------------------------------------- 1 | def call(String projectName, String imageTag, String dockerHubUser) { 2 | withCredentials([usernamePassword(credentialsId: 'dockerHub', passwordVariable: 'dockerHubPass', usernameVariable: 'dockerHubUser')]) { 3 | // Login to DockerHub 4 | sh "docker login -u ${dockerHubUser} -p ${dockerHubPass}" 5 | 6 | // Tag the Docker image 7 | sh "docker image tag ${dockerHubUser}/${projectName}:${imageTag} ${dockerHubUser}/${projectName}:${imageTag}" 8 | 9 | // Push the Docker image 10 | sh "docker push ${dockerHubUser}/${projectName}:${imageTag}" 11 | 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /vars/owasp_dependency.groovy: -------------------------------------------------------------------------------- 1 | def call(){ 2 | dependencyCheck additionalArguments: '--scan ./', odcInstallation: 'OWASP' 3 | dependencyCheckPublisher pattern: '**/dependency-check-report.xml' 4 | } 5 | -------------------------------------------------------------------------------- /vars/sonarqube_analysis.groovy: -------------------------------------------------------------------------------- 1 | def call(String Sonar,String projectName,String projectKey){ 2 | withSonarQubeEnv("${Sonar}"){ 3 | sh "${SONAR_HOME}/bin/sonar-scanner -Dsonar.projectName=${projectName} -Dsonar.projectKey=${projectKey} -Dsonar.java.binaries=. -X" 4 | } 5 | } -------------------------------------------------------------------------------- /vars/sonarqube_code_quality.groovy: -------------------------------------------------------------------------------- 1 | def call(){ 2 | timeout(time: 15, unit: "MINUTES"){ 3 | waitForQualityGate abortPipeline: true 4 | } 5 | } -------------------------------------------------------------------------------- /vars/trivy_scan.groovy: -------------------------------------------------------------------------------- 1 | def call(String reportFile = "table-report.html") { 2 | script { 3 | sh "trivy fs --format table -o ${reportFile} ." 4 | archiveArtifacts artifacts: reportFile, fingerprint: true 5 | } 6 | } --------------------------------------------------------------------------------