Skip to main content

Command Palette

Search for a command to run...

A hands-on guide for DevOps Engineers implementing security at every layer - PART 1

Updated
15 min read
A hands-on guide for DevOps Engineers implementing security at every layer - PART 1
N

👋 Welcome to my Hashnode profile! I'm a passionate technologist with expertise in AWS, DevOps, Kubernetes, Terraform, Datree, and various cloud technologies. Here's a glimpse into what I bring to the table: 🌟 Cloud Aficionado: I thrive in the world of cloud technologies, particularly AWS. From architecting scalable infrastructure to optimizing cost efficiency, I love diving deep into the AWS ecosystem and crafting robust solutions. 🚀 DevOps Champion: As a DevOps enthusiast, I embrace the culture of collaboration and continuous improvement. I specialize in streamlining development workflows, implementing CI/CD pipelines, and automating infrastructure deployment using modern tools like Kubernetes. ⛵ Kubernetes Navigator: Navigating the seas of containerization is my forte. With a solid grasp on Kubernetes, I orchestrate containerized applications, manage deployments, and ensure seamless scalability while maximizing resource utilization. 🏗️ Terraform Magician: Building infrastructure as code is where I excel. With Terraform, I conjure up infrastructure blueprints, define infrastructure-as-code, and provision resources across multiple cloud platforms, ensuring consistent and reproducible deployments. 🌳 Datree Guardian: In my quest for secure and compliant code, I leverage Datree to enforce best practices and prevent misconfigurations. I'm passionate about maintaining code quality, security, and reliability in every project I undertake. 🌐 Cloud Explorer: The ever-evolving cloud landscape fascinates me, and I'm constantly exploring new technologies and trends. From serverless architectures to big data analytics, I'm eager to stay ahead of the curve and help you harness the full potential of the cloud. Whether you need assistance in designing scalable architectures, optimizing your infrastructure, or enhancing your DevOps practices, I'm here to collaborate and share my knowledge. Let's embark on a journey together, where we leverage cutting-edge technologies to build robust and efficient solutions in the cloud! 🚀💻

Security is no longer a gate at the end of the pipeline — it's a responsibility shared across every commit, every config file, and every container image. This guide walks you through practical DevSecOps techniques across three critical domains: Git repository security, Infrastructure as Code (IaC) security, and Container security.

Who is this for? DevOps Engineers, Platform Engineers, and Security Engineers who want actionable, tool-backed strategies to embed security into their daily workflows.

🔐 Part 1: DevSecOps for Git

Protecting your source of truth — from the first commit to production

Your Git repository is the foundation of your entire software supply chain. A single leaked API key, a misconfigured access policy, or an unreviewed merge can cascade into a production breach. Let's lock it down layer by layer.

1. .gitignore — Your First Line of Defense

Before any code reaches your remote repository, .gitignore acts as a filter that prevents sensitive files from ever being tracked. It's simple but often overlooked.

What you should always exclude:

•        .env, .env.local, .env.production — environment files containing secrets

•        .pem, .key, *.p12, id_rsa — private keys and certificates

•        terraform.tfvars, *.tfstate — Terraform state files with infrastructure secrets

•        kubeconfig, ~/.kube/config — Kubernetes credentials

•        credentials.json, secrets.yaml — cloud provider credential files

Example .gitignore entries:

# Environment & Secrets
.env
.env.*
!.env.example

# Terraform
*.tfstate
*.tfstate.backup
*.tfvars
.terraform/

# Keys & Certs
*.pem
*.key
*.p12
id_rsa*

# Kube
kubeconfig
.kube/

2. Native Git Pre-Commit Hooks

Git hooks are scripts that run automatically at key points in the Git workflow. Pre-commit hooks fire before a commit is created — making them the perfect checkpoint to catch issues before they ever reach the remote.

Setting up a basic pre-commit hook:

# Create the hook file
touch .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

# .git/hooks/pre-commit
#!/bin/bash

# Block commits with AWS key patterns
if git diff --cached | grep -qE "AKIA[0-9A-Z]{16}"; then
  echo "ERROR: Possible AWS key detected. Commit blocked."
  exit 1
fi

# Run linting
npm run lint --silent
if [ $? -ne 0 ]; then
  echo "Linting failed. Fix errors before committing."
  exit 1
fi

exit 0

For team-wide hooks, use the pre-commit framework (pre-commit.com) which manages hooks via a .pre-commit-config.yaml file that can be committed alongside your code.

3. Block Commits with Gitleaks

Gitleaks is an open-source SAST tool designed specifically for detecting secrets in Git repositories. It scans for 150+ secret types including AWS keys, GitHub tokens, Stripe keys, and private keys.

Install Gitleaks as a pre-commit hook:

# Install Gitleaks (macOS)
brew install gitleaks

# Install via binary (Linux)
wget https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_linux_x64.tar.gz
tar -xzf gitleaks_linux_x64.tar.gz
sudo mv gitleaks /usr/local/bin/

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

Why Gitleaks? It's fast, highly configurable, and integrates into every layer of your pipeline — local hooks, CI/CD, and repo scanning. Custom rules let you catch org-specific secret patterns too.

4. Gitleaks — Repository & History Scanning


# Scan the current working directory
gitleaks detect --source . --verbose

# Scan full git history
gitleaks detect --source . --log-opts='--all' --verbose

# Scan a specific branch
gitleaks detect --source . --log-opts='origin/main..HEAD'

# Output results to a report
gitleaks detect --source . --report-path gitleaks-report.json --report-format json

# Scan a remote repo directly
gitleaks detect --source https://github.com/your-org/your-repo

Beyond blocking new commits, Gitleaks can scan your entire repository history — including deleted branches — to find secrets that were committed in the past. This is critical for incident response and compliance audits.

If Gitleaks finds a secret in history, the secret must be considered compromised. Rotate it immediately, then use git filter-repo to rewrite history and remove it from all branches.

5. Gitleaks in GitHub Actions

Integrate Gitleaks into your CI/CD pipeline to catch secrets in every pull request — even if a developer's local hooks were bypassed.

# .github/workflows/gitleaks.yml
name: Gitleaks Secret Scan

on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main]

jobs:
  gitleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Full history scan

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}

6. Branch Protection Rules

Branch protection rules in GitHub/GitLab enforce quality and security gates before code can be merged into critical branches like main or production.

Recommended rules for main/production branches:

•        Require pull request before merging (no direct pushes)

•        Require a minimum of 2 approving reviews

•        Dismiss stale reviews when new commits are pushed

•        Require status checks to pass (CI, security scans)

•        Require branches to be up to date before merging

•        Restrict who can push to this branch (only release managers)

•        Do not allow bypassing the above settings — not even for admins

GitHub Rulesets: In 2023, GitHub introduced Repository Rulesets — a more powerful and auditable replacement for branch protection rules. Rulesets support layering, enforcement levels (Active/Evaluate/Disabled), and can be applied org-wide.

7. Role-Based Access Control (RBAC)

Not every developer needs write access to every repository. Implement the principle of least privilege with proper RBAC.

GitHub Permission Levels:

Tool

Purpose

Read

View and clone the repository. Suitable for external stakeholders.

Triage

Manage issues and PRs without write access. Good for project managers.

Write

Push to non-protected branches. Standard for developers.

Maintain

Manage the repo without access to sensitive settings. For team leads.

Admin

Full control. Reserve for 2-3 people maximum.

Use GitHub Teams to manage access at scale. Assign permissions to teams, add users to teams — never assign permissions to individuals directly.

8. Mandatory Code Reviews

Code reviews are not just a quality practice — they're a security control. Requiring human review before merging ensures that logic vulnerabilities, insecure patterns, and secret leaks get a second set of eyes.

Security-focused review checklist:

•        Are secrets hardcoded anywhere? (Even in test files)

•        Are all inputs validated and sanitized?

•        Are authentication and authorization checks correct?

•        Are third-party dependencies newly added? Are they trusted?

•        Are SQL queries using parameterized statements?

•        Are error messages safe to expose to end users?

9. CODEOWNERS

The CODEOWNERS file automatically assigns reviewers to pull requests based on which files were changed. This ensures that security-sensitive files always get reviewed by the right people.

# .github/CODEOWNERS

# Security team must review any auth-related changes
src/auth/           @org/security-team

# DevOps must review infrastructure changes
terraform/          @org/devops-team
kubernetes/         @org/devops-team
.github/workflows/  @org/devops-team

# Secrets management — requires both security and devops
vault/              @org/security-team @org/devops-team

# Default owners for everything else
*                   @org/senior-engineers

10. Dependabot

Dependabot is GitHub's automated dependency update tool. It monitors your dependency files and opens pull requests when vulnerabilities or outdated versions are detected.

Configure Dependabot in .github/dependabot.yml:

# .github/dependabot.yml
version: 2
updates:
  # Node.js
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    open-pull-requests-limit: 10
    reviewers:
      - org/security-team

  # Python
  - package-ecosystem: pip
    directory: /
    schedule:
      interval: weekly

  # GitHub Actions
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

  # Terraform
  - package-ecosystem: terraform
    directory: /terraform
    schedule:
      interval: monthly

Tool

Purpose

Gitleaks

Secret detection in commits, history, and CI/CD pipelines

pre-commit

Framework for managing and sharing multi-language pre-commit hooks

Dependabot

Automated dependency vulnerability detection and updates

Branch Protection / Rulesets

Enforce merge policies and required status checks

CODEOWNERS

Auto-assign expert reviewers for sensitive file paths

🏗️ Part 2: IaC Security

Catching misconfigurations before they reach the cloud — Terraform, Checkov & Vault

Infrastructure as Code is a force multiplier for both developers and attackers. A misconfigured S3 bucket or an overly permissive security group defined in Terraform will be deployed exactly as written — at scale, to every environment. IaC security means scanning your code before it becomes infrastructure.

Core Concepts

IaC Best Practices

•        Use modules to enforce secure-by-default configurations (e.g., an S3 module that always enables encryption and blocks public access)

•        Store Terraform state remotely in S3 with DynamoDB locking — never locally

•        Enable state encryption with a KMS key

•        Use workspaces or separate state files per environment (dev/staging/prod)

•        Pin provider versions in required_providers blocks

•        Separate IAM permissions per environment — never share prod IAM roles

•        Use data sources instead of hardcoding resource IDs

•        Tag every resource with environment, owner, team, and cost-center

IaC Scanning: Catching Misconfigurations Before Deployment

IaC scanners analyze your Terraform, CloudFormation, or Kubernetes manifests against a library of security rules and compliance benchmarks — before you run terraform apply. This is shift-left security at the infrastructure layer.

Common misconfigurations caught by IaC scanners:

•        S3 buckets without server-side encryption or with public access enabled

•        Security groups with 0.0.0.0/0 ingress on port 22 (SSH) or 3389 (RDP)

•        RDS instances without encryption at rest or with public accessibility enabled

•        IAM roles with wildcard (*) actions or resources — overly permissive

•        EKS clusters with public API endpoint access

•        CloudTrail logging disabled in a region

•        KMS keys without key rotation enabled

•        Lambda functions with no reserved concurrency (denial-of-wallet risk)

Secret Management with HashiCorp Vault

Hardcoding secrets in Terraform is one of the most common and dangerous IaC mistakes. Even if stored in a private repo, secrets in .tf files will appear in Terraform state (which is often stored unencrypted). HashiCorp Vault solves this with dynamic secret injection.

How Vault dynamic secrets work:

•        Vault generates short-lived, just-in-time credentials on demand

•        Credentials are automatically revoked after their TTL (e.g., 1 hour)

•        Each application/pipeline gets unique credentials — no sharing

•        Full audit log of every secret access

•        No long-lived static credentials that can be stolen

Tool Spotlight

Checkov — IaC Security Scanner

Checkov is an open-source static analysis tool for IaC. It supports Terraform, CloudFormation, Kubernetes, Helm, ARM templates, and more. It ships with 1000+ built-in policies mapped to CIS Benchmarks, HIPAA, PCI-DSS, SOC2, and other compliance frameworks.


# Install Checkov
pip install checkov

# Scan a Terraform directory
checkov -d ./terraform

# Scan with specific framework
checkov -d ./terraform --framework terraform

# Output as JUnit XML (for CI integration)
checkov -d . --output junitxml > results.xml

# Skip specific checks (use sparingly!)
checkov -d . --skip-check CKV_AWS_18,CKV_AWS_21

# Scan and fail on HIGH/CRITICAL only
checkov -d . --check HIGH

Checkov in GitHub Actions

# .github/workflows/checkov.yml
name: Checkov IaC Scan
on: [pull_request]

jobs:
  checkov:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: terraform/
          framework: terraform
          output_format: sarif
          output_file_path: results.sarif
          soft_fail: false

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif

Tool

Purpose

Checkov

1000+ policies for Terraform, CF, K8s, Helm — CIS, PCI, HIPAA mapped

Gitleaks

Secret detection — catches .tfvars secrets before they're committed

HashiCorp Vault

Dynamic secret injection — no static credentials in config files

🐳 Part 3: Container Security

Securing the ship (Docker) and the harbor (Kubernetes) — Trivy, Hadolint & more

Containers package your application and its dependencies — but they also package vulnerabilities. A container running as root with a bloated base image containing 500 unnecessary packages is an attacker's dream. Container security starts at build time and extends all the way to runtime enforcement in Kubernetes.

Core Concepts

Distroless Images and Multi-Stage Builds

The attack surface of a container is directly proportional to the number of packages installed. Distroless images (from Google) contain only your application and its runtime dependencies — no shell, no package manager, no curl. This means if an attacker gains code execution in your container, they have almost nothing to work with.

Multi-stage build pattern:

# Multi-stage build with Distroless final image

# ── Stage 1: Build ──
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server

# ── Stage 2: Final (Distroless) ──
FROM gcr.io/distroless/static-debian12

# Run as non-root user (built into distroless)
USER nonroot:nonroot

COPY --from=builder /app/server /server

EXPOSE 8080
ENTRYPOINT ["/server"]

# Result: ~10MB image with zero shell or package manager
# vs 800MB+ for a full ubuntu-based image

Why Distroless? Traditional base images (ubuntu, debian, alpine) include hundreds of packages that your app never uses — but attackers can exploit. Distroless reduces your CVE exposure by 90%+ and removes post-exploitation utilities like bash, sh, apt, wget, and curl.

Image Linting: Finding Root Users and Insecure Instructions

Before building and scanning, lint your Dockerfile for security anti-patterns. Common issues include running as root, using ADD instead of COPY, hardcoding environment variables with secrets, and using the :latest tag.

Dockerfile security anti-patterns:

•        USER root or no USER instruction — container runs as root by default

•        ADD remote_url /dir — ADD fetches remote URLs, COPY is safer

•        RUN apt-get update && apt-get install curl wget nano vim — unnecessary tools

•        ENV SECRET_KEY=mysupersecret — secrets in ENV end up in image layers and docker inspect

•        FROM ubuntu:latest — unpinned tags lead to non-reproducible builds

•        COPY . . as root — copies entire project including .git and node_modules

Tool Spotlight

Trivy — Container Image Vulnerability Scanner

Trivy is an all-in-one security scanner from Aqua Security. It scans container images for OS package CVEs, application dependency CVEs, misconfigurations, secrets, and IaC issues. It's the most comprehensive open-source scanner available.


# Install Trivy
brew install aquasecurity/trivy/trivy

# Scan a local Docker image
trivy image nginx:latest

# Scan and show only HIGH and CRITICAL CVEs
trivy image --severity HIGH,CRITICAL nginx:latest

# Scan and fail the pipeline on CRITICAL CVEs
trivy image --exit-code 1 --severity CRITICAL nginx:latest

# Scan a Dockerfile for misconfigs
trivy config ./Dockerfile

# Scan a running container
trivy image --input my-app.tar

# Output as JSON
trivy image --format json --output report.json nginx:latest

Trivy in GitHub Actions


# .github/workflows/trivy.yml
name: Trivy Container Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Run Trivy vulnerability scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: HIGH,CRITICAL
          exit-code: 1

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

Hadolint — Dockerfile Linter

Hadolint is a Dockerfile linter that catches security and best-practice violations before you build an image. It validates against Dockerfile best practices and integrates with shellcheck to lint the RUN commands inside your Dockerfile.

# Install Hadolint
brew install hadolint

# Lint a Dockerfile
hadolint Dockerfile

# Lint with custom rules and ignore list
hadolint --ignore DL3008 --ignore DL3009 Dockerfile

# Output in JSON format
hadolint -f json Dockerfile

# Use in GitHub Actions
# .github/workflows/hadolint.yml
- name: Lint Dockerfile
  uses: hadolint/hadolint-action@v3.1.0
  with:
    dockerfile: Dockerfile
    failure-threshold: warning

Common Hadolint rules:

•        DL3002: Last USER should not be root

•        DL3007: Using latest is prone to errors — pin your version

•        DL3008: Pin versions in apt-get install

•        DL4006: Set SHELL option -o pipefail before RUN with pipe

•        SC2086: Double-quote to prevent globbing and word splitting

Hands-on Lab 1: Scan a Docker Image for CVEs

Step 1 — Pull a Known Vulnerable Image

# Pull an older, vulnerability-rich image for demonstration
docker pull node:14

# Scan it with Trivy
trivy image --severity HIGH,CRITICAL node:14

# You'll see hundreds of CVEs — this demonstrates why pinning
# to a specific patch version AND using minimal base images matters

Step 2 — Compare Against a Hardened Image

# Scan a distroless Node image
docker pull gcr.io/distroless/nodejs20-debian12
trivy image --severity HIGH,CRITICAL gcr.io/distroless/nodejs20-debian12

# Expected: significantly fewer CVEs
# This is the power of minimal base images

🚀 Putting It All Together

Your DevSecOps security pipeline from commit to container

Each layer catches what the previous one missed. A secret that slips past .gitignore is caught by Gitleaks. A misconfigured Terraform file is caught by Checkov. A vulnerable image is caught by Trivy.

The DevSecOps Mindset: Security is not a team or a phase — it's a capability embedded at every stage. Start with the basics (gitignore, branch protection, image scanning) and progressively layer in more controls. Incremental security is far better than security theater.

Security is a practice, not a product.

Start small, automate early, and shift left at every opportunity

More from this blog

NavyaDevops

78 posts

DevOps Engineer with advanced skills in cloud technologies. Proven track record in optimizing development and deployment processes. Dedicated to innovation and scalability in software development.