Security
    Open Source

    Deploy Checkov as a Scheduled IaC Scanner

    A dedicated scanner VPS that pulls your IaC repos on a schedule, runs Checkov, and produces SARIF for Slack, ticketing, or long-term audit storage.

    At a Glance

    ProjectCheckov (Bridgecrew / Prisma Cloud)
    LicenseApache 2.0
    Recommended PlanRamNode Cloud VPS 2 GB+ (4 GB+ for image scans)
    OSUbuntu 24.04 LTS
    Estimated Setup Time30 minutes
    1

    Prerequisites + Service User

    Base packages
    sudo apt update && sudo apt -y full-upgrade
    sudo apt -y install python3 python3-venv python3-pip python3-dev \
      build-essential git curl jq ca-certificates
    Service user
    sudo useradd -r -m -d /opt/checkov -s /bin/bash checkov
    sudo -iu checkov
    2

    Install Checkov

    As checkov user
    python3 -m venv ~/venv
    source ~/venv/bin/activate
    pip install --upgrade pip setuptools
    pip install checkov pre-commit
    checkov --version
    3

    Repository Layout + Config

    Layout
    mkdir -p ~/repos ~/policies ~/reports ~/config ~/bin
    ~/config/checkov.yaml
    quiet: true
    compact: true
    download-external-modules: true
    output: sarif
    output-file-path: console
    external-checks-dir:
      - /opt/checkov/policies
    skip-check:
      - CKV_AWS_18
    soft-fail: false
    ~/config/repos.yaml
    repos:
      - name: platform-terraform
        url: git@github.com:tca/platform-terraform.git
        branch: main
        framework: terraform_plan
        scan_target: tf.json
      - name: k8s-manifests
        url: git@github.com:tca/k8s-manifests.git
        branch: main
        framework: kubernetes
        scan_target: .
      - name: helm-charts
        url: git@github.com:tca/helm-charts.git
        branch: main
        framework: helm
        scan_target: charts/
    4

    Git Access via Deploy Keys

    Generate per-host deploy key
    ssh-keygen -t ed25519 -C "checkov@$(hostname)" -f ~/.ssh/checkov_ed25519 -N ""
    cat ~/.ssh/checkov_ed25519.pub
    # add as read-only deploy key on each repo
    ~/.ssh/config
    Host github.com
      IdentityFile /opt/checkov/.ssh/checkov_ed25519
      IdentitiesOnly yes
      StrictHostKeyChecking accept-new
    5

    The Scan Script

    ~/bin/scan.sh
    #!/usr/bin/env bash
    set -euo pipefail
    CHECKOV_HOME=/opt/checkov
    source "$CHECKOV_HOME/venv/bin/activate"
    
    REPOS_FILE="$CHECKOV_HOME/config/repos.yaml"
    CONFIG_FILE="$CHECKOV_HOME/config/checkov.yaml"
    REPORT_DIR="$CHECKOV_HOME/reports/$(date -u +%Y-%m-%d_%H%M%S)"
    mkdir -p "$REPORT_DIR"
    
    count=$(yq '.repos | length' "$REPOS_FILE")
    for i in $(seq 0 $((count - 1))); do
      name=$(yq ".repos[$i].name" "$REPOS_FILE")
      url=$(yq ".repos[$i].url" "$REPOS_FILE")
      branch=$(yq ".repos[$i].branch" "$REPOS_FILE")
      framework=$(yq ".repos[$i].framework" "$REPOS_FILE")
      scan_target=$(yq ".repos[$i].scan_target" "$REPOS_FILE")
    
      repo_dir="$CHECKOV_HOME/repos/$name"
      if [[ -d "$repo_dir/.git" ]]; then
        git -C "$repo_dir" fetch --quiet origin "$branch"
        git -C "$repo_dir" reset --hard "origin/$branch" --quiet
      else
        git clone --quiet --depth 1 --branch "$branch" "$url" "$repo_dir"
      fi
    
      report_path="$REPORT_DIR/${name}.sarif"
      (cd "$repo_dir" && checkov --config-file "$CONFIG_FILE" \
         --framework "$framework" --directory "$scan_target" \
         --output sarif --output-file-path "$report_path" --soft-fail) || true
    done
    
    python3 "$CHECKOV_HOME/bin/summarize.py" "$REPORT_DIR" > "$REPORT_DIR/summary.json"
    find "$CHECKOV_HOME/reports" -maxdepth 1 -type d -mtime +90 -exec rm -rf {} +
    Install yq + chmod
    sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
    sudo chmod +x /usr/local/bin/yq
    chmod +x ~/bin/scan.sh
    6

    Schedule with systemd Timers

    /etc/systemd/system/checkov-scan.service
    [Unit]
    Description=Run Checkov scans across configured repos
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=oneshot
    User=checkov
    Group=checkov
    WorkingDirectory=/opt/checkov
    ExecStart=/opt/checkov/bin/scan.sh
    Nice=10
    IOSchedulingClass=best-effort
    
    NoNewPrivileges=true
    PrivateTmp=true
    ProtectSystem=strict
    ProtectHome=true
    ReadWritePaths=/opt/checkov
    CPUQuota=80%
    MemoryMax=2G
    /etc/systemd/system/checkov-scan.timer
    [Unit]
    Description=Hourly Checkov scan
    
    [Timer]
    OnCalendar=hourly
    AccuracySec=5m
    Persistent=true
    RandomizedDelaySec=2m
    
    [Install]
    WantedBy=timers.target
    Enable
    sudo systemctl daemon-reload
    sudo systemctl enable --now checkov-scan.timer
    systemctl list-timers checkov-scan.timer
    7

    Routing Findings

    • Slack: jq filter on summary.json piped to an incoming webhook — track finding ID hashes to alert only on new findings
    • Issue tracker: open Jira/Linear ticket per error-level finding with the SARIF doc URL
    • Object storage: tar reports and upload to B2 / R2 for long-term audit
    • Custom policies: drop YAML or Python checks into /opt/checkov/policies

    Container Image Scanning (optional)

    For SCA / image scans, install Docker and add checkov to the docker group. For tighter isolation, run scans inside the official bridgecrew/checkov image with read-only volume mounts.