Verify and Deliver a Secure Release

CybersecurityBeginner
Practice Now

Introduction

A useful release must be the version that actually passed its checks. A report saying "passed" is insufficient if the package contains a different image or staging runs an older version.

This cumulative DeskLite baseline already applies the earlier source, credential, dependency, container, logging, and gate controls. You will bind the current source commit, actual checked image, generated report, package bytes, and running local stage. The only new operations are the supplied release workflow, tar packaging, and checksum verification.

Review the Working Cumulative Baseline

In this step, you will identify the source version and working baseline you are about to release.

cd /home/labex/project/ticket-release
git status

Expect a clean working tree. git rev-parse HEAD prints the full identifier of the current commit; HEAD names the currently checked-out version:

git rev-parse HEAD

The identifier varies by session. Later it must match the release manifest and live application's version.

cat Dockerfile

Find the explicit business-file copies, offline dependency installation, and USER nobody. The cached Python base and wheels are prepared. Building packages as part of image construction is separate from the final application's non-root runtime identity.

.venv/bin/python -m pip show requests

Expect 2.32.4. The same pinned requirements will be installed into the candidate image.

bash /srv/ticket-release-client/sender.sh

Expect HTTP 200 and the original ticket. This dedicated client already reads its protected replacement Token file; the old Token is revoked. Do not copy either value into release metadata.

Run the Actual Gate and Its Negative Controls

In this step, you will establish that the candidate and gate both work before packaging.

bash gate.sh

The prepared stage wrappers run real secret/history, dependency, and source checks, build the committed application into a Docker image, and start that actual candidate. Functional tests check its source version, Requests 2.32.4, real non-root UID 65534, original ticket, and rejected old/missing/wrong Tokens.

The gate does not publish the final package yet. It records an accepted candidate only after checks and tests agree on the same source and image.

Now exercise trusted negative controls:

course-release gate-test

This helper runs your same gate with a real fixed-rule credential finding and then an actual missing-input scanner error. Expect both to block before build/test, with statuses 1 and 2. It restores the normal control mode afterward and leaves the running stage unchanged.

The checks still cover the course's fixed practice-Token/SQL rules and two selected dependency advisories; they are not a complete production review.

Generate Metadata Bound to the Checked Image

In this step, you will generate release evidence from actual accepted results instead of typing a pass claim.

course-release manifest

The helper exports the actual checked Docker image and creates three files under release: image.tar, checks.json, and manifest.json. These are engineering delivery inputs, not a separate learner investigation report.

cat release/manifest.json

source_commit must equal git rev-parse HEAD. image_id identifies the exact built image rather than a movable tag. checks_sha256 binds the actual redacted check report; image_sha256 binds the exported image bytes. run_id links the record to this real accepted check run.

cat release/checks.json

Expect status 0, individual check statuses, tool versions, and the same source commit. No credential values belong here. The manifest is generated automatically; do not edit fields to claim that a different artifact passed.

Package and Check the Release Bytes

In this step, you will create a real archive and its checksum.

tar groups files into an archive. -c creates one, -f names its output file, and -C release selects the directory whose contents will be read. The final . means all contents of that directory:

tar -cf release.tar -C release .

The archive sits outside the input directory, so it does not include itself. -t lists an existing archive, with -f naming it:

tar -tf release.tar

Expect the three generated files, possibly prefixed with ./, plus a directory entry.

SHA-256 computes a digest from file bytes. sha256sum prints the digest and filename. The familiar > redirection saves that result:

sha256sum release.tar > release.tar.sha256

-c reads the saved checksum record and compares the actual file:

sha256sum -c release.tar.sha256

Expect release.tar: OK. This proves the bytes match that recorded digest. A checksum by itself does not prove who published the file or whether its checks were valid.

Verify the Report-to-Artifact Binding

In this step, you will check more than the package's self-reported checksum.

course-release verify release.tar

The release helper reads the actual archive, rejects unexpected/duplicate members, compares the manifest with its real checked export, checks the original report, and hashes the contained image bytes. It also requires the current source commit to match.

Expect a PASS binding the source, report, image, and full package digest. This registers approval for these exact package bytes in the course's local release workflow. Editing checks.json to say passed or recalculating a checksum for a changed image would not create a matching accepted check run.

This is a teaching workflow with a protected local record. The learner's sudo access means this VM is not a production trust boundary. We are testing the supplied release procedure, not claiming to implement publisher signatures or an external authority.

Deploy That Exact Package and Inspect Runtime

In this step, you will deploy the approved bytes and compare live behavior with the generated record.

course-release deploy release.tar

The helper verifies the package again, loads its contained image, and starts local staging from that exact image ID. Only the local service port and required read-only issuer registry are exposed; private credentials are not baked into the image.

course-release state

The state includes the same source/image identifiers and full package digest. Now inspect the application itself:

curl http://127.0.0.1:8080/health

Expect the same source_commit, Requests 2.32.4, and UID 65534.

bash /srv/ticket-release-client/sender.sh

Expect HTTP 200 and the original ticket. Check unauthenticated access:

curl -i http://127.0.0.1:8080/tickets

Expect 401. The candidate gate also tested old and wrong Tokens. A matching manifest alone would not have proven this real running behavior.

Reject a Byte-Changed Package

In this step, you will test integrity enforcement without changing the running service.

cp copies a file. Keep a byte-for-byte backup of the valid package:

cp release.tar release-original.tar

printf prints this known marker. >> appends to a file, while > would replace it. Append a few bytes only to the exercise's package:

printf 'changed' >> release.tar
sha256sum -c release.tar.sha256

Expect release.tar: FAILED and a nonzero exit. Even if a tar reader ignores trailing data, the full package bytes have changed.

course-release deploy release.tar

Expect RELEASE BLOCKED because those exact bytes were not approved. This nonzero result is intentional. A new user-written checksum would not replace the workflow's source/report/image binding or its approval for the original package.

curl http://127.0.0.1:8080/health

The previously approved version remains running. The next step restores the valid package before final verification.

Restore and Confirm the Final Delivery

In this step, you will leave the original verified package and working stage intact.

cp release-original.tar release.tar
sha256sum -c release.tar.sha256

Expect release.tar: OK again.

course-release verify release.tar
course-release state

The restored package matches the real checked source/image and the already running stage. The course check independently tests the gate's failure controls, rejects changed bytes, checks actual image layers for target credentials, and verifies live positive/rejection behavior.

You have a release archive, checksum, generated manifest and real report, and a matching running version. No manually written investigation is required. Production delivery would additionally need controls such as publisher identity, external authorization, and broader security coverage; this course does not claim those from a local checksum.

Summary

You bound an actual checked source commit, tested non-root image, original generated report, release-package bytes, and live local staging version. Positive and rejection behavior remained correct, while modified bytes were rejected. Checksums support integrity; this local workflow does not claim publisher signatures or external production authorization.