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
4 changes: 2 additions & 2 deletions .github/workflows/smoke-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ jobs:
# failure: Docker preserved an empty resolver file in the existing
# host-networked container. The downloaded current script must repair
# all three before entering the transactional update.
sudo sed -i 's/NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=6/NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=3/' /usr/local/bin/npmplus-safe-update
sudo sed -i 's/NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=7/NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=3/' /usr/local/bin/npmplus-safe-update
sudo chmod 0644 /opt/npmplus/setup-npmplus.sh
for _ in $(seq 1 30); do
health="$(sudo docker inspect --format '{{.State.Health.Status}}' npmplus)"
Expand Down Expand Up @@ -434,7 +434,7 @@ jobs:
fi
rm -f "$update_log"
sudo test -x /opt/npmplus/setup-npmplus.sh
sudo grep -qx '# NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=6' /usr/local/bin/npmplus-safe-update
sudo grep -qx '# NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=7' /usr/local/bin/npmplus-safe-update
sudo test -f /var/backups/npmplus-last-good/override.yaml
test "$(stat -c '%u:%g' /opt/anubis-data)" = '1000:1000'
sudo test -f /opt/anubis-data/anubis/honeypot.addrs
Expand Down
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

- Installer v1.67 / safe-update wrapper v7 refuse to deploy an older setup script: a candidate whose `SCRIPT_VERSION` predates the installed script is rejected while the stack is still healthy, since an older script pins older image channels over a database already migrated by newer code and the app crash-loops at startup until the health check reverts. `NPMPLUS_FORCE_DOWNGRADE=true` overrides the refusal. `pin_image` also no longer treats a failed `docker pull` as success — a transient registry error previously re-pinned the local image's stale digest and reported a healthy update on yesterday's image; the pin now fails so the wrapper reverts instead.

- Installer v1.66 adds an admin-only **Enable CRS observation** button under CrowdSec → WAF for managed stacks with AppSec enabled. A private, single-action host helper validates the shared observation policy, snapshots configuration, waits for CrowdSec health, and rolls back failures without exposing Docker or arbitrary host commands to the application. Ordinary installer updates install the helper; existing deployments require that update once.

- Added installer v1.65 `--update --enable-crs` for transactional CRS observation beside existing high-confidence AppSec blocking. The WAF dashboard separates installer configuration, observed CRS matches, and in-band warnings; match counts stay distinct from blocked requests. Hub-managed policies and ordinary-update choices remain intact. Safe-update wrapper v6 also snapshots and restores the bouncer configuration.
Expand Down
23 changes: 19 additions & 4 deletions setup-npmplus.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ set -euo pipefail

# bump this on every meaningful change - the script compares it against the
# copy on github at startup and tells the operator when theirs is stale
SCRIPT_VERSION="1.66"
SCRIPT_VERSION="1.67"

DATA_DIR="/opt/npmplus"
CROWDSEC_DIR="/opt/crowdsec"
Expand Down Expand Up @@ -2134,7 +2134,10 @@ finalize_admin_bootstrap() {

pin_image() { # mutable channel -> immutable local platform repo digest
local channel="$1" repository short_repository docker_repository candidate digest=""
docker pull "$channel" >/dev/null
docker pull "$channel" >/dev/null || {
echo "could not pull $channel" >&2
return 1
}
repository=${channel%:*}
short_repository=${repository#docker.io/}
docker_repository=${short_repository#library/}
Expand Down Expand Up @@ -3000,7 +3003,7 @@ if [[ -s "$DATA_DIR/setup-npmplus.sh" ]]; then
chmod 700 "$DATA_DIR/setup-npmplus.sh"
write_root_file /usr/local/bin/npmplus-safe-update 700 <<'EOF'
#!/bin/bash
# NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=6
# NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=7
# monthly npmplus update with a safety net: snapshots the running state,
# runs the update, health-checks it, and reverts to the snapshot on failure
set -euo pipefail
Expand Down Expand Up @@ -3240,6 +3243,18 @@ trap - EXIT

log "running update"
if [[ -n "$CANDIDATE" && "$CANDIDATE" != "$SETUP" ]]; then
# An older candidate pins older image channels over a database already
# migrated by newer code; the app then crash-loops at startup until the
# health check reverts. Refuse it while the stack is still healthy.
candidate_version=$(sed -n 's/^SCRIPT_VERSION="\([^"]*\)".*/\1/p' "$CANDIDATE" 2>/dev/null | head -1) || true
installed_version=$(sed -n 's/^SCRIPT_VERSION="\([^"]*\)".*/\1/p' "$SETUP" 2>/dev/null | head -1) || true
if [[ -n "$candidate_version" && -n "$installed_version" && "$candidate_version" != "$installed_version" ]] &&
[[ "$(printf '%s\n%s\n' "$candidate_version" "$installed_version" | sort -V | tail -1)" == "$installed_version" ]] &&
[[ "${NPMPLUS_FORCE_DOWNGRADE:-false}" != "true" ]]; then
log "candidate script v$candidate_version is older than the installed v$installed_version - refusing the downgrade"
echo "the running stack is untouched; set NPMPLUS_FORCE_DOWNGRADE=true to override" >&2
exit 1
fi
cp -a "$CANDIDATE" "$SETUP" || revert
chmod 700 "$SETUP" || revert
fi
Expand Down Expand Up @@ -3850,7 +3865,7 @@ if [[ "${1:-}" == "--update" ]]; then
# overwrite themselves in place during a nested tooling refresh. Replace
# them before delegation; this also repairs a missing execute bit.
if [[ ! -x /usr/local/bin/npmplus-safe-update ]] || \
! grep -qx '# NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=6' /usr/local/bin/npmplus-safe-update; then
! grep -qx '# NPMPLUS_SAFE_UPDATE_WRAPPER_VERSION=7' /usr/local/bin/npmplus-safe-update; then
install_host_tooling
fi
# Repair the v1.6 root-owned Anubis bind mount before the wrapper checks
Expand Down
Loading