A DevOps lab that grows from interactive menus to a Jenkins delivery pipeline hardening, CVE gates, IaC and runtime threat detection, built one commit per step. מעבדת DevOps שצומחת מתפריטים אינטראקטיביים לפייפליין אספקה ב-Jenkins הקשחה, שערי CVE, תשתית-כקוד וזיהוי איומים בזמן ריצה, בנויה קומיט-אחד-לכל-שלב.
|
A DevOps lab: two interactive Labs, six build/deploy Jobs (Docker) wired as a Jenkins delivery pipeline, plus 8 standalone Security & Cloud jobs (numbered 7–18, with gaps) — SSH/firewall hardening, CVE + file-integrity scanning, encrypted backups, async health monitoring, and AWS/GCP provisioning & IAM audit. It ships a 2026 open-source toolchain: |
מעבדת DevOps: שני Labs אינטראקטיביים, שישה Jobs של build/deploy (Docker) המחוברים כפייפליין אספקה ב-Jenkins, ובנוסף שמונה Jobs עצמאיים של אבטחה וענן (ממוספרים 7–18, עם פערים) הקשחת SSH/פיירוול, סריקת CVE ושלמות-קבצים, גיבויים מוצפנים, ניטור בריאות אסינכרוני, והקמת AWS/GCP + ביקורת IAM. הפרויקט מגיע עם טולצ'יין קוד-פתוח 2026: |
🐧 Scripts target a Linux Jenkins agent (
useradd, a package manager,docker). Install scripts auto-detectapt-get/dnf/yum— Debian/Ubuntu and RHEL/CentOS.
No cloud, no root, no risk make demo runs the non-destructive dry-run jobs, make test
runs the full suite:
$ make test
40 passed — coverage 93% (jobs/*.py)
✓ tests/bats 16 tests (jobs 7/8/10/13/15)
$ THRESHOLD=0 ./jobs/job8_trivy_docker_scan.sh # CVE gate
Found 2 HIGH,CRITICAL vulnerabilities (threshold: 0).
✗ Vulnerability count exceeds threshold blocking deployment. (exit 1)
$ DRY_RUN=1 ./jobs/job10_iptables_lockdown.sh # firewall, rendered not applied
:INPUT DROP [0:0]
-A INPUT -p tcp --dport 8351 -m conntrack --ctstate NEW -j ACCEPT
✓ Done: dry run, no rules applied.git clone https://github.com/www8351/build-deploy-test.git
cd build-deploy-test
chmod +x labs/*.sh jobs/*.sh
# Labs (interactive menus)
./labs/lab1_menu.sh
python3 labs/lab2_menu.py
# Build jobs (parameters via env vars)
USER_NAME=tester1 ./jobs/job1_users_tar.sh
HOST_PORT=8351 ./jobs/job2_docker_nginx.sh
./jobs/job3_containers_log.sh
# Security & cloud jobs (7-18) — most destructive or need creds; read the header first
IMAGE=nginx THRESHOLD=0 ./jobs/job8_trivy_docker_scan.sh
sudo DRY_RUN=true APP_PORT=8351 ./jobs/job10_iptables_lockdown.sh
DRY_RUN=true ./jobs/job15_aws_ec2_provision.sh # needs aws-cli v2| Job | Script | What it does | Params |
|---|---|---|---|
| 1 | job1_users_tar.sh |
create user, 5 files, tar → zipfile.tgz |
USER_NAME |
| 2 | job2_docker_nginx.sh |
pull nginx, run on :8351, curl it |
HOST_PORT |
| 3 | job3_containers_log.sh |
write each container's ID/Image/Name/IP → Log.txt |
— |
| 4 | job4_pull_remote.sh |
ssh to remote host, docker pull |
REMOTE_HOST, REMOTE_USER, IMAGE |
| 5 | job5_deploy3_ips.sh |
deploy 3 containers, print IPs | COUNT, IMAGE |
| 6 | job6_send_mail.sh |
send an "all good" mail | RECIPIENT, SUBJECT, BODY |
Chain: Job 1 → 2 → 3 → 4 → 5 → 6. Jobs 4 & 6 auto-skip when REMOTE_HOST / RECIPIENT
are empty, so the pipeline runs green on a single agent.
🛡️ Security & Cloud jobs (8 shipped, numbered 7–18) hardening, scanning, provisioning
Each is standalone (not part of the 1→6 flow) and self-contained: set -Eeuo pipefail /
custom exceptions, idempotent where it edits state, dry-run or rollback on destructive ones,
structured (JSON/Markdown) output, and a non-zero exit that gates a pipeline.
| Job | Script | What it does | Exit gate |
|---|---|---|---|
| 7 | job7_ssh_hardening.sh |
idempotent sshd_config hardening, backup, sshd -t validate, trap auto-restores on fail |
fails if sshd -t rejects |
| 8 | job8_trivy_docker_scan.sh |
Trivy CVE scan → JSON (+ optional SBOM), counts HIGH/CRITICAL | exit ≠0 if findings > THRESHOLD |
| 9 | job9_fim_baseline.py |
file-integrity monitor: SHA-256 → SQLite baseline; --verify diffs → syslog |
0 clean / 1 drift / 2 error |
| 10 | job10_iptables_lockdown.sh |
default-DROP + allow lo/established/SSH/APP, atomic iptables-restore |
applies only when DRY_RUN=false |
| 11 | job11_api_health_monitor.py |
async probes (aiohttp), Pydantic v2 gate, p50/p95/p99 | exit 1 if any endpoint unhealthy |
| 13 | job13_pg_dump_encrypt.sh |
pg_dump | zstd | gpg streamed (no plaintext on disk) |
fails on any pipe error |
| 15 | job15_aws_ec2_provision.sh |
aws-cli v2: least-open SG, IMDSv2 required, base64 user-data | fails fast on missing creds |
| 18 | job18_gcp_iam_least_priv.py |
GCP IAM audit: flag owner/editor bindings, Markdown report |
exit 1 on violations / 2 on error |
⚠️ Jobs 7 & 10 change system state. Job 10 defaults to dry-run; job 7 validates and auto-rolls-back. Cloud jobs 15/18 need real credentials. Gaps (12/14/16/17) are backlog items the numbering is intentional, not missing work.
🧰 2026 toolchain & IaC — uv · OpenTofu · Cilium · Falco · SBOM
Each was validated locally through its official container (ghcr.io/opentofu/opentofu,
falcosecurity/falco, aquasec/trivy).
uv— the only Python workflow (nopip/venv). Deps pinned in a committeduv.lock(68 packages); CI runsuv sync --all-extras --lockeda drifted lock fails the build.- OpenTofu (
infra/) — declarative twin ofjob15: hardened EC2, IMDSv2 required, least-open SG, SSM-resolved AL2023. State on a self-hostedpgbackend (no S3 / no TF Cloud). - Cilium (
k8s/cilium/) — eBPF twin ofjob10: default-denywebnamespace, re-open only TCP 8351 + DNS, plus a clusterwide Host Firewall (audit-mode stageable). - Falco (
falco/) runtime rules (sensitive-file reads, container shells,/etcwrites) on themodern_ebpfCO-RE driver; all external outputs disabled (data sovereignty). - SBOM (Job 8) CycloneDX by default / SPDX on request, generated before the CVE gate so it exists as evidence even for a failing image.
uv sync --all-extras --locked && uv run pytest tests/python
tofu -chdir=infra init -backend=false && tofu -chdir=infra validate
falco --validate falco/falco_rules.local.yaml
SBOM_FORMAT=cyclonedx ./jobs/job8_trivy_docker_scan.sh # -> sbom.cdx.json🧪 Testing a real CI gate, not decoration
make test # pytest (Python jobs) + bats (shell jobs)
make test-py # Python only, with the coverage gate (≥80%, currently 93%)
make test-bats # shell only
make lint # ruff + shellcheck + bash -n- Python (pytest) digit-prefixed job modules loaded via
importlibinconftest.py. job9 FIM (hash + drift 0/1/raise), job11 health monitor (loopback aiohttp test server, no real network), job18 IAM (offline--bindings-filee2e). - Shell (bats-core) dry-run / idempotency / arg-guard only; a
setup()stub dir shadowssudo/sshd/iptables-restore/awsand "must never run" stubs fail loudly.
🔧 Jenkins setup pipeline-as-code (recommended)
- Install Jenkins + a JDK, unlock, install suggested plugins.
- Let the
jenkinsuser run job commands (visudoNOPASSWD for lab scope;usermod -aG docker jenkins). - New Item → Pipeline → Pipeline script from SCM → Git
https://github.com/www8351/build-deploy-test.git, branchmain, script pathJenkinsfile. - Build with Parameters every job param exposed with a sane default (
USER_NAME=tester1,HOST_PORT=8351,IMAGE=nginx,COUNT=3, …). Security jobs 7–18 wired as an opt-inSecurity & Compliancestage group, all default-skipped; destructive ones guarded default-false.
A Preflight stage wipes stale artifacts and fails fast if the agent lacks docker or passwordless
sudo. Log.txt / zipfile.tgz archived every run. A freestyle-jobs alternative (4b) exists too.
🔒 Security & notes
A security repo should scan itself CI has a dedicated security job: gitleaks (secret
detection + weekly full-history sweep), ruff flake8-bandit (Python SAST), Dependabot
(pip + github-actions). Policy & threat model: SECURITY.md honest about
what the lab does not protect against.
.gitignore keeps the repo clean (every *.md ignored except this README; local lifecycle files
stay on disk). .gitattributes forces LF on *.sh/*.py so the Linux agent never chokes on CRLF.