Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copy this file to .env and set real values before running docker-compose.
# NEVER commit the .env file to version control.
MYSQL_ROOT_PASSWORD=
SPRING_DATASOURCE_PASSWORD=
DUSER=
IMAGE=
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ build/

### VS Code ###
.vscode/

### Security: prevent accidental commit of secrets ###
.env
*.env.local
37 changes: 19 additions & 18 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
#----------------------------------
# Stage 1
# Stage 1: Build
#----------------------------------

# Import docker image with maven installed
FROM maven:3.8.3-openjdk-17 as builder
# Security: pin base image to SHA digest to prevent supply-chain attacks via tag mutation
FROM maven:3.8.3-openjdk-17@sha256:8a66581a077762c8752a9f64f73cdd8c59e9c4446eb810417119e0436b075931 AS builder

# Add maintainer, so that new user will understand who had written this Dockerfile
MAINTAINER Madhup Pandey<madhuppandey2908@gmail.com>

# Add labels to the image to filter out if we have multiple application running
LABEL app=bankapp

# Set working directory
WORKDIR /src

# Copy source code from local to container
COPY . /src

# Build application and skip test cases
RUN mvn clean install -DskipTests=true

#--------------------------------------
# Stage 2
# Stage 2: Runtime
#--------------------------------------

# Import small size java image
FROM openjdk:17-alpine as deployer
# Security: replaced deprecated openjdk:17-alpine with actively maintained eclipse-temurin;
# pinned to SHA digest for reproducible builds
FROM eclipse-temurin:17-jre-alpine@sha256:02320dd4ce20e243dfb915c686089cf9315c763084fafbb12d5c9993aee18b57

# Security: remove unnecessary OS packages and caches from final image
RUN apk --no-cache upgrade \
&& rm -rf /var/cache/apk/*

# Copy build from stage 1 (builder)
COPY --from=builder /src/target/*.jar /src/target/bankapp.jar
# Security: create a non-root user to run the application (CIS Docker Benchmark 4.1)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

COPY --from=builder --chown=appuser:appgroup /src/target/*.jar /app/bankapp.jar

# Expose application port
EXPOSE 8080

# Start the application
ENTRYPOINT ["java", "-jar", "/src/target/bankapp.jar"]
# Security: run as non-root user to limit blast radius of container compromise
USER appuser

ENTRYPOINT ["java", "-jar", "/app/bankapp.jar"]
11 changes: 7 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
version: "3.8"
services:
mysql:
image: mysql:latest
# Security: pin image to SHA digest for reproducible, tamper-resistant builds
image: mysql:8.0@sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b
container_name: mysql
environment:
- MYSQL_ROOT_PASSWORD=Test@123
# Security: credentials sourced from .env file or environment variables instead of hardcoded values
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD in .env}
- MYSQL_DATABASE=BankDB
volumes:
- bankapp-volume:/var/lib/mysql
Expand All @@ -23,7 +25,8 @@ services:
environment:
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/BankDB?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
- SPRING_DATASOURCE_PASSWORD=Test@123
# Security: credentials sourced from .env file or environment variables instead of hardcoded values
- SPRING_DATASOURCE_PASSWORD=${SPRING_DATASOURCE_PASSWORD:?Set SPRING_DATASOURCE_PASSWORD in .env}
ports:
- "8080:8080"
depends_on:
Expand All @@ -43,4 +46,4 @@ networks:
bankapp:

volumes:
bankapp-volume:
bankapp-volume:
6 changes: 5 additions & 1 deletion helm/bankapp/templates/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ spec:
labels:
app: {{ .Values.app_deployment.name }}
spec:
# Security: use dedicated SA with least-privilege; disable API token mount
serviceAccountName: "{{ .Values.app_deployment.name }}-sa"
automountServiceAccountToken: false
initContainers:
- name: "wait-for-{{ .Values.db_statefulset.name }}"
image: busybox:1.28
# Security: pin init container image to SHA digest for supply-chain integrity
image: busybox:1.28@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
command: ['sh', '-c', 'until nc -z {{ .Values.db_statefulset.name }}-0.{{ .Values.db_statefulset.name }}-headless 3306; do echo waiting for mysql; sleep 5; done;']
containers:
- name: {{ .Values.app_deployment.name }}
Expand Down
3 changes: 3 additions & 0 deletions helm/bankapp/templates/mysqlStatefulSet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ spec:
labels:
app: {{ .Values.db_statefulset.name }}
spec:
# Security: use dedicated SA with least-privilege; disable API token mount
serviceAccountName: "{{ .Values.db_statefulset.name }}-sa"
automountServiceAccountToken: false
containers:
- name: {{ .Values.db_statefulset.name }}
image: {{ .Values.image.db }}
Expand Down
102 changes: 102 additions & 0 deletions helm/bankapp/templates/networkPolicies.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Security: default-deny all ingress and egress traffic in the namespace.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: {{ default "bankapp-namespace" .Values.namespace }}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Security: allow bankapp pods to receive traffic only from the ingress controller.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: "allow-ingress-to-{{ .Values.app_deployment.name }}"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
spec:
podSelector:
matchLabels:
app: {{ .Values.app_deployment.name }}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
---
# Security: bankapp egress — allow only MySQL and DNS.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: "{{ .Values.app_deployment.name }}-egress"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
spec:
podSelector:
matchLabels:
app: {{ .Values.app_deployment.name }}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- podSelector:
matchLabels:
app: {{ .Values.db_statefulset.name }}
ports:
- protocol: TCP
port: 3306
---
# Security: MySQL ingress — allow only from bankapp pods on port 3306.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: "allow-{{ .Values.app_deployment.name }}-to-{{ .Values.db_statefulset.name }}"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
spec:
podSelector:
matchLabels:
app: {{ .Values.db_statefulset.name }}
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: {{ .Values.app_deployment.name }}
ports:
- protocol: TCP
port: 3306
---
# Security: MySQL egress — DNS only.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: "{{ .Values.db_statefulset.name }}-egress"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
spec:
podSelector:
matchLabels:
app: {{ .Values.db_statefulset.name }}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
72 changes: 72 additions & 0 deletions helm/bankapp/templates/rbac.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Security: dedicated ServiceAccount for the bankapp deployment.
# Pods should not use the default SA, which may carry cluster-wide permissions.
apiVersion: v1
kind: ServiceAccount
metadata:
name: "{{ .Values.app_deployment.name }}-sa"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
labels:
app: {{ .Values.app_deployment.name }}
automountServiceAccountToken: false
---
# Security: dedicated ServiceAccount for MySQL.
apiVersion: v1
kind: ServiceAccount
metadata:
name: "{{ .Values.db_statefulset.name }}-sa"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
labels:
app: {{ .Values.db_statefulset.name }}
automountServiceAccountToken: false
---
# Security: namespace-scoped Role for bankapp — read-only ConfigMaps/Secrets.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: "{{ .Values.app_deployment.name }}-role"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
# Security: bind the Role to bankapp SA (namespace-scoped, not ClusterRoleBinding).
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: "{{ .Values.app_deployment.name }}-rolebinding"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
subjects:
- kind: ServiceAccount
name: "{{ .Values.app_deployment.name }}-sa"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
roleRef:
kind: Role
name: "{{ .Values.app_deployment.name }}-role"
apiGroup: rbac.authorization.k8s.io
---
# Security: namespace-scoped Role for MySQL — read-only ConfigMaps/Secrets.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: "{{ .Values.db_statefulset.name }}-role"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get"]
---
# Security: bind the Role to MySQL SA (namespace-scoped).
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: "{{ .Values.db_statefulset.name }}-rolebinding"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
subjects:
- kind: ServiceAccount
name: "{{ .Values.db_statefulset.name }}-sa"
namespace: {{ default "bankapp-namespace" .Values.namespace }}
roleRef:
kind: Role
name: "{{ .Values.db_statefulset.name }}-role"
apiGroup: rbac.authorization.k8s.io
14 changes: 8 additions & 6 deletions helm/bankapp/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ app_deployment:
mem_limit: 700Mi

## image repository and tag of app and db.
## Security: pin images to SHA digests to prevent tag-mutation supply-chain attacks
image:
app: trainwithshubham/springboot-bankapp:latest
db: mysql:latest
db: mysql:8.0@sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b

# NodePort svc for bankapp
bankapp_svc:
Expand All @@ -44,11 +45,12 @@ hpa:
max_replica: 5
cpu_utilizatoion: 40

# Secret for Database connectivity

# Security: in production, use an external secrets operator (e.g., AWS Secrets Manager,
# HashiCorp Vault, or Sealed Secrets) instead of storing credentials in values.yaml.
# Override these placeholder values via --set or a values-override file at deploy time.
secret:
name: mysql-secret
data:
MYSQL_ROOT_PASSWORD: Test@123 # (Base64 encoded 'Test@123')
SPRING_DATASOURCE_PASSWORD: Test@123 # (Base64 encoded 'Test@123')
data:
MYSQL_ROOT_PASSWORD: CHANGEME
SPRING_DATASOURCE_PASSWORD: CHANGEME

3 changes: 3 additions & 0 deletions kubernetes/bankapp-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ spec:
labels:
app: bankapp-deploy
spec:
# Security: use dedicated SA with least-privilege; disable API token mount
serviceAccountName: bankapp-sa
automountServiceAccountToken: false
containers:
- name: bankapp
image: trainwithshubham/bankapp-eks:v2
Expand Down
6 changes: 5 additions & 1 deletion kubernetes/mysql-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ spec:
labels:
app: mysql
spec:
# Security: use dedicated SA with least-privilege; disable API token mount
serviceAccountName: mysql-sa
automountServiceAccountToken: false
containers:
- name: mysql
image: mysql:8.0 # Use a specific, stable version for production
# Security: pin image to SHA digest for supply-chain integrity
image: mysql:8.0@sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b
ports:
- containerPort: 3306
env:
Expand Down
Loading