Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to the NPMplus Security Fork are documented here. The fork u

## Unreleased

- Adopt Anubis 1.28.1 with immutable source/runtime pins and the patched Go networking dependencies. Build its optional WASM and JavaScript fallback assets with pinned Rust tooling, preserve upstream challenge defaults, and verify the embedded baseline/SIMD assets alongside the existing non-root, honeypot and restart contracts. This satisfies the daily scanner's latest-stable compatibility guard and includes upstream's reverse-DNS verification fix.

- Reconcile upstream through October 16: migrate custom locations and their ordered access lists into relational storage, adopt host hard-deletion and mTLS auth-request controls, and update navigation and translations. Keep the fork's session revocation, replay protection, ACL rollback, bounded fetches, setup gate, certificate pipeline, and reviewed dependency versions. Adapt Anubis coverage and ACL recovery to the new relations, with a populated-database upgrade and snapshot regression test. Template version 23 regenerates existing nginx configurations. Database rollback requires restoring the matching pre-upgrade snapshot.

- Refresh nginx to 1.31.7 with matching OpenResty patches and njs 1.0.2. Update the Caddy, CrowdSec, and Anubis security builds to Go 1.26.9 and patched networking dependencies; update CrowdSec's OpenAPI parser and Caddy to 2.11.7. Override vulnerable proxy-addr and source-map-js transitive dependencies. Reassess the pinned pip vendor inventory and scope the remaining packaging-tool exceptions to exact package versions until October 23, preserving runtime Certbot DNS-plugin installation.
Expand Down
30 changes: 18 additions & 12 deletions security-images/anubis/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
# syntax=docker/dockerfile:1.27.1@sha256:4edf897a3ffa55b89f906fc8cc78afdb3f1834cc9c7083565e611a8a7d5fe99e
FROM rust:1.99.0-alpine3.23@sha256:42c2519fdf75d9e34cc61a1aaf34b1a684da9bfd65fe5daefef98d92a801b001 AS rust
RUN rustup target add wasm32-unknown-unknown

FROM golang:1.26.9-alpine3.23@sha256:6750308561953d451210ed1a8086d36c8c8bdcbf672d38d9d0d424b8a7a0216c AS builder

ARG ANUBIS_COMMIT=d39e26cedcc96bea5e4915297c756e7eec74aaf7
ARG ANUBIS_VERSION=v1.27.0-mangyan1.security.1
ARG ANUBIS_COMMIT=da1270fcb927e2feac922711f7a5ce54b6a09ef5
ARG ANUBIS_VERSION=v1.28.1-mangyan1.security.1
ARG X_TEXT_VERSION=v0.42.0
ARG X_NET_VERSION=v0.60.0
ARG GRPC_VERSION=v1.83.2
ARG CEL_GO_VERSION=v0.29.2
ARG GRPC_VERSION=v1.84.0
ARG CEL_GO_VERSION=v0.32.0

RUN apk upgrade --no-cache -a && apk add --no-cache nodejs npm bash brotli zstd git make
COPY --from=rust /usr/local/cargo /usr/local/cargo
COPY --from=rust /usr/local/rustup /usr/local/rustup
ENV CARGO_HOME=/usr/local/cargo RUSTUP_HOME=/usr/local/rustup PATH=/usr/local/cargo/bin:$PATH
RUN apk upgrade --no-cache -a && apk add --no-cache nodejs npm bash brotli zstd git make gcc musl-dev
WORKDIR /src
RUN git init && git remote add origin https://github.com/TecharoHQ/anubis.git && \
git fetch --depth 1 origin "$ANUBIS_COMMIT" && git checkout --detach FETCH_HEAD && \
git fetch --depth 1 origin refs/tags/v1.27.0:refs/tags/v1.27.0 && \
git fetch --depth 1 origin refs/tags/v1.28.1:refs/tags/v1.28.1 && \
test "$(git rev-parse HEAD)" = "$ANUBIS_COMMIT" && \
test "$(git rev-parse 'v1.27.0^{commit}')" = "$ANUBIS_COMMIT"
test "$(git rev-parse 'v1.28.1^{commit}')" = "$ANUBIS_COMMIT"
# Preserve the stable application and generated assets; patch its dependencies.
RUN go get "golang.org/x/text@$X_TEXT_VERSION" "google.golang.org/grpc@$GRPC_VERSION" \
"github.com/google/cel-go@$CEL_GO_VERSION" "golang.org/x/net@$X_NET_VERSION" && go mod verify
"cel.dev/cel-go@$CEL_GO_VERSION" "golang.org/x/net@$X_NET_VERSION" && go mod verify
RUN npm ci --ignore-scripts && npm run assets
RUN SKIP_INTEGRATION=1 go test ./... && go vet ./...
RUN cargo test --workspace --locked && SKIP_INTEGRATION=1 go test ./... && go vet ./...
RUN CGO_ENABLED=0 go build -mod=readonly -trimpath \
-ldflags "-s -w -X github.com/TecharoHQ/anubis.Version=$ANUBIS_VERSION" \
-o /out/anubis ./cmd/anubis

# Keep upstream's non-root user, trust store, data, and runtime entrypoint.
FROM ghcr.io/techarohq/anubis:v1.27.0@sha256:8828275668b7bc675679f100970f9714f731388fbbf66ae94de8aca952e3fc4a
FROM ghcr.io/techarohq/anubis:v1.28.1@sha256:1744c03a918160a28b0d2e8c1656de6585f4071b30b4c3b0d51d02282c9b7c25
COPY --from=builder /out/anubis /ko-app/anubis
COPY --from=builder /src/LICENSE /licenses/anubis/LICENSE
LABEL org.opencontainers.image.source="https://github.com/mangyan1/NPMplus" \
org.opencontainers.image.title="Anubis security build for NPMplus" \
org.opencontainers.image.version="v1.27.0-mangyan1.security.1" \
org.opencontainers.image.version="v1.28.1-mangyan1.security.1" \
org.opencontainers.image.licenses="MIT" \
io.npmplus.upstream.source="https://github.com/TecharoHQ/anubis" \
io.npmplus.upstream.revision="d39e26cedcc96bea5e4915297c756e7eec74aaf7"
io.npmplus.upstream.revision="da1270fcb927e2feac922711f7a5ce54b6a09ef5"
23 changes: 15 additions & 8 deletions security-images/anubis/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# Patched Anubis build

This build retains Techaro's Anubis v1.27.0 application and runtime layout while
rebuilding its executable with Go 1.26.8, gRPC 1.83.2, x/text 0.41.0, and CEL
0.29.2. Source and runtime image are pinned to immutable revisions. The upstream
MIT license is included in the final image.
This build retains Techaro's Anubis v1.28.1 application and runtime layout while
rebuilding its executable with Go 1.26.9, gRPC 1.84.0, x/text 0.42.0, x/net 0.60.0,
and cel.dev/cel-go 0.32.0. Source and runtime image are pinned to immutable
revisions. The upstream MIT license is included in the final image.

The generated JavaScript, CSS, localization, and templates are rebuilt using the
upstream lockfile. Upstream unit tests and Go vet run before the executable is
copied into the final image. The runtime retains user 1000, `/ko-app/anubis`, the
trust store, and upstream data. No challenge, honeypot, or policy rule is removed.
The generated JavaScript, CSS, localization, templates, WASM and JavaScript
fallback assets are rebuilt using the upstream lockfiles and a digest-pinned Rust
1.99.0 build stage. Rust tests, upstream Go unit tests and Go vet run before the
executable is copied into the final image. The runtime retains user 1000,
`/ko-app/anubis`, the trust store, and upstream data. No challenge, honeypot, or
policy rule is removed.

The Security Images workflow tests and scans both AMD64 and ARM64 candidates
before promoting their exact digests to `ghcr.io/mangyan1/npmplus:anubis`.
Expand All @@ -26,3 +28,8 @@ python3 security-images/anubis/smoke.py npmplus-anubis:check
The fork owns updates to these dependency pins and the base image. Reassess the
build whenever upstream releases a newer stable version; do not silently combine
a new policy document with an older binary.

Anubis 1.28.1 checks for a 64-byte signing key at startup, including when
`HS512_SECRET` is supplied. Manual deployments using that variable must provide
exactly 64 bytes. The synthetic smoke fixture uses a 64-byte key; the installer
does not set `HS512_SECRET` and keeps upstream's Ed25519 default.
8 changes: 7 additions & 1 deletion security-images/anubis/smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def main():
"--security-opt", "no-new-privileges", "--tmpfs", "/tmp",
"--mount", f"type=volume,source={volume},target=/data",
"--mount", f"type=bind,source={policy_path.resolve()},target=/policy.yaml,readonly",
"-e", "HS512_SECRET=npmplus-synthetic-smoke-key-only-20261002",
"-e", "HS512_SECRET=" + "smoke-test-only!" * 4,
"-e", "REDIRECT_DOMAINS=example.test",
"-p", "127.0.0.1::8923", "-p", "127.0.0.1::9090", image,
"--target=", "--policy-fname=/policy.yaml")
Expand Down Expand Up @@ -103,6 +103,12 @@ def main():
assert status == 401 and b"anubis" in html.lower(), "challenge page missing"
status, asset = request(base, "/.within.website/x/cmd/anubis/static/js/main.mjs")
assert status == 200 and asset, "embedded challenge JavaScript missing"
for variant in ["baseline", "simd128"]:
for algorithm in ["sha256", "argon2id", "hashx"]:
status, asset = request(base, f"/.within.website/x/cmd/anubis/static/wasm/{variant}/{algorithm}.wasm")
assert status == 200 and asset.startswith(b"\x00asm"), f"missing {variant} {algorithm} WASM asset"
status, asset = request(base, "/.within.website/x/cmd/anubis/static/js/worker/wasm2js.mjs")
assert status == 200 and asset, "embedded JavaScript WASM fallback worker missing"
assert request(metrics, "/metrics")[0] == 200, "Prometheus listener missing"
print("PASS allow/deny/challenge, embedded assets, metrics", flush=True)
assert request(base, "/.within.website/x/cmd/anubis/api/honeypot/smoke/init")[0] == 200
Expand Down
Loading