Skip to content

Weekly rebuild + Dependabot - #99

Merged
carole-lavillonniere merged 12 commits into
mainfrom
cosy-926-automate-cve-remediation
Aug 27, 2026
Merged

Weekly rebuild + Dependabot#99
carole-lavillonniere merged 12 commits into
mainfrom
cosy-926-automate-cve-remediation

Conversation

@carole-lavillonniere

@carole-lavillonniere carole-lavillonniere commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Closes COSY-926

Goal

This PR is automating a weekly rebuild and introducing dependabot to open PRs bumping go library patches.
The goal is to reduce the amount of manual work needed to remediate CVEs.

Changes

  • Pin version of base image, otherwise a pure rebuild without code change would potentially pull more changes than just patch bumps (which is what we want to avoid. we want a rebuild to not require manual testing)
  • Simple rebuild/republish weekly on Tuesdays 08:00 UTC (~22h ahead of the secops Wednesday 06:00 UTC). Published only if the rebuild solved at least one CVE. Without manual testing but with new smoke test.
  • New smoke test against fresh image (starts the container, makes sure the socket exists etc). Run on every PR.
  • Dependabot enabled for go only. It will open PRs that need to be manually tested and human-reviewed. The weekly rebuild will ship them if they fix CVEs. They can also be manually released.
  • edit files with new version. next version computed by taking last version from Makefile and bumping patch. commit/push

Testing

Tested by forking the repo (since the new workflow does not exist on main it cannot be run here) and performing a dry run there.

carole-lavillonniere and others added 3 commits August 20, 2026 16:20
Add a gated weekly security rebuild, scope Dependabot to security updates
for vm/go.mod, and add a smoke test the rebuild must pass before it ships.

Most CVEs on this image come from the Go toolchain baked into the service
binary rather than a dependency manifest, so they are fixed by rebuilding
rather than by bumping anything. The rebuild only republishes when it
actually clears a CVE, so a no-op never surfaces as an update in Docker
Desktop.
Dependency bumps are the only way to clear CVEs a rebuild cannot, but nothing
verified them before a merge. Run make smoke-test on every PR so a bump is
tested on its branch rather than after it lands.
@carole-lavillonniere
carole-lavillonniere force-pushed the cosy-926-automate-cve-remediation branch from 424c8cb to be43c27 Compare August 24, 2026 13:11
Builds are now always --pull --no-cache, in the Makefile rather than at each
call site: a cached base layer reproduces the old image and clears no CVE, and
that property matters for every build, not just the weekly rebuild's.

IMAGE and TAG were already overridable, so the rebuild's separate buildx call
and script invocation collapse into make smoke-test IMAGE=dde-candidate
TAG=scan, leaving scripts/smoke-test.sh with a single caller. build-extension
already guards the binary download, so that step goes too.
no-cache on build-push-docker.yml put every push to main and every tag through
an uncached QEMU arm64 build, on the path you wait on when shipping a security
fix. pull: true invalidates the downstream layers whenever a base digest moves,
which is what the freshness actually depends on, and the weekly rebuild covers
the apk upgrade layer that only no-cache can re-run.

Restores the gha cache: without it the runner has nothing to import, so
dropping no-cache alone would have left the build cold anyway.
Pushing the bump and the tag first meant a failed publish left main claiming
TAG?=x.y.z for an image that was never pushed. The next run reads TAG from the
Makefile and scans that tag, so the scan failed and the job died every week
after, with nothing watching for CVEs in the meantime.

Publishing first fails safe: the working-tree edits go with the runner and main
is untouched. The reverse failure -- published, then the push to main rejected
because main moved -- leaves an unreferenced image and republishes the same
version next week.
Comment thread Makefile
build-extension: ## Build service image to be deployed as a desktop extension
ls binaries/linux/localstack-* > /dev/null 2>&1 || ./downloadBinaries.sh
docker build --tag=$(IMAGE):$(TAG) .
docker build --pull --no-cache --tag=$(IMAGE):$(TAG) .

@carole-lavillonniere carole-lavillonniere Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added --pull to force pull images if there were rebuilt with same tag and --no-cache to force apk upgrade to re-run even when the Alpine digest hasn't moved.

IMAGE was hardcoded in the job env, so testing the publish path meant editing
the workflow to point somewhere safe -- easy to get wrong on a file whose whole
job is pushing to production. Now a fork can override it at dispatch.

The || fallback keeps the scheduled run working: inputs is null on a schedule
trigger, so the default only applies to workflow_dispatch.
@carole-lavillonniere carole-lavillonniere changed the title [COSY-926] Automate CVE remediation: gated weekly rebuild + Dependabot + smoke test [COSY-926] Weekly rebuild + Dependabot Aug 26, 2026
@carole-lavillonniere carole-lavillonniere changed the title [COSY-926] Weekly rebuild + Dependabot Weekly rebuild + Dependabot Aug 26, 2026
@carole-lavillonniere
carole-lavillonniere marked this pull request as ready for review August 26, 2026 07:43

@Pive01 Pive01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM great job!

I left though some comments that we might want to address, mainly into categories as:

  • Why we use hashes instead of stable releases? Is it a standard security practice? Does it really matter for actions and images though that are not dependency of the final docker image?
  • Scripts I imagine do the job but are quite hard to follow, would be great if they could be a bit more human readable (or maybe I'm just bad). But not a blocker

Comment thread .github/workflows/pr.yml

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why the specific hash?


- name: Scan published image (before)
# --ignore-unfixed matches what the secops pipeline files, so an unfixable advisory can't loop here forever.
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why the hash and not a stable version (live v1 or something)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a hash is recommended by GH for security reasons.
Secure actions matter even if they're not in the final artifact because they get access to the job's credentials, they can potentially push any image to our docker repo or any code to our github repo etc.

Pinning an action to a full-length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository
https://docs.github.com/en/actions/reference/security/secure-use#using-third-party-actions

Added the github actions to dependabot though, otherwise it's counterproductive and they end up never being bumped 6eb1c2c

Comment thread .github/workflows/security-rebuild.yml Outdated

- name: Build and smoke-test candidate (amd64, for scanning)
if: steps.before.outputs.proceed == 'true'
run: make smoke-test IMAGE=dde-candidate TAG=scan

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: where does this magic string dde-candidate comes from?

@carole-lavillonniere carole-lavillonniere Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that was confusing. Is it better now with some renaming 8286d0c?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes thank you!

Comment thread scripts/bump-version.sh Outdated
CURRENT=$(sed -n 's/^TAG?=\(.*\)$/\1/p' Makefile)
[ -n "$CURRENT" ] || { echo "could not read TAG from Makefile" >&2; exit 1; }

# Security rebuilds only move the patch component; feature releases set year.month by hand.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: aside that it's a bit hard to follow what is going on, if the patch is in a new month shouldn't we still change the version in order to follow the calendar versioning? Not sure if is what the script is doing

@carole-lavillonniere carole-lavillonniere Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I did not realise it was using calendar versioning. How does 2d63822 look?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for addressing 💪

Comment thread .github/workflows/security-rebuild.yml Outdated
if: steps.delta.outputs.cleared == 'true' && !inputs.dry_run
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this maybe be the localstack bot instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated! d4e6e86

@Pive01 Pive01 Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it works already but you might need to set some credentials somewhere (?)
Let me know if you need me to add some secrets

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new credentials should be needed because this is just metadata, however I changed to use the user's id 88328844+localstack-bot@users.noreply.github.com instead so github can link it better to the actual profile 8227bca

Bumping only the patch meant the date part froze at whatever the last hand-made
feature release set, so a security release in December 2026 would still have
shipped as 2026.8.x. Take year.month from the current date and bump the patch
only within the same month; the first release of a new month restarts at 0.

Also drops the CHANGELOG's claim that the project follows semver, which it has
not for as long as versions have looked like 2026.8.0.
IMAGE meant the published Docker Hub image in the job env, and then line 83
reused the same name for the throwaway local build, which is what made
dde-candidate:scan hard to place. Now PUBLISHED_IMAGE is the thing on Docker Hub
and localstack-docker-desktop:candidate is the local build being evaluated, with
the scan artefacts named after whichever image they describe.
Matches the identity the rest of the org releases under (openapi, localstack-cli,
localstack-sdk-python, localstack-pro all set these four vars). git honours the
GIT_AUTHOR_*/GIT_COMMITTER_* environment directly, so the git config calls go.

Attribution only: the push still uses GITHUB_TOKEN, which the publish step relies
on, since pushes made with it do not trigger workflows. Pushing the tag as the
bot with a PAT would fire build-push-docker.yml and publish the tag twice.
@carole-lavillonniere
carole-lavillonniere force-pushed the cosy-926-automate-cve-remediation branch from b81d2e8 to 9cc0691 Compare August 26, 2026 11:36
A commit SHA is immutable, which is the point, but it also means nothing tells
you a newer release exists. Dependabot updates the pin and the trailing version
comment together, so the pins stay current without being unpinned.

Unlike the gomod entry this one wants version updates, so no limit of 0; grouped
into a single PR so the pins arrive as one review rather than one per action.
@carole-lavillonniere
carole-lavillonniere force-pushed the cosy-926-automate-cve-remediation branch from 9cc0691 to 6eb1c2c Compare August 26, 2026 11:43

@Pive01 Pive01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for your effort 🚀

The plain localstack-bot@users.noreply.github.com form copied from openapi's
release.yml can render as an unlinked author: the account (id 88328844) was
created in 2021, and GitHub links post-2017 accounts by the ID-prefixed
address. localstack-cli's homebrew.yml already commits with that form.

Cosmetic — attribution only, no change to how the push authenticates.
@carole-lavillonniere
carole-lavillonniere merged commit b2fc334 into main Aug 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants