Files
blog/content/posts/trivy-gitea-actions-en.md
bojemoi cc434a1f50
All checks were successful
Hugo Build & Deploy / build-deploy (push) Successful in 7s
post: trivy gitea actions (EN)
2026-03-03 19:39:23 +00:00

4.0 KiB

title, date, draft, tags, summary, description, author, ShowToc, ShowReadingTime
title date draft tags summary description author ShowToc ShowReadingTime
Physician, Heal Thyself: Scanning My Own Dockerfiles with Trivy in Gitea Actions 2026-03-03T20:01:00+00:00 false
cybersecurity
devops
docker
gitops
homelab
selfhosted
infosec
opensource
build-in-public
blue-team
soc
devsecops
I integrated Trivy into my Gitea Actions pipeline to automatically scan 30+ Dockerfiles and Docker Swarm stacks on every push. First finding: my own infrastructure had obvious gaps. Hands-on integration of Trivy in Gitea Actions to scan IaC misconfigurations and exposed secrets — no vulnerability database download, running on a 916 MB RAM Lightsail runner. Bojemoi true true

Physician, heal thyself.

I run an offensive homelab — mass nmap scans, Metasploit exploitation, threat intelligence pipelines. But my own Dockerfiles and Docker Swarm stacks had zero automated security scanning. Not a great look for a security lab.

Why Trivy?

Trivy is an open-source security scanner from Aqua Security covering multiple attack surfaces: image vulnerabilities, IaC misconfigurations, exposed secrets.

For my use case, two scanners are particularly relevant and require no vulnerability database download (~300 MB — too heavy for my 916 MB Lightsail runner):

  • trivy config — misconfigurations in Dockerfiles and YAML stacks
  • trivy fs --scanners secret — hardcoded secrets in the codebase

The Gitea Actions Integration

The workflow follows the same pattern as my existing Hugo CI/CD: container image + manual git clone against the internal Gitea URL.

name: Trivy Security Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  trivy:
    runs-on: ubuntu-latest
    container:
      image: aquasec/trivy:latest

    steps:
      - name: Clone repo
        run: |
          git clone --depth 1 --branch "${GITHUB_REF_NAME:-main}" \
            "http://oauth2:${{ secrets.GITEA_TOKEN }}@gitea:3000/${GITHUB_REPOSITORY}.git" /repo

      - name: Scan — misconfigurations
        run: |
          trivy config \
            --severity HIGH,CRITICAL \
            --exit-code 0 \
            /repo
        continue-on-error: true

      - name: Scan — exposed secrets
        run: |
          trivy fs \
            --scanners secret \
            --exit-code 0 \
            /repo
        continue-on-error: true

--exit-code 0 = advisory mode, no pipeline blocking. Inventory first, harden later.

Two Bugs Fixed Along the Way

Bug 1: The Gitea Act runner automatically mounts a volume at /workspace/owner/repo. Cloning to /workspace → "not an empty directory". Fix: clone to /repo instead.

Bug 2: The repo is private. git clone without credentials → "could not read Username". Fix: embed oauth2:${{ secrets.GITEA_TOKEN }} in the URL — the token is automatically injected by Gitea Actions.

What the First Scan Found

Misconfigurations (trivy config)

Running as root (DS-0002 — HIGH)

Multiple images run as root without an explicit non-privileged user: berezina, borodino, narva, karacho... Classic attack surface — if the container is compromised, the attacker gets root directly.

Secrets in build-args / ENV (CRITICAL)

karacho, oblast, and oblast-1 Dockerfiles pass secrets via environment variables or build-args. These secrets end up baked into image layers and visible in Docker history.

apt-get without --no-install-recommends (DS-0029 — HIGH)

ZAP Dockerfiles (oblast/Dockerfile.zaproxy) install packages without --no-install-recommends, unnecessarily inflating image size and attack surface.

Exposed Secrets (trivy fs)

No hardcoded secrets detected. Good news.

What's Next

The workflow is live. Next steps:

  1. Fix critical Dockerfiles (secrets in ENV first)
  2. Add non-root USER declarations where feasible
  3. Flip --exit-code 1 on the secret scanner once false positives are triaged
  4. Extend to trivy image to scan built images (requires more RAM)

Security infrastructure starts with its own hygiene.