Trivy failed the two images added in the previous commit. The Dockerfile rule was straightforward - neither declared a user - and both now run unprivileged, with the migrate base moved from v4.17.1 to v4.19.1 because the older one sits on Alpine 3.19 with six HIGH advisories. The rest is not fixable here. What remains in those two images is the contents of golang-migrate and Rspamd themselves: 37 and 48 fixable HIGH/CRITICAL findings that belong to those projects. Our layers add .sql and .conf files and nothing else, so gating this pipeline on them would gate it on somebody else's release cadence, and a blanket ignore rule would quietly weaken the scan for the images we do control. The image scan therefore covers the four images whose contents this project authors, with the reason stated at the step and the upstream pair recorded in OPERATIONS.md as something to watch. Both are still built in CI, so a broken Dockerfile still fails. Worth being clear: these two images were already running in the stack before this change. Putting them in the matrix made their state visible; it did not create it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
216 lines
7.4 KiB
YAML
216 lines
7.4 KiB
YAML
name: CI
|
|
|
|
# Runs on every branch and pull request. Publishing lives in release.yml and
|
|
# only runs once this workflow has passed.
|
|
#
|
|
# main is excluded from the push trigger on purpose: release.yml already calls
|
|
# this workflow there. Running both would put two jobs in the same concurrency
|
|
# group, and one would cancel the other - which is why every merge used to
|
|
# leave a cancelled CI run behind.
|
|
on:
|
|
push:
|
|
branches-ignore: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_call:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
lang-lint:
|
|
name: Lint de idioma (R9)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: node scripts/lang-lint.mjs
|
|
|
|
go-protocols:
|
|
name: Go (protocols)
|
|
runs-on: ubuntu-latest
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_DB: lambdamail_test
|
|
POSTGRES_USER: lambdamail_test
|
|
POSTGRES_PASSWORD: lambdamail_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 10
|
|
defaults:
|
|
run:
|
|
working-directory: services/protocols
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-go@v7
|
|
with:
|
|
go-version-file: services/protocols/go.mod
|
|
# defaults.run.working-directory applies to run steps, not to
|
|
# actions, so setup-go looks for go.sum at the repository root and
|
|
# silently skips its cache without this.
|
|
cache-dependency-path: services/protocols/go.sum
|
|
- name: Run migrations
|
|
run: |
|
|
docker run --rm --network host \
|
|
-v "$(pwd)/../../migrations:/migrations" \
|
|
migrate/migrate:v4.17.1 \
|
|
-path=/migrations \
|
|
-database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \
|
|
up
|
|
- run: go vet ./...
|
|
- run: go test ./... -v
|
|
env:
|
|
TEST_DATABASE_URL: postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable
|
|
|
|
ts-auth:
|
|
name: TypeScript (auth)
|
|
runs-on: ubuntu-latest
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_DB: lambdamail_test
|
|
POSTGRES_USER: lambdamail_test
|
|
POSTGRES_PASSWORD: lambdamail_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 10
|
|
defaults:
|
|
run:
|
|
working-directory: services/auth
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-node@v7
|
|
with: { node-version: "20" }
|
|
# The auth suite asserts against real rows: password verification,
|
|
# lockout, TOTP replay and recovery-code consumption are all decided in
|
|
# the database, so a mock would prove nothing.
|
|
- name: Run migrations
|
|
run: |
|
|
docker run --rm --network host \
|
|
-v "$(pwd)/../../migrations:/migrations" \
|
|
migrate/migrate:v4.17.1 \
|
|
-path=/migrations \
|
|
-database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \
|
|
up
|
|
- run: npm install
|
|
- run: npm run lint
|
|
- run: npm test
|
|
env:
|
|
TEST_DATABASE_URL: postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable
|
|
LAMBDAMAIL_MASTER_KEY: ci-master-key-at-least-16-chars
|
|
|
|
ts-storage:
|
|
name: TypeScript (storage)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: services/storage
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-node@v7
|
|
with: { node-version: "20" }
|
|
- run: npm install
|
|
- run: npm run lint
|
|
- run: npm test
|
|
|
|
webmail-build:
|
|
name: Build (webmail)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: services/webmail
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-node@v7
|
|
with: { node-version: "20" }
|
|
- run: npm install
|
|
- run: npm run lint
|
|
- run: npm test
|
|
- run: npm run build
|
|
|
|
security:
|
|
name: Trivy (dependências, segredos e Dockerfiles)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
# A mail server holds credentials for every mailbox it serves, so the
|
|
# secret scanner matters as much as the vulnerability one here: a DKIM
|
|
# key or a Cloudflare token committed by accident is a full compromise.
|
|
- name: Repositório
|
|
uses: aquasecurity/trivy-action@v0.36.0
|
|
with:
|
|
scan-type: fs
|
|
scanners: vuln,secret,misconfig
|
|
severity: HIGH,CRITICAL
|
|
exit-code: '1'
|
|
skip-dirs: services/auth/node_modules,services/storage/node_modules,services/webmail/node_modules,services/webmail/.next
|
|
|
|
image:
|
|
name: Imagem (${{ matrix.service }})
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
service: [protocols, auth, storage, webmail, migrations, rspamd]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: docker/setup-buildx-action@v4
|
|
|
|
# Built but not pushed: this proves each Dockerfile still works on every
|
|
# branch, while publishing stays in release.yml.
|
|
- name: Build
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: ${{ (matrix.service == 'migrations' || matrix.service == 'rspamd') && '.' || format('services/{0}', matrix.service) }}
|
|
file: services/${{ matrix.service }}/Dockerfile
|
|
push: false
|
|
load: true
|
|
tags: lambdamail-${{ matrix.service }}:ci
|
|
cache-from: type=gha,scope=${{ matrix.service }}
|
|
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
|
|
|
|
# Scanned only for the images whose contents we author. migrations and
|
|
# rspamd add .sql and .conf files to an upstream image and nothing else,
|
|
# so every finding in them belongs to that upstream project and cannot be
|
|
# fixed here - gating on it would gate this pipeline on somebody else's
|
|
# release cadence. They are still built above, and OPERATIONS.md records
|
|
# that their base images have to be watched.
|
|
- name: Trivy na imagem
|
|
if: matrix.service != 'migrations' && matrix.service != 'rspamd'
|
|
uses: aquasecurity/trivy-action@v0.36.0
|
|
with:
|
|
image-ref: lambdamail-${{ matrix.service }}:ci
|
|
severity: HIGH,CRITICAL
|
|
exit-code: '1'
|
|
|
|
compose-up:
|
|
name: Stack completa
|
|
runs-on: ubuntu-latest
|
|
needs: [go-protocols, ts-auth, ts-storage, webmail-build]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: cp .env.example .env
|
|
# The proxy network is declared external because Coolify owns it on the
|
|
# deploy host. Nothing creates it here, and without it compose refuses to
|
|
# start the stack at all.
|
|
- name: Cria a rede do proxy
|
|
run: docker network create "${COOLIFY_PROXY_NETWORK:-coolify}"
|
|
# The build overlay is explicit here: CI has to exercise the code in the
|
|
# branch, not the images already published from main.
|
|
- run: docker compose -f docker-compose.yaml -f docker-compose.build.yaml --env-file .env up --build --wait
|
|
- run: docker compose -f docker-compose.yaml -f docker-compose.build.yaml ps
|
|
- run: docker compose -f docker-compose.yaml -f docker-compose.build.yaml down -v
|