Skip to content

Releases

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 01 Nov 22:24

The second public release. It publishes the same images, chart and yj CLI as 0.1.0, now signed, each image with an SBOM and build provenance.

Before upgrading from 0.1.0, check these changes to default behaviour:

  • Package uploads (npm publish, twine upload, mvn deploy) are refused under the default FW_WRITE_POLICY=block (#184).

  • Every package from a PyPI index with no JSON API (pypiserver, a Nexus PyPI proxy) is held for approval under the default FW_UNSCORABLE_POLICY=block (#221).

  • On npm and PyPI, a package the gate was told does not exist, but which the registry serves to the client, is withheld as temporarily unavailable (#204, #221).

  • A known-malware snapshot that removes many entries at once is held until FW_MALWARE_FEED_RELEASE releases it (#216).

  • A request path still holding % after one decode is refused with a 400 (#208).

  • Listeners are plain TCP, no longer Multipath TCP; GODEBUG=multipathtcp=1 restores it.

  • A scanner launched outside the shipped compose file needs SCHEDULER_DOCKER_NETWORK pointed at a network the approval service is not on.

  • Containers: the release cooldown can hold back new images from Docker Hub, Quay and Google's registries (#127). These registries record when each image was pushed, and an image gate now reads that time. Docker Hub's comes from its tag API on hub.docker.com, a new destination (docs/EGRESS.md). Quay's comes from its tag API, and that of gcr.io, *-docker.pkg.dev and registry.k8s.io from the registry's own tag listing. The date inside an image is still never used, because whoever built the image writes it. A time counts only for the exact image the gate serves. On Quay and Docker Hub, a pull by digest is dated only as the platform image of a multi-arch pull by tag. Any other pull by digest is held while the window is on. It is off by default for containers (FW_MIN_RELEASE_AGE_DAYS is 0 on an image gate). Popular tags are re-pushed every few days when their base image changes: on 2026-10-31, 10 of 10 popular official tags were under 14 days old. A held tag is a failed pull, because there is no older version for the client to fall back to. Behind any other registry (ghcr.io, registry:2) the setting is still not enforced, and the gate says so at startup and on /policy.

  • Security: no Multipath TCP on any listener (CVE-2026-98432). Go has opened every Linux listener as Multipath TCP since 1.24 where the kernel supports it, and the one demonstrated remote-root exploit for CVE-2026-98432, a use-after-free in the kernel's IPv4 TCP connection setup, targets MPTCP listeners. Every Yellow Jack binary is now built with multipathtcp=0, so its listeners are plain TCP. This removes the exploit path that was demonstrated, not the kernel bug: patch the host kernel (fixed in 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56 and 7.2.10, and in 7.3; Ubuntu and RHEL had no fixed kernel as of 2026-10-30). An operator who needs MPTCP sets GODEBUG=multipathtcp=1. Containers share the host kernel, so this applies to the images as well.

  • Security: scanners run sandboxed, on a network of their own. A scanner runs Scorecard against a repository anyone can publish, so a parsing bug in it, or in the 18-month-old Go runtime the bundled Scorecard v5.2.1 is built with (#224), would be hostile input running in that container. That container ran with the daemon's default capabilities and a writable root, with no limits, and on the stack's network, where the approval service approves packages without authentication (D77). Now, under either launcher, each scanner container:

    • runs with no capabilities and no-new-privileges;
    • has a read-only root, with /tmp as an anonymous volume deleted along with the container;
    • is limited to 2 GiB of memory (the largest scan measured, torvalds/linux, peaked at 1,153 MiB) and 256 processes;
    • runs on ${YJ_NETWORK:-yellowjack}-scan, which only the scheduler also joins, so it cannot reach approval, postgres or the gates (checked on a running stack by e2e/async_local.sh leg 33).

    With the real scanner image, kubernetes/kubernetes (6.2) and torvalds/linux (2.3) scored the same sandboxed and unsandboxed. A memory-backed /tmp was tried and rejected: when too small, it raised torvalds/linux's score from 2.3 to 2.7, because the file checks read fewer files. Anyone launching scanners outside the shipped compose file must point SCHEDULER_DOCKER_NETWORK at a network approval is not on.

  • Go: the gate serves the checksum database, and Go modules work under TLS interception (paid build) (#218). The go command verifies every module against sum.golang.org, and goes to it directly when its proxy does not offer to serve it, which proxy.golang.org does not. The Go gate now offers it: it answers the go command's check itself and relays the database's signed answers verbatim, so a client needs no route of its own to sum.golang.org. Verification is unchanged, because the go command checks those answers against a key built into the toolchain. sum.golang.org is a new destination for the Go gate (docs/EGRESS.md). With that, TLS interception was measured with a real go get through the paid build's intercept port: the module is evaluated, the checksum lookup stays on the one host, and a refused module fails go get with the gate's reason.

  • Docs: a CA reaches a systemd-managed Docker daemon after systemctl restart docker (#143). The "install the CA, then restart the Docker daemon" instruction in docs/CLIENT_TRUST.md was measured with a daemon run as a bare process. It now holds, measured, for the daemon a Debian or Ubuntu server runs under systemd: still not trusted before the restart, trusted after systemctl restart docker.

  • Security: a package the gate could not look up is no longer recorded as simply "allowed" on PyPI, Maven and Go (#221). The gate's lookup reads a resource the client never asks for: PyPI's JSON API, Maven's maven-metadata.xml, a Go proxy's @latest. Some registries, measured, do not serve it: pypiserver and a Nexus PyPI proxy serve project pages but no JSON API, and a Go proxy served from a module cache directory has no @latest. Before this change, a lookup "not found" on those registries meant the package was served with no evaluation.

    • PyPI: the lookup now asks the project page, the page pip itself asks for, before it says a package is unknown. A project page served to a developer's credential but not to the gate's own is withheld as on npm. A project served by an index with no JSON API cannot be evaluated, so it now takes FW_UNSCORABLE_POLICY. Under the default block, every package from such an index (pypiserver, a Nexus PyPI proxy) is held for approval, where before it was served unevaluated. An operator who accepts that sets FW_UNSCORABLE_POLICY=allow-but-log, and each such serve is recorded as not evaluated.
    • Go: the lookup falls back to the version list when the proxy has no @latest, so those modules are now evaluated.
    • Maven and Go: where the lookup found nothing but the registry served the request, the request is still served, because withholding might never clear, and the record says it was not evaluated.
    • Where to see it: these cases show in the console's audit log as "not evaluated" and are counted on yellowjack_served_unevaluated_total.
  • Docs: client trust on Ubuntu holds for an ordinary user, not only root (#143). docs/CLIENT_TRUST.md's Ubuntu results were measured as root. Every client was measured again as an ordinary account with a cleared environment, with the same results: pip in a developer's own venv and every apt-installed client are reached by a CA installed the ordinary Debian way, while uv, a Node or JDK release tarball unpacked into the user's home, and code using the venv's own certifi are not. The page now says so.

  • Licences: every image and release carries the third-party notices (#213). The binaries statically link the Go standard library and, in the approval service, the PostgreSQL driver and its dependencies, whose MIT and BSD licences require their notices to travel with the binary. THIRD_PARTY_NOTICES.md lists each module, its version and licence, and which binary links it, followed by each licence in full. It is generated from what the binaries actually link, and a test fails when a dependency changes without it. Every image now has /licenses holding it and Yellow Jack's own LICENSE, the GitHub release attaches it, and the optional scanner image also carries OpenSSF Scorecard's licence. The file also states the terms of the data the product uses: the known-malware feed is derived from OpenSSF Malicious Packages (Apache License 2.0), and each feed snapshot now begins with a comment line saying so; the gate queries deps.dev from your network, and Yellow Jack does not redistribute deps.dev data.

  • Release: the images and the chart are signed, and every image carries an SBOM and build provenance (#212). The release workflow signs the three images and the Helm chart with Sigstore keyless signing, each by digest. It attaches to each image an SPDX SBOM, made by syft from the published image and signed as a cosign attestation by the same workflow identity, and GitHub's SLSA build provenance for the run that built it. Both are stored beside the image, so the image index a cluster pulls is unchanged. The release checks every signature and attestation as a user would (cosign verify, cosign verify-attestation, gh attestation verify) before it creates the GitHub release; docs/RELEASING.md gives the commands. 0.1.0 is not signed; verify it by the digests in its release notes.

  • Contributing: the guide says what is true (#211). Until a Contributor License Agreement exists, this repository cannot merge outside code. CONTRIBUTING.md says so, asks for issues instead, and explains that the repository is published from the developme...

Read more

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 20:17

The first public release. It publishes the gate, the control plane and the console as
ghcr.io/flyyellowjack/yellowjack-{firewall,approval,console}:0.1.0, each built for
linux/amd64 and linux/arm64 against Go's certified FIPS 140-3 module; the Helm chart as
oci://ghcr.io/flyyellowjack/charts/yellowjack at 0.1.0, pinned to those images by digest;
and the yj CLI for five platforms, with a signed checksum list.

helm install yellowjack oci://ghcr.io/flyyellowjack/charts/yellowjack --version 0.1.0

On the version number: a 0.1.0 tag of an earlier build existed in this repository from
2026-09-27, but its images were never made public, so nobody outside could install it. This
release replaces it.

What it does

  • A package firewall for five ecosystems: npm, PyPI, Maven, container images (OCI) and Go
    modules.
    Clients point at the gate with one registry line (.npmrc, pip.conf,
    settings.xml, a registry mirror, GOPROXY). Every package they fetch, transitive
    dependencies included, is served from your registry or refused with a reason and a next
    step.
  • Known-malware refusal, first and offline. A feed of published malicious packages
    (build it from OpenSSF Malicious Packages data with scripts/build-malware-feed.py) is
    checked before anything else and needs no network, so it keeps enforcing through an
    upstream outage or in an air gap. Advisories may name every version or one release.
  • A release cooldown. A release younger than FW_MIN_RELEASE_AGE_DAYS (14 days by
    default) is held back, and the client's resolver is steered to the newest release old enough.
    It is on for npm, PyPI, Maven and Go modules; container images carry no trustworthy publish
    date, so it is not enforced for them.
  • Your own allow and block lists, by package (name@*), by one release, or by a version
    range on the allow list, re-read while the gate runs. An allow entry outranks a malware
    advisory by default (FW_ADMIN_OUTRANKS); every such pull is logged and audited.
  • Optional OpenSSF Scorecard scoring of each package's source repository, from deps.dev or
    scanned on your own machines, with packages below a threshold held for a person to review.
    FW_SCORECARD_MODE=off runs the gate on the lists, the malware feed and the cooldown alone.
  • Refusing the bytes, not only the metadata. A lockfile install, a direct blob pull or a
    chosen file name fetches bytes without asking for metadata; those routes are judged too.
    Under FW_BYTE_GATE=enforce, a container layer the gate cannot tie to a release is refused.
  • npm build provenance. The Sigstore build-provenance attestation of each npm release is
    verified inside the gate and logged; FW_ATTESTATION_POLICY=require refuses a release
    without valid provenance.
  • Install-time code. An npm release that runs code at install can be refused or held for
    a person (FW_INSTALL_SCRIPT_POLICY, off by default).
  • People decide what scoring cannot. A quarantined release waits in the console's
    Decisions queue. A person allows exactly the release that would install, or a scope of
    releases, and every ruling is recorded. Blocks can be posted to Slack or Microsoft Teams.
  • Report mode (FW_MODE=report): every verdict computed and logged, nothing refused.
  • The console: what the gate is doing, the approval queue, every refusal explained, the
    policy in force as text, gate health, a per-package lookup, request and download volumes,
    and the audit log. Sign-in with OIDC (tested with Keycloak) or a shared credential, with
    Admin and Read-only roles.
  • A decision record you can export (NDJSON): every verdict with the inputs that produced
    it, including the score, how old the score was, the threshold and a fingerprint of the
    policy in force, and the file and SHA-256 of the bytes each npm and PyPI install received.
  • yj dry-run: ask the gate what an npm install or pip install would be refused
    before it runs.
  • Deployment: a Helm chart with a high-availability layout for the gates, a Docker
    Compose stack, a reference deployment, and tested guides for Keycloak, Nexus Repository and
    Harbor. An optional Prometheus endpoint (FW_METRICS_LISTEN).
  • Opt-in usage data, off by default. Nothing is sent unless you set APPROVAL_USAGE_URL;
    the console shows the exact report before you turn it on.

Security

  • Every image is built against the certified FIPS 140-3 Go Cryptographic Module, with Go
    1.26.10, and reports at startup whether FIPS mode is on.
  • Every base image is pinned by digest. Services run as a non-root user on a read-only root
    filesystem and need no writable host directory.
  • The vulnerability scan in CI runs on the same Go toolchain the images are built with, so a
    new standard-library advisory fails the build instead of passing unnoticed.
  • A credential written into FW_UPSTREAM is masked in the gate's log.
  • See SECURITY.md for how to report a vulnerability, and docs/RELEASING.md for which
    releases get fixes.

Known limitations

Stated here rather than discovered later:

  • The approval service has no authentication of its own. Keep it off untrusted networks.
    The Helm chart's NetworkPolicy (on by default) admits only the gate and console pods, and the
    Compose stack publishes it on loopback only.
  • Scorecard scores the source repository, not the published artifact, so a poisoned
    release of a healthy project is not caught by the score alone. The malware feed, the
    cooldown, provenance and the byte gate are the layers for that.
  • Enforcement is cooperative. A developer who removes the registry line is not gated.
    Closing that takes network egress rules (no direct route from build machines to the public
    registries); TLS interception is not part of this edition.
  • Build-provenance verification covers npm only.
  • Scanning on your own machines (FW_SCORECARD_MODE=local) runs with Docker Compose from
    this repository.
    The scheduler and scanner images are not published, and the Helm chart
    does not run local mode.
  • Editing the lists in the console needs a console image built with git. The published
    console shows the lists; you edit them in your list files.
  • OIDC sign-in is tested with Keycloak. Other providers are not yet measured.

Images

  • ghcr.io/flyyellowjack/yellowjack-firewall:0.1.0@sha256:5f9f651b149c59a37afc1a0cd4831e873c4912bcad1614313a49dd9f16dc1992
  • ghcr.io/flyyellowjack/yellowjack-approval:0.1.0@sha256:04b2b31f29dbd87dce0aa2a39c60168779b43b9a851c7e2e654a81986dee9598
  • ghcr.io/flyyellowjack/yellowjack-console:0.1.0@sha256:3466fe5f7e3628b30f31c4115f60f2c5fef1127d7c29b9217460be21f9ca57ed

Install

helm install yellowjack oci://ghcr.io/flyyellowjack/charts/yellowjack --version 0.1.0

yj

The dry-run CLI for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64 and windows/amd64.
SHA256SUMS lists every binary and is signed with Sigstore keyless signing by this
release workflow. Verify the signature, then the binary:

cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity https://github.com/flyyellowjack/yellowjack/.github/workflows/release.yml@refs/tags/v0.1.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS