
See why security keeps getting bolted on at the end of the DevOps pipeline, and how DevSecOps fixes that by shifting security left into every stage of CI/CD. You'll get the map for the whole course: how to secure your code before and after commit, your build, your container images, and your Kubernetes deployments, so security moves at the same speed as development.
Understand what DevSecOps really means and why it matters, using defense in depth, the idea of security as layers of an onion. This intro chapter sets up the core concepts before you start building, plus how to think about picking the right security tools. Since these lessons are text-based, keep notes handy so you're ready to apply the ideas to a real pipeline later.
Get your two environments planned before any hands-on work: a Linux dev server for running Docker and testing tools, and a multi-node Kubernetes environment for the CI/CD and security pipeline. You'll learn why a single-node setup like Minikube or Docker Desktop won't cut it here, and why a cloud cluster (Google Cloud with free credits) gives you the resources to learn DevSecOps properly.
Spin up a managed Kubernetes cluster on Google Kubernetes Engine (GKE) from scratch. You'll create a GCP project, enable the Kubernetes API, choose a GKE Standard cluster on the rapid release channel for the latest version, and connect with kubectl through Cloud Shell. By the end you can verify your nodes and pods and know your cluster is ready for the pipeline.
Open up your GKE cluster so applications running in your nodes are reachable from outside. You'll edit the firewall rules in the VPC network, work with network tags, and set a permissive rule for this training setup. You'll also see clearly why this frictionless, allow-all approach is training-only, and where least-privilege firewall rules belong in a real environment.
Build the Ubuntu dev VM you'll use all course long on Google Compute Engine, sized right with a 30GB disk and Docker plus Docker Compose baked in through a startup script. You'll set up SSH key access, attach a firewall via network tags, find the correct login username, and troubleshoot the common SSH connection errors so you can log in and confirm your tools are ready.
Move off Cloud Shell and manage your GKE cluster the long-term way, from your own dev VM. You'll install the Google Cloud SDK, run gcloud init to authorize your account, set your default region and zone, and install the latest kubectl by downloading the binary and verifying its SHA256 checksum. After this you can run kubectl get nodes against your cluster straight from your development machine.
Install Helm, the package manager you'll lean on to deploy Jenkins and other tools onto Kubernetes. You'll find charts on Artifact Hub, add a repository, and run a quick smoke test by installing NGINX with a LoadBalancer service, then cleanly uninstall it. By the end you're comfortable using helm install and helm uninstall to add and remove apps on your cluster.
Preview the simple DevOps pipeline you're about to build with a sample application, running Jenkins as a Kubernetes-native CI platform deployed through a Helm chart. You'll see how Jenkins uses Kubernetes as its agent so every job runs inside a pod, and walk through a Jenkinsfile with build, test, and package stages, the first real step in your DevSecOps journey.
Deploy Jenkins as a Kubernetes-native CI server using Helm. You'll add the Jenkins chart repo, pull the chart to inspect its values.yaml, and write a custom values file that sets a NodePort service and proper CPU and memory requests so Jenkins doesn't crash under the default limits. You'll install it in its own namespace, find the external IP and node port, and retrieve the admin password to log in.
Get Jenkins ready for real pipeline work. You will walk the admin console, install the Blue Ocean and Configuration as Code plugins, and restart Jenkins cleanly when no jobs are running. You will also set your own administrator password and keep the command to retrieve the default one, so a restart never locks you out.
Read a real Jenkinsfile and understand how a pipeline is defined as code. You will tell a declarative pipeline from a scripted one, follow the build, test, and package stages, and see how the pipeline uses Kubernetes as its build agent, running each job inside a pod. You will also learn how the build-agent YAML keeps container config out of the main pipeline.
Run your first pipeline end to end. You will fork the DSO demo repo, connect Jenkins to GitHub with a personal access token, and create a multibranch pipeline in Blue Ocean that scans branches and runs build, test, and package. Since Jenkins runs on Kubernetes, you will watch each job launch as a pod and set a scan trigger so commits kick off runs automatically.
Build a container image from your application by hand before automating it. Using the project's Dockerfile, you will run docker image build, tag the image for your registry, then test it by launching a container with docker run and mapping its port. You will finish knowing how layers are built from Dockerfile instructions and how to reach the running Spring Boot app from outside the VM.
Understand why the Docker daemon is a problem in shared CI. Because dockerd runs as root, building images from Jenkins means mounting the socket or using Docker-in-Docker with privileged access. You will see how Kaniko builds OCI-compliant images inside a container with no privileges, and how its build agent mounts registry credentials as a secret so you build securely with least privilege.
Wire Kaniko into your pipeline as a real build-and-publish stage. You will create a Kubernetes secret with your registry credentials, mount it into the Kaniko container through the build-agent YAML, and add the stage inside the parallel package block. You will push the change, let the scan trigger the run, debug a placeholder tagging error, and confirm the image lands in Docker Hub.
Modern applications lean on frameworks and third-party libraries, and that software supply chain is often the most ignored security risk. This chapter shows you how to scan it: which components you pull in, how vulnerable they are, and whether their licenses create legal trouble for your organization. You will see how to fold supply chain checks into your DevOps pipeline.
Scan your project's dependencies for known vulnerabilities with OWASP Dependency-Check. You will add the Maven plugin to pom.xml and run the checker inside a disposable Docker container that mounts your source code, then read the report of CVEs matched against your libraries. You will practice on both the Spring Boot demo and a second Java app so software composition analysis becomes routine.
Apply software composition analysis to a Python project using PyRaider. You will build a small Docker image from a slim Python base, install PyRaider through pip, and run it against your source in a throwaway container. You will scan requirements.txt for vulnerable packages, read the findings, and see how PyRaider can auto-fix by updating affected packages to safer versions.
Move SCA into your Jenkins pipeline. You will rename the test stage to static analysis, then add a Dependency-Check stage that runs in parallel with the unit tests and publishes its report. Editing the Jenkinsfile in VS Code, you will commit and push, let the scan trigger the run, and fix a stray closing-bracket syntax error, ending with a working static analysis stage.
Add automated open source license scanning to your Jenkins pipeline using Pivotal's LicenseFinder. You'll set up an approved-license whitelist, wire the scanner into the static analysis stage as a container in your build agent, and test it safely on a feature branch so a failed check never breaks anyone else's build. By the end you can catch risky dependency licenses before they reach production.
Install OWASP Dependency-Track with Helm to get a single console for SBOM analysis, policy management, and dependency insights across all your projects. You'll create a temporary GKE node pool to meet its CPU and memory needs, write a custom values.yaml to cut replicas and expose the service, and set up ingress so the CycloneDX reports land in one dashboard. You walk away able to stand up Dependency-Track on Kubernetes.
Work through a real pod-stuck-in-Pending problem with Dependency-Track and learn the debugging approach you can reuse anywhere. You'll read kubectl describe to spot insufficient CPU and memory, pull the Helm chart to find the API server's resource requests, lower them in your custom values, and run helm upgrade to reschedule the pod. You'll also see why node port access is blocked and how to reach the app through the ingress controller and a hosts file entry.
Connect Jenkins to Dependency-Track so builds can push SBOM results automatically. You'll create a scoped API key with project creation, upload, vulnerability, and policy violation permissions, install the OWASP Dependency-Track plugin, and configure the internal Kubernetes service URL (svc.cluster.local) with the key stored as secret text. By the end you can test the connection and enable auto-create projects for hands-off reporting.
Add a stage that generates a CycloneDX software bill of materials with the Maven plugin, then publishes it to Dependency-Track and archives an HTML report as a build artifact. You'll see how the tracker surfaces components, CVEs, severities, and outdated dependencies, and how to write a policy that flags or fails builds on critical findings. You come away with automated SBOM scanning and a single pane of glass across projects.
Tear down Dependency-Track cleanly so it stops burning cloud credits. You'll comment out the publisher line in the Jenkinsfile first so the pipeline keeps passing, uninstall the release with helm uninstall, and delete the temporary GKE node pool to shrink the cluster back to its original size. You'll also merge your static branch into main and delete it both locally and on the remote with git branch -d and git push -d.
Get the case for SAST and why shifting security testing left gives you faster feedback and less rework. You'll see how static analysis checks your code against best practices and known vulnerabilities for your language and framework, catching problems before they reach production. This sets up the rest of the module, where you fold a SAST stage into the DevOps pipeline.
Meet SCAN from slscan.io, an open source tool that rolls SAST, dependency analysis, and license checks into one Docker image with scanners for Python, Java, Node, Ruby, Docker, and Kubernetes. You'll run it against sample apps by mounting your source as a volume, use -t to target a single language, add --build for compiled Java projects, and read the reports it writes for CVEs in dependencies. By the end you can scan multi-service repos locally before wiring the tool into CI.
Move SCAN into Jenkins as a real CI stage. You'll add the container to build-agent.yaml, place a non-parallel SAST stage at the right level in the Jenkinsfile, and test it on a feature branch that the multibranch project picks up on its own. You'll watch the scan fail the build on a critical dependency vulnerability, and spot the puzzle for next time: why the earlier SCA stage found the same JSON-smart and Tomcat issues but did not fail.
Fix the SCA stage that detected vulnerable dependencies but let the build pass. You'll find the missing failBuildOnCVSS setting in the OWASP Dependency-Check plugin config, add the configuration block to the right place in pom.xml with a threshold of 8.0, and push it to your branch. The build now fails on the critical JSON-smart CVE, and you can confirm the 9.1 severity score in the SCAN output.
Fix a critical vulnerability flagged by the SCA stage by upgrading the Spring Boot framework version in pom.xml, since most of the risky code lives in the framework and its transitive dependencies, not your own application. You'll see how bumping the version patches the known CVEs, but also watch a new license problem surface once the dependencies change, which sets up the next lesson.
Clear the license failure the OSS license checker raised after your dependency upgrade by editing dependency_decisions.yaml, the whitelist that says which licenses are approved. You'll add entries for the newer Logback versions and permit the new BSD license, then re-run the Jenkins pipeline to confirm the license tracker and SAST stages pass. Also covers reading the generated SAST, SCA, and SBOM reports, then merging the feature branch back into main and cleaning up branches.
Get oriented for the container image security chapter. You'll learn why scanning the images you ship matters, since even images you use today often carry known vulnerabilities from outdated base layers. This module covers finding and mitigating those risks, plus writing Dockerfiles that follow security best practices and stay small in size.
Lint container images with Dockle, a tool that checks a built image against CIS Docker benchmarks and best practices like running as non-root, adding health checks, and dropping redundant files. You'll compare Dockle against Hadolint and understand why linting the finished image beats only scanning the Dockerfile. Runs Dockle inside a container in CI against images pulled from the registry, including nginx, alpine, and your own app.
Scan container images for known vulnerabilities with Trivy, which inspects every component, library, and base OS layer inside an image, plus your filesystem and configuration. You'll see how an outdated alpine 3.9 is full of CVEs while the latest alpine reports zero, and set the exit code to 1 so critical findings break the Jenkins build. Also covers Trivy config scanning that flags a container running as root.
Understand how a multi-stage Dockerfile works and why it makes images smaller and more secure. You'll see that a single-stage build ships the base OS, build tools like Maven and JDK, source code, and the app all in one image, when only the runtime and the app belong there. A build stage produces the artifact, then a minimal runtime stage copies just that artifact in, leaving build tools and source behind in a discarded intermediate image.
Refactor a single-stage Dockerfile into a multi-stage build, using a slim Maven image with OpenJDK 17 for the build stage and a minimal OpenJDK base for the runtime stage that copies in only the built JAR. You'll build, publish, and re-scan the image with Dockle and Trivy, and watch the size drop by half while the vulnerability count falls to zero. Covers the COPY --from syntax, stage aliases, and the commit-push-pull flow to a remote dev environment.
Stop your container from running as root, which is dangerous because a breakout could hand an attacker root on the host. You'll create an unprivileged user in the Dockerfile with the ARG, ENV, RUN, and USER instructions, and learn why the USER line must sit near the end, just before you launch the app. Verify with ps aux and exec that the process runs as your DevOps user, and see how Kubernetes can enforce non-root as a cluster policy.
Add a HEALTHCHECK to your Dockerfile so orchestrators can tell when a container is up but the app inside is hung, which also helps you spot trouble during attacks like DDoS. You'll set the interval, timeout, start period, and retries, install curl to run an HTTP check against localhost:8080, and inspect the layers with docker image history. When the scan then flags a package cache issue, you'll fix it with apk add --no-cache, showing why continuous DevSecOps feedback catches problems you introduce while fixing others.
Wire both image checks into the Jenkins pipeline, running Dockle linting and Trivy vulnerability scanning as parallel stages right after the image is packaged and pushed to the registry. You'll first point the stage at the old vulnerable v1 image to prove it fails the build on critical findings and produces reports, then switch back to your hardened multi-stage image so the pipeline passes. Finishes by merging to main and cleaning up the branch, leaving automated image analysis in place.
Now that your CI feedback loop produces a secure container image, this module shows you how to take that package and deploy it safely to a Kubernetes environment, and keep doing it. You will also run dynamic scans against the live application, simulating an outside attacker probing your running web app. This is where DAST fits into a secure deployment pipeline.
Set up ArgoCD, the Kubernetes-native GitOps deployment tool, so your deployment pipeline stays separate from CI and your CI server gets only restricted trigger access. You will create the argocd namespace, install ArgoCD, inspect the objects it creates, set an admin password with a bcrypt hash, and patch the argocd-server service to NodePort so you can reach the UI from your browser.
Install and log into the ArgoCD command-line client so you can manage deployments beyond what the web UI allows. You will run argocd login against your server's external IP and NodePort, work through the self-signed certificate warning, and then list clusters, projects, accounts, and applications to confirm the CLI is connected and ready to manage your environment.
Understand the core Kubernetes objects behind any deployment: pods as the unit of deployment, replica sets for scaling and self-healing, deployments for rollout strategies like rolling updates, and services for load balancing and DNS-based service discovery. Using a frontend-and-database example, you will see why each two-tier app needs at least a deployment and a service per component.
Generate working Kubernetes YAML manifests fast using kubectl create with --dry-run=client -o yaml instead of writing them from scratch. You will produce a deployment manifest for your DevSecOps demo image and a NodePort service, save them with tee, then commit and push to a feature branch so ArgoCD can deploy from Git.
Deploy your app to Kubernetes through the ArgoCD UI. You will create a project with source, destination, and cluster resource permissions, then add an application pointing at your repo's deploy branch and path, target the dev namespace, and sync. You will also fix the common HEAD-versus-branch revision error and reach the running app over its NodePort.
Give Jenkins the least privilege it needs to trigger deployments and nothing more. You will create an ArgoCD Jenkins account with API-key access, patch the argocd-rbac-cm config map with an RBAC policy that allows only get and sync, verify it with argocd admin settings rbac can, generate a token, and test a remote argocd app sync.
Wire the Jenkins pipeline to trigger ArgoCD automatically. You will store the Jenkins user's ArgoCD token as a Secret Text credential, add an ARGO_SERVER environment variable and a deploy-to-dev stage that runs the ArgoCD CLI in a container, and wait for the sync to finish. You will also switch the app's target revision to HEAD after merging to main.
Run dynamic application security testing against your live dev environment with OWASP ZAP (Zed Attack Proxy). You will first scan from the ZAP desktop tool to read the alerts it finds, then add a ZAP baseline scan stage to the Jenkins pipeline right after deploy, so every deployment gets pen-tested automatically and low-severity findings surface without blocking the build.
Security reaches past CI/CD into the systems your apps run on, which is where a separate ops or SecOps pipeline comes in. This module covers compliance as code: scanning your systems and Kubernetes against CIS security benchmarks with InSpec, then using Ansible and infrastructure as code to remediate the findings continuously and automatically.
Get compliance-as-code working with InSpec, from install to your first passing scan. You will create a profile with inspec init, read the controls directory, and run inspec exec against real checks like SSH root login and open ports. Since InSpec is built on RSpec, you can also pull ready-made controls from the InSpec docs and ServerSpec and drop them straight into your own tests.
Stop writing every compliance test by hand. You will clone the dev-sec Linux baseline profile, run it with inspec exec, and read the pass, fail, and skipped results against CIS benchmark rules. This gives you a well-tested library of checks for SSH, Docker, Kubernetes, MySQL, and Apache that you can run as-is and later fold into a SecOps pipeline.
Set up Jenkins to reach out to a remote Linux server over SSH using the SSH Steps plugin. You will install the plugin, generate a PEM-format key pair (with the -m PEM and -b 4096 options the plugin needs), add the public key to authorized_keys, and store the private key as an SSH credential in Jenkins. After this you can run remote commands, copy files, and trigger scripts from any pipeline.
Work through a real SSH auth failure in a Jenkins pipeline step by step. You will reproduce the login from your local machine to confirm the key is being rejected, then switch from root to a non-root sudo user for a safer, auditable connection. Along the way you will see why the PEM key format matters and how to add a public key through the GCP VM console.
Wire your InSpec scan into Jenkins so it runs remotely over SSH instead of by hand. You will fork the LFS262 SecOps repo, point the Jenkinsfile at your Linux host, and create a Blue Ocean pipeline that triggers the compliance run through the SSH Steps plugin. You will also see why the build correctly fails while the system is still out of compliance, setting up the Ansible remediation that comes next.
Install Ansible and get it talking to your Linux server, ready to remediate the issues InSpec found. You will set up the inventory, understand how playbooks map roles to hosts, and run a ping check with the Ansible ping module to confirm connectivity. Using the SecOps repo scaffold, you will have the config, inventory, and compliance playbook in place to enforce hardening.
Turn failing compliance checks into a hardened system with one Ansible run. You will install the dev-sec hardening collection from Ansible Galaxy, apply the os_hardening role through your playbook, and watch InSpec failures drop from 45 to 1. This shows how compliance-as-code and infrastructure-as-code work together to detect and fix security issues automatically.
Handle the last failing control when it is a false positive, not a real problem. You will trace the squashfs unused-filesystem check against /etc/mtab, edit the InSpec control to comment out the legitimate case, and confirm a clean scan. You will also add the --no-distinct-exit option so skipped tests do not return a non-zero exit code that breaks a Jenkins build.
Bring the compliance scan and Ansible remediation together into one scheduled SecOps pipeline. You will edit the Jenkinsfile to first enforce hardening with the Ansible playbook, then run InSpec with the right exit-code option, using sudo: true on the SSH step for your non-root user. You will also set a one-day scan interval so Jenkins checks and fixes compliance on its own every day.
Move up from OS-level compliance to securing Kubernetes itself. This chapter maps out cluster-level controls like RBAC and network policies, scanning your cluster against CIS benchmarks, and the main focus: securing your deployments. You will see how small changes to your pod and deployment specs can improve security many times over, even with sensible defaults, before you deploy and analyze workloads at runtime.
Build your own single-node Kubernetes cluster with kubeadm so you have full access to the nodes that a managed service like GKE hides from you. You'll configure the Docker daemon with cgroups, install kubectl, kubelet and kubeadm, apply a pod network, and untaint the master so it runs pods. By the end you can juggle multiple clusters using kubeconfig contexts and switch between them safely, ready to run security scans.
Run CIS Benchmark compliance scans against your cluster using two tools: an InSpec profile for Kubernetes and Aqua Security's kube-bench, launched as a Kubernetes job. You'll read the findings across audit policy, admission controllers, pod security policies and authentication, and learn why running both tools catches what one misses. You'll walk away knowing how to take an iterative approach to hardening instead of chasing 100% compliance on day one.
Use kube-hunter from Aqua Security to pen-test a running cluster from three angles: remote scanning against the control plane, interface scanning across the subnet, and an insider scan run as a pod. You'll see what an external attacker learns about your API version and exposed components, and what a compromised pod with open capabilities could reach. This shows you why restrictive access control and network isolation matter for your Kubernetes security strategy.
Add kubesec as a static scanner for your Kubernetes manifests and wire it into the Jenkins pipeline right before the deploy-to-dev stage. You'll scan a deployment file, read its scored suggestions on resource limits, non-root users and read-only filesystems, and watch the build fail on a starting score of zero. By the end you can gate deployments so even a developer new to security can't ship an unhardened manifest.
Add CPU and memory requests and limits to your deployment's container spec to raise your kubesec score from zero and cut off resource-exhaustion and DoS style attacks. You'll learn how millicores and mebibytes work, the difference between a request used for scheduling and a hard limit, and how to confirm a passing scan by checking the exit code. You'll be able to size resource constraints correctly and verify the result.
Apply a container security context that drops all Linux capabilities, blocks privilege escalation, forces a non-root runAsUser, and mounts the root filesystem read-only. You'll push the kubesec score from four to eight, then hit a real crash when the app can't write to /tmp and learn to balance a static scan against actually running the pod. By the end you can tell pod-level from container-level security context and set each property with confidence.
Fix the crash from a read-only root filesystem by mounting an emptyDir volume at /tmp so the app can still write scratch files while everything else stays immutable. You'll add the volume to the pod spec and the volumeMount to the container spec, set readOnlyRootFilesystem to true, and verify the pod runs clean past a two-minute health check. This closes the outstanding finding and lifts your kubesec score back to eight.
Enable a seccomp profile in your pod's security context and see the real difference it makes: Kubernetes runs a liberal default that blocks only 22 syscalls, while Docker's RuntimeDefault profile blocks around 60. You'll run before-and-after jobs to count blocked syscalls, and hit a case where an outdated scanner rejects the new schema and forces the older annotation. You'll be able to lock down system calls without writing a custom profile.
Set runAsUser above 10000 so a container UID can't collide with a host user and enable a breakout, then create a dedicated ServiceAccount with automountServiceAccountToken set to false to stop the default API token from being mounted. You'll verify the UID by exec-ing into the pod, and see why an outdated kubesec pushes you toward Checkov for actively maintained scanning. You'll harden pod identity and token exposure in a few small, tested changes.
Are you building or deploying applications on Kubernetes? Whether you're a DevOps Engineer, Platform Engineer, or AI/ML Engineer, security can no longer be an afterthought.
This hands-on DevSecOps Bootcamp will help you build secure, production-ready CI/CD pipelines using open-source tools and industry best practices. Learn how to integrate security across the software development lifecycle and ensure your applications are secure by design.
We will walk you through step-by-step labs that combine Jenkins, Kubernetes, ArgoCD, Vault, Trivy, Falco, OWASP ZAP, and other essential tools used in modern DevSecOps workflows.
This course is ideal for teams building cloud-native applications, AI/ML models, or any containerized workload that needs to be deployed securely at scale.
What You Will Learn:
Core DevSecOps principles and the secure software delivery lifecycle
How to build a CI/CD pipeline with Jenkins on Kubernetes
Software Composition Analysis (SCA) using OWASP Dependency-Check, Pyraider, and Dependency-Track
Static and Dynamic Application Security Testing (SAST & DAST) using slscan and OWASP ZAP
Securing container images using Trivy, Dockle, and multi-stage Dockerfiles
Enforcing compliance as code using InSpec and Ansible
Secrets management using HashiCorp Vault and Kubernetes RBAC
Runtime security monitoring using Falco with automated response pipelines
Secure deployment workflows with GitOps using ArgoCD and Kubernetes
Tools and Technologies You Will Use:
Jenkins, Helm, Kubernetes (GKE), ArgoCD
Trivy, Dockle, OWASP ZAP, slscan, Pyraider
Vault, InSpec, Ansible, Falco, Argo Workflows
Docker, Kubernetes RBAC, GitHub, GitOps
Who Should Take This Course:
DevOps and Cloud Engineers who want to add security to their toolbelt
AI/ML Engineers deploying models and services on Kubernetes
Platform Engineers managing modern microservices at scale
Security Engineers transitioning to DevSecOps practices
Developers building containerized applications for production
This is not a theoretical course. You will be working on real-world labs and projects that simulate what modern engineering teams do to secure their software pipelines in production environments.
Whether you're deploying a machine learning model, a microservice, or a SaaS product — this course will help you ensure that your deployments are secure, scalable, and compliant.