diff --git a/job-eks.yaml b/job-eks.yaml new file mode 100644 index 0000000..90b1c3a --- /dev/null +++ b/job-eks.yaml @@ -0,0 +1,34 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: kube-bench + namespace: kube-system +spec: + template: + spec: + hostPID: true + containers: + - name: kube-bench + image: aquasec/kube-bench:latest + command: ["kube-bench", "node", "--benchmark", "eks-1.0"] + volumeMounts: + - name: var-lib-kubelet + mountPath: /var/lib/kubelet + readOnly: true + - name: etc-systemd + mountPath: /etc/systemd + readOnly: true + - name: etc-kubernetes + mountPath: /etc/kubernetes + readOnly: true + restartPolicy: Never + volumes: + - name: var-lib-kubelet + hostPath: + path: "/var/lib/kubelet" + - name: etc-systemd + hostPath: + path: "/etc/systemd" + - name: etc-kubernetes + hostPath: + path: "/etc/kubernetes" diff --git a/kube-bench/README.md b/kube-bench/README.md new file mode 100644 index 0000000..a610bbd --- /dev/null +++ b/kube-bench/README.md @@ -0,0 +1,10 @@ +These are the kube-bench jobs and cronjob yaml files to check for CIS benchmark of your k8 cluster. +To run the cronjob, you need to setup a slack webhook secret first. + +Use this command: + +`kubectl create secret generic slack-webhook-secret --from-literal=slack_webhook-url=https://hooks.slack.com/services/ -n default` + +then apply the cronjob in your cluster. + +`kubectl apply -f kube-bench_cronjob-minikube.yaml` diff --git a/kube-bench/kube-bench-job.yaml b/kube-bench/kube-bench-job.yaml new file mode 100644 index 0000000..ac934ae --- /dev/null +++ b/kube-bench/kube-bench-job.yaml @@ -0,0 +1,42 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: kube-bench +spec: + template: + spec: + hostPID: true + containers: + - name: kube-bench + image: docker.io/aquasec/kube-bench:latest + command: + [ + "kube-bench", + "run", + "--targets", + "node", + "--benchmark", + "eks-1.2.0", + ] + volumeMounts: + - name: var-lib-kubelet + mountPath: /var/lib/kubelet + readOnly: true + - name: etc-systemd + mountPath: /etc/systemd + readOnly: true + - name: etc-kubernetes + mountPath: /etc/kubernetes + readOnly: true + restartPolicy: Never + volumes: + - name: var-lib-kubelet + hostPath: + path: "/var/lib/kubelet" + - name: etc-systemd + hostPath: + path: "/etc/systemd" + - name: etc-kubernetes + hostPath: + path: "/etc/kubernetes" diff --git a/kube-bench/kube-bench_cronjob-minikube.yaml b/kube-bench/kube-bench_cronjob-minikube.yaml new file mode 100644 index 0000000..3b40145 --- /dev/null +++ b/kube-bench/kube-bench_cronjob-minikube.yaml @@ -0,0 +1,43 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: kube-bench-cron + namespace: default +spec: + schedule: "*/1 * * * *" # Runs every 1 minute + jobTemplate: + spec: + template: + spec: + hostPID: true + containers: + - name: kube-bench + image: aquasec/kube-bench:latest + command: ["sh", "-c", "apk add --no-cache curl && kube-bench | jq -R -s '{\"text\": .}' | curl -X POST -H 'Content-type: application/json' --data @- $(WEBHOOK_URL)"] + env: + - name: WEBHOOK_URL + valueFrom: + secretKeyRef: + name: slack-webhook-secret + key: webhook-url + volumeMounts: + - name: var-lib-kubelet + mountPath: /var/lib/kubelet + readOnly: true + - name: etc-systemd + mountPath: /etc/systemd + readOnly: true + - name: etc-kubernetes + mountPath: /etc/kubernetes + readOnly: true + restartPolicy: Never + volumes: + - name: var-lib-kubelet + hostPath: + path: "/var/lib/kubelet" + - name: etc-systemd + hostPath: + path: "/etc/systemd" + - name: etc-kubernetes + hostPath: + path: "/etc/kubernetes" diff --git a/kube-bench/kube-bench_job-minikube.yaml b/kube-bench/kube-bench_job-minikube.yaml new file mode 100644 index 0000000..be7b97e --- /dev/null +++ b/kube-bench/kube-bench_job-minikube.yaml @@ -0,0 +1,34 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: kube-bench + namespace: default +spec: + template: + spec: + hostPID: true + containers: + - name: kube-bench + image: aquasec/kube-bench:latest + command: ["kube-bench"] + volumeMounts: + - name: var-lib-kubelet + mountPath: /var/lib/kubelet + readOnly: true + - name: etc-systemd + mountPath: /etc/systemd + readOnly: true + - name: etc-kubernetes + mountPath: /etc/kubernetes + readOnly: true + restartPolicy: Never + volumes: + - name: var-lib-kubelet + hostPath: + path: "/var/lib/kubelet" + - name: etc-systemd + hostPath: + path: "/etc/systemd" + - name: etc-kubernetes + hostPath: + path: "/etc/kubernetes" diff --git a/src/main/java/com/javatechie/k8s/SpringbootK8sDemoApplication.java b/src/main/java/com/javatechie/k8s/SpringbootK8sDemoApplication.java index 3b9023f..832e0d7 100644 --- a/src/main/java/com/javatechie/k8s/SpringbootK8sDemoApplication.java +++ b/src/main/java/com/javatechie/k8s/SpringbootK8sDemoApplication.java @@ -9,13 +9,17 @@ @RestController public class SpringbootK8sDemoApplication { - @GetMapping("/message") - public String welcome(){ - return "Congratulation you successfully deployed your application to kubernetes !!"; - } + public static void main(String[] args) { + SpringApplication.run(SpringbootK8sDemoApplication.class, args); + } - public static void main(String[] args) { - SpringApplication.run(SpringbootK8sDemoApplication.class, args); - } + @GetMapping("/message") + public String welcome() { + return "Congratulations! You have successfully deployed your application to Kubernetes!"; + } + @GetMapping("/") + public String home() { + return "Home! Congratulations! You have successfully deployed your application to Kubernetes!"; + } } diff --git a/trivy/README.md b/trivy/README.md new file mode 100644 index 0000000..6df732c --- /dev/null +++ b/trivy/README.md @@ -0,0 +1,12 @@ +Create secret before running these jobs/cronjobs + +``` +kubectl create secret generic slack-webhook-secret --from-literal=slack_webhook-url=https://hooks.slack.com/services/ -n default +kubectl create secret generic slack-security-secret --from-literal=SLACK-TOKEN=xoxb- -n default +``` + +Make sure that your bot token have permission to upload file in your channel. And you must need to integrate your bot in your slack channel. + + +Then apply these jobs: +`kubectl apply -f .yaml` diff --git a/trivy/cronjob-trivy.yaml b/trivy/cronjob-trivy.yaml new file mode 100644 index 0000000..f40d07d --- /dev/null +++ b/trivy/cronjob-trivy.yaml @@ -0,0 +1,72 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: trivy-k8s-scan + namespace: default +spec: + schedule: "37 20 * * *" + jobTemplate: + spec: + template: + spec: + serviceAccountName: trivy-sa + containers: + - name: trivy-k8s + image: aquasec/trivy:latest + command: + - /bin/sh + - -c + - | + apk add --no-cache curl && + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && + chmod +x kubectl && + mv kubectl /usr/local/bin/ && + echo "kubectl installed successfully" && + TOTAL_NS=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | wc -w) && + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $(cat /var/secrets/SLACK-TOKEN)" \ + -H "Content-Type: application/json" \ + -d '{ + "channel": "#security-aws-eks", + "text": "Total number of namespaces are '"$TOTAL_NS"'. Running Trivy in each namespace" + }' && + for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do + DATE_TIME=$(date +%Y-%m-%d_%H-%M-%S) && + FILENAME="/tmp/trivy_${ns}_${DATE_TIME}.txt" && + RESOURCE_COUNT=$(kubectl get all -n $ns --no-headers 2>/dev/null | wc -l) && + if [ "$RESOURCE_COUNT" -eq 0 ]; then + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $(cat /var/secrets/SLACK-TOKEN)" \ + -H "Content-Type: application/json" \ + -d '{ + "channel": "#security-aws-eks", + "text": "There are no resources in namespace '"$ns"' to scan." + }'; + else + echo "Scanning namespace: $ns" && + trivy k8s --include-namespaces $ns --report all --severity CRITICAL --format table > $FILENAME && + if [ -f $FILENAME ]; then + curl -F file=@$FILENAME \ + -F "channels=#security-aws-eks" \ + -F "initial_comment=Trivy scan results for namespace: $ns" \ + -F "token=$(cat /var/secrets/SLACK-TOKEN)" \ + https://slack.com/api/files.upload; + else + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $(cat /var/secrets/SLACK-TOKEN)" \ + -H "Content-Type: application/json" \ + -d '{ + "channel": "#security-aws-eks", + "text": "Trivy scan failed for namespace '"$ns"'." + }'; + fi; + fi; + done; + volumeMounts: + - name: slack-secret + mountPath: /var/secrets + restartPolicy: OnFailure + volumes: + - name: slack-secret + secret: + secretName: slack-security-secret diff --git a/trivy/trivy-rbac.yaml b/trivy/trivy-rbac.yaml new file mode 100644 index 0000000..2d91eb2 --- /dev/null +++ b/trivy/trivy-rbac.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: trivy-cluster-admin-binding +subjects: + - kind: ServiceAccount + name: trivy-sa + namespace: default +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io diff --git a/trivy/trivy-serviceaccount.yaml b/trivy/trivy-serviceaccount.yaml new file mode 100644 index 0000000..fec9940 --- /dev/null +++ b/trivy/trivy-serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: trivy-sa + namespace: default diff --git a/trivy/trivy_job_eks_dev_all_ns.yaml b/trivy/trivy_job_eks_dev_all_ns.yaml new file mode 100644 index 0000000..a0e88f4 --- /dev/null +++ b/trivy/trivy_job_eks_dev_all_ns.yaml @@ -0,0 +1,69 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: trivy-k8s-scan + namespace: default +spec: + template: + spec: + serviceAccountName: trivy-sa + containers: + - name: trivy-k8s + image: aquasec/trivy:latest + command: + - /bin/sh + - -c + - | + apk add --no-cache curl && + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && + chmod +x kubectl && + mv kubectl /usr/local/bin/ && + echo "kubectl installed successfully" && + TOTAL_NS=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | wc -w) && + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $(cat /var/secrets/SLACK-TOKEN)" \ + -H "Content-Type: application/json" \ + -d '{ + "channel": "#my-automation", + "text": "Total number of namespaces are '"$TOTAL_NS"'. Running Trivy in each namespace" + }' && + for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do + DATE_TIME=$(date +%Y-%m-%d_%H-%M-%S) && + FILENAME="/tmp/trivy_${ns}_${DATE_TIME}.txt" && + RESOURCE_COUNT=$(kubectl get all -n $ns --no-headers 2>/dev/null | wc -l) && + if [ "$RESOURCE_COUNT" -eq 0 ]; then + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $(cat /var/secrets/SLACK-TOKEN)" \ + -H "Content-Type: application/json" \ + -d '{ + "channel": "#my-automation", + "text": "There are no resources in namespace '"$ns"' to scan." + }'; + else + echo "Scanning namespace: $ns" && + trivy k8s --include-namespaces $ns --report all --severity CRITICAL --format table > $FILENAME && + if [ -f $FILENAME ]; then + curl -F file=@$FILENAME \ + -F "channels=#my-automation" \ + -F "initial_comment=Trivy scan results for namespace: $ns" \ + -F "token=$(cat /var/secrets/SLACK-TOKEN)" \ + https://slack.com/api/files.upload; + else + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $(cat /var/secrets/SLACK-TOKEN)" \ + -H "Content-Type: application/json" \ + -d '{ + "channel": "#my-automation", + "text": "Trivy scan failed for namespace '"$ns"'." + }'; + fi; + fi; + done; + volumeMounts: + - name: slack-secret + mountPath: /var/secrets + restartPolicy: OnFailure + volumes: + - name: slack-secret + secret: + secretName: slack-security-secret diff --git a/trivy/trivy_job_eks_dev_single_ns.yaml b/trivy/trivy_job_eks_dev_single_ns.yaml new file mode 100644 index 0000000..507be87 --- /dev/null +++ b/trivy/trivy_job_eks_dev_single_ns.yaml @@ -0,0 +1,41 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: trivy-k8s-scan + namespace: default +spec: + # activeDeadlineSeconds: 3600 # Set timeout for the Job to 10 minutes + # backoffLimit: 1 # Optional: Retry once if the Job fails + template: + spec: + serviceAccountName: trivy-sa + containers: + - name: trivy-k8s + image: aquasec/trivy:latest + command: + - /bin/sh + - -c + - | + apk add --no-cache curl && + trivy k8s --include-namespaces default --report all --severity CRITICAL --format table --timeout 60m > /tmp/trivy_k8_scan.txt && + if [ -f /tmp/trivy_k8_scan.txt ]; then + curl -F file=@/tmp/trivy_k8_scan.txt \ + -F "channels=#my-automation" \ + -F "initial_comment=Trivy K8s Scan Results" \ + -F "token=$(cat /var/secrets/SLACK-TOKEN)" \ + https://slack.com/api/files.upload; + else + echo "Trivy scan failed or no results generated."; + fi + volumeMounts: + - name: tmp-dir + mountPath: /tmp + - name: slack-secret + mountPath: /var/secrets + restartPolicy: OnFailure + volumes: + - name: tmp-dir + emptyDir: {} + - name: slack-secret + secret: + secretName: slack-security-secret diff --git a/trivy/trivy_job_minikube.yaml b/trivy/trivy_job_minikube.yaml new file mode 100644 index 0000000..eeb2a9c --- /dev/null +++ b/trivy/trivy_job_minikube.yaml @@ -0,0 +1,45 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: trivy-k8s-scan + namespace: default +spec: + template: + spec: + serviceAccountName: trivy-sa + containers: + - name: trivy-k8s + image: aquasec/trivy:latest + command: ["/bin/sh", "-c"] + args: + - | + apk add --no-cache curl && \ + trivy k8s --report all --severity CRITICAL --format table > /tmp/trivy_k8_$(date +%Y-%m-%d_%H-%M-%S).txt && \ + FILENAME=$(ls /tmp/trivy_k8_*.txt) && \ + SLACK_TOKEN=$(cat /var/secrets/SLACK-TOKEN) && \ + curl -F file=@$FILENAME -F "channels=#my-automation" -F "initial_comment=trivy_k8_result" -F "token=$SLACK_TOKEN" https://slack.com/api/files.upload + env: + - name: KUBECONFIG + value: /root/.kube/config + volumeMounts: + - name: kubeconfig + mountPath: /root/.kube + - name: minikube-certs + mountPath: /root/.minikube + - name: tmp-dir + mountPath: /tmp + - name: slack-secret + mountPath: /var/secrets + restartPolicy: OnFailure + volumes: + - name: kubeconfig + hostPath: + path: /Users/abhijeetsingh/.kube + - name: minikube-certs + hostPath: + path: /Users/abhijeetsingh/.minikube + - name: tmp-dir + emptyDir: {} + - name: slack-secret + secret: + secretName: slack-security-secret